mirror of
https://github.com/oneclickvirt/basics.git
synced 2025-10-05 16:48:09 +08:00
update
This commit is contained in:
7
cmd/main.go
Normal file
7
cmd/main.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "github.com/oneclickvirt/basics/system"
|
||||
|
||||
func main() {
|
||||
system.GetSystemInfo()
|
||||
}
|
35
defaultset/defaultset.go
Normal file
35
defaultset/defaultset.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package defaultset
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Red(text string) string {
|
||||
return fmt.Sprintf("\033[31m\033[01m%s\033[0m", text)
|
||||
}
|
||||
|
||||
func Green(text string) string {
|
||||
return fmt.Sprintf("\033[32m\033[01m%s\033[0m", text)
|
||||
}
|
||||
|
||||
func DarkGreen(text string) string {
|
||||
return fmt.Sprintf("\033[32m\033[02m%s\033[0m", text)
|
||||
}
|
||||
|
||||
func Yellow(text string) string {
|
||||
return fmt.Sprintf("\033[33m\033[01m%s\033[0m", text)
|
||||
}
|
||||
|
||||
func Blue(text string) string {
|
||||
return fmt.Sprintf("\033[36m\033[01m%s\033[0m", text)
|
||||
}
|
||||
|
||||
func Purple(text string) string {
|
||||
return fmt.Sprintf("\033[35m\033[01m%s\033[0m", text)
|
||||
}
|
||||
|
||||
func Cyan(text string) string {
|
||||
return fmt.Sprintf("\033[36m\033[01m%s\033[0m", text)
|
||||
}
|
||||
|
||||
func White(text string) string {
|
||||
return fmt.Sprintf("\033[37m\033[01m%s\033[0m", text)
|
||||
}
|
24
go.mod
Normal file
24
go.mod
Normal file
@@ -0,0 +1,24 @@
|
||||
module github.com/oneclickvirt/basics
|
||||
|
||||
go 1.21.5
|
||||
|
||||
require (
|
||||
github.com/libp2p/go-nat v0.2.0
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/huin/goupnp v1.2.0 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/koron/go-ssdp v0.0.4 // indirect
|
||||
github.com/libp2p/go-netroute v0.2.1 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
)
|
50
go.sum
Normal file
50
go.sum
Normal file
@@ -0,0 +1,50 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY=
|
||||
github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
|
||||
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
|
||||
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
|
||||
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
|
||||
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
|
||||
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
|
||||
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
254
network/baseinfo/baseinfo.go
Normal file
254
network/baseinfo/baseinfo.go
Normal file
@@ -0,0 +1,254 @@
|
||||
package baseinfo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/oneclickvirt/basics/network/model"
|
||||
"github.com/oneclickvirt/basics/network/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// FetchIPInfoIo 从 ipinfo.io 获取 IP 信息
|
||||
func FetchIPInfoIo(netType string) (*model.IpInfo, *model.SecurityInfo, error) {
|
||||
data, err := utils.FetchJsonFromURL("http://ipinfo.io", netType, false, "")
|
||||
if err == nil {
|
||||
res := &model.IpInfo{}
|
||||
if ip, ok := data["ip"].(string); ok && ip != "" {
|
||||
res.Ip = ip
|
||||
}
|
||||
if city, ok := data["city"].(string); ok && city != "" {
|
||||
res.City = city
|
||||
}
|
||||
if region, ok := data["region"].(string); ok && region != "" {
|
||||
res.Region = region
|
||||
}
|
||||
if country, ok := data["country"].(string); ok && country != "" {
|
||||
res.Country = country
|
||||
}
|
||||
if org, ok := data["org"].(string); ok && org != "" {
|
||||
parts := strings.Split(org, " ")
|
||||
if len(parts) > 0 {
|
||||
res.ASN = parts[0]
|
||||
res.Org = strings.Join(parts[1:], " ")
|
||||
} else {
|
||||
res.ASN = org
|
||||
}
|
||||
}
|
||||
return res, nil, nil
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// FetchCloudFlare 从 speed.cloudflare.com 获取 IP 信息
|
||||
func FetchCloudFlare(netType string) (*model.IpInfo, *model.SecurityInfo, error) {
|
||||
data, err := utils.FetchJsonFromURL("https://speed.cloudflare.com/meta", netType, false, "")
|
||||
if err == nil {
|
||||
res := &model.IpInfo{}
|
||||
if ip, ok := data["clientIp"].(string); ok && ip != "" {
|
||||
res.Ip = ip
|
||||
}
|
||||
if city, ok := data["city"].(string); ok && city != "" {
|
||||
res.City = city
|
||||
}
|
||||
if region, ok := data["region"].(string); ok && region != "" {
|
||||
res.Region = region
|
||||
}
|
||||
if country, ok := data["country"].(string); ok && country != "" {
|
||||
res.Country = country
|
||||
}
|
||||
if asnFloat, ok := data["asn"].(float64); ok {
|
||||
res.ASN = strconv.FormatInt(int64(asnFloat), 10)
|
||||
} else if asnStr, ok := data["asn"].(string); ok && asnStr != "" {
|
||||
res.ASN = asnStr
|
||||
}
|
||||
if org, ok := data["asOrganization"].(string); ok && org != "" {
|
||||
res.Org = org
|
||||
}
|
||||
return res, nil, nil
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// FetchIpSb 从 api.ip.sb 获取 IP 信息
|
||||
func FetchIpSb(netType string) (*model.IpInfo, *model.SecurityInfo, error) {
|
||||
data, err := utils.FetchJsonFromURL("https://api.ip.sb/geoip", netType, true, "")
|
||||
if err == nil {
|
||||
res := &model.IpInfo{}
|
||||
if ip, ok := data["ip"].(string); ok && ip != "" {
|
||||
res.Ip = ip
|
||||
}
|
||||
if city, ok := data["city"].(string); ok && city != "" {
|
||||
res.City = city
|
||||
}
|
||||
if region, ok := data["region"].(string); ok && region != "" {
|
||||
res.Region = region
|
||||
}
|
||||
if country, ok := data["country"].(string); ok && country != "" {
|
||||
res.Country = country
|
||||
}
|
||||
if asnFloat, ok := data["asn"].(float64); ok {
|
||||
res.ASN = strconv.FormatInt(int64(asnFloat), 10)
|
||||
} else if asnStr, ok := data["asn"].(string); ok && asnStr != "" {
|
||||
res.ASN = asnStr
|
||||
}
|
||||
if org, ok := data["asn_organization"].(string); ok && org != "" {
|
||||
res.Org = org
|
||||
}
|
||||
return res, nil, nil
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// FetchIpDataCheerVision 从 ipdata.cheervision.co 获取 IP 信息
|
||||
func FetchIpDataCheerVision(netType string) (*model.IpInfo, *model.SecurityInfo, error) {
|
||||
data, err := utils.FetchJsonFromURL("https://ipdata.cheervision.co", netType, true, "")
|
||||
if err == nil {
|
||||
ipInfo := utils.ParseIpInfo(data)
|
||||
securityInfo := utils.ParseSecurityInfo(data)
|
||||
return ipInfo, securityInfo, nil
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// executeFunctions 并发执行函数
|
||||
// 仅区分IPV4或IPV6,BOTH的情况需要两次执行本函数分别指定
|
||||
func executeFunctions(checkType string, fetchFunc func(string) (*model.IpInfo, *model.SecurityInfo, error), ipInfoChan chan *model.IpInfo, securityInfoChan chan *model.SecurityInfo, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
ipFetcher := func(ipType string) {
|
||||
ipInfo, securityInfo, err := fetchFunc(ipType)
|
||||
if err == nil {
|
||||
select {
|
||||
case ipInfoChan <- ipInfo:
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case securityInfoChan <- securityInfo:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case ipInfoChan <- nil:
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case securityInfoChan <- nil:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
if checkType == "ipv4" {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
ipFetcher("tcp4")
|
||||
}()
|
||||
}
|
||||
if checkType == "ipv6" {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
ipFetcher("tcp6")
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// RunIpCheck 并发请求获取信息
|
||||
func RunIpCheck(checkType string) (*model.IpInfo, *model.SecurityInfo, *model.IpInfo, *model.SecurityInfo, error) {
|
||||
// 定义函数名数组
|
||||
functions := []func(string) (*model.IpInfo, *model.SecurityInfo, error){
|
||||
FetchIPInfoIo,
|
||||
FetchCloudFlare,
|
||||
FetchIpSb,
|
||||
FetchIpDataCheerVision,
|
||||
}
|
||||
// 定义通道
|
||||
ipInfoIPv4 := make(chan *model.IpInfo, len(functions))
|
||||
securityInfoIPv4 := make(chan *model.SecurityInfo, len(functions))
|
||||
ipInfoIPv6 := make(chan *model.IpInfo, len(functions))
|
||||
securityInfoIPv6 := make(chan *model.SecurityInfo, len(functions))
|
||||
var wg sync.WaitGroup
|
||||
if checkType == "both" {
|
||||
wg.Add(len(functions) * 2) // 每个函数都会产生一个 IPv4 和一个 IPv6 结果
|
||||
// 启动协程执行函数
|
||||
for _, f := range functions {
|
||||
go executeFunctions("ipv4", f, ipInfoIPv4, securityInfoIPv4, &wg)
|
||||
go executeFunctions("ipv6", f, ipInfoIPv6, securityInfoIPv6, &wg)
|
||||
}
|
||||
} else if checkType == "ipv4" {
|
||||
wg.Add(len(functions)) // 每个函数都会产生一个 IPv4 结果
|
||||
// 启动协程执行函数
|
||||
for _, f := range functions {
|
||||
go executeFunctions("ipv4", f, ipInfoIPv4, securityInfoIPv4, &wg)
|
||||
}
|
||||
} else if checkType == "ipv6" {
|
||||
wg.Add(len(functions)) // 每个函数都会产生一个 IPv6 结果
|
||||
// 启动协程执行函数
|
||||
for _, f := range functions {
|
||||
go executeFunctions("ipv6", f, ipInfoIPv6, securityInfoIPv6, &wg)
|
||||
}
|
||||
} else {
|
||||
return nil, nil, nil, nil, fmt.Errorf("wrong checkType")
|
||||
}
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(ipInfoIPv4)
|
||||
close(securityInfoIPv4)
|
||||
close(ipInfoIPv6)
|
||||
close(securityInfoIPv6)
|
||||
}()
|
||||
// 读取结果并处理
|
||||
var ipInfoV4Result *model.IpInfo
|
||||
var ipInfoV6Result *model.IpInfo
|
||||
var securityInfoV4Result *model.SecurityInfo
|
||||
var securityInfoV6Result *model.SecurityInfo
|
||||
for ipInfo := range ipInfoIPv4 {
|
||||
if ipInfo != nil {
|
||||
if ipInfoV4Result == nil {
|
||||
ipInfoV4Result = &model.IpInfo{}
|
||||
}
|
||||
ipInfoV4TempResult, err := utils.CompareAndMergeIpInfo(ipInfoV4Result, ipInfo)
|
||||
if err == nil {
|
||||
ipInfoV4Result = ipInfoV4TempResult
|
||||
}
|
||||
}
|
||||
}
|
||||
for ipInfo := range ipInfoIPv6 {
|
||||
if ipInfo != nil {
|
||||
if ipInfoV6Result == nil {
|
||||
ipInfoV6Result = &model.IpInfo{}
|
||||
}
|
||||
ipInfoV6TempResult, err := utils.CompareAndMergeIpInfo(ipInfoV6Result, ipInfo)
|
||||
if err == nil {
|
||||
ipInfoV6Result = ipInfoV6TempResult
|
||||
}
|
||||
}
|
||||
}
|
||||
for securityInfo := range securityInfoIPv4 {
|
||||
if securityInfo != nil {
|
||||
if securityInfoV4Result == nil {
|
||||
securityInfoV4Result = &model.SecurityInfo{}
|
||||
}
|
||||
securityInfoV4TempResult, err := utils.CompareAndMergeSecurityInfo(securityInfoV4Result, securityInfo)
|
||||
if err == nil {
|
||||
securityInfoV4Result = securityInfoV4TempResult
|
||||
}
|
||||
}
|
||||
}
|
||||
for securityInfo := range securityInfoIPv6 {
|
||||
if securityInfo != nil {
|
||||
if securityInfoV6Result == nil {
|
||||
securityInfoV6Result = &model.SecurityInfo{}
|
||||
}
|
||||
securityInfoV6TempResult, err := utils.CompareAndMergeSecurityInfo(securityInfoV6Result, securityInfo)
|
||||
if err == nil {
|
||||
securityInfoV6Result = securityInfoV6TempResult
|
||||
}
|
||||
}
|
||||
}
|
||||
return ipInfoV4Result, securityInfoV4Result, ipInfoV6Result, securityInfoV6Result, nil
|
||||
}
|
82
network/baseinfo/baseinfo_test.go
Normal file
82
network/baseinfo/baseinfo_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package baseinfo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
networkModel "github.com/oneclickvirt/basics/network/model"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// printIPInfo 重构输出函数
|
||||
func printIPInfo(ipInfo *networkModel.IpInfo, securityInfo *networkModel.SecurityInfo, err error) {
|
||||
if err != nil {
|
||||
fmt.Println("获取 IP 信息时出错:", err)
|
||||
return
|
||||
}
|
||||
if ipInfo != nil {
|
||||
fmt.Println("IPInfo:")
|
||||
fmt.Println("IP:", ipInfo.Ip)
|
||||
fmt.Println("ASN:", ipInfo.ASN)
|
||||
fmt.Println("Org:", ipInfo.Org)
|
||||
fmt.Println("Country:", ipInfo.Country)
|
||||
fmt.Println("Region:", ipInfo.Region)
|
||||
fmt.Println("City:", ipInfo.City)
|
||||
fmt.Println("---------------------------------")
|
||||
}
|
||||
if securityInfo != nil {
|
||||
fmt.Println("Security Info:")
|
||||
fmt.Println("IsAbuser:", securityInfo.IsAbuser)
|
||||
fmt.Println("IsAttacker:", securityInfo.IsAttacker)
|
||||
fmt.Println("IsBogon:", securityInfo.IsBogon)
|
||||
fmt.Println("IsCloudProvider:", securityInfo.IsCloudProvider)
|
||||
fmt.Println("IsProxy:", securityInfo.IsProxy)
|
||||
fmt.Println("IsRelay:", securityInfo.IsRelay)
|
||||
fmt.Println("IsTor:", securityInfo.IsTor)
|
||||
fmt.Println("IsTorExit:", securityInfo.IsTorExit)
|
||||
fmt.Println("IsVpn:", securityInfo.IsVpn)
|
||||
fmt.Println("IsAnonymous:", securityInfo.IsAnonymous)
|
||||
fmt.Println("IsThreat:", securityInfo.IsThreat)
|
||||
fmt.Println("---------------------------------")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPInfo(t *testing.T) {
|
||||
// Test for IPv4
|
||||
fmt.Println("IPv4 Testing:")
|
||||
startV4 := time.Now()
|
||||
ipInfoV4Result, securityInfoV4Result, _, _, err := RunIpCheck("ipv4")
|
||||
elapsedV4 := time.Since(startV4)
|
||||
if err == nil {
|
||||
fmt.Println("IPv4:")
|
||||
fmt.Println("------")
|
||||
printIPInfo(ipInfoV4Result, securityInfoV4Result, nil)
|
||||
}
|
||||
fmt.Println("---***********************************---")
|
||||
// Test for IPv6
|
||||
fmt.Println("IPv6 Testing:")
|
||||
startV6 := time.Now()
|
||||
ipInfoV6Result, securityInfoV6Result, _, _, err := RunIpCheck("ipv6")
|
||||
elapsedV6 := time.Since(startV6)
|
||||
if err == nil {
|
||||
fmt.Println("IPv6:")
|
||||
fmt.Println("------")
|
||||
printIPInfo(ipInfoV6Result, securityInfoV6Result, nil)
|
||||
}
|
||||
fmt.Println("---***********************************---")
|
||||
// Test for both IPv4 and IPv6
|
||||
fmt.Println("Both Testing:")
|
||||
startBoth := time.Now()
|
||||
ipInfoV4Result, securityInfoV4Result, ipInfoV6Result, securityInfoV6Result, err = RunIpCheck("both")
|
||||
elapsedBoth := time.Since(startBoth)
|
||||
if err == nil {
|
||||
fmt.Println("IPv4:")
|
||||
fmt.Println("------")
|
||||
printIPInfo(ipInfoV4Result, securityInfoV4Result, nil)
|
||||
fmt.Println("IPv6:")
|
||||
fmt.Println("------")
|
||||
printIPInfo(ipInfoV6Result, securityInfoV6Result, nil)
|
||||
}
|
||||
fmt.Printf("IPv4 test took %s\n", elapsedV4)
|
||||
fmt.Printf("IPv6 test took %s\n", elapsedV6)
|
||||
fmt.Printf("Both test took %s\n", elapsedBoth)
|
||||
}
|
0
network/baseinfo/ecs.log
Normal file
0
network/baseinfo/ecs.log
Normal file
90
network/model/model.go
Normal file
90
network/model/model.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package model
|
||||
|
||||
type IpInfo struct {
|
||||
Ip string
|
||||
ASN string
|
||||
Org string
|
||||
Country string
|
||||
Region string
|
||||
City string
|
||||
}
|
||||
|
||||
type SecurityScore struct {
|
||||
Tag string
|
||||
Reputation *int
|
||||
TrustScore *int
|
||||
VpnScore *int
|
||||
ProxyScore *int
|
||||
CommunityVoteHarmless *int
|
||||
CommunityVoteMalicious *int
|
||||
CloudFlareRisk *int // 还没有加入
|
||||
ThreatScore *int
|
||||
FraudScore *int
|
||||
AbuseScore *int
|
||||
HarmlessnessRecords *int
|
||||
MaliciousRecords *int
|
||||
SuspiciousRecords *int
|
||||
NoRecords *int
|
||||
}
|
||||
|
||||
type SecurityInfo struct {
|
||||
Tag string
|
||||
ASNAbuseScore string // 这三个实际是得分类型,但由于是字符串所以还在这解析
|
||||
CompannyAbuseScore string
|
||||
ThreatLevel string
|
||||
UsageType string // connection_type、usage_type、asn_type
|
||||
CompanyType string // company type
|
||||
IsCloudProvider string
|
||||
IsDatacenter string // datacenter、server、hosting
|
||||
IsMobile string
|
||||
IsProxy string // Public Proxy、Web Proxy
|
||||
IsVpn string
|
||||
IsTor string
|
||||
IsTorExit string
|
||||
IsCrawler string
|
||||
IsAnonymous string
|
||||
IsAttacker string
|
||||
IsAbuser string
|
||||
IsThreat string
|
||||
IsRelay string // icloud_relay、is_relay
|
||||
IsBogon string
|
||||
IsBot string // Search Engine Robot
|
||||
}
|
||||
|
||||
// TranslationMap 定义英文到中文的映射表
|
||||
var TranslationMap = map[string]string{
|
||||
"Reputation": "声誉(越高越好)",
|
||||
"TrustScore": "信任得分(越高越好)",
|
||||
"VpnScore": "VPN得分(越低越好)",
|
||||
"ProxyScore": "代理得分(越低越好)",
|
||||
"CommunityVoteHarmless": "社区投票-无害",
|
||||
"CommunityVoteMalicious": "社区投票-恶意",
|
||||
"CloudFlareRisk": "CloudFlare风险(越低越好)",
|
||||
"ThreatScore": "威胁得分(越低越好)",
|
||||
"FraudScore": "欺诈得分(越低越好)",
|
||||
"AbuseScore": "滥用得分(越低越好)",
|
||||
"HarmlessnessRecords": "无害记录数",
|
||||
"MaliciousRecords": "恶意记录数",
|
||||
"SuspiciousRecords": "可疑记录数",
|
||||
"NoRecords": "无记录数",
|
||||
"ASNAbuseScore": "ASN滥用得分(越低越好)",
|
||||
"CompannyAbuseScore": "公司滥用得分(越低越好)",
|
||||
"ThreatLevel": "威胁级别",
|
||||
"UsageType": "使用类型",
|
||||
"CompanyType": "公司类型",
|
||||
"IsCloudProvider": "是否云提供商",
|
||||
"IsDatacenter": "是否数据中心",
|
||||
"IsMobile": "是否移动设备",
|
||||
"IsProxy": "是否代理",
|
||||
"IsVpn": "是否VPN",
|
||||
"IsTor": "是否Tor",
|
||||
"IsTorExit": "是否Tor出口",
|
||||
"IsCrawler": "是否网络爬虫",
|
||||
"IsAnonymous": "是否匿名",
|
||||
"IsAttacker": "是否攻击者",
|
||||
"IsAbuser": "是否滥用者",
|
||||
"IsThreat": "是否威胁",
|
||||
"IsRelay": "是否中继",
|
||||
"IsBogon": "是否Bogon",
|
||||
"IsBot": "是否机器人",
|
||||
}
|
97
network/network.go
Normal file
97
network/network.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/oneclickvirt/basics/network/baseinfo"
|
||||
"github.com/oneclickvirt/basics/network/model"
|
||||
)
|
||||
|
||||
// sortAndTranslateText 对原始文本进行排序和翻译
|
||||
func sortAndTranslateText(orginList []string, language string, fields []string) string {
|
||||
var result string
|
||||
for _, key := range fields {
|
||||
var displayKey string
|
||||
if language == "zh" {
|
||||
displayKey = model.TranslationMap[key]
|
||||
if displayKey == "" {
|
||||
displayKey = key
|
||||
}
|
||||
} else {
|
||||
displayKey = key
|
||||
}
|
||||
for _, line := range orginList {
|
||||
if strings.Contains(line, key) {
|
||||
if displayKey == key {
|
||||
result = result + line + "\n"
|
||||
} else {
|
||||
result = result + strings.ReplaceAll(line, key, displayKey) + "\n"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// processPrintIPInfo 处理IP信息
|
||||
func processPrintIPInfo(headASNString string, headLocationString string, ipResult *model.IpInfo) string {
|
||||
var info string
|
||||
// 处理ASN信息
|
||||
if ipResult.ASN != "" || ipResult.Org != "" {
|
||||
info += headASNString
|
||||
if ipResult.ASN != "" {
|
||||
info += "AS" + ipResult.ASN
|
||||
if ipResult.Org != "" {
|
||||
info += " "
|
||||
}
|
||||
}
|
||||
info += ipResult.Org + "\n"
|
||||
}
|
||||
// 处理位置信息
|
||||
if ipResult.City != "" || ipResult.Region != "" || ipResult.Country != "" {
|
||||
info += headLocationString
|
||||
if ipResult.City != "" {
|
||||
info += ipResult.City + " / "
|
||||
}
|
||||
if ipResult.Region != "" {
|
||||
info += ipResult.Region + " / "
|
||||
}
|
||||
if ipResult.Country != "" {
|
||||
info += ipResult.Country
|
||||
}
|
||||
info += "\n"
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// NetworkCheck 查询网络信息
|
||||
// checkType 可选 both ipv4 ipv6
|
||||
// language 暂时仅支持 en 或 zh
|
||||
func NetworkCheck(checkType string, enableSecurityCheck bool, language string) (string, string, error) {
|
||||
var ipInfo string
|
||||
if checkType == "both" {
|
||||
ipInfoV4Result, _, ipInfoV6Result, _, _ := baseinfo.RunIpCheck("both")
|
||||
if ipInfoV4Result != nil {
|
||||
ipInfo += processPrintIPInfo(" IPV4 ASN: ", " IPV4 Location: ", ipInfoV4Result)
|
||||
}
|
||||
if ipInfoV6Result != nil {
|
||||
ipInfo += processPrintIPInfo(" IPV6 ASN: ", " IPV6 Location: ", ipInfoV6Result)
|
||||
}
|
||||
return ipInfo, "", nil
|
||||
} else if checkType == "ipv4" {
|
||||
ipInfoV4Result, _, _, _, _ := baseinfo.RunIpCheck("ipv4")
|
||||
if ipInfoV4Result != nil {
|
||||
ipInfo += processPrintIPInfo(" IPV4 ASN: ", " IPV4 Location: ", ipInfoV4Result)
|
||||
}
|
||||
return ipInfo, "", nil
|
||||
} else if checkType == "ipv6" {
|
||||
_, _, ipInfoV6Result, _, _ := baseinfo.RunIpCheck("ipv6")
|
||||
if ipInfoV6Result != nil {
|
||||
ipInfo += processPrintIPInfo(" IPV6 ASN: ", " IPV6 Location: ", ipInfoV6Result)
|
||||
}
|
||||
return ipInfo, "", nil
|
||||
}
|
||||
return "", "", fmt.Errorf("wrong in NetworkCheck")
|
||||
}
|
14
network/network_test.go
Normal file
14
network/network_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIpv4SecurityCheck(t *testing.T) {
|
||||
// 全项测试
|
||||
ipInfo, _, _ := NetworkCheck("both", false, "zh")
|
||||
fmt.Println("--------------------------------------------------")
|
||||
fmt.Printf(ipInfo)
|
||||
fmt.Println("--------------------------------------------------")
|
||||
}
|
54
network/utils/merge.go
Normal file
54
network/utils/merge.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
networkModel "github.com/oneclickvirt/basics/network/model"
|
||||
)
|
||||
|
||||
// chooseString 用于选择非空字符串
|
||||
func chooseString(src, dst string) string {
|
||||
if src != "" {
|
||||
return src
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// CompareAndMergeIpInfo 用于比较和合并两个 IpInfo 结构体
|
||||
func CompareAndMergeIpInfo(dst, src *networkModel.IpInfo) (res *networkModel.IpInfo, err error) {
|
||||
if src == nil {
|
||||
return nil, fmt.Errorf("Error merge IpInfo")
|
||||
}
|
||||
if dst == nil {
|
||||
dst = &networkModel.IpInfo{}
|
||||
}
|
||||
dst.Ip = chooseString(src.Ip, dst.Ip)
|
||||
dst.ASN = chooseString(src.ASN, dst.ASN)
|
||||
dst.Org = chooseString(src.Org, dst.Org)
|
||||
dst.Country = chooseString(src.Country, dst.Country)
|
||||
dst.Region = chooseString(src.Region, dst.Region)
|
||||
dst.City = chooseString(src.City, dst.City)
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// CompareAndMergeSecurityInfo 用于比较和合并两个 SecurityInfo 结构体
|
||||
func CompareAndMergeSecurityInfo(dst, src *networkModel.SecurityInfo) (res *networkModel.SecurityInfo, err error) {
|
||||
if src == nil {
|
||||
return nil, fmt.Errorf("Error merge SecurityInfo")
|
||||
}
|
||||
if dst == nil {
|
||||
dst = &networkModel.SecurityInfo{}
|
||||
}
|
||||
dst.IsAbuser = chooseString(src.IsAbuser, dst.IsAbuser)
|
||||
dst.IsAttacker = chooseString(src.IsAttacker, dst.IsAttacker)
|
||||
dst.IsBogon = chooseString(src.IsBogon, dst.IsBogon)
|
||||
dst.IsCloudProvider = chooseString(src.IsCloudProvider, dst.IsCloudProvider)
|
||||
dst.IsProxy = chooseString(src.IsProxy, dst.IsProxy)
|
||||
dst.IsRelay = chooseString(src.IsRelay, dst.IsRelay)
|
||||
dst.IsTor = chooseString(src.IsTor, dst.IsTor)
|
||||
dst.IsTorExit = chooseString(src.IsTorExit, dst.IsTorExit)
|
||||
dst.IsVpn = chooseString(src.IsVpn, dst.IsVpn)
|
||||
dst.IsAnonymous = chooseString(src.IsAnonymous, dst.IsAnonymous)
|
||||
dst.IsThreat = chooseString(src.IsThreat, dst.IsThreat)
|
||||
return dst, nil
|
||||
}
|
86
network/utils/parse.go
Normal file
86
network/utils/parse.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
networkModel "github.com/oneclickvirt/basics/network/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseIpInfo(data map[string]interface{}) *networkModel.IpInfo {
|
||||
ipInfo := &networkModel.IpInfo{}
|
||||
if ip, ok := data["ip"].(string); ok {
|
||||
ipInfo.Ip = ip
|
||||
}
|
||||
if location, ok := data["location"].(map[string]interface{}); ok {
|
||||
if city, ok := location["city"].(string); ok {
|
||||
ipInfo.City = city
|
||||
}
|
||||
if region, ok := location["region"].(map[string]interface{}); ok {
|
||||
if name, ok := region["name"].(string); ok {
|
||||
ipInfo.Region = name
|
||||
}
|
||||
}
|
||||
if country, ok := location["country"].(map[string]interface{}); ok {
|
||||
if name, ok := country["name"].(string); ok {
|
||||
ipInfo.Country = name
|
||||
}
|
||||
}
|
||||
}
|
||||
if connection, ok := data["connection"].(map[string]interface{}); ok {
|
||||
if asn, ok := connection["asn"].(float64); ok {
|
||||
ipInfo.ASN = strconv.Itoa(int(asn))
|
||||
}
|
||||
if org, ok := connection["organization"].(string); ok {
|
||||
ipInfo.Org = org
|
||||
}
|
||||
}
|
||||
return ipInfo
|
||||
}
|
||||
|
||||
func ParseSecurityInfo(data map[string]interface{}) *networkModel.SecurityInfo {
|
||||
securityInfo := &networkModel.SecurityInfo{}
|
||||
if security, ok := data["security"].(map[string]interface{}); ok {
|
||||
if isAbuser, ok := security["is_abuser"].(bool); ok {
|
||||
securityInfo.IsAbuser = strconv.FormatBool(isAbuser)
|
||||
}
|
||||
if isAttacker, ok := security["is_attacker"].(bool); ok {
|
||||
securityInfo.IsAttacker = strconv.FormatBool(isAttacker)
|
||||
}
|
||||
if isBogon, ok := security["is_bogon"].(bool); ok {
|
||||
securityInfo.IsBogon = strconv.FormatBool(isBogon)
|
||||
}
|
||||
if isCloudProvider, ok := security["is_cloud_provider"].(bool); ok {
|
||||
securityInfo.IsCloudProvider = strconv.FormatBool(isCloudProvider)
|
||||
}
|
||||
if isProxy, ok := security["is_proxy"].(bool); ok {
|
||||
securityInfo.IsProxy = strconv.FormatBool(isProxy)
|
||||
}
|
||||
if isRelay, ok := security["is_relay"].(bool); ok {
|
||||
securityInfo.IsRelay = strconv.FormatBool(isRelay)
|
||||
}
|
||||
if isTor, ok := security["is_tor"].(bool); ok {
|
||||
securityInfo.IsTor = strconv.FormatBool(isTor)
|
||||
}
|
||||
if isTorExit, ok := security["is_tor_exit"].(bool); ok {
|
||||
securityInfo.IsTorExit = strconv.FormatBool(isTorExit)
|
||||
}
|
||||
if isVpn, ok := security["is_vpn"].(bool); ok {
|
||||
securityInfo.IsVpn = strconv.FormatBool(isVpn)
|
||||
}
|
||||
if isAnonymous, ok := security["is_anonymous"].(bool); ok {
|
||||
securityInfo.IsAnonymous = strconv.FormatBool(isAnonymous)
|
||||
}
|
||||
if isThreat, ok := security["is_threat"].(bool); ok {
|
||||
securityInfo.IsThreat = strconv.FormatBool(isThreat)
|
||||
}
|
||||
}
|
||||
return securityInfo
|
||||
}
|
||||
|
||||
// ParseYesNo 检测文本内容含No则返回No,否则返回Yes
|
||||
func ParseYesNo(text string) string {
|
||||
if strings.Contains(strings.ToLower(text), "no") {
|
||||
return "No"
|
||||
}
|
||||
return "Yes"
|
||||
}
|
93
network/utils/utils.go
Normal file
93
network/utils/utils.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FetchJsonFromURL 函数用于从指定的 URL 获取信息
|
||||
// url 参数表示要获取信息的 URL
|
||||
// netType 参数表示网络类型,只能为 "tcp4" 或 "tcp6"。
|
||||
// enableHeader 参数表示是否启用请求头信息。
|
||||
// additionalHeader 参数表示传入的额外的请求头信息(用于传输api的key)。
|
||||
// 返回一个解析 json 得到的 map 和 一个可能发生的错误 。
|
||||
func FetchJsonFromURL(url, netType string, enableHeader bool, additionalHeader string) (map[string]interface{}, error) {
|
||||
// 检查网络类型是否有效
|
||||
if netType != "tcp4" && netType != "tcp6" {
|
||||
return nil, fmt.Errorf("Invalid netType: %s. Expected 'tcp4' or 'tcp6'.", netType)
|
||||
}
|
||||
// 创建 HTTP 客户端
|
||||
client := &http.Client{
|
||||
Timeout: 6 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
|
||||
return (&net.Dialer{}).DialContext(ctx, netType, addr)
|
||||
},
|
||||
TLSHandshakeTimeout: 3 * time.Second,
|
||||
ResponseHeaderTimeout: 3 * time.Second,
|
||||
ExpectContinueTimeout: 3 * time.Second,
|
||||
},
|
||||
}
|
||||
// 创建 HTTP 请求
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating request: %v", err)
|
||||
}
|
||||
// 如果启用请求头,则设置请求头信息
|
||||
if enableHeader {
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36")
|
||||
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
|
||||
if additionalHeader != "" {
|
||||
tempList := strings.Split(additionalHeader, ":")
|
||||
if len(tempList) == 2 {
|
||||
req.Header.Set(tempList[0], tempList[1])
|
||||
} else if len(tempList) > 2 {
|
||||
req.Header.Set(tempList[0], strings.Join(tempList[1:], ":"))
|
||||
}
|
||||
}
|
||||
}
|
||||
// 执行 HTTP 请求
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
//fmt.Printf("Error fetching %s info: %v \n", url, err)
|
||||
return nil, fmt.Errorf("Error fetching %s info: %v", url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// 解析 JSON 响应体
|
||||
var data map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&data)
|
||||
if err != nil {
|
||||
//fmt.Printf("Error decoding %s info: %v \n", url, err)
|
||||
return nil, fmt.Errorf("Error decoding %s info: %v ", url, err)
|
||||
}
|
||||
// 返回解析后的数据和 nil 错误
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// BoolToString 将布尔值转换为对应的字符串表示,true 则返回 "Yes",false 则返回 "No"
|
||||
func BoolToString(value bool) string {
|
||||
if value {
|
||||
return "Yes"
|
||||
}
|
||||
return "No"
|
||||
}
|
||||
|
||||
// ExtractFieldNames 获取结构体的属性名字
|
||||
func ExtractFieldNames(data interface{}) []string {
|
||||
var fields []string
|
||||
val := reflect.ValueOf(data).Elem()
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := val.Type().Field(i)
|
||||
name := field.Name
|
||||
if name != "Tag" {
|
||||
fields = append(fields, name)
|
||||
}
|
||||
}
|
||||
return fields
|
||||
}
|
69
system/cpu_linux.go
Normal file
69
system/cpu_linux.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/oneclickvirt/basics/system/model"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
)
|
||||
|
||||
func checkCPUFeatureLinux(filename string, feature string) (string, bool) {
|
||||
content, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "Error reading file", false
|
||||
}
|
||||
lines := strings.Split(string(content), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, feature) {
|
||||
return "✔️ Enabled", true
|
||||
}
|
||||
}
|
||||
return "❌ Disabled", false
|
||||
}
|
||||
|
||||
func checkCPUFeature(filename string, feature string) (string, bool) {
|
||||
if runtime.GOOS == "linux" {
|
||||
return checkCPUFeatureLinux(filename, feature)
|
||||
}
|
||||
return "Unsupported OS", false
|
||||
}
|
||||
|
||||
func getCpuInfo(ret *model.SystemInfo, cpuType string) (*model.SystemInfo, error) {
|
||||
var aesFeature, virtFeature, hypervFeature string
|
||||
var st bool
|
||||
ci, err := cpu.Info()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cpu.Info error: %v", err.Error())
|
||||
} else {
|
||||
ret.CpuModel = ""
|
||||
for i := 0; i < len(ci); i++ {
|
||||
if len(ret.CpuModel) < len(ci[i].ModelName) {
|
||||
ret.CpuModel = ci[i].ModelName + fmt.Sprintf("%d %s Core", len(ci), cpuType) + " @ " +
|
||||
strconv.FormatFloat(ci[i].Mhz, 'f', 2, 64) + " MHz"
|
||||
ret.CpuCores = fmt.Sprintf("%d vCPU(s)", int(ci[i].Cores))
|
||||
if ci[i].CacheSize != 0 { // Windows查不到CPU的三缓
|
||||
ret.CpuCache = string(ci[i].CacheSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
aesFeature = `HARDWARE\DESCRIPTION\System\CentralProcessor\0`
|
||||
virtFeature = `HARDWARE\DESCRIPTION\System\CentralProcessor\0`
|
||||
hypervFeature = `SYSTEM\CurrentControlSet\Control\Hypervisor\0`
|
||||
} else if runtime.GOOS == "linux" {
|
||||
aesFeature = "/proc/cpuinfo"
|
||||
virtFeature = "/proc/cpuinfo"
|
||||
hypervFeature = "/proc/cpuinfo"
|
||||
}
|
||||
ret.CpuAesNi, _ = checkCPUFeature(aesFeature, "aes")
|
||||
ret.CpuVAH, st = checkCPUFeature(virtFeature, "vmx")
|
||||
if !st {
|
||||
ret.CpuVAH, _ = checkCPUFeature(hypervFeature, "hypervisor")
|
||||
}
|
||||
return ret, nil
|
||||
}
|
96
system/disk.go
Normal file
96
system/disk.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getDiskInfo() (string, string, string, error) {
|
||||
var diskTotalStr, diskUsageStr, bootPath string
|
||||
tempDiskTotal, tempDiskUsage := getDiskTotalAndUsed()
|
||||
diskTotalGB := float64(tempDiskTotal) / (1024 * 1024 * 1024)
|
||||
diskUsageGB := float64(tempDiskUsage) / (1024 * 1024 * 1024)
|
||||
// 字节为单位 进行单位转换
|
||||
if diskTotalGB < 1 {
|
||||
diskTotalStr = strconv.FormatFloat(diskTotalGB*1024, 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
diskTotalStr = strconv.FormatFloat(diskTotalGB, 'f', 2, 64) + " GB"
|
||||
}
|
||||
if diskUsageGB < 1 {
|
||||
diskUsageStr = strconv.FormatFloat(diskUsageGB*1024, 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
diskUsageStr = strconv.FormatFloat(diskUsageGB, 'f', 2, 64) + " GB"
|
||||
}
|
||||
parts, err := disk.Partitions(true)
|
||||
if err != nil {
|
||||
bootPath = ""
|
||||
} else {
|
||||
for _, part := range parts {
|
||||
if part.Fstype == "tmpfs" {
|
||||
continue
|
||||
}
|
||||
usageStat, err := disk.Usage(part.Mountpoint)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if usageStat.Total > 0 {
|
||||
bootPath = part.Mountpoint
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return diskTotalStr, diskUsageStr, bootPath, nil
|
||||
}
|
||||
|
||||
func getDiskTotalAndUsed() (total uint64, used uint64) {
|
||||
devices := make(map[string]string)
|
||||
// 使用默认过滤规则
|
||||
diskList, _ := disk.Partitions(false)
|
||||
for _, d := range diskList {
|
||||
fsType := strings.ToLower(d.Fstype)
|
||||
// 不统计 K8s 的虚拟挂载点:https://github.com/shirou/gopsutil/issues/1007
|
||||
if devices[d.Device] == "" && isListContainsStr(expectDiskFsTypes, fsType) && !strings.Contains(d.Mountpoint, "/var/lib/kubelet") {
|
||||
devices[d.Device] = d.Mountpoint
|
||||
}
|
||||
}
|
||||
for _, mountPath := range devices {
|
||||
diskUsageOf, err := disk.Usage(mountPath)
|
||||
if err == nil {
|
||||
total += diskUsageOf.Total
|
||||
used += diskUsageOf.Used
|
||||
}
|
||||
}
|
||||
// Fallback 到这个方法,仅统计根路径,适用于OpenVZ之类的.
|
||||
if runtime.GOOS == "linux" && total == 0 && used == 0 {
|
||||
cmd := exec.Command("df")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
s := strings.Split(string(out), "\n")
|
||||
for _, c := range s {
|
||||
info := strings.Fields(c)
|
||||
if len(info) == 6 {
|
||||
if info[5] == "/" {
|
||||
total, _ = strconv.ParseUint(info[1], 0, 64)
|
||||
used, _ = strconv.ParseUint(info[2], 0, 64)
|
||||
// 默认获取的是1K块为单位的.
|
||||
total = total * 1024
|
||||
used = used * 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func isListContainsStr(list []string, str string) bool {
|
||||
for i := 0; i < len(list); i++ {
|
||||
if strings.Contains(str, list[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
52
system/host_linux.go
Normal file
52
system/host_linux.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-nat"
|
||||
"github.com/shirou/gopsutil/host"
|
||||
)
|
||||
|
||||
func getHostInfo() (string, string, string, string, string, string, string, error) {
|
||||
var Platform, Kernal, Arch, VmType, NatType string
|
||||
var cachedBootTime time.Time
|
||||
hi, err := host.Info()
|
||||
if err != nil {
|
||||
println("host.Info error:", err)
|
||||
} else {
|
||||
if hi.VirtualizationRole == "guest" {
|
||||
cpuType = "Virtual"
|
||||
} else {
|
||||
cpuType = "Physical"
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
Platform = hi.Platform
|
||||
Kernal = hi.PlatformVersion
|
||||
} else {
|
||||
Platform = hi.Platform + " " + hi.PlatformVersion
|
||||
}
|
||||
Arch = hi.KernelArch
|
||||
// 查询虚拟化类型
|
||||
VmType = hi.VirtualizationSystem
|
||||
// 系统运行时长查询
|
||||
cachedBootTime = time.Unix(int64(hi.BootTime), 0)
|
||||
}
|
||||
uptimeDuration := time.Since(cachedBootTime)
|
||||
days := int(uptimeDuration.Hours() / 24)
|
||||
uptimeDuration -= time.Duration(days*24) * time.Hour
|
||||
hours := int(uptimeDuration.Hours())
|
||||
uptimeDuration -= time.Duration(hours) * time.Hour
|
||||
minutes := int(uptimeDuration.Minutes())
|
||||
uptimeFormatted := fmt.Sprintf("%d days, %02d hours, %02d minutes", days, hours, minutes)
|
||||
// 查询NAT类型
|
||||
ctx := context.Background()
|
||||
gateway, err := nat.DiscoverGateway(ctx)
|
||||
if err == nil {
|
||||
natType := gateway.Type()
|
||||
NatType = natType
|
||||
}
|
||||
return cpuType, uptimeFormatted, Platform, Kernal, Arch, VmType, NatType, nil
|
||||
}
|
56
system/load_linux.go
Normal file
56
system/load_linux.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/load"
|
||||
)
|
||||
|
||||
// 获取系统负载信息
|
||||
func getSystemLoad() (float64, float64, float64, error) {
|
||||
var load1, load5, load15 float64
|
||||
var err error
|
||||
if runtime.GOOS == "linux" {
|
||||
// 尝试从 /proc/loadavg 文件获取负载信息
|
||||
load1, load5, load15, err = getLoadFromProc()
|
||||
if err != nil {
|
||||
load1, load5, load15 = 0, 0, 0
|
||||
}
|
||||
}
|
||||
// 使用 gopsutil 获取负载
|
||||
avg, err := load.Avg()
|
||||
if err != nil {
|
||||
load1, load5, load15 = 0, 0, 0
|
||||
} else {
|
||||
if avg.Load1 != 0 && avg.Load5 != 0 && avg.Load15 != 0 {
|
||||
load1, load5, load15 = avg.Load1, avg.Load5, avg.Load15
|
||||
}
|
||||
}
|
||||
return load1, load5, load15, nil
|
||||
}
|
||||
|
||||
// getLoadFromProc 从 /proc/loadavg 文件中获取负载信息
|
||||
func getLoadFromProc() (float64, float64, float64, error) {
|
||||
file, err := os.Open("/proc/loadavg")
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
// 解析负载信息并转换为 float64 类型
|
||||
load1, _ := strconv.ParseFloat(fields[0], 64)
|
||||
load5, _ := strconv.ParseFloat(fields[1], 64)
|
||||
load15, _ := strconv.ParseFloat(fields[2], 64)
|
||||
return load1, load5, load15, nil
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
return 0, 0, 0, nil
|
||||
}
|
89
system/memory.go
Normal file
89
system/memory.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getMemoryInfo() (string, string, string, string, string, string) {
|
||||
var memoryTotalStr, memoryUsageStr, swapTotalStr, swapUsageStr, virtioBalloonStatus, KernelSamepageMerging string
|
||||
mv, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
println("mem.VirtualMemory error:", err)
|
||||
} else {
|
||||
memoryTotal := float64(mv.Total)
|
||||
memoryUsage := float64(mv.Total - mv.Available)
|
||||
if memoryTotal < 1024*1024*1024 {
|
||||
memoryTotalStr = strconv.FormatFloat(memoryTotal/(1024*1024), 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
memoryTotalStr = strconv.FormatFloat(memoryTotal/(1024*1024*1024), 'f', 2, 64) + " GB"
|
||||
}
|
||||
if memoryUsage < 1024*1024*1024 {
|
||||
memoryUsageStr = strconv.FormatFloat(memoryUsage/(1024*1024), 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
memoryUsageStr = strconv.FormatFloat(memoryUsage/(1024*1024*1024), 'f', 2, 64) + " GB"
|
||||
}
|
||||
if runtime.GOOS != "windows" {
|
||||
swapTotal := float64(mv.SwapTotal)
|
||||
swapUsage := float64(mv.SwapTotal - mv.SwapFree)
|
||||
if swapTotal != 0 {
|
||||
if swapTotal < 1024*1024*1024 {
|
||||
swapTotalStr = strconv.FormatFloat(swapTotal/(1024*1024), 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
swapTotalStr = strconv.FormatFloat(swapTotal/(1024*1024*1024), 'f', 2, 64) + " GB"
|
||||
}
|
||||
if swapUsage < 1024*1024*1024 {
|
||||
swapUsageStr = strconv.FormatFloat(swapUsage/(1024*1024), 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
swapUsageStr = strconv.FormatFloat(swapUsage/(1024*1024*1024), 'f', 2, 64) + " GB"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
// gopsutil 在 Windows 下不能正确取 swap
|
||||
ms, err := mem.SwapMemory()
|
||||
if err != nil {
|
||||
println("mem.SwapMemory error:", err)
|
||||
} else {
|
||||
swapTotal := float64(ms.Total)
|
||||
swapUsage := float64(ms.Used)
|
||||
if swapTotal != 0 {
|
||||
if swapTotal < 1024*1024*1024 {
|
||||
swapTotalStr = strconv.FormatFloat(swapTotal/(1024*1024), 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
swapTotalStr = strconv.FormatFloat(swapTotal/(1024*1024*1024), 'f', 2, 64) + " GB"
|
||||
}
|
||||
if swapUsage < 1024*1024*1024 {
|
||||
swapUsageStr = strconv.FormatFloat(swapUsage/(1024*1024), 'f', 2, 64) + " MB"
|
||||
} else {
|
||||
swapUsageStr = strconv.FormatFloat(swapUsage/(1024*1024*1024), 'f', 2, 64) + " GB"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
virtioBalloon, err := os.ReadFile("/proc/modules")
|
||||
if err == nil {
|
||||
if strings.Contains(string(virtioBalloon), "virtio_balloon") {
|
||||
virtioBalloonStatus = "✔️ Enabled"
|
||||
} else {
|
||||
virtioBalloonStatus = ""
|
||||
}
|
||||
} else {
|
||||
virtioBalloonStatus = ""
|
||||
}
|
||||
ksmStatus, err := os.ReadFile("/sys/kernel/mm/ksm/run")
|
||||
if err == nil {
|
||||
if strings.Contains(string(ksmStatus), "1") {
|
||||
KernelSamepageMerging = "✔️ Enabled"
|
||||
} else {
|
||||
KernelSamepageMerging = ""
|
||||
}
|
||||
} else {
|
||||
KernelSamepageMerging = ""
|
||||
}
|
||||
return memoryTotalStr, memoryUsageStr, swapTotalStr, swapUsageStr, virtioBalloonStatus, KernelSamepageMerging
|
||||
}
|
52
system/model/model.go
Normal file
52
system/model/model.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package model
|
||||
|
||||
type CpuInfo struct {
|
||||
CpuModel string
|
||||
CpuCores string
|
||||
CpuCache string
|
||||
CpuAesNi string
|
||||
CpuVAH string
|
||||
}
|
||||
|
||||
type MemoryInfo struct {
|
||||
MemoryUsage string
|
||||
MemoryTotal string
|
||||
SwapUsage string
|
||||
SwapTotal string
|
||||
}
|
||||
|
||||
type DiskInfo struct {
|
||||
DiskUsage string
|
||||
DiskTotal string
|
||||
BootPath string
|
||||
}
|
||||
|
||||
type SystemInfo struct {
|
||||
CpuInfo
|
||||
MemoryInfo
|
||||
DiskInfo
|
||||
Platform string // 系统名字 Distro1
|
||||
PlatformVersion string // 系统版本 Distro2
|
||||
Kernel string // 系统内核
|
||||
Arch string //
|
||||
Uptime string // 正常运行时间
|
||||
VmType string // 虚拟化架构
|
||||
Load string // load1 load2 load3
|
||||
NatType string // stun
|
||||
VirtioBalloon string // 气球驱动
|
||||
KSM string // 内存合并
|
||||
TcpAccelerationMethod string // TCP拥塞控制
|
||||
}
|
||||
|
||||
type Win32_Processor struct {
|
||||
L2CacheSize uint32
|
||||
L3CacheSize uint32
|
||||
}
|
||||
|
||||
type Win32_ComputerSystem struct {
|
||||
SystemType string
|
||||
}
|
||||
|
||||
type Win32_OperatingSystem struct {
|
||||
BuildType string
|
||||
}
|
75
system/system.go
Normal file
75
system/system.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/oneclickvirt/basics/system/model"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
expectDiskFsTypes = []string{
|
||||
"apfs", "ext4", "ext3", "ext2", "f2fs", "reiserfs", "jfs", "btrfs",
|
||||
"fuseblk", "zfs", "simfs", "ntfs", "fat32", "exfat", "xfs", "fuse.rclone",
|
||||
}
|
||||
cpuType string
|
||||
)
|
||||
|
||||
// GetHost 获取主机硬件信息
|
||||
func GetHost() *model.SystemInfo {
|
||||
var ret = &model.SystemInfo{}
|
||||
// 系统信息查询
|
||||
cpuType, ret.Uptime, ret.Platform, ret.Kernel, ret.Arch, ret.VmType, ret.NatType, _ = getHostInfo()
|
||||
// CPU信息查询
|
||||
ret, _ = getCpuInfo(ret, cpuType)
|
||||
// 硬盘信息查询
|
||||
ret.DiskTotal, ret.DiskUsage, ret.BootPath, _ = getDiskInfo()
|
||||
// 内存信息查询
|
||||
ret.MemoryTotal, ret.MemoryUsage, ret.SwapTotal, ret.SwapUsage, ret.VirtioBalloon, ret.KSM = getMemoryInfo()
|
||||
// 获取负载信息
|
||||
load1, load5, load15, err := getSystemLoad()
|
||||
if err != nil {
|
||||
load1, load5, load15 = 0, 0, 0
|
||||
}
|
||||
ret.Load = strconv.FormatFloat(load1, 'f', 2, 64) + " / " +
|
||||
strconv.FormatFloat(load5, 'f', 2, 64) + " / " +
|
||||
strconv.FormatFloat(load15, 'f', 2, 64)
|
||||
// 获取TCP控制算法
|
||||
ret.TcpAccelerationMethod = getTCPAccelerateStatus()
|
||||
return ret
|
||||
}
|
||||
|
||||
func GetSystemInfo() {
|
||||
ret := GetHost()
|
||||
fmt.Println("Cpu Model :", ret.CpuModel)
|
||||
fmt.Println("Cpu Cores :", ret.CpuCores)
|
||||
if ret.CpuCache != "" {
|
||||
fmt.Println("Cpu Cache :", ret.CpuCache)
|
||||
}
|
||||
fmt.Println("AES-NI :", ret.CpuAesNi)
|
||||
fmt.Println("VM-x/AMD-V/Hyper-V :", ret.CpuVAH)
|
||||
fmt.Println("RAM :", ret.MemoryUsage+" / "+ret.MemoryTotal)
|
||||
if ret.VirtioBalloon != "" {
|
||||
fmt.Println("Virtio Balloon :", ret.VirtioBalloon)
|
||||
}
|
||||
if ret.KSM != "" {
|
||||
fmt.Println("KSM :", ret.KSM)
|
||||
}
|
||||
if ret.SwapTotal == "" && ret.SwapUsage == "" {
|
||||
fmt.Println("Swap : [ no swap partition or swap file detected ]")
|
||||
} else if ret.SwapTotal != "" && ret.SwapUsage != "" {
|
||||
fmt.Println("Swap :", ret.SwapUsage+" / "+ret.SwapTotal)
|
||||
}
|
||||
fmt.Println("Disk :", ret.DiskUsage+" / "+ret.DiskTotal)
|
||||
fmt.Println("Boot Path :", ret.BootPath)
|
||||
fmt.Println("OS Release :", ret.Platform+" ["+ret.Arch+"] ")
|
||||
if ret.Kernel != "" {
|
||||
fmt.Println("Kernel :", ret.Kernel)
|
||||
}
|
||||
fmt.Println("Uptime :", ret.Uptime)
|
||||
fmt.Println("Load :", ret.Load)
|
||||
fmt.Println("VM Type :", ret.VmType)
|
||||
fmt.Println("NAT Type :", ret.NatType)
|
||||
if ret.TcpAccelerationMethod != "" {
|
||||
fmt.Println("Tcp Accelerate :", ret.TcpAccelerationMethod)
|
||||
}
|
||||
}
|
9
system/system_test.go
Normal file
9
system/system_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetSystemInfo(t *testing.T) {
|
||||
GetSystemInfo()
|
||||
}
|
19
system/tcpcontrol_linux.go
Normal file
19
system/tcpcontrol_linux.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// getTCPAccelerateStatus 查询TCP控制算法
|
||||
func getTCPAccelerateStatus() string {
|
||||
cmd := exec.Command("sysctl", "-n", "net.ipv4.tcp_congestion_control")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return out.String()
|
||||
}
|
||||
}
|
6
system/tcpcontrol_windows.go
Normal file
6
system/tcpcontrol_windows.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package system
|
||||
|
||||
// getTCPAccelerateStatus 查询TCP控制算法
|
||||
func getTCPAccelerateStatus() string {
|
||||
return ""
|
||||
}
|
Reference in New Issue
Block a user