update
This commit is contained in:
parent
98f50488ca
commit
1879810c9e
56
astro/cmd.go
56
astro/cmd.go
@ -7,6 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var isFormat bool
|
var isFormat bool
|
||||||
|
var jieqi string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Cmd.PersistentFlags().Float64Var(&lon, "lon", -273, "经度,WGS84坐标系")
|
Cmd.PersistentFlags().Float64Var(&lon, "lon", -273, "经度,WGS84坐标系")
|
||||||
@ -168,6 +169,61 @@ var CmdStar = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var CmdJieqi = &cobra.Command{
|
||||||
|
Use: "jq",
|
||||||
|
Short: "节气计算",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
fmt.Println("请输入年份或节气")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
year := args[0]
|
||||||
|
if year == "" {
|
||||||
|
year = time.Now().Format("2006")
|
||||||
|
}
|
||||||
|
year = year[:4]
|
||||||
|
fmt.Println("年份: ", year)
|
||||||
|
/*
|
||||||
|
var jqname = map[string]int{
|
||||||
|
"春分": 0,
|
||||||
|
"清明": 15,
|
||||||
|
"谷雨": 30,
|
||||||
|
"立夏": 45,
|
||||||
|
"小满": 60,
|
||||||
|
"芒种": 75,
|
||||||
|
"夏至": 90,
|
||||||
|
"小暑": 105,
|
||||||
|
"大暑": 120,
|
||||||
|
"立秋": 135,
|
||||||
|
"处暑": 150,
|
||||||
|
"白露": 165,
|
||||||
|
"秋分": 180,
|
||||||
|
"寒露": 195,
|
||||||
|
"霜降": 210,
|
||||||
|
"立冬": 225,
|
||||||
|
"小雪": 240,
|
||||||
|
"大雪": 255,
|
||||||
|
"冬至": 270,
|
||||||
|
"小寒": 285,
|
||||||
|
"大寒": 300,
|
||||||
|
"立春": 315,
|
||||||
|
"雨水": 330,
|
||||||
|
"惊蛰": 345,
|
||||||
|
}
|
||||||
|
if jieqi != "" {
|
||||||
|
if v, ok := jqname[jieqi]; !ok {
|
||||||
|
fmt.Println("节气名错误")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
fmt.Println("节气名: ", jieqi)
|
||||||
|
fmt.Println("时间: ", calendar.JieQi(year, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func CliLoadLonLatHeight() bool {
|
func CliLoadLonLatHeight() bool {
|
||||||
if city != "" {
|
if city != "" {
|
||||||
if !GetFromCity(city) {
|
if !GetFromCity(city) {
|
||||||
|
1
astro/jieqi.go
Normal file
1
astro/jieqi.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package astro
|
28
cert/cert.go
28
cert/cert.go
@ -105,9 +105,9 @@ func ParseCert(data []byte, pwd string) {
|
|||||||
switch n := priv.(type) {
|
switch n := priv.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
starlog.Green("这是一个RSA私钥\n")
|
starlog.Green("这是一个RSA私钥\n")
|
||||||
starlog.Green("私钥位数:%d\n", n.Size())
|
starlog.Green("公钥位数:%d\n", n.Size())
|
||||||
starlog.Green("私钥长度:%d\n", n.N.BitLen())
|
starlog.Green("公钥长度:%d\n", n.N.BitLen())
|
||||||
starlog.Green("私钥指数:%d\n", n.E)
|
starlog.Green("公钥指数:%d\n", n.E)
|
||||||
starlog.Green("私钥系数:%d\n", n.D)
|
starlog.Green("私钥系数:%d\n", n.D)
|
||||||
starlog.Green("私钥质数p:%d\n", n.Primes[0])
|
starlog.Green("私钥质数p:%d\n", n.Primes[0])
|
||||||
starlog.Green("私钥质数q:%d\n", n.Primes[1])
|
starlog.Green("私钥质数q:%d\n", n.Primes[1])
|
||||||
@ -116,8 +116,8 @@ func ParseCert(data []byte, pwd string) {
|
|||||||
starlog.Green("私钥系数qInv:%d\n", n.Precomputed.Qinv)
|
starlog.Green("私钥系数qInv:%d\n", n.Precomputed.Qinv)
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
starlog.Green("这是一个ECDSA私钥\n")
|
starlog.Green("这是一个ECDSA私钥\n")
|
||||||
starlog.Green("私钥位数:%d\n", n.Curve.Params().BitSize)
|
starlog.Green("公钥位数:%d\n", n.Curve.Params().BitSize)
|
||||||
starlog.Green("私钥曲线:%s\n", n.Curve.Params().Name)
|
starlog.Green("公钥曲线:%s\n", n.Curve.Params().Name)
|
||||||
starlog.Green("私钥长度:%d\n", n.Params().BitSize)
|
starlog.Green("私钥长度:%d\n", n.Params().BitSize)
|
||||||
starlog.Green("私钥系数:%d\n", n.D)
|
starlog.Green("私钥系数:%d\n", n.D)
|
||||||
starlog.Green("私钥公钥X:%d\n", n.PublicKey.X)
|
starlog.Green("私钥公钥X:%d\n", n.PublicKey.X)
|
||||||
@ -237,9 +237,9 @@ func ParseCert(data []byte, pwd string) {
|
|||||||
switch n := priv.(type) {
|
switch n := priv.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
starlog.Green("这是一个RSA私钥\n")
|
starlog.Green("这是一个RSA私钥\n")
|
||||||
starlog.Green("私钥位数:%d\n", n.Size())
|
starlog.Green("公钥位数:%d\n", n.Size())
|
||||||
starlog.Green("私钥长度:%d\n", n.N.BitLen())
|
starlog.Green("公钥长度:%d\n", n.N.BitLen())
|
||||||
starlog.Green("私钥指数:%d\n", n.E)
|
starlog.Green("公钥指数:%d\n", n.E)
|
||||||
starlog.Green("私钥系数:%d\n", n.D)
|
starlog.Green("私钥系数:%d\n", n.D)
|
||||||
starlog.Green("私钥质数p:%d\n", n.Primes[0])
|
starlog.Green("私钥质数p:%d\n", n.Primes[0])
|
||||||
starlog.Green("私钥质数q:%d\n", n.Primes[1])
|
starlog.Green("私钥质数q:%d\n", n.Primes[1])
|
||||||
@ -378,9 +378,9 @@ func ParseCert(data []byte, pwd string) {
|
|||||||
switch n := priv.(type) {
|
switch n := priv.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
starlog.Green("这是一个RSA私钥\n")
|
starlog.Green("这是一个RSA私钥\n")
|
||||||
starlog.Green("私钥位数:%d\n", n.Size())
|
starlog.Green("公钥位数:%d\n", n.Size())
|
||||||
starlog.Green("私钥长度:%d\n", n.N.BitLen())
|
starlog.Green("公钥长度:%d\n", n.N.BitLen())
|
||||||
starlog.Green("私钥指数:%d\n", n.E)
|
starlog.Green("公钥指数:%d\n", n.E)
|
||||||
starlog.Green("私钥系数:%d\n", n.D)
|
starlog.Green("私钥系数:%d\n", n.D)
|
||||||
starlog.Green("私钥质数p:%d\n", n.Primes[0])
|
starlog.Green("私钥质数p:%d\n", n.Primes[0])
|
||||||
starlog.Green("私钥质数q:%d\n", n.Primes[1])
|
starlog.Green("私钥质数q:%d\n", n.Primes[1])
|
||||||
@ -640,7 +640,7 @@ func GetCert(data []byte, pwd string) ([]any, []x509.Certificate, error) {
|
|||||||
switch n := priv.(type) {
|
switch n := priv.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
starlog.Green("这是一个RSA私钥\n")
|
starlog.Green("这是一个RSA私钥\n")
|
||||||
starlog.Green("私钥位数:%d\n", n.Size())
|
starlog.Green("公钥位数:%d\n", n.Size())
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
starlog.Green("这是一个ECDSA私钥\n")
|
starlog.Green("这是一个ECDSA私钥\n")
|
||||||
starlog.Green("私钥位数:%d\n", n.Curve.Params().BitSize)
|
starlog.Green("私钥位数:%d\n", n.Curve.Params().BitSize)
|
||||||
@ -760,8 +760,8 @@ func GetCert(data []byte, pwd string) ([]any, []x509.Certificate, error) {
|
|||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
common = append(common, n)
|
common = append(common, n)
|
||||||
starlog.Green("这是一个RSA私钥\n")
|
starlog.Green("这是一个RSA私钥\n")
|
||||||
starlog.Green("私钥位数:%d\n", n.Size())
|
starlog.Green("公钥位数:%d\n", n.Size())
|
||||||
starlog.Green("私钥长度:%d\n", n.N.BitLen())
|
starlog.Green("公钥长度:%d\n", n.N.BitLen())
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
common = append(common, n)
|
common = append(common, n)
|
||||||
starlog.Green("这是一个ECDSA私钥\n")
|
starlog.Green("这是一个ECDSA私钥\n")
|
||||||
|
4
go.mod
4
go.mod
@ -20,6 +20,7 @@ require (
|
|||||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2
|
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2
|
||||||
github.com/emersion/go-smtp v0.20.2
|
github.com/emersion/go-smtp v0.20.2
|
||||||
github.com/florianl/go-nfqueue/v2 v2.0.0
|
github.com/florianl/go-nfqueue/v2 v2.0.0
|
||||||
|
github.com/gdamore/tcell/v2 v2.7.1
|
||||||
github.com/go-acme/lego/v4 v4.16.1
|
github.com/go-acme/lego/v4 v4.16.1
|
||||||
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9
|
github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9
|
||||||
github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42
|
github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42
|
||||||
@ -29,6 +30,7 @@ require (
|
|||||||
github.com/inconshreveable/mousetrap v1.1.0
|
github.com/inconshreveable/mousetrap v1.1.0
|
||||||
github.com/miekg/dns v1.1.58
|
github.com/miekg/dns v1.1.58
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
|
github.com/rivo/tview v0.0.0-20241227133733-17b7edb88c57
|
||||||
github.com/shirou/gopsutil/v4 v4.24.10
|
github.com/shirou/gopsutil/v4 v4.24.10
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/things-go/go-socks5 v0.0.5
|
github.com/things-go/go-socks5 v0.0.5
|
||||||
@ -56,6 +58,7 @@ require (
|
|||||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||||
github.com/ebitengine/purego v0.8.1 // indirect
|
github.com/ebitengine/purego v0.8.1 // indirect
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
||||||
|
github.com/gdamore/encoding v1.0.0 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
@ -71,6 +74,7 @@ require (
|
|||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
|
10
go.sum
10
go.sum
@ -72,6 +72,10 @@ github.com/emersion/go-smtp v0.20.2/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVR
|
|||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/florianl/go-nfqueue/v2 v2.0.0 h1:NTCxS9b0GSbHkWv1a7oOvZn679fsyDkaSkRvOYpQ9Oo=
|
github.com/florianl/go-nfqueue/v2 v2.0.0 h1:NTCxS9b0GSbHkWv1a7oOvZn679fsyDkaSkRvOYpQ9Oo=
|
||||||
github.com/florianl/go-nfqueue/v2 v2.0.0/go.mod h1:M2tBLIj62QpwqjwV0qfcjqGOqP3qiTuXr2uSRBXH9Qk=
|
github.com/florianl/go-nfqueue/v2 v2.0.0/go.mod h1:M2tBLIj62QpwqjwV0qfcjqGOqP3qiTuXr2uSRBXH9Qk=
|
||||||
|
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||||
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
|
github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc=
|
||||||
|
github.com/gdamore/tcell/v2 v2.7.1/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
|
||||||
github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ=
|
github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ=
|
||||||
github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE=
|
github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||||
@ -131,10 +135,13 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||||
@ -159,7 +166,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
|
github.com/rivo/tview v0.0.0-20241227133733-17b7edb88c57 h1:LmsF7Fk5jyEDhJk0fYIqdWNuTxSyid2W42A0L2YWjGE=
|
||||||
|
github.com/rivo/tview v0.0.0-20241227133733-17b7edb88c57/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||||
|
32
hash/hash.go
32
hash/hash.go
@ -29,7 +29,7 @@ var Cmd = &cobra.Command{
|
|||||||
var cumethod, method []string
|
var cumethod, method []string
|
||||||
var result = make(map[string]string)
|
var result = make(map[string]string)
|
||||||
var err error
|
var err error
|
||||||
cumethod = []string{"md5", "crc32", "sha512", "sha384", "sha256", "sha224", "sha1", "md4", "ripemd160", "hmacmd5", "hmacmd4", "hmacsha1", "hmacsha224", "hmacsha256", "hmacsha384", "hmacsha512"}
|
cumethod = []string{"md5", "crc32a", "crc32", "sha512", "sha384", "sha256", "sha224", "sha1", "md4", "ripemd160", "hmacmd5", "hmacmd4", "hmacsha1", "hmacsha224", "hmacsha256", "hmacsha384", "hmacsha512"}
|
||||||
if ok, _ := this.Flags().GetBool("all"); ok {
|
if ok, _ := this.Flags().GetBool("all"); ok {
|
||||||
method = cumethod
|
method = cumethod
|
||||||
} else {
|
} else {
|
||||||
@ -79,6 +79,7 @@ func init() {
|
|||||||
Cmd.Flags().BoolP("file", "f", false, "对指定文件进行校验")
|
Cmd.Flags().BoolP("file", "f", false, "对指定文件进行校验")
|
||||||
Cmd.Flags().BoolP("md5", "m", false, "进行MD5校验(默认)")
|
Cmd.Flags().BoolP("md5", "m", false, "进行MD5校验(默认)")
|
||||||
Cmd.Flags().BoolP("crc32", "c", false, "进行CRC32校验")
|
Cmd.Flags().BoolP("crc32", "c", false, "进行CRC32校验")
|
||||||
|
Cmd.Flags().Bool("crc32a", false, "进行CRC32A校验")
|
||||||
Cmd.Flags().BoolP("sha512", "s", false, "进行SHA512校验")
|
Cmd.Flags().BoolP("sha512", "s", false, "进行SHA512校验")
|
||||||
Cmd.Flags().Bool("sha384", false, "进行SHA384校验")
|
Cmd.Flags().Bool("sha384", false, "进行SHA384校验")
|
||||||
Cmd.Flags().Bool("sha256", false, "进行SHA256校验")
|
Cmd.Flags().Bool("sha256", false, "进行SHA256校验")
|
||||||
@ -98,10 +99,9 @@ func init() {
|
|||||||
func FileSumAll(filepath string, key string, method []string, shell func(float64)) (map[string]string, error) {
|
func FileSumAll(filepath string, key string, method []string, shell func(float64)) (map[string]string, error) {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
methods := make(map[string]hash.Hash)
|
methods := make(map[string]hash.Hash)
|
||||||
var iscrc bool
|
|
||||||
|
|
||||||
if len(method) == 0 {
|
if len(method) == 0 {
|
||||||
method = []string{"sha512", "sha256", "sha384", "sha224", "sha1", "crc32", "md5", "md4", "ripemd160", "hmacmd5", "hmacmd4", "hmacsha1", "hmacsha224", "hmacsha256", "hmacsha384", "hmacsha512"}
|
method = []string{"sha512", "sha256", "sha384", "sha224", "sha1", "crc32a", "crc32", "md5", "md4", "ripemd160", "hmacmd5", "hmacmd4", "hmacsha1", "hmacsha224", "hmacsha256", "hmacsha384", "hmacsha512"}
|
||||||
}
|
}
|
||||||
fp, err := os.Open(filepath)
|
fp, err := os.Open(filepath)
|
||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
@ -115,6 +115,7 @@ func FileSumAll(filepath string, key string, method []string, shell func(float64
|
|||||||
sum256 := sha256.New()
|
sum256 := sha256.New()
|
||||||
sum224 := sha256.New224()
|
sum224 := sha256.New224()
|
||||||
sum1 := sha1.New()
|
sum1 := sha1.New()
|
||||||
|
crc32a := crc32.New(crc32.MakeTable(0x04C11DB7))
|
||||||
crcsum := crc32.NewIEEE()
|
crcsum := crc32.NewIEEE()
|
||||||
md5sum := md5.New()
|
md5sum := md5.New()
|
||||||
md4sum := md4.New()
|
md4sum := md4.New()
|
||||||
@ -130,8 +131,10 @@ func FileSumAll(filepath string, key string, method []string, shell func(float64
|
|||||||
switch v {
|
switch v {
|
||||||
case "md5":
|
case "md5":
|
||||||
methods["md5"] = md5sum
|
methods["md5"] = md5sum
|
||||||
|
case "crc32a":
|
||||||
|
methods["crc32a"] = crc32a
|
||||||
case "crc32":
|
case "crc32":
|
||||||
iscrc = true
|
methods["crc32"] = crcsum
|
||||||
case "sha1":
|
case "sha1":
|
||||||
methods["sha1"] = sum1
|
methods["sha1"] = sum1
|
||||||
case "sha224":
|
case "sha224":
|
||||||
@ -179,31 +182,25 @@ func FileSumAll(filepath string, key string, method []string, shell func(float64
|
|||||||
for _, v := range methods {
|
for _, v := range methods {
|
||||||
v.Write(buf[0:n])
|
v.Write(buf[0:n])
|
||||||
}
|
}
|
||||||
if iscrc {
|
|
||||||
crcsum.Write(buf[0:n])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for k, v := range methods {
|
for k, v := range methods {
|
||||||
result[k] = hex.EncodeToString(v.Sum(nil))
|
result[k] = hex.EncodeToString(v.Sum(nil))
|
||||||
}
|
}
|
||||||
if iscrc {
|
|
||||||
result["crc32"] = hex.EncodeToString(crcsum.Sum(nil))
|
|
||||||
}
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SumAll(data []byte, key string, method []string) (map[string][]byte, error) {
|
func SumAll(data []byte, key string, method []string) (map[string][]byte, error) {
|
||||||
result := make(map[string][]byte)
|
result := make(map[string][]byte)
|
||||||
methods := make(map[string]hash.Hash)
|
methods := make(map[string]hash.Hash)
|
||||||
var iscrc bool
|
|
||||||
if len(method) == 0 {
|
if len(method) == 0 {
|
||||||
method = []string{"sha512", "sha256", "sha384", "sha224", "sha1", "crc32", "md5", "md4", "ripemd160", "hmacmd5", "hmacmd4", "hmacsha1", "hmacsha224", "hmacsha256", "hmacsha384", "hmacsha512"}
|
method = []string{"sha512", "sha256", "sha384", "sha224", "sha1", "crc32a", "crc32", "md5", "md4", "ripemd160", "hmacmd5", "hmacmd4", "hmacsha1", "hmacsha224", "hmacsha256", "hmacsha384", "hmacsha512"}
|
||||||
}
|
}
|
||||||
sum512 := sha512.New()
|
sum512 := sha512.New()
|
||||||
sum384 := sha512.New384()
|
sum384 := sha512.New384()
|
||||||
sum256 := sha256.New()
|
sum256 := sha256.New()
|
||||||
sum224 := sha256.New224()
|
sum224 := sha256.New224()
|
||||||
sum1 := sha1.New()
|
sum1 := sha1.New()
|
||||||
|
crc32a := crc32.New(crc32.MakeTable(0x04C11DB7))
|
||||||
crcsum := crc32.NewIEEE()
|
crcsum := crc32.NewIEEE()
|
||||||
md5sum := md5.New()
|
md5sum := md5.New()
|
||||||
md4sum := md4.New()
|
md4sum := md4.New()
|
||||||
@ -219,8 +216,10 @@ func SumAll(data []byte, key string, method []string) (map[string][]byte, error)
|
|||||||
switch v {
|
switch v {
|
||||||
case "md5":
|
case "md5":
|
||||||
methods["md5"] = md5sum
|
methods["md5"] = md5sum
|
||||||
|
case "crc32a":
|
||||||
|
methods["crc32a"] = crc32a
|
||||||
case "crc32":
|
case "crc32":
|
||||||
iscrc = true
|
methods["crc32"] = crcsum
|
||||||
case "sha1":
|
case "sha1":
|
||||||
methods["sha1"] = sum1
|
methods["sha1"] = sum1
|
||||||
case "sha224":
|
case "sha224":
|
||||||
@ -254,15 +253,8 @@ func SumAll(data []byte, key string, method []string) (map[string][]byte, error)
|
|||||||
for _, v := range methods {
|
for _, v := range methods {
|
||||||
v.Write(data)
|
v.Write(data)
|
||||||
}
|
}
|
||||||
if iscrc {
|
|
||||||
crcsum.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range methods {
|
for k, v := range methods {
|
||||||
result[k] = v.Sum(nil)
|
result[k] = v.Sum(nil)
|
||||||
}
|
}
|
||||||
if iscrc {
|
|
||||||
result["crc32"] = crcsum.Sum(nil)
|
|
||||||
}
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"b612.me/starcrypto"
|
"b612.me/starcrypto"
|
||||||
"b612.me/staros"
|
"b612.me/staros"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/ecdh"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
@ -67,7 +68,7 @@ func (k *KeyGen) Gen() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "ecdsa", "ecdh":
|
case "ecdsa":
|
||||||
var cr elliptic.Curve
|
var cr elliptic.Curve
|
||||||
switch k.Bits {
|
switch k.Bits {
|
||||||
case 224:
|
case 224:
|
||||||
@ -90,6 +91,33 @@ func (k *KeyGen) Gen() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case "x25519":
|
||||||
|
priv, err = ecdh.X25519().GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pub = priv.(*ecdh.PrivateKey).Public()
|
||||||
|
case "ecdh":
|
||||||
|
switch k.Bits {
|
||||||
|
case 256:
|
||||||
|
priv, err = ecdh.P256().GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pub = priv.(*ecdh.PrivateKey).Public()
|
||||||
|
case 384:
|
||||||
|
priv, err = ecdh.P384().GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pub = priv.(*ecdh.PrivateKey).Public()
|
||||||
|
case 521:
|
||||||
|
priv, err = ecdh.P521().GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pub = priv.(*ecdh.PrivateKey).Public()
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return errors.New("invalid key type,only support rsa,ecdsa")
|
return errors.New("invalid key type,only support rsa,ecdsa")
|
||||||
}
|
}
|
||||||
|
3
main.go
3
main.go
@ -24,6 +24,7 @@ import (
|
|||||||
"b612.me/apps/b612/merge"
|
"b612.me/apps/b612/merge"
|
||||||
"b612.me/apps/b612/mget"
|
"b612.me/apps/b612/mget"
|
||||||
"b612.me/apps/b612/net"
|
"b612.me/apps/b612/net"
|
||||||
|
"b612.me/apps/b612/nmon"
|
||||||
"b612.me/apps/b612/rmt"
|
"b612.me/apps/b612/rmt"
|
||||||
"b612.me/apps/b612/search"
|
"b612.me/apps/b612/search"
|
||||||
"b612.me/apps/b612/smtpclient"
|
"b612.me/apps/b612/smtpclient"
|
||||||
@ -55,7 +56,7 @@ func init() {
|
|||||||
base64.Cmd, base85.Cmd, base91.Cmd, attach.Cmd, detach.Cmd, df.Cmd, dfinder.Cmd,
|
base64.Cmd, base85.Cmd, base91.Cmd, attach.Cmd, detach.Cmd, df.Cmd, dfinder.Cmd,
|
||||||
ftp.Cmd, generate.Cmd, hash.Cmd, image.Cmd, merge.Cmd, search.Cmd, split.Cmd, vic.Cmd,
|
ftp.Cmd, generate.Cmd, hash.Cmd, image.Cmd, merge.Cmd, search.Cmd, split.Cmd, vic.Cmd,
|
||||||
calc.Cmd, net.Cmd, rmt.Cmds, rmt.Cmdc, keygen.Cmd, dns.Cmd, whois.Cmd, socks5.Cmd, httproxy.Cmd, smtpserver.Cmd, smtpclient.Cmd,
|
calc.Cmd, net.Cmd, rmt.Cmds, rmt.Cmdc, keygen.Cmd, dns.Cmd, whois.Cmd, socks5.Cmd, httproxy.Cmd, smtpserver.Cmd, smtpclient.Cmd,
|
||||||
cert.Cmd, aes.Cmd, tls.Cmd, mget.Cmd, tcpkill.Cmd, tcm.Cmd, astro.CmdCal, astro.Cmd)
|
cert.Cmd, aes.Cmd, tls.Cmd, mget.Cmd, tcpkill.Cmd, tcm.Cmd, astro.CmdCal, astro.Cmd, nmon.Cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -259,7 +259,10 @@ func (w *Mget) Run() error {
|
|||||||
}
|
}
|
||||||
if !w.NoWriteRedo {
|
if !w.NoWriteRedo {
|
||||||
if len(r) == 0 {
|
if len(r) == 0 {
|
||||||
return os.Remove(w.Tareget + ".bgrd")
|
if staros.Exists(w.Tareget + ".bgrd") {
|
||||||
|
return os.Remove(w.Tareget + ".bgrd")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return w.Redo.Save()
|
return w.Redo.Save()
|
||||||
}
|
}
|
||||||
|
40
net/cmd.go
40
net/cmd.go
@ -8,6 +8,8 @@ import (
|
|||||||
"b612.me/starlog"
|
"b612.me/starlog"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,6 +38,7 @@ var natt NatThroughs
|
|||||||
|
|
||||||
var scanip ScanIP
|
var scanip ScanIP
|
||||||
var scanport ScanPort
|
var scanport ScanPort
|
||||||
|
var monitorip Monitor
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
CmdNatPClient.Flags().StringVarP(&natc.ServiceTarget, "target", "t", "", "forward server target address")
|
CmdNatPClient.Flags().StringVarP(&natc.ServiceTarget, "target", "t", "", "forward server target address")
|
||||||
@ -60,7 +63,7 @@ func init() {
|
|||||||
CmdNetTrace.Flags().IntVarP(&timeout, "timeout", "t", 800, "超时时间,单位毫秒")
|
CmdNetTrace.Flags().IntVarP(&timeout, "timeout", "t", 800, "超时时间,单位毫秒")
|
||||||
CmdNetTrace.Flags().IntVarP(&maxHop, "max-hop", "m", 32, "最大跳数")
|
CmdNetTrace.Flags().IntVarP(&maxHop, "max-hop", "m", 32, "最大跳数")
|
||||||
CmdNetTrace.Flags().BoolVarP(&disableIpInfo, "disable-ipinfo", "D", false, "禁用ip信息查询")
|
CmdNetTrace.Flags().BoolVarP(&disableIpInfo, "disable-ipinfo", "D", false, "禁用ip信息查询")
|
||||||
CmdNetTrace.Flags().StringVarP(&bindAddr, "bind", "b", "0.0.0.0", "绑定地址")
|
CmdNetTrace.Flags().StringVarP(&bindAddr, "bind", "b", "", "绑定地址")
|
||||||
CmdNetTrace.Flags().BoolVarP(&hideIncorrect, "hide-incorrect", "H", false, "隐藏错误节点")
|
CmdNetTrace.Flags().BoolVarP(&hideIncorrect, "hide-incorrect", "H", false, "隐藏错误节点")
|
||||||
Cmd.AddCommand(CmdNetTrace, cmdSSHJar)
|
Cmd.AddCommand(CmdNetTrace, cmdSSHJar)
|
||||||
|
|
||||||
@ -106,6 +109,17 @@ func init() {
|
|||||||
CmdScanPort.Flags().IntVarP(&scanport.Retry, "retry", "r", 2, "重试次数")
|
CmdScanPort.Flags().IntVarP(&scanport.Retry, "retry", "r", 2, "重试次数")
|
||||||
Cmd.AddCommand(CmdScanPort)
|
Cmd.AddCommand(CmdScanPort)
|
||||||
|
|
||||||
|
CmdMonitorIP.Flags().StringSliceVarP(&monitorip.IPs, "ip", "i", []string{}, "扫描IP地址列表")
|
||||||
|
CmdMonitorIP.Flags().IntVarP(&monitorip.Port, "port", "p", 80, "TCP模式扫描端口")
|
||||||
|
CmdMonitorIP.Flags().IntVarP(&monitorip.Timeout, "timeout", "t", 1200, "超时时间,毫秒")
|
||||||
|
CmdMonitorIP.Flags().IntVarP(&monitorip.Interval, "interval", "I", 1, "扫描间隔,秒")
|
||||||
|
CmdMonitorIP.Flags().IntVarP(&monitorip.Threads, "threads", "m", 100, "最大线程数")
|
||||||
|
CmdMonitorIP.Flags().StringVarP(&monitorip.Log, "log", "l", "", "日志文件地址")
|
||||||
|
CmdMonitorIP.Flags().StringVarP(&monitorip.ScanType, "type", "T", "icmp", "扫描类型")
|
||||||
|
CmdMonitorIP.Flags().IntVarP(&monitorip.Retry, "retry", "r", 1, "重试次数")
|
||||||
|
CmdMonitorIP.Flags().BoolVarP(&monitorip.WithHostname, "with-hostname", "H", false, "显示主机名")
|
||||||
|
Cmd.AddCommand(CmdMonitorIP)
|
||||||
|
|
||||||
Cmd.AddCommand(tcpkill.Cmd, tcping.Cmd, tcm.Cmd)
|
Cmd.AddCommand(tcpkill.Cmd, tcping.Cmd, tcm.Cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +235,30 @@ var CmdScanIP = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var CmdMonitorIP = &cobra.Command{
|
||||||
|
Use: "monitorip",
|
||||||
|
Short: "监控IP",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if len(monitorip.IPs) == 0 {
|
||||||
|
cmd.Help()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
monitorip.status = make(map[string]bool)
|
||||||
|
monitorip.stopChan = make(chan struct{})
|
||||||
|
sig := make(chan os.Signal)
|
||||||
|
signal.Notify(sig, os.Interrupt, os.Kill)
|
||||||
|
go func() {
|
||||||
|
err := monitorip.Start()
|
||||||
|
if err != nil {
|
||||||
|
starlog.Errorln(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
<-sig
|
||||||
|
monitorip.Stop()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
var CmdScanPort = &cobra.Command{
|
var CmdScanPort = &cobra.Command{
|
||||||
Use: "scanport",
|
Use: "scanport",
|
||||||
Short: "扫描端口",
|
Short: "扫描端口",
|
||||||
|
183
net/icmp.go
Normal file
183
net/icmp.go
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
readBufferSize = 1500
|
||||||
|
maxSeq = 0xffff
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pinger struct {
|
||||||
|
icmpID int
|
||||||
|
conn4 *icmp.PacketConn
|
||||||
|
conn6 *icmp.PacketConn
|
||||||
|
connOnce4 sync.Once
|
||||||
|
connOnce6 sync.Once
|
||||||
|
connErr4 error
|
||||||
|
connErr6 error
|
||||||
|
sequenceNum4 uint32
|
||||||
|
sequenceNum6 uint32
|
||||||
|
pending sync.Map
|
||||||
|
closed chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type pendingKey struct {
|
||||||
|
proto string
|
||||||
|
seq int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPinger(icmpID int) *Pinger {
|
||||||
|
return &Pinger{
|
||||||
|
icmpID: icmpID & 0xffff,
|
||||||
|
closed: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pinger) Close() error {
|
||||||
|
close(p.closed)
|
||||||
|
var err error
|
||||||
|
if p.conn4 != nil {
|
||||||
|
if e := p.conn4.Close(); e != nil {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.conn6 != nil {
|
||||||
|
if e := p.conn6.Close(); e != nil {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pinger) Ping(ip string, timeout time.Duration) error {
|
||||||
|
var conn *icmp.PacketConn
|
||||||
|
var proto string
|
||||||
|
var sequence *uint32
|
||||||
|
dest, err := net.ResolveIPAddr("ip", ip)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("resolve IP address error: %w", err)
|
||||||
|
}
|
||||||
|
if dest.IP.To4() != nil {
|
||||||
|
// IPv4处理
|
||||||
|
p.connOnce4.Do(func() {
|
||||||
|
p.conn4, p.connErr4 = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
||||||
|
if p.connErr4 == nil {
|
||||||
|
go p.receiveLoop(p.conn4, "ip4")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if p.connErr4 != nil {
|
||||||
|
return fmt.Errorf("ICMPv4 connection error: %w", p.connErr4)
|
||||||
|
}
|
||||||
|
conn = p.conn4
|
||||||
|
proto = "ip4"
|
||||||
|
sequence = &p.sequenceNum4
|
||||||
|
} else {
|
||||||
|
// IPv6处理
|
||||||
|
p.connOnce6.Do(func() {
|
||||||
|
p.conn6, p.connErr6 = icmp.ListenPacket("ip6:ipv6-icmp", "::")
|
||||||
|
if p.connErr6 == nil {
|
||||||
|
go p.receiveLoop(p.conn6, "ip6")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if p.connErr6 != nil {
|
||||||
|
return fmt.Errorf("ICMPv6 connection error: %w", p.connErr6)
|
||||||
|
}
|
||||||
|
conn = p.conn6
|
||||||
|
proto = "ip6"
|
||||||
|
sequence = &p.sequenceNum6
|
||||||
|
}
|
||||||
|
|
||||||
|
seq := int(atomic.AddUint32(sequence, 1) & maxSeq)
|
||||||
|
key := pendingKey{proto, seq}
|
||||||
|
resultChan := make(chan struct{})
|
||||||
|
p.pending.Store(key, resultChan)
|
||||||
|
defer p.pending.Delete(key)
|
||||||
|
|
||||||
|
var msgType icmp.Type
|
||||||
|
if proto == "ip4" {
|
||||||
|
msgType = ipv4.ICMPTypeEcho
|
||||||
|
} else {
|
||||||
|
msgType = ipv6.ICMPTypeEchoRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &icmp.Message{
|
||||||
|
Type: msgType,
|
||||||
|
Code: 0,
|
||||||
|
Body: &icmp.Echo{
|
||||||
|
ID: p.icmpID,
|
||||||
|
Seq: seq,
|
||||||
|
Data: []byte("HELLO-PING"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
packet, err := msg.Marshal(nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshal error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := conn.WriteTo(packet, dest); err != nil {
|
||||||
|
return fmt.Errorf("write error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-resultChan:
|
||||||
|
return nil
|
||||||
|
case <-time.After(timeout):
|
||||||
|
return fmt.Errorf("timeout")
|
||||||
|
case <-p.closed:
|
||||||
|
return fmt.Errorf("pinger closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pinger) receiveLoop(conn *icmp.PacketConn, proto string) {
|
||||||
|
buffer := make([]byte, readBufferSize)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-p.closed:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
n, _, err := conn.ReadFrom(buffer)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedType icmp.Type
|
||||||
|
var protocol int
|
||||||
|
if proto == "ip4" {
|
||||||
|
expectedType = ipv4.ICMPTypeEchoReply
|
||||||
|
protocol = 1 // ICMPv4协议号
|
||||||
|
} else {
|
||||||
|
expectedType = ipv6.ICMPTypeEchoReply
|
||||||
|
protocol = 58 // ICMPv6协议号
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := icmp.ParseMessage(protocol, buffer[:n])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Type != expectedType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
echo, ok := msg.Body.(*icmp.Echo)
|
||||||
|
if !ok || echo.ID != p.icmpID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := pendingKey{proto, echo.Seq}
|
||||||
|
if ch, exists := p.pending.LoadAndDelete(key); exists {
|
||||||
|
close(ch.(chan struct{}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
193
net/monitorip.go
Normal file
193
net/monitorip.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"b612.me/apps/b612/netforward"
|
||||||
|
"b612.me/starlog"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Monitor struct {
|
||||||
|
IPs []string
|
||||||
|
Port int
|
||||||
|
ScanType string
|
||||||
|
Timeout int
|
||||||
|
Interval int
|
||||||
|
Log string
|
||||||
|
Retry int
|
||||||
|
Threads int
|
||||||
|
WithHostname bool
|
||||||
|
pinger *Pinger
|
||||||
|
status map[string]bool
|
||||||
|
statusLock sync.RWMutex
|
||||||
|
stopChan chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMonitor(ips []string, scanType string) *Monitor {
|
||||||
|
return &Monitor{
|
||||||
|
IPs: ips,
|
||||||
|
ScanType: scanType,
|
||||||
|
status: make(map[string]bool),
|
||||||
|
stopChan: make(chan struct{}),
|
||||||
|
Timeout: 1000,
|
||||||
|
Interval: 1,
|
||||||
|
Retry: 1,
|
||||||
|
Threads: 50,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) Start() error {
|
||||||
|
if m.Log != "" {
|
||||||
|
starlog.SetLogFile(m.Log, starlog.Std, true)
|
||||||
|
}
|
||||||
|
m.pinger = NewPinger(1127)
|
||||||
|
// Initialize status
|
||||||
|
m.statusLock.Lock()
|
||||||
|
for _, ip := range m.IPs {
|
||||||
|
m.status[ip] = false // Initial state as down
|
||||||
|
}
|
||||||
|
m.statusLock.Unlock()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(time.Duration(m.Interval) * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
m.checkAllIPs()
|
||||||
|
m.displayStatus()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
m.checkAllIPs()
|
||||||
|
m.displayStatus()
|
||||||
|
case <-m.stopChan:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) Stop() {
|
||||||
|
close(m.stopChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) checkAllIPs() {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
sem := make(chan struct{}, m.Threads)
|
||||||
|
|
||||||
|
for _, ip := range m.IPs {
|
||||||
|
wg.Add(1)
|
||||||
|
sem <- struct{}{}
|
||||||
|
|
||||||
|
go func(ip string) {
|
||||||
|
defer func() {
|
||||||
|
<-sem
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
currentStatus := m.checkIP(ip)
|
||||||
|
m.updateStatus(ip, currentStatus)
|
||||||
|
}(ip)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) checkIP(ip string) bool {
|
||||||
|
for i := 0; i < m.Retry+1; i++ {
|
||||||
|
var success bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch m.ScanType {
|
||||||
|
case "icmp":
|
||||||
|
err = m.pinger.Ping(ip, time.Duration(m.Timeout)*time.Millisecond)
|
||||||
|
success = err == nil
|
||||||
|
case "tcp":
|
||||||
|
dialer := net.Dialer{
|
||||||
|
Timeout: time.Duration(m.Timeout) * time.Millisecond,
|
||||||
|
Control: netforward.ControlSetReUseAddr,
|
||||||
|
}
|
||||||
|
conn, err := dialer.Dial("tcp", fmt.Sprintf("%s:%d", ip, m.Port))
|
||||||
|
if err == nil {
|
||||||
|
conn.Close()
|
||||||
|
success = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if success {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < m.Retry {
|
||||||
|
time.Sleep(time.Duration(m.Timeout) * time.Millisecond / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) updateStatus(ip string, current bool) {
|
||||||
|
m.statusLock.Lock()
|
||||||
|
defer m.statusLock.Unlock()
|
||||||
|
|
||||||
|
previous := m.status[ip]
|
||||||
|
if current != previous {
|
||||||
|
m.logStatusChange(ip, previous, current)
|
||||||
|
m.status[ip] = current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) logStatusChange(ip string, from, to bool) {
|
||||||
|
statusToStr := func(s bool) string {
|
||||||
|
if s {
|
||||||
|
return "UP"
|
||||||
|
}
|
||||||
|
return "DOWN"
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostname string
|
||||||
|
if m.WithHostname {
|
||||||
|
names, err := net.LookupAddr(ip)
|
||||||
|
if err == nil && len(names) > 0 {
|
||||||
|
hostname = names[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
starlog.Infof("[Status Change] %s (%s): %s → %s\n",
|
||||||
|
ip,
|
||||||
|
hostname,
|
||||||
|
statusToStr(from),
|
||||||
|
statusToStr(to),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Monitor) displayStatus() {
|
||||||
|
fmt.Print("\033[H\033[2J") // Clear screen
|
||||||
|
fmt.Printf("Monitoring Status (%s)\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
fmt.Println("======================================")
|
||||||
|
|
||||||
|
m.statusLock.RLock()
|
||||||
|
defer m.statusLock.RUnlock()
|
||||||
|
|
||||||
|
for _, ip := range m.IPs {
|
||||||
|
status := m.status[ip]
|
||||||
|
statusStr := "DOWN"
|
||||||
|
if status {
|
||||||
|
statusStr = "UP"
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostname string
|
||||||
|
if m.WithHostname {
|
||||||
|
names, err := net.LookupAddr(ip)
|
||||||
|
if err == nil && len(names) > 0 {
|
||||||
|
hostname = names[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%-15s", ip)
|
||||||
|
if m.WithHostname {
|
||||||
|
fmt.Printf(" (%s)", hostname)
|
||||||
|
}
|
||||||
|
if statusStr == "UP" {
|
||||||
|
starlog.Green(": [%s]\n", statusStr)
|
||||||
|
} else {
|
||||||
|
starlog.Red(": [%s]\n", statusStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import (
|
|||||||
"b612.me/apps/b612/netforward"
|
"b612.me/apps/b612/netforward"
|
||||||
"b612.me/stario"
|
"b612.me/stario"
|
||||||
"b612.me/starlog"
|
"b612.me/starlog"
|
||||||
"b612.me/starnet"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
@ -153,6 +152,7 @@ func (s *ScanIP) ICMP() error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
idx := 0
|
idx := 0
|
||||||
|
pinger := NewPinger(1127)
|
||||||
for {
|
for {
|
||||||
ip := firstIP.String()
|
ip := firstIP.String()
|
||||||
if ip == lastIP.String() {
|
if ip == lastIP.String() {
|
||||||
@ -166,7 +166,7 @@ func (s *ScanIP) ICMP() error {
|
|||||||
}()
|
}()
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for i := 0; i < s.Retry+1; i++ {
|
for i := 0; i < s.Retry+1; i++ {
|
||||||
_, err := starnet.Ping(ip, idx, time.Duration(s.Timeout)*time.Millisecond)
|
err := pinger.Ping(ip, time.Duration(s.Timeout)*time.Millisecond)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
atomic.AddInt32(&count, 1)
|
atomic.AddInt32(&count, 1)
|
||||||
if s.WithHostname {
|
if s.WithHostname {
|
||||||
|
242
net/sshjar.go
242
net/sshjar.go
@ -9,6 +9,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -23,6 +25,9 @@ var (
|
|||||||
curlUrl string
|
curlUrl string
|
||||||
serverVersion string
|
serverVersion string
|
||||||
curlArg []string
|
curlArg []string
|
||||||
|
passwds []string
|
||||||
|
allowAny bool
|
||||||
|
logPath string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -31,6 +36,10 @@ func init() {
|
|||||||
cmdSSHJar.Flags().StringVarP(&KeyPasswd, "passwd", "p", "", "私钥密码")
|
cmdSSHJar.Flags().StringVarP(&KeyPasswd, "passwd", "p", "", "私钥密码")
|
||||||
cmdSSHJar.Flags().StringVarP(&outpath, "output", "o", "", "输出文件")
|
cmdSSHJar.Flags().StringVarP(&outpath, "output", "o", "", "输出文件")
|
||||||
cmdSSHJar.Flags().StringVarP(&serverVersion, "version", "v", "SSH-2.0-OpenSSH_8.0", "SSH版本")
|
cmdSSHJar.Flags().StringVarP(&serverVersion, "version", "v", "SSH-2.0-OpenSSH_8.0", "SSH版本")
|
||||||
|
cmdSSHJar.Flags().StringVarP(&curlUrl, "curl", "c", "", "Curl URL")
|
||||||
|
cmdSSHJar.Flags().StringSliceVarP(&passwds, "allow-passwds", "P", nil, "密码列表,格式:[用户名]:[密码]")
|
||||||
|
cmdSSHJar.Flags().BoolVarP(&allowAny, "allow-any", "A", false, "允许任意密码登录")
|
||||||
|
cmdSSHJar.Flags().StringVarP(&logPath, "log", "L", "", "日志文件")
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdSSHJar = &cobra.Command{
|
var cmdSSHJar = &cobra.Command{
|
||||||
@ -38,11 +47,41 @@ var cmdSSHJar = &cobra.Command{
|
|||||||
Short: "SSH蜜罐",
|
Short: "SSH蜜罐",
|
||||||
Long: "SSH蜜罐",
|
Long: "SSH蜜罐",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
runSSHHoneyJar(listenAddr, keyFile, KeyPasswd, outpath, serverVersion)
|
var mypwds [][]string
|
||||||
|
for _, v := range passwds {
|
||||||
|
args := strings.SplitN(v, ":", 2)
|
||||||
|
if len(args) == 2 {
|
||||||
|
mypwds = append(mypwds, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runSSHHoneyJar(SSHJar{
|
||||||
|
listenAddr: listenAddr,
|
||||||
|
keyFile: keyFile,
|
||||||
|
keyPasswd: KeyPasswd,
|
||||||
|
outpath: outpath,
|
||||||
|
logpath: logPath,
|
||||||
|
version: serverVersion,
|
||||||
|
passwds: mypwds,
|
||||||
|
allowAny: allowAny,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runSSHHoneyJar(listenAddr, keyFile, KeyPasswd, outpath, version string) {
|
type SSHJar struct {
|
||||||
|
listenAddr string
|
||||||
|
keyFile string
|
||||||
|
keyPasswd string
|
||||||
|
outpath string
|
||||||
|
logpath string
|
||||||
|
version string
|
||||||
|
passwds [][]string
|
||||||
|
allowAny bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSSHHoneyJar(jar SSHJar) {
|
||||||
|
if jar.logpath != "" {
|
||||||
|
starlog.SetLogFile(jar.logpath, starlog.Std, true)
|
||||||
|
}
|
||||||
var f *os.File
|
var f *os.File
|
||||||
var err error
|
var err error
|
||||||
if outpath != "" {
|
if outpath != "" {
|
||||||
@ -56,10 +95,10 @@ func runSSHHoneyJar(listenAddr, keyFile, KeyPasswd, outpath, version string) {
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
defer conn.Flush()
|
defer conn.Flush()
|
||||||
config := &ssh.ServerConfig{
|
config := &ssh.ServerConfig{
|
||||||
ServerVersion: version,
|
ServerVersion: jar.version,
|
||||||
// 密码验证回调函数
|
// 密码验证回调函数
|
||||||
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
|
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
|
||||||
starlog.Infof("Login attempt from %s with %s %s\n", c.RemoteAddr(), c.User(), string(pass))
|
starlog.Infof("Login attempt from %s with %s by %s\n", c.RemoteAddr(), c.User(), string(pass))
|
||||||
data := []string{time.Now().Format("2006-01-02 15:04:05"), c.RemoteAddr().String(), c.User(), string(pass)}
|
data := []string{time.Now().Format("2006-01-02 15:04:05"), c.RemoteAddr().String(), c.User(), string(pass)}
|
||||||
if f != nil {
|
if f != nil {
|
||||||
conn.Write(data)
|
conn.Write(data)
|
||||||
@ -83,9 +122,30 @@ func runSSHHoneyJar(listenAddr, keyFile, KeyPasswd, outpath, version string) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
perm := &ssh.Permissions{
|
||||||
|
Extensions: map[string]string{
|
||||||
|
"user": c.User(),
|
||||||
|
"passwd": string(pass),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if jar.allowAny {
|
||||||
|
return perm, nil
|
||||||
|
}
|
||||||
|
for _, v := range jar.passwds {
|
||||||
|
if c.User() == v[0] && string(pass) == v[1] {
|
||||||
|
return perm, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("password rejected for %q", c.User())
|
return nil, fmt.Errorf("password rejected for %q", c.User())
|
||||||
},
|
},
|
||||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||||
|
if jar.allowAny {
|
||||||
|
return &ssh.Permissions{
|
||||||
|
Extensions: map[string]string{
|
||||||
|
"user": conn.User(),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("public key rejected for %q", conn.User())
|
return nil, fmt.Errorf("public key rejected for %q", conn.User())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -132,9 +192,179 @@ func runSSHHoneyJar(listenAddr, keyFile, KeyPasswd, outpath, version string) {
|
|||||||
}
|
}
|
||||||
starlog.Infof("New connection from %s\n", conn.RemoteAddr())
|
starlog.Infof("New connection from %s\n", conn.RemoteAddr())
|
||||||
go func(conn net.Conn) {
|
go func(conn net.Conn) {
|
||||||
ssh.NewServerConn(conn, config)
|
sConn, chans, reqs, err := ssh.NewServerConn(conn, config)
|
||||||
conn.Close()
|
if err != nil {
|
||||||
|
starlog.Errorf("SSH handshake failed: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer sConn.Close()
|
||||||
|
defer starlog.Noticef("Connection from %s closed\n", sConn.RemoteAddr())
|
||||||
|
go ssh.DiscardRequests(reqs)
|
||||||
|
for newChannel := range chans {
|
||||||
|
if newChannel.ChannelType() != "session" {
|
||||||
|
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
channel, requests, err := newChannel.Accept()
|
||||||
|
if err != nil {
|
||||||
|
starlog.Errorf("Failed to accept channel: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleSession(channel, requests, sConn)
|
||||||
|
}
|
||||||
}(conn)
|
}(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleSession(channel ssh.Channel, requests <-chan *ssh.Request, conn *ssh.ServerConn) {
|
||||||
|
defer channel.Close()
|
||||||
|
term := terminal.NewTerminal(channel, "$ ") // 设置 shell 提示符
|
||||||
|
term.AutoCompleteCallback = nil // 禁用自动补全
|
||||||
|
for req := range requests {
|
||||||
|
switch req.Type {
|
||||||
|
case "pty-req":
|
||||||
|
// 接受伪终端请求(攻击者希望获得交互式体验)
|
||||||
|
req.Reply(true, nil)
|
||||||
|
term.SetSize( // 简单设置终端尺寸
|
||||||
|
24, // 行
|
||||||
|
80, // 列
|
||||||
|
)
|
||||||
|
case "shell":
|
||||||
|
req.Reply(true, nil)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
line, err := term.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
starlog.Infof("[%s %s] Command: %s\n", conn.RemoteAddr(), conn.Permissions.Extensions["user"], line)
|
||||||
|
time.Sleep(time.Millisecond * 200)
|
||||||
|
term.Write([]byte(FakeCommand(line)))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case "exec":
|
||||||
|
// 处理非交互式命令(如 ssh user@host 'ls -l')
|
||||||
|
var payload struct{ Command string }
|
||||||
|
ssh.Unmarshal(req.Payload, &payload)
|
||||||
|
req.Reply(true, nil)
|
||||||
|
|
||||||
|
// 记录并返回假输出
|
||||||
|
starlog.Infof("[%s %s] Exec: %s\n", conn.RemoteAddr(), conn.Permissions.Extensions["user"], payload.Command)
|
||||||
|
term.Write([]byte(FakeCommand(payload.Command)))
|
||||||
|
channel.Close()
|
||||||
|
default:
|
||||||
|
req.Reply(false, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FakeCommand(cmd string) string {
|
||||||
|
// 按命令类型分级模拟
|
||||||
|
switch {
|
||||||
|
//---------------- 系统信息探测类 ----------------
|
||||||
|
case strings.Contains(cmd, "uname -a"):
|
||||||
|
return "Linux core 6.1.0-21-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.90-1 (2024-05-03) x86_64 GNU/Linux\n\n"
|
||||||
|
|
||||||
|
case strings.Contains(cmd, "cat /etc/os-release"):
|
||||||
|
return `PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
|
||||||
|
NAME="Debian GNU/Linux"
|
||||||
|
VERSION_ID="11"
|
||||||
|
VERSION="11 (bullseye)"
|
||||||
|
VERSION_CODENAME=bullseye
|
||||||
|
ID=debian
|
||||||
|
HOME_URL="https://www.debian.org/"
|
||||||
|
SUPPORT_URL="https://www.debian.org/support"
|
||||||
|
BUG_REPORT_URL="https://bugs.debian.org/"
|
||||||
|
`
|
||||||
|
|
||||||
|
case strings.Contains(cmd, "free -h"):
|
||||||
|
return ` total used free shared buff/cache available
|
||||||
|
Mem: 1022Gi 12Gi 3.0Gi 1.0Gi 47Gi 480Gi
|
||||||
|
Swap: 0B 0B 0B
|
||||||
|
`
|
||||||
|
|
||||||
|
//---------------- 敏感文件诱导类 ----------------
|
||||||
|
case strings.Contains(cmd, "ls /home"):
|
||||||
|
return "admin backup devops secret\n"
|
||||||
|
|
||||||
|
case strings.Contains(cmd, "ls /var/log"):
|
||||||
|
return `auth.log apache2 payment_system.log database_backup.log
|
||||||
|
`
|
||||||
|
case strings.Contains(cmd, "ls"):
|
||||||
|
return "password.txt\n"
|
||||||
|
|
||||||
|
case strings.Contains(cmd, "cat /etc/passwd"):
|
||||||
|
return `root:x:0:0:root:/root:/bin/bash
|
||||||
|
admin:x:1000:1000:,,,:/home/admin:/bin/bash
|
||||||
|
mysql:x:106:113:MySQL Server,,,:/nonexistent:/bin/false
|
||||||
|
core:x:0:0:,,,:/root:/bin/bash
|
||||||
|
`
|
||||||
|
|
||||||
|
//---------------- 网络配置诱导类 ----------------
|
||||||
|
case strings.Contains(cmd, "ifconfig") || strings.Contains(cmd, "ip addr"):
|
||||||
|
return `eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
|
||||||
|
inet 192.168.1.105 netmask 255.255.255.0 broadcast 192.168.1.255
|
||||||
|
inet6 fe80::250:56ff:fec0:8888 prefixlen 64 scopeid 0x20<link>
|
||||||
|
ether 00:50:56:c0:88:88 txqueuelen 1000 (Ethernet)
|
||||||
|
RX packets 123456 bytes 123456789 (117.7 MiB)
|
||||||
|
RX errors 0 dropped 0 overruns 0 frame 0
|
||||||
|
TX packets 98765 bytes 9876543 (9.4 MiB)
|
||||||
|
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
||||||
|
|
||||||
|
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
|
||||||
|
inet 127.0.0.1 netmask 255.0.0.0
|
||||||
|
loop txqueuelen 1000 (Local Loopback)
|
||||||
|
`
|
||||||
|
|
||||||
|
case strings.Contains(cmd, "netstat -antp"):
|
||||||
|
return `Active Internet connections (servers and established)
|
||||||
|
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
|
||||||
|
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1234/sshd
|
||||||
|
tcp 0 0 192.168.1.105:5432 203.0.113.5:43892 ESTABLISHED 5678/postgres
|
||||||
|
tcp6 0 0 :::8080 :::* LISTEN 91011/java
|
||||||
|
`
|
||||||
|
|
||||||
|
//---------------- 凭证钓鱼类 ----------------
|
||||||
|
case strings.Contains(cmd, "mysql -u root -p"):
|
||||||
|
return "ERROR 1045 (28000): Access denied\n"
|
||||||
|
|
||||||
|
case strings.Contains(cmd, "sudo -l"):
|
||||||
|
return `Matching Defaults entries for admin on this host:
|
||||||
|
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
|
||||||
|
|
||||||
|
User admin may run the following commands on honeypot:
|
||||||
|
(ALL) NOPASSWD: /usr/bin/vim /etc/shadow
|
||||||
|
`
|
||||||
|
|
||||||
|
//---------------- 进程服务类 ----------------
|
||||||
|
case strings.Contains(cmd, "ps aux"):
|
||||||
|
return `USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
||||||
|
root 1 0.0 0.1 169020 13184 ? Ss May01 0:12 /sbin/init
|
||||||
|
admin 1234 0.3 2.1 1123456 178912 ? Sl May01 12:34 /opt/payment_system/payment_processor --debug
|
||||||
|
`
|
||||||
|
|
||||||
|
//---------------- 定制化陷阱 ----------------
|
||||||
|
case strings.Contains(cmd, "find / -name *.db"):
|
||||||
|
return `/var/lib/mysql/transactions.db
|
||||||
|
/home/backup/internal_users.db
|
||||||
|
`
|
||||||
|
|
||||||
|
case strings.Contains(cmd, "curl"):
|
||||||
|
return `<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
||||||
|
<html><head>
|
||||||
|
<title>404 Not Found</title>
|
||||||
|
</head><body>
|
||||||
|
<h1>Not Found</h1>
|
||||||
|
<p>The requested URL was not found on this server.</p>
|
||||||
|
</body></html>
|
||||||
|
`
|
||||||
|
|
||||||
|
default:
|
||||||
|
// 模糊响应增加真实感
|
||||||
|
if rand.Intn(100) > 70 { // 30%概率返回"command not found"
|
||||||
|
return "sh: command not found: " + strings.Fields(cmd)[0] + "\n"
|
||||||
|
}
|
||||||
|
return "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,5 +3,5 @@ package net
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestSSHJar(t *testing.T) {
|
func TestSSHJar(t *testing.T) {
|
||||||
runSSHHoneyJar("127.0.0.1:22", "", "", "./test.csv", "")
|
//runSSHHoneyJar("127.0.0.1:22", "", "", "./test.csv", "")
|
||||||
}
|
}
|
||||||
|
278
net/trace.go
278
net/trace.go
@ -52,6 +52,7 @@ func Traceroute(address string, bindaddr string, dns string, maxHops int, timeou
|
|||||||
}
|
}
|
||||||
traceroute(address, bindaddr, maxHops, timeout, ipinfoAddr, hideIncorrect)
|
traceroute(address, bindaddr, maxHops, timeout, ipinfoAddr, hideIncorrect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func traceroute(address string, bindaddr string, maxHops int, timeout time.Duration, ipinfoAddr string, hideIncorrect bool) {
|
func traceroute(address string, bindaddr string, maxHops int, timeout time.Duration, ipinfoAddr string, hideIncorrect bool) {
|
||||||
ipinfo := net.ParseIP(address)
|
ipinfo := net.ParseIP(address)
|
||||||
if ipinfo == nil {
|
if ipinfo == nil {
|
||||||
@ -59,26 +60,46 @@ func traceroute(address string, bindaddr string, maxHops int, timeout time.Durat
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var echoType icmp.Type = ipv4.ICMPTypeEcho
|
var (
|
||||||
var exceededType icmp.Type = ipv4.ICMPTypeTimeExceeded
|
echoType icmp.Type
|
||||||
var replyType icmp.Type = ipv4.ICMPTypeEchoReply
|
exceededType icmp.Type
|
||||||
var proto = 1
|
replyType icmp.Type
|
||||||
var network = "ip4:icmp"
|
unreachType icmp.Type
|
||||||
var resolveIP = "ip4"
|
proto int
|
||||||
if ipinfo.To4() == nil {
|
network string
|
||||||
network = "ip6:ipv6-icmp"
|
resolveIP string
|
||||||
resolveIP = "ip6"
|
isIPv4 bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if ipinfo.To4() != nil {
|
||||||
|
echoType = ipv4.ICMPTypeEcho
|
||||||
|
exceededType = ipv4.ICMPTypeTimeExceeded
|
||||||
|
replyType = ipv4.ICMPTypeEchoReply
|
||||||
|
unreachType = ipv4.ICMPTypeDestinationUnreachable
|
||||||
|
proto = 1
|
||||||
|
network = "ip4:icmp"
|
||||||
|
resolveIP = "ip4"
|
||||||
|
isIPv4 = true
|
||||||
|
} else {
|
||||||
echoType = ipv6.ICMPTypeEchoRequest
|
echoType = ipv6.ICMPTypeEchoRequest
|
||||||
exceededType = ipv6.ICMPTypeTimeExceeded
|
exceededType = ipv6.ICMPTypeTimeExceeded
|
||||||
replyType = ipv6.ICMPTypeEchoReply
|
replyType = ipv6.ICMPTypeEchoReply
|
||||||
|
unreachType = ipv6.ICMPTypeDestinationUnreachable
|
||||||
proto = 58
|
proto = 58
|
||||||
|
network = "ip6:ipv6-icmp"
|
||||||
|
resolveIP = "ip6"
|
||||||
|
isIPv4 = false
|
||||||
}
|
}
|
||||||
if bindaddr == "" {
|
if bindaddr == "" {
|
||||||
bindaddr = "0.0.0.0"
|
bindaddr = "0.0.0.0"
|
||||||
|
if !isIPv4 {
|
||||||
|
bindaddr = "::"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := icmp.ListenPacket(network, bindaddr)
|
c, err := icmp.ListenPacket(network, bindaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
starlog.Errorln("监听失败:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
@ -90,121 +111,179 @@ func traceroute(address string, bindaddr string, maxHops int, timeout time.Durat
|
|||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
timeout = time.Second * 3
|
timeout = time.Second * 3
|
||||||
}
|
}
|
||||||
|
|
||||||
exitfor:
|
exitfor:
|
||||||
for i := 1; i <= maxHops; i++ {
|
for ttl := 1; ttl <= maxHops; ttl++ {
|
||||||
retry := 0
|
if atomic.LoadInt32(&firstTargetHop) <= int32(ttl) {
|
||||||
doRetry:
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dst, err := net.ResolveIPAddr(resolveIP, address)
|
dst, err := net.ResolveIPAddr(resolveIP, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
starlog.Errorln("IP地址解析失败:", address, err)
|
starlog.Errorln("解析失败:", address, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if atomic.LoadInt32(&firstTargetHop) <= int32(i) {
|
|
||||||
return
|
// 构造ICMP报文
|
||||||
}
|
msg := icmp.Message{
|
||||||
m := icmp.Message{
|
|
||||||
Type: echoType, Code: 0,
|
Type: echoType, Code: 0,
|
||||||
Body: &icmp.Echo{
|
Body: &icmp.Echo{
|
||||||
ID: i, Seq: i,
|
ID: ttl, // 使用TTL作为ID
|
||||||
|
Seq: ttl, // 使用TTL作为序列号
|
||||||
Data: []byte("B612.ME-ROUTER-TRACE"),
|
Data: []byte("B612.ME-ROUTER-TRACE"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := m.Marshal(nil)
|
msgBytes, err := msg.Marshal(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("%d\tMarshal error: %v\n", i, err)
|
starlog.Warningf("%d\t封包失败: %v\n", ttl, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置TTL/HopLimit
|
||||||
if network == "ip4:icmp" {
|
if network == "ip4:icmp" {
|
||||||
if err := c.IPv4PacketConn().SetTTL(i); err != nil {
|
if err := c.IPv4PacketConn().SetTTL(ttl); err != nil {
|
||||||
fmt.Printf("%d\tSetTTL error: %v\n", i, err)
|
starlog.Warningf("%d\t设置TTL失败: %v\n", ttl, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := c.IPv6PacketConn().SetHopLimit(i); err != nil {
|
if err := c.IPv6PacketConn().SetHopLimit(ttl); err != nil {
|
||||||
fmt.Printf("%d\tSetHopLimit error: %v\n", i, err)
|
starlog.Warningf("%d\t设置HopLimit失败: %v\n", ttl, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
startTime := time.Now()
|
||||||
n, err := c.WriteTo(b, dst)
|
if _, err := c.WriteTo(msgBytes, dst); err != nil {
|
||||||
if err != nil {
|
starlog.Warningf("%d\t发送失败: %v\n", ttl, err)
|
||||||
fmt.Printf("%d\tWriteTo error: %v\n", i, err)
|
|
||||||
continue
|
|
||||||
} else if n != len(b) {
|
|
||||||
fmt.Printf("%d\tWrite Short: %v Expected: %v\n", i, n, len(b))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
// 接收响应处理
|
||||||
exitrecheck:
|
timeoutCh := time.After(timeout)
|
||||||
for {
|
responsesReceived := 0
|
||||||
reply := make([]byte, 1500)
|
|
||||||
err = c.SetReadDeadline(time.Now().Add(timeout))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%d\tSetReadDeadline error: %v\n", i, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n, peer, err := c.ReadFrom(reply)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%d\tReadFrom error: %v\n", i, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
duration := time.Since(start)
|
|
||||||
|
|
||||||
rm, err := icmp.ParseMessage(proto, reply[:n])
|
recvLoop:
|
||||||
if err != nil {
|
for responsesReceived < 3 {
|
||||||
fmt.Printf("%d\tParseMessage error: %v\n", i, err)
|
select {
|
||||||
break
|
case <-timeoutCh:
|
||||||
}
|
if responsesReceived == 0 {
|
||||||
|
fmt.Printf("%d\t*\n", ttl)
|
||||||
switch rm.Type {
|
|
||||||
case exceededType:
|
|
||||||
fmt.Printf("%d\thops away:\t%s\t(%s) %s\n", i, peer, duration, GetIPInfo(peer.String(), ipinfoAddr))
|
|
||||||
break exitrecheck
|
|
||||||
case replyType:
|
|
||||||
fmt.Printf("%d\thops away:\t%s\t(%s) %s\n", i, peer, duration, GetIPInfo(peer.String(), ipinfoAddr))
|
|
||||||
if peer.String() == dst.String() {
|
|
||||||
break exitfor
|
|
||||||
}
|
|
||||||
case ipv4.ICMPTypeEcho, ipv6.ICMPTypeEchoRequest:
|
|
||||||
if time.Now().Sub(now).Seconds() > timeout.Seconds() {
|
|
||||||
if retry < 1 {
|
|
||||||
retry++
|
|
||||||
goto doRetry
|
|
||||||
}
|
|
||||||
if !hideIncorrect {
|
|
||||||
fmt.Printf("%d\tInvalid Echo Request:%s (%s) %s\n", i, peer, duration, GetIPInfo(peer.String(), ipinfoAddr))
|
|
||||||
}
|
|
||||||
break exitrecheck
|
|
||||||
}
|
|
||||||
case ipv4.ICMPTypeDestinationUnreachable, ipv6.ICMPTypeDestinationUnreachable:
|
|
||||||
if time.Now().Sub(now).Seconds() > timeout.Seconds() {
|
|
||||||
if retry < 1 {
|
|
||||||
retry++
|
|
||||||
goto doRetry
|
|
||||||
}
|
|
||||||
if !hideIncorrect {
|
|
||||||
fmt.Printf("%d\tInvalid DstInv Request:%s (%s) %s\n", i, peer, duration, GetIPInfo(peer.String(), ipinfoAddr))
|
|
||||||
}
|
|
||||||
break exitrecheck
|
|
||||||
}
|
}
|
||||||
|
break recvLoop
|
||||||
default:
|
default:
|
||||||
if time.Now().Sub(now).Seconds() > timeout.Seconds() {
|
reply := make([]byte, 1500)
|
||||||
if retry < 1 {
|
if err := c.SetReadDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
|
||||||
retry++
|
break recvLoop
|
||||||
goto doRetry
|
}
|
||||||
|
|
||||||
|
n, peer, err := c.ReadFrom(reply)
|
||||||
|
if err != nil {
|
||||||
|
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if !hideIncorrect {
|
starlog.Debugf("%d\t接收错误: %v", ttl, err)
|
||||||
fmt.Printf("%d\tgot %+v from %v (%s) %s\n", i, rm.Type, peer, duration, GetIPInfo(peer.String(), ipinfoAddr))
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
rm, err := icmp.ParseMessage(proto, reply[:n])
|
||||||
|
if err != nil {
|
||||||
|
starlog.Debugf("%d\t解析错误: %v", ttl, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证响应匹配
|
||||||
|
if match := checkResponseMatch(rm, ttl, peer.String() == dst.String(), isIPv4, exceededType, replyType, unreachType); match {
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
fmt.Printf("%d\t%s\t%s\t%s\n",
|
||||||
|
ttl,
|
||||||
|
peer,
|
||||||
|
duration.Round(time.Millisecond),
|
||||||
|
GetIPInfo(peer.String(), ipinfoAddr),
|
||||||
|
)
|
||||||
|
responsesReceived++
|
||||||
|
|
||||||
|
if peer.String() == dst.String() {
|
||||||
|
atomic.StoreInt32(&firstTargetHop, int32(ttl))
|
||||||
|
break exitfor
|
||||||
}
|
}
|
||||||
break exitrecheck
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResponseMatch(rm *icmp.Message, ttl int, isFinal bool, isIPv4 bool,
|
||||||
|
exceededType, replyType, unreachType icmp.Type) bool {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case rm.Type == exceededType:
|
||||||
|
if body, ok := rm.Body.(*icmp.TimeExceeded); ok {
|
||||||
|
return validateOriginalPacket(body.Data, ttl, isIPv4)
|
||||||
|
}
|
||||||
|
|
||||||
|
case rm.Type == replyType:
|
||||||
|
if isFinal {
|
||||||
|
if body, ok := rm.Body.(*icmp.Echo); ok {
|
||||||
|
return body.ID == ttl && body.Seq == ttl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
|
||||||
|
case rm.Type == unreachType:
|
||||||
|
if body, ok := rm.Body.(*icmp.DstUnreach); ok {
|
||||||
|
return validateOriginalPacket(body.Data, ttl, isIPv4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOriginalPacket(data []byte, ttl int, isIPv4 bool) bool {
|
||||||
|
var (
|
||||||
|
proto byte
|
||||||
|
header []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
if isIPv4 {
|
||||||
|
if len(data) < 20+8 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ihl := data[0] & 0x0F
|
||||||
|
if ihl < 5 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
proto = data[9]
|
||||||
|
header = data[:ihl*4]
|
||||||
|
} else {
|
||||||
|
if len(data) < 40+8 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
proto = data[6]
|
||||||
|
header = data[:40]
|
||||||
|
}
|
||||||
|
|
||||||
|
if proto != 1 && proto != 58 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := data[len(header):]
|
||||||
|
if len(payload) < 8 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if isIPv4 {
|
||||||
|
return payload[0] == 8 && // ICMP Echo Request
|
||||||
|
payload[4] == byte(ttl>>8) &&
|
||||||
|
payload[5] == byte(ttl) &&
|
||||||
|
payload[6] == byte(ttl>>8) &&
|
||||||
|
payload[7] == byte(ttl)
|
||||||
|
} else {
|
||||||
|
return payload[0] == 128 && // ICMPv6 Echo Request
|
||||||
|
payload[4] == byte(ttl>>8) &&
|
||||||
|
payload[5] == byte(ttl) &&
|
||||||
|
payload[6] == byte(ttl>>8) &&
|
||||||
|
payload[7] == byte(ttl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,16 +292,23 @@ func GetIPInfo(ip string, addr string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
uri := strings.ReplaceAll(addr, "{ip}", ip)
|
uri := strings.ReplaceAll(addr, "{ip}", ip)
|
||||||
res, err := starnet.Curl(starnet.NewSimpleRequest(uri, "GET", starnet.WithTimeout(time.Second*2), starnet.WithDialTimeout(time.Second*3)))
|
res, err := starnet.Curl(starnet.NewSimpleRequest(uri, "GET",
|
||||||
|
starnet.WithTimeout(time.Second*2),
|
||||||
|
starnet.WithDialTimeout(time.Second*3)))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "获取IP信息失败:" + err.Error()
|
return "IP信息获取失败"
|
||||||
}
|
}
|
||||||
|
|
||||||
var ipinfo IPInfo
|
var ipinfo IPInfo
|
||||||
err = res.Body().Unmarshal(&ipinfo)
|
if err := res.Body().Unmarshal(&ipinfo); err != nil {
|
||||||
if err != nil {
|
return "IP信息解析失败"
|
||||||
return "解析IP信息失败:" + err.Error()
|
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s %s %s %s %s", ipinfo.CountryName, ipinfo.RegionName, ipinfo.CityName, ipinfo.OwnerDomain, ipinfo.ISP)
|
|
||||||
|
return fmt.Sprintf("%s %s %s",
|
||||||
|
ipinfo.CountryName,
|
||||||
|
ipinfo.RegionName,
|
||||||
|
ipinfo.ISP)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPInfo struct {
|
type IPInfo struct {
|
||||||
|
28
nmon/cpu.go
28
nmon/cpu.go
@ -1,28 +0,0 @@
|
|||||||
package nmon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/shirou/gopsutil/v4/cpu"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Cpu() {
|
|
||||||
go func() {
|
|
||||||
flat, err := cpu.Percent(time.Second, false)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(flat)
|
|
||||||
}()
|
|
||||||
flat, err := cpu.Percent(time.Second, true)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c := 0.0000
|
|
||||||
for _, v := range flat {
|
|
||||||
c += v
|
|
||||||
}
|
|
||||||
fmt.Println(flat)
|
|
||||||
fmt.Println(c / float64(len(flat)))
|
|
||||||
time.Sleep(time.Millisecond * 200)
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package nmon
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestCpu(t *testing.T) {
|
|
||||||
Cpu()
|
|
||||||
}
|
|
409
nmon/mon.go
Normal file
409
nmon/mon.go
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
package nmon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
"github.com/shirou/gopsutil/v4/cpu"
|
||||||
|
"github.com/shirou/gopsutil/v4/disk"
|
||||||
|
"github.com/shirou/gopsutil/v4/load"
|
||||||
|
"github.com/shirou/gopsutil/v4/mem"
|
||||||
|
"github.com/shirou/gopsutil/v4/net"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
refreshInterval time.Duration
|
||||||
|
savePath string
|
||||||
|
app *tview.Application
|
||||||
|
showCores bool
|
||||||
|
noTUI bool
|
||||||
|
diskIOHistory = make(map[string]disk.IOCountersStat)
|
||||||
|
netStats = make(map[string]*NetStat)
|
||||||
|
fileMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetStat struct {
|
||||||
|
LastBytesSent uint64
|
||||||
|
LastBytesRecv uint64
|
||||||
|
LastTime time.Time
|
||||||
|
CurrentSentRate float64
|
||||||
|
CurrentRecvRate float64
|
||||||
|
}
|
||||||
|
|
||||||
|
var Cmd = &cobra.Command{
|
||||||
|
Use: "nmon",
|
||||||
|
Short: "System Monitoring Tool",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if noTUI {
|
||||||
|
runTextMode()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
startTUI()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Cmd.Flags().BoolVarP(&noTUI, "no-tui", "t", false, "Run in text mode")
|
||||||
|
Cmd.Flags().BoolVarP(&showCores, "cores", "c", false, "Show per-core CPU usage")
|
||||||
|
Cmd.Flags().DurationVarP(&refreshInterval, "interval", "i", time.Second, "Refresh interval")
|
||||||
|
Cmd.Flags().StringVarP(&savePath, "save", "s", "", "Save monitoring data to file (nmon format)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := Cmd.Execute(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createClickableTextView(title string) *tview.TextView {
|
||||||
|
view := tview.NewTextView().SetDynamicColors(true)
|
||||||
|
view.SetBorder(true).SetTitle(title)
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func startTUI() {
|
||||||
|
app = tview.NewApplication()
|
||||||
|
flex := tview.NewFlex().SetDirection(tview.FlexRow)
|
||||||
|
|
||||||
|
cpuView := tview.NewTextView().SetDynamicColors(true)
|
||||||
|
memView := tview.NewTextView().SetDynamicColors(true)
|
||||||
|
netView := tview.NewTextView().SetDynamicColors(true)
|
||||||
|
diskView := tview.NewTextView().SetDynamicColors(true)
|
||||||
|
|
||||||
|
// 动态布局更新函数
|
||||||
|
ioStats, _ := disk.IOCounters()
|
||||||
|
updateLayout := func() {
|
||||||
|
flex.Clear()
|
||||||
|
cpuLines := 2 // 固定基础2行(CPU Load + Load Avg)
|
||||||
|
if showCores {
|
||||||
|
perCPU, _ := cpu.Percent(0, true)
|
||||||
|
cpuLines = 2 + len(perCPU)
|
||||||
|
}
|
||||||
|
flex.AddItem(cpuView, cpuLines, 1, false)
|
||||||
|
flex.AddItem(memView, 3, 1, false)
|
||||||
|
flex.AddItem(diskView, len(ioStats), 1, false)
|
||||||
|
flex.AddItem(netView, 3, 1, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
updateLayout()
|
||||||
|
|
||||||
|
app.SetMouseCapture(func(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
|
||||||
|
// 示例:打印点击坐标
|
||||||
|
if action == tview.MouseLeftClick {
|
||||||
|
showCores = !showCores
|
||||||
|
app.QueueUpdateDraw(updateLayout)
|
||||||
|
}
|
||||||
|
return event, action
|
||||||
|
})
|
||||||
|
|
||||||
|
go updateLoop(cpuView, memView, netView, diskView)
|
||||||
|
|
||||||
|
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if event.Key() == tcell.KeyCtrlC {
|
||||||
|
app.Stop()
|
||||||
|
}
|
||||||
|
return event
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := app.SetRoot(flex, true).Run(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateLoop(cpuView, memView, netView, diskView *tview.TextView) {
|
||||||
|
var lastSave time.Time
|
||||||
|
for {
|
||||||
|
app.QueueUpdateDraw(func() {
|
||||||
|
updateCPU(cpuView)
|
||||||
|
updateMemory(memView)
|
||||||
|
updateNetwork(netView)
|
||||||
|
updateDiskIO(diskView)
|
||||||
|
})
|
||||||
|
|
||||||
|
if savePath != "" && time.Since(lastSave) > time.Minute {
|
||||||
|
go saveNMONData()
|
||||||
|
lastSave = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(refreshInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCPU(view *tview.TextView) {
|
||||||
|
percent, _ := cpu.Percent(time.Second, false)
|
||||||
|
loadAvg, _ := load.Avg()
|
||||||
|
|
||||||
|
text := fmt.Sprintf("[red]CPU Load: [white]%.2f%%\n[red]Load Avg: [white]%.2f, %.2f, %.2f",
|
||||||
|
percent[0],
|
||||||
|
loadAvg.Load1,
|
||||||
|
loadAvg.Load5,
|
||||||
|
loadAvg.Load15)
|
||||||
|
|
||||||
|
if showCores {
|
||||||
|
perCPU, _ := cpu.Percent(time.Second, true)
|
||||||
|
for i, p := range perCPU {
|
||||||
|
text += fmt.Sprintf("\nCore %d: [green]%s[white] %.2f%%",
|
||||||
|
i+1,
|
||||||
|
progressBar(p, 20),
|
||||||
|
p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.SetText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateMemory(view *tview.TextView) {
|
||||||
|
memStat, _ := mem.VirtualMemory()
|
||||||
|
text := fmt.Sprintf("[red]Memory: [white]%.2f%% Used (%.2f GB / %.2f GB)",
|
||||||
|
memStat.UsedPercent,
|
||||||
|
float64(memStat.Used)/1024/1024/1024,
|
||||||
|
float64(memStat.Total)/1024/1024/1024)
|
||||||
|
view.SetText(text + "\n" + progressBar(memStat.UsedPercent, 50))
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNetwork(view *tview.TextView) {
|
||||||
|
interfaces, _ := net.IOCounters(true)
|
||||||
|
now := time.Now()
|
||||||
|
var totalSent, totalRecv float64
|
||||||
|
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.Name == "lo" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, exists := netStats[iface.Name]
|
||||||
|
if !exists {
|
||||||
|
stat = &NetStat{
|
||||||
|
LastBytesSent: iface.BytesSent,
|
||||||
|
LastBytesRecv: iface.BytesRecv,
|
||||||
|
LastTime: now,
|
||||||
|
}
|
||||||
|
netStats[iface.Name] = stat
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
timeDiff := now.Sub(stat.LastTime).Seconds()
|
||||||
|
sentDiff := float64(iface.BytesSent - stat.LastBytesSent)
|
||||||
|
recvDiff := float64(iface.BytesRecv - stat.LastBytesRecv)
|
||||||
|
|
||||||
|
stat.CurrentSentRate = sentDiff / timeDiff
|
||||||
|
stat.CurrentRecvRate = recvDiff / timeDiff
|
||||||
|
stat.LastBytesSent = iface.BytesSent
|
||||||
|
stat.LastBytesRecv = iface.BytesRecv
|
||||||
|
stat.LastTime = now
|
||||||
|
|
||||||
|
totalSent += stat.CurrentSentRate
|
||||||
|
totalRecv += stat.CurrentRecvRate
|
||||||
|
}
|
||||||
|
|
||||||
|
view.SetText(fmt.Sprintf("[red]Network: [white]↑%s ↓%s",
|
||||||
|
formatSpeed(totalSent),
|
||||||
|
formatSpeed(totalRecv)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateDiskIO(view *tview.TextView) {
|
||||||
|
ioStats, _ := disk.IOCounters()
|
||||||
|
text := "[red]Disk I/O:\n"
|
||||||
|
|
||||||
|
for name, io := range ioStats {
|
||||||
|
last, exists := diskIOHistory[name]
|
||||||
|
if exists {
|
||||||
|
interval := float64(refreshInterval.Seconds())
|
||||||
|
readSpeed := float64(io.ReadBytes-last.ReadBytes) / interval
|
||||||
|
writeSpeed := float64(io.WriteBytes-last.WriteBytes) / interval
|
||||||
|
|
||||||
|
text += fmt.Sprintf(" %-8s R:%s W:%s\n",
|
||||||
|
name,
|
||||||
|
formatSpeed(readSpeed),
|
||||||
|
formatSpeed(writeSpeed))
|
||||||
|
}
|
||||||
|
diskIOHistory[name] = io
|
||||||
|
}
|
||||||
|
view.SetText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func progressBar(percent float64, width int) string {
|
||||||
|
filled := int(percent / 100 * float64(width))
|
||||||
|
bar := "[green]"
|
||||||
|
for i := 0; i < width; i++ {
|
||||||
|
if i < filled {
|
||||||
|
bar += "■"
|
||||||
|
} else {
|
||||||
|
bar += "□"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bar + "[white]"
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSpeed(speed float64) string {
|
||||||
|
const (
|
||||||
|
KB = 1024
|
||||||
|
MB = KB * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case speed >= MB:
|
||||||
|
return fmt.Sprintf("%7.3f MB/s", speed/MB)
|
||||||
|
case speed >= KB:
|
||||||
|
return fmt.Sprintf("%7.3f KB/s", speed/KB)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%7.3f B/s", speed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveNMONData() {
|
||||||
|
fileMutex.Lock()
|
||||||
|
defer fileMutex.Unlock()
|
||||||
|
|
||||||
|
f, err := os.OpenFile(savePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
now := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
cpuPercent, _ := cpu.Percent(0, false)
|
||||||
|
memStat, _ := mem.VirtualMemory()
|
||||||
|
ioStats, _ := disk.IOCounters()
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "%s,CPU_ALL,%.2f\n", now, cpuPercent[0])
|
||||||
|
fmt.Fprintf(f, "%s,MEM,%.2f,%.2f\n", now, memStat.UsedPercent, memStat.Available)
|
||||||
|
|
||||||
|
for name, io := range ioStats {
|
||||||
|
fmt.Fprintf(f, "%s,DISKIO,%s,%d,%d\n",
|
||||||
|
now, name, io.ReadBytes, io.WriteBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文本模式相关函数
|
||||||
|
func runTextMode() {
|
||||||
|
|
||||||
|
ticker := time.NewTicker(refreshInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Println(getCPUInfo())
|
||||||
|
fmt.Println(getMemoryInfo())
|
||||||
|
fmt.Println(getNetworkInfo())
|
||||||
|
fmt.Println(getDiskIOInfo())
|
||||||
|
|
||||||
|
if savePath != "" {
|
||||||
|
go saveNMONData()
|
||||||
|
}
|
||||||
|
<-ticker.C
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据获取函数
|
||||||
|
func getCPUInfo() string {
|
||||||
|
|
||||||
|
percent, _ := cpu.Percent(time.Second, false)
|
||||||
|
loadAvg, _ := load.Avg()
|
||||||
|
fmt.Print("\033[H\033[2J")
|
||||||
|
fmt.Println("=== System Monitor ===")
|
||||||
|
text := fmt.Sprintf("[CPU]\nLoad: %.2f%% Avg: %.2f, %.2f, %.2f",
|
||||||
|
percent[0],
|
||||||
|
loadAvg.Load1,
|
||||||
|
loadAvg.Load5,
|
||||||
|
loadAvg.Load15)
|
||||||
|
|
||||||
|
if showCores {
|
||||||
|
perCPU, _ := cpu.Percent(time.Second, true)
|
||||||
|
for i, p := range perCPU {
|
||||||
|
text += fmt.Sprintf("\nCore %d: %s %.2f%%",
|
||||||
|
i+1,
|
||||||
|
textProgressBar(p, 20),
|
||||||
|
p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMemoryInfo() string {
|
||||||
|
memStat, _ := mem.VirtualMemory()
|
||||||
|
return fmt.Sprintf("[Memory]\nUsed: %.2f%% (%.2fG/%.2fG)\n%s",
|
||||||
|
memStat.UsedPercent,
|
||||||
|
float64(memStat.Used)/1024/1024/1024,
|
||||||
|
float64(memStat.Total)/1024/1024/1024,
|
||||||
|
textProgressBar(memStat.UsedPercent, 50))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNetworkInfo() string {
|
||||||
|
interfaces, _ := net.IOCounters(true)
|
||||||
|
now := time.Now()
|
||||||
|
var sent, recv float64
|
||||||
|
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.Name == "lo" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, exists := netStats[iface.Name]
|
||||||
|
if !exists {
|
||||||
|
stat = &NetStat{
|
||||||
|
LastBytesSent: iface.BytesSent,
|
||||||
|
LastBytesRecv: iface.BytesRecv,
|
||||||
|
LastTime: now,
|
||||||
|
}
|
||||||
|
netStats[iface.Name] = stat
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
timeDiff := now.Sub(stat.LastTime).Seconds()
|
||||||
|
sentDiff := float64(iface.BytesSent - stat.LastBytesSent)
|
||||||
|
recvDiff := float64(iface.BytesRecv - stat.LastBytesRecv)
|
||||||
|
|
||||||
|
stat.CurrentSentRate = sentDiff / timeDiff
|
||||||
|
stat.CurrentRecvRate = recvDiff / timeDiff
|
||||||
|
stat.LastBytesSent = iface.BytesSent
|
||||||
|
stat.LastBytesRecv = iface.BytesRecv
|
||||||
|
stat.LastTime = now
|
||||||
|
|
||||||
|
sent += stat.CurrentSentRate
|
||||||
|
recv += stat.CurrentRecvRate
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[Network]\nUpload: %s\nDownload: %s",
|
||||||
|
formatSpeed(sent),
|
||||||
|
formatSpeed(recv))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDiskIOInfo() string {
|
||||||
|
ioStats, _ := disk.IOCounters()
|
||||||
|
text := "[Disk I/O]"
|
||||||
|
|
||||||
|
for name, io := range ioStats {
|
||||||
|
last, exists := diskIOHistory[name]
|
||||||
|
if exists {
|
||||||
|
interval := float64(refreshInterval.Seconds())
|
||||||
|
read := float64(io.ReadBytes-last.ReadBytes) / interval
|
||||||
|
write := float64(io.WriteBytes-last.WriteBytes) / interval
|
||||||
|
|
||||||
|
text += fmt.Sprintf("\n%-8s Read: %s Write: %s",
|
||||||
|
name,
|
||||||
|
formatSpeed(read),
|
||||||
|
formatSpeed(write))
|
||||||
|
}
|
||||||
|
diskIOHistory[name] = io
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
func textProgressBar(percent float64, width int) string {
|
||||||
|
filled := int(percent / 100 * float64(width))
|
||||||
|
bar := ""
|
||||||
|
for i := 0; i < width; i++ {
|
||||||
|
if i < filled {
|
||||||
|
bar += "■"
|
||||||
|
} else {
|
||||||
|
bar += "□"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bar
|
||||||
|
}
|
@ -845,7 +845,7 @@ func sendIPv6(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
addr := syscall.SockaddrInet6{
|
addr := syscall.SockaddrInet6{
|
||||||
Port: dPort,
|
Port: 0,
|
||||||
Addr: [16]byte(dstNetIP.To16()),
|
Addr: [16]byte(dstNetIP.To16()),
|
||||||
}
|
}
|
||||||
return syscall.Sendto(fd, buffer.Bytes(), 0, &addr)
|
return syscall.Sendto(fd, buffer.Bytes(), 0, &addr)
|
||||||
|
@ -365,7 +365,7 @@ func sendIPv6(srcIP, srcPort, dstIP, dstPort string, seq uint32, isSyn bool) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
addr := syscall.SockaddrInet6{
|
addr := syscall.SockaddrInet6{
|
||||||
Port: dPort,
|
Port: 0,
|
||||||
Addr: [16]byte(dstNetIP.To16()),
|
Addr: [16]byte(dstNetIP.To16()),
|
||||||
}
|
}
|
||||||
return syscall.Sendto(fd, buffer.Bytes(), 0, &addr)
|
return syscall.Sendto(fd, buffer.Bytes(), 0, &addr)
|
||||||
|
@ -3,5 +3,5 @@ package tls
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestCert(t *testing.T) {
|
func TestCert(t *testing.T) {
|
||||||
showTls("139.199.163.65:443", true, "")
|
//showTls("139.199.163.65:443", true, "")
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
var Version = "2.1.0.beta.16"
|
var Version = "2.1.0.beta.17"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user