mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
feat: add DN42 Mode
This commit is contained in:
10
cmd/cmd.go
10
cmd/cmd.go
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
"github.com/xgadget-lab/nexttrace/config"
|
||||
fastTrace "github.com/xgadget-lab/nexttrace/fast_trace"
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/printer"
|
||||
@@ -42,6 +43,7 @@ func Excute() {
|
||||
alwaysRdns := parser.Flag("a", "always-rdns", &argparse.Options{Help: "Always resolve IP addresses to their domain names"})
|
||||
routePath := parser.Flag("P", "route-path", &argparse.Options{Help: "Print traceroute hop path by ASN and location"})
|
||||
report := parser.Flag("r", "report", &argparse.Options{Help: "output using report mode"})
|
||||
dn42 := parser.Flag("", "dn42", &argparse.Options{Help: "DN42 Mode"})
|
||||
output := parser.Flag("o", "output", &argparse.Options{Help: "Write trace result to file (RealTimePrinter ONLY)"})
|
||||
tablePrint := parser.Flag("t", "table", &argparse.Options{Help: "Output trace results as table"})
|
||||
classicPrint := parser.Flag("c", "classic", &argparse.Options{Help: "Classic Output trace results like BestTrace"})
|
||||
@@ -129,6 +131,13 @@ func Excute() {
|
||||
}
|
||||
}
|
||||
|
||||
if *dn42 {
|
||||
// 初始化配置
|
||||
config.InitConfig()
|
||||
*dataOrigin = "DN42"
|
||||
*maptrace = true
|
||||
}
|
||||
|
||||
if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
|
||||
w := wshandle.New()
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
@@ -156,6 +165,7 @@ func Excute() {
|
||||
}
|
||||
|
||||
var conf = trace.Config{
|
||||
DN42: *dn42,
|
||||
SrcAddr: *src_addr,
|
||||
BeginHop: *beginHop,
|
||||
DestIP: ip,
|
||||
|
||||
34
config/viper.go
Normal file
34
config/viper.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func InitConfig() {
|
||||
|
||||
// 配置文件名, 不加扩展
|
||||
viper.SetConfigName("nt_config") // name of config file (without extension)
|
||||
// 设置文件的扩展名
|
||||
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
|
||||
// 查找配置文件所在路径
|
||||
viper.AddConfigPath("/etc/bin/nexttrace/")
|
||||
viper.AddConfigPath("/usr/local/bin/nexttrace/")
|
||||
// 在当前路径进行查找
|
||||
viper.AddConfigPath(".")
|
||||
// viper.AddConfigPath("./config/")
|
||||
|
||||
// 配置默认值
|
||||
viper.SetDefault("ptrPath", "./ptr.csv")
|
||||
viper.SetDefault("geoFeedPath", "./geofeed.csv")
|
||||
|
||||
// 开始查找并读取配置文件
|
||||
err := viper.ReadInConfig() // Find and read the config file
|
||||
if err != nil { // Handle errors reading the config file
|
||||
fmt.Println("未能找到配置文件,我们将在您的运行目录为您创建 nt_config.yaml 默认配置")
|
||||
viper.SafeWriteConfigAs("./nt_config.yaml")
|
||||
}
|
||||
|
||||
viper.ReadInConfig()
|
||||
}
|
||||
7
dn42/dn42.go
Normal file
7
dn42/dn42.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package dn42
|
||||
|
||||
/***
|
||||
[DN42 Package]
|
||||
谨献给 DN42 所有的小伙伴们,祝你们终有一天能有自己的公网 ASN ~
|
||||
By Leo
|
||||
***/
|
||||
101
dn42/geofeed.go
Normal file
101
dn42/geofeed.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package dn42
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type GeoFeedRow struct {
|
||||
IPNet *net.IPNet
|
||||
CIDR string
|
||||
LtdCode string
|
||||
ISO3166 string
|
||||
City string
|
||||
ASN string
|
||||
IPWhois string
|
||||
}
|
||||
|
||||
func GetGeoFeed(ip string) (GeoFeedRow, bool) {
|
||||
rows, err := ReadGeoFeed()
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
panic(err)
|
||||
}
|
||||
|
||||
row, find := FindGeoFeedRow(ip, rows)
|
||||
return row, find
|
||||
|
||||
}
|
||||
|
||||
func ReadGeoFeed() ([]GeoFeedRow, error) {
|
||||
path := viper.Get("geoFeedPath").(string)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := csv.NewReader(f)
|
||||
rows, err := r.ReadAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 将 CSV 中的每一行转换为 GeoFeedRow 类型,并保存到 rowsSlice 中
|
||||
var rowsSlice []GeoFeedRow
|
||||
for _, row := range rows {
|
||||
cidr := row[0] // 假设第一列是 CIDR 字段
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
// 如果解析 CIDR 失败,跳过这一行
|
||||
continue
|
||||
}
|
||||
if len(row) == 4 {
|
||||
rowsSlice = append(rowsSlice, GeoFeedRow{
|
||||
IPNet: ipnet,
|
||||
CIDR: cidr,
|
||||
LtdCode: row[1],
|
||||
ISO3166: row[2],
|
||||
City: row[3],
|
||||
})
|
||||
} else {
|
||||
rowsSlice = append(rowsSlice, GeoFeedRow{
|
||||
IPNet: ipnet,
|
||||
CIDR: cidr,
|
||||
LtdCode: row[1],
|
||||
ISO3166: row[2],
|
||||
City: row[3],
|
||||
ASN: row[4],
|
||||
IPWhois: row[5],
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
// 根据 CIDR 范围从小到大排序,方便后面查找
|
||||
sort.Slice(rowsSlice, func(i, j int) bool {
|
||||
return rowsSlice[i].IPNet.Mask.String() > rowsSlice[j].IPNet.Mask.String()
|
||||
})
|
||||
|
||||
return rowsSlice, nil
|
||||
}
|
||||
|
||||
func FindGeoFeedRow(ipStr string, rows []GeoFeedRow) (GeoFeedRow, bool) {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
// 如果传入的 IP 无效,直接返回
|
||||
return GeoFeedRow{}, false
|
||||
}
|
||||
|
||||
// 遍历每个 CIDR 范围,找到第一个包含传入的 IP 的 CIDR
|
||||
for _, row := range rows {
|
||||
if row.IPNet.Contains(ip) {
|
||||
return row, true
|
||||
}
|
||||
}
|
||||
|
||||
return GeoFeedRow{}, false
|
||||
}
|
||||
17
dn42/geofeed_test.go
Normal file
17
dn42/geofeed_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package dn42
|
||||
|
||||
// func TestGeoFeed(t *testing.T) {
|
||||
// rows, err := ReadGeoFeed()
|
||||
// if err != nil {
|
||||
// // 处理错误
|
||||
// }
|
||||
|
||||
// row, found := FindGeoFeedRow("2001:0418:1403:8080::6fff", rows)
|
||||
// if found {
|
||||
// // 处理符合条件的 row
|
||||
// log.Println(row)
|
||||
// } else {
|
||||
// // 处理未找到的情况
|
||||
// }
|
||||
|
||||
// }
|
||||
83
dn42/ptr.go
Normal file
83
dn42/ptr.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package dn42
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type PtrRow struct {
|
||||
IATACode string
|
||||
LtdCode string
|
||||
Region string
|
||||
City string
|
||||
}
|
||||
|
||||
func matchesPattern(prefix string, s string) bool {
|
||||
pattern := fmt.Sprintf(`^(.*[-.\d]|^)%s[-.\d].*$`, prefix)
|
||||
|
||||
r, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
fmt.Println("Invalid regular expression:", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return r.MatchString(s)
|
||||
}
|
||||
|
||||
func FindPtrRecord(ptr string) (PtrRow, error) {
|
||||
path := viper.Get("ptrPath").(string)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return PtrRow{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := csv.NewReader(f)
|
||||
rows, err := r.ReadAll()
|
||||
if err != nil {
|
||||
return PtrRow{}, err
|
||||
}
|
||||
// 转小写
|
||||
ptr = strings.ToLower(ptr)
|
||||
// 先查城市名
|
||||
for _, row := range rows {
|
||||
city := row[3]
|
||||
if city == "" {
|
||||
continue
|
||||
}
|
||||
city = strings.ReplaceAll(city, " ", "")
|
||||
city = strings.ToLower(city)
|
||||
|
||||
if matchesPattern(city, ptr) {
|
||||
return PtrRow{
|
||||
LtdCode: row[1],
|
||||
Region: row[2],
|
||||
City: row[3],
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
// 查 IATA Code
|
||||
for _, row := range rows {
|
||||
iata := row[0]
|
||||
if iata == "" {
|
||||
continue
|
||||
}
|
||||
iata = strings.ToLower(iata)
|
||||
if matchesPattern(iata, ptr) {
|
||||
return PtrRow{
|
||||
IATACode: iata,
|
||||
LtdCode: row[1],
|
||||
Region: row[2],
|
||||
City: row[3],
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return PtrRow{}, errors.New("ptr not found")
|
||||
}
|
||||
50
dn42/ptr_test.go
Normal file
50
dn42/ptr_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package dn42
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPTR(t *testing.T) {
|
||||
|
||||
// example_mis := []string{
|
||||
// "sloutravel.com",
|
||||
// "memeslou.org",
|
||||
// "followsloucity.net",
|
||||
// "slouslou.slou",
|
||||
// "slouslou8.slou",
|
||||
// }
|
||||
|
||||
// examples := []string{
|
||||
|
||||
// "1ge.slou.as1299.net",
|
||||
// "1ge.slou2.as1299.net",
|
||||
// "1ge-slou.as1299.net",
|
||||
// "slou-1.as1299.net",
|
||||
// "slou.as1299.com",
|
||||
// "1ge-snge-6.as1299.net",
|
||||
// "c-1.sin.sg.atlas.moeqing.com",
|
||||
// "core.hkg1.hk.atlas.moeqing.com",
|
||||
// "core.losangles.us.atlas.moeqing.com",
|
||||
// }
|
||||
|
||||
// fmt.Println("容易误匹配的 PTR")
|
||||
|
||||
// for _, s := range example_mis {
|
||||
// if r, err := FindPtrRecord("ptr.csv"); err == nil {
|
||||
// fmt.Println(s, r)
|
||||
// } else {
|
||||
// fmt.Println(s, err)
|
||||
// }
|
||||
|
||||
// }
|
||||
// fmt.Println("\n应该正常匹配的 PTR")
|
||||
// for _, s := range examples {
|
||||
// if r, err := FindPtrRecord("ptr.csv"); err == nil {
|
||||
// fmt.Println(s, r)
|
||||
// } else {
|
||||
// fmt.Println(s, err)
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
}
|
||||
3
geofeed.example.csv
Normal file
3
geofeed.example.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
154.48.0.0/12,,,,174,COGENT-NET
|
||||
200.15.12.0/22,BR,BR-SP,Sao Paulo,2914,NTT-BACKBONE
|
||||
2001:0418:1403::/48,US,US-VA,Ashburn,2914,NTT-BACKBONE
|
||||
|
62
ipgeo/dn42.go
Normal file
62
ipgeo/dn42.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/dn42"
|
||||
)
|
||||
|
||||
func LtdCodeToCountryOrAreaName(Code string) string {
|
||||
countryName := []string{"United States", "Afghanistan", "Åland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", " Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada ", "Cape Verde", "Cayman Islands", "Central Africa", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo (Brazzaville)", "DRC", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", " Denmark", "Djibouti", "Dominica", "Dominica", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", " French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Vatican", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "British Isles of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "North Korea", "South Korea", " Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "FYROM", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", " Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia (Federated States of)", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana", "Norway", "Oman", "Pakistan", "Palau", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "St. Helena", "St. Kitts and Nevis", "St. Lucia", "St. Pierre and Miquelon", "St. Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen Islands", "Swaziland", "Sweden ", "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor-Leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", " United Kingdom", "U.S. Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Vietnam", "British Virgin Islands", "U.S. Virgin Islands", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"}
|
||||
countryCode := []string{"us", "af", "ax", "al", "dz", "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "au", "at", "az", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm", "bt", "bo", "ba", "bw", "bv", "br", "io", "bn", "bg", "bf", "bi", "kh", "cm", "ca", "cv", "ky", "cf", "td", "cl", "cn", "cx", "cc", "co", "km", "cg", "cd", "ck", "cr", "ci", "hr", "cu", "cy", "cz", "dk", "dj", "dm", "do", "ec", "eg", "sv", "gq", "er", "ee", "et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm", "ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gg", "gn", "gw", "gy", "ht", "hm", "va", "hn", "hk", "hu", "is", "in", "id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo", "kz", "ke", "ki", "kp", "kr", "kw", "kg", "la", "lv", "lb", "ls", "lr", "ly", "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv", "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc", "mn", "me", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "an", "nc", "nz", "ni", "ne", "ng", "nu", "nf", "mp", "no", "om", "pk", "pw", "ps", "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re", "ro", "ru", "rw", "sh", "kn", "lc", "pm", "vc", "ws", "sm", "st", "sa", "sn", "rs", "sc", "sl", "sg", "sk", "si", "sb", "so", "za", "gs", "es", "lk", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj", "tz", "th", "tl", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", "ug", "ua", "ae", "gb", "um", "uy", "uz", "vu", "ve", "vn", "vg", "vi", "wf", "eh", "ye", "zm", "zw"}
|
||||
Code = strings.ToLower(Code)
|
||||
for i, v := range countryCode {
|
||||
if strings.Contains(Code, v) {
|
||||
return countryName[i]
|
||||
}
|
||||
}
|
||||
return Code
|
||||
}
|
||||
|
||||
func DN42(ip string) (*IPGeoData, error) {
|
||||
data := &IPGeoData{}
|
||||
// 先解析传入过来的数据
|
||||
ipTmp := strings.Split(ip, ",")
|
||||
if len(ipTmp) > 1 {
|
||||
ip = ipTmp[0]
|
||||
}
|
||||
// 先查找 GeoFeed
|
||||
if geo, find := dn42.GetGeoFeed(ip); find {
|
||||
data.Country = geo.LtdCode
|
||||
data.City = geo.City
|
||||
data.Asnumber = geo.ASN
|
||||
data.Whois = geo.IPWhois
|
||||
}
|
||||
// 如果没找到,查找 PTR
|
||||
if len(ipTmp) > 1 {
|
||||
// 存在 PTR 记录
|
||||
if res, err := dn42.FindPtrRecord(ipTmp[1]); err == nil && res.LtdCode != "" {
|
||||
data.Country = res.LtdCode
|
||||
data.Prov = res.Region
|
||||
data.City = res.City
|
||||
}
|
||||
}
|
||||
|
||||
data.Country = LtdCodeToCountryOrAreaName(data.Country)
|
||||
|
||||
switch data.Country {
|
||||
case "Hong Kong":
|
||||
data.Country = "China"
|
||||
data.Prov = "Hong Kong"
|
||||
case "Taiwan":
|
||||
data.Country = "China"
|
||||
data.Prov = "Taiwan"
|
||||
case "Macao":
|
||||
data.Country = "China"
|
||||
data.Prov = "Macao"
|
||||
case "":
|
||||
data.Country = "Unknown"
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
5
ipgeo/dn42_test.go
Normal file
5
ipgeo/dn42_test.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package ipgeo
|
||||
|
||||
// func TestDN42(t *testing.T) {
|
||||
// DN42("")
|
||||
// }
|
||||
@@ -29,6 +29,8 @@ type Source = func(ip string) (*IPGeoData, error)
|
||||
|
||||
func GetSource(s string) Source {
|
||||
switch strings.ToUpper(s) {
|
||||
case "DN42":
|
||||
return DN42
|
||||
case "LEOMOEAPI":
|
||||
return LeoIP
|
||||
case "IP.SB":
|
||||
|
||||
2
nt_config.yaml
Normal file
2
nt_config.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
geofeedpath: ./geofeed.csv
|
||||
ptrpath: ./ptr.csv
|
||||
4
ptr.example.csv
Normal file
4
ptr.example.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
snge,SG,,Singapore
|
||||
CXS,CN,Hunan,Changsha
|
||||
LAX,US,California,Los Angeles
|
||||
SJC,US,California,San Jose
|
||||
|
@@ -31,6 +31,7 @@ type Config struct {
|
||||
PacketInterval int
|
||||
TTLInterval int
|
||||
Lang string
|
||||
DN42 bool
|
||||
RealtimePrinter func(res *Result, ttl int)
|
||||
AsyncPrinter func(res *Result)
|
||||
}
|
||||
@@ -121,6 +122,19 @@ type Hop struct {
|
||||
}
|
||||
|
||||
func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
// DN42
|
||||
if c.DN42 {
|
||||
var ip string
|
||||
// 首先查找 PTR 记录
|
||||
r, err := net.LookupAddr(h.Address.String())
|
||||
if err == nil && len(r) > 0 {
|
||||
h.Hostname = r[0][:len(r[0])-1]
|
||||
ip = h.Address.String() + "," + h.Hostname
|
||||
}
|
||||
h.Geo, err = c.IPGeoSource(ip)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debug Area
|
||||
// c.AlwaysWaitRDNS = true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user