From b20c4b74cc6c1b86b1ff08c7b057f80b4adda1d5 Mon Sep 17 00:00:00 2001 From: Yunlq <140733746+Yunlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 02:54:19 +0800 Subject: [PATCH 1/6] add support for custom source ports and optimize some code --- cmd/cmd.go | 8 +++++--- trace/tcp_ipv4.go | 7 ++++++- trace/tcp_ipv6.go | 7 ++++++- trace/trace.go | 1 + trace/udp_ipv4.go | 7 +++++-- trace/udp_ipv6.go | 7 +++++-- util/util.go | 24 ++++++++++++------------ 7 files changed, 40 insertions(+), 21 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 8d11558..7e63c82 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -57,12 +57,13 @@ func Excute() { disableMaptrace := parser.Flag("M", "map", &argparse.Options{Help: "Disable Print Trace Map"}) disableMPLS := parser.Flag("e", "disable-mpls", &argparse.Options{Help: "Disable MPLS"}) ver := parser.Flag("v", "version", &argparse.Options{Help: "Print version info and exit"}) - srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr for outgoing packets"}) + srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source address src_addr for outgoing packets"}) + srcPort := parser.Int("", "source-port", &argparse.Options{Help: "Use source port src_port for outgoing packets (TCP and UDP only)"}) srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the following Network Devices as the source address in outgoing packets"}) //router := parser.Flag("R", "route", &argparse.Options{Help: "Show Routing Table [Provided By BGP.Tools]"}) - packetInterval := parser.Int("z", "send-time", &argparse.Options{Default: 50, Help: "Set how many [milliseconds] between sending each packet.. Useful when some routers use rate-limit for ICMP messages"}) + packetInterval := parser.Int("z", "send-time", &argparse.Options{Default: 50, Help: "Set how many [milliseconds] between sending each packet. Useful when some routers use rate-limit for ICMP messages"}) ttlInterval := parser.Int("i", "ttl-time", &argparse.Options{Default: 50, Help: "Set how many [milliseconds] between sending packets groups by TTL. Useful when some routers use rate-limit for ICMP messages"}) - timeout := parser.Int("", "timeout", &argparse.Options{Default: 1000, Help: "The number of [milliseconds] to keep probe sockets open before giving up on the connection."}) + timeout := parser.Int("", "timeout", &argparse.Options{Default: 1000, Help: "The number of [milliseconds] to keep probe sockets open before giving up on the connection"}) packetSize := parser.Int("", "psize", &argparse.Options{Default: 52, Help: "Set the payload size"}) str := parser.StringPositional(&argparse.Options{Help: "IP Address or domain name"}) dot := parser.Selector("", "dot-server", []string{"dnssb", "aliyun", "dnspod", "google", "cloudflare"}, &argparse.Options{ @@ -255,6 +256,7 @@ func Excute() { var conf = trace.Config{ DN42: *dn42, SrcAddr: *srcAddr, + SrcPort: *srcPort, BeginHop: *beginHop, DestIP: ip, DestPort: *port, diff --git a/trace/tcp_ipv4.go b/trace/tcp_ipv4.go index 01e27c5..5eaa7e0 100644 --- a/trace/tcp_ipv4.go +++ b/trace/tcp_ipv4.go @@ -211,7 +211,12 @@ func (t *TCPTracer) send(ttl int) error { } // 随机种子 r := rand.New(rand.NewSource(time.Now().UnixNano())) - _, srcPort := util.LocalIPPort(t.DestIP) + _, srcPort := func() (net.IP, int) { + if util.EnvRandomPort == "" && t.SrcPort != 0 { + return nil, t.SrcPort + } + return util.LocalIPPort(t.DestIP) + }() ipHeader := &layers.IPv4{ SrcIP: t.SrcIP, DstIP: t.DestIP, diff --git a/trace/tcp_ipv6.go b/trace/tcp_ipv6.go index dc462a9..294640f 100644 --- a/trace/tcp_ipv6.go +++ b/trace/tcp_ipv6.go @@ -200,7 +200,12 @@ func (t *TCPTracerIPv6) send(ttl int) error { } // 随机种子 r := rand.New(rand.NewSource(time.Now().UnixNano())) - _, srcPort := util.LocalIPPortv6(t.DestIP) + _, srcPort := func() (net.IP, int) { + if util.EnvRandomPort == "" && t.SrcPort != 0 { + return nil, t.SrcPort + } + return util.LocalIPPortv6(t.DestIP) + }() ipHeader := &layers.IPv6{ SrcIP: t.SrcIP, DstIP: t.DestIP, diff --git a/trace/trace.go b/trace/trace.go index fc92af9..3df1d2b 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -23,6 +23,7 @@ var ( type Config struct { SrcAddr string + SrcPort int BeginHop int MaxHops int NumMeasurements int diff --git a/trace/udp_ipv4.go b/trace/udp_ipv4.go index 06cd517..b63a5a2 100644 --- a/trace/udp_ipv4.go +++ b/trace/udp_ipv4.go @@ -149,7 +149,10 @@ func (t *UDPTracer) getUDPConn(try int) (net.IP, int, net.PacketConn, error) { } // Check environment variable to decide caching behavior - if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" { + if util.EnvRandomPort == "" { + if t.SrcPort != 0 { + cachedLocalPort = t.SrcPort + } // Use cached random port logic if cachedLocalPort == 0 { // First time: listen on a random port @@ -193,7 +196,7 @@ func (t *UDPTracer) send(ttl int) error { return nil } - if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" { + if util.EnvRandomPort == "" { t.udpMutex.Lock() defer t.udpMutex.Unlock() } diff --git a/trace/udp_ipv6.go b/trace/udp_ipv6.go index 652e04f..15bc596 100644 --- a/trace/udp_ipv6.go +++ b/trace/udp_ipv6.go @@ -214,7 +214,10 @@ func (t *UDPTracerIPv6) getUDPConn(try int) (net.IP, int, net.PacketConn, error) } // Check environment variable to decide caching behavior - if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" { + if util.EnvRandomPort == "" { + if t.SrcPort != 0 { + cachedLocalPortv6 = t.SrcPort + } // Use cached random port logic if cachedLocalPortv6 == 0 { // First time: listen on a random port @@ -258,7 +261,7 @@ func (t *UDPTracerIPv6) send(ttl int) error { return nil } - if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" { + if util.EnvRandomPort == "" { t.udpMutex.Lock() defer t.udpMutex.Unlock() } diff --git a/util/util.go b/util/util.go index bb75c16..b13929f 100644 --- a/util/util.go +++ b/util/util.go @@ -16,15 +16,16 @@ import ( "github.com/fatih/color" ) -var Uninterrupted = GetenvDefault("NEXTTRACE_UNINTERRUPTED", "") -var EnvToken = GetenvDefault("NEXTTRACE_TOKEN", "") -var EnvIPInfoLocalPath = GetenvDefault("NEXTTRACE_IPINFOLOCALPATH", "") -var UserAgent = fmt.Sprintf("NextTrace %s/%s/%s", config.Version, runtime.GOOS, runtime.GOARCH) -var RdnsCache sync.Map -var PowProviderParam = "" var DisableMPLS = GetenvDefault("NEXTTRACE_DISABLEMPLS", "") var EnableHidDstIP = GetenvDefault("NEXTTRACE_ENABLEHIDDENDSTIP", "") +var EnvIPInfoLocalPath = GetenvDefault("NEXTTRACE_IPINFOLOCALPATH", "") +var EnvRandomPort = GetenvDefault("NEXTTRACE_RANDOMPORT", "") +var EnvToken = GetenvDefault("NEXTTRACE_TOKEN", "") +var Uninterrupted = GetenvDefault("NEXTTRACE_UNINTERRUPTED", "") var DestIP string +var PowProviderParam = "" +var RdnsCache sync.Map +var UserAgent = fmt.Sprintf("NextTrace %s/%s/%s", config.Version, runtime.GOOS, runtime.GOARCH) var cachedLocalIP net.IP var cachedLocalPort int var localIPOnce sync.Once @@ -84,10 +85,10 @@ func getLocalIPPortv6(dstip net.IP) (net.IP, int) { return nil, -1 } -// LocalIPPort returns the local IP and port based on our destination IP, with caching unless NEXTTRACE_RANDOMPORT is set. +// LocalIPPort returns the local IP and port based on our destination IP, with caching unless EnvRandomPort is set. func LocalIPPort(dstip net.IP) (net.IP, int) { - // If NEXTTRACE_RANDOMPORT is set, bypass caching and return a new port every time. - if GetenvDefault("NEXTTRACE_RANDOMPORT", "") != "" { + // If EnvRandomPort is set, bypass caching and return a new port every time. + if EnvRandomPort != "" { return getLocalIPPort(dstip) } @@ -102,9 +103,8 @@ func LocalIPPort(dstip net.IP) (net.IP, int) { } func LocalIPPortv6(dstip net.IP) (net.IP, int) { - // If NEXTTRACE_RANDOMPORT is set, bypass caching and return a new port every time. - // 该ENV仅对TCP Mode有效,UDP Mode暂无办法固定Port - if GetenvDefault("NEXTTRACE_RANDOMPORT", "") != "" { + // If EnvRandomPort is set, bypass caching and return a new port every time. + if EnvRandomPort != "" { return getLocalIPPortv6(dstip) } From 28b329e365674cfffe3f3a54586d3a843f84b781 Mon Sep 17 00:00:00 2001 From: tsosunchia <59512455+tsosunchia@users.noreply.github.com> Date: Wed, 16 Apr 2025 09:44:22 +0800 Subject: [PATCH 2/6] update README --- README.md | 9 ++++++++- README_zh_CN.md | 9 +++++++-- cmd/cmd.go | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 079fbee..ff8c36c 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,12 @@ nexttrace --tcp --port 443 2001:4860:4860::8888 # UDP Trace nexttrace --udp 1.0.0.1 -nexttrace --udp --port 53 1.0.0.1 +# You can specify the target port yourself [here it is 5353], the default is port 33494 +nexttrace --udp --port 5353 1.0.0.1 + +# For TCP/UDP Trace, you can specify the source port; by default, a fixed random port is used +# (if you need to use a different random source port for each packet, please set the ENV variable NEXTTRACE_RANDOMPORT) +nexttrace --tcp --source-port 14514 www.bing.com ``` `NextTrace` also supports some advanced functions, such as ttl control, concurrent probe packet count control, mode switching, etc. @@ -383,6 +388,8 @@ Arguments: -e --disable-mpls Disable MPLS -v --version Print version info and exit -s --source Use source src_addr for outgoing packets + --source-port Use source port src_port for outgoing + packets -D --dev Use the following Network Devices as the source address in outgoing packets -z --send-time Set how many [milliseconds] between diff --git a/README_zh_CN.md b/README_zh_CN.md index 8aa5d00..9947146 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -226,14 +226,17 @@ nexttrace --source 204.98.134.56 9.9.9.9 # TCP SYN Trace nexttrace --tcp www.bing.com -# 可以自行指定端口[此处为443],默认80端口 +# 可以自行指定目标端口[此处为443],默认80端口 nexttrace --tcp --port 443 2001:4860:4860::8888 # UDP Trace nexttrace --udp 1.0.0.1 -# 可以自行指定端口[此处为5353],默认33494端口 +# 可以自行指定目标端口[此处为5353],默认33494端口 nexttrace --udp --port 5353 1.0.0.1 + +# TCP/UDP Trace 可以自行指定源端口,默认使用随机一个固定的端口(如需每次发包随机使用不同的源端口,请设置`ENV` `NEXTTRACE_RANDOMPORT`) +nexttrace --tcp --source-port 14514 www.bing.com ``` `NextTrace`也同样支持一些进阶功能,如 TTL 控制、并发数控制、模式切换等 @@ -377,6 +380,8 @@ Arguments: -e --disable-mpls Disable MPLS -v --version Print version info and exit -s --source Use source src_addr for outgoing packets + --source-port Use source port src_port for outgoing + packets -D --dev Use the following Network Devices as the source address in outgoing packets -z --send-time Set how many [milliseconds] between diff --git a/cmd/cmd.go b/cmd/cmd.go index 7e63c82..0012f1c 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -58,7 +58,7 @@ func Excute() { disableMPLS := parser.Flag("e", "disable-mpls", &argparse.Options{Help: "Disable MPLS"}) ver := parser.Flag("v", "version", &argparse.Options{Help: "Print version info and exit"}) srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source address src_addr for outgoing packets"}) - srcPort := parser.Int("", "source-port", &argparse.Options{Help: "Use source port src_port for outgoing packets (TCP and UDP only)"}) + srcPort := parser.Int("", "source-port", &argparse.Options{Help: "Use source port src_port for outgoing packets"}) srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the following Network Devices as the source address in outgoing packets"}) //router := parser.Flag("R", "route", &argparse.Options{Help: "Show Routing Table [Provided By BGP.Tools]"}) packetInterval := parser.Int("z", "send-time", &argparse.Options{Default: 50, Help: "Set how many [milliseconds] between sending each packet. Useful when some routers use rate-limit for ICMP messages"}) From 946095458b36459369187d76db9beb4030b0789b Mon Sep 17 00:00:00 2001 From: tsosunchia <59512455+tsosunchia@users.noreply.github.com> Date: Wed, 16 Apr 2025 17:18:52 +0800 Subject: [PATCH 3/6] =?UTF-8?q?update=20README=20=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E5=91=BD=E4=BB=A4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README_zh_CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff8c36c..4216f2f 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Please note, there are exceptions to this synchronization. If a version of NTrac * Linux * One-click installation script ```shell - curl nxtrace.org/nt |bash + curl -sL nxtrace.org/nt |bash ``` * Install nxtrace from the APT repository diff --git a/README_zh_CN.md b/README_zh_CN.md index 9947146..8228e03 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -65,7 +65,7 @@ Document Language: [English](README.md) | 简体中文 * Linux * 一键安装脚本 ```shell - curl nxtrace.org/nt | bash + curl -sL nxtrace.org/nt | bash ``` * 从 nxtrace的APT源安装 From b5673b65a14aef884a5800a838ced65828e60172 Mon Sep 17 00:00:00 2001 From: SaltyFishEd Date: Fri, 18 Apr 2025 14:43:56 +0800 Subject: [PATCH 4/6] feat(geoip): support geoip data from ipdb.one --- cmd/cmd.go | 2 +- ipgeo/ipdbone.go | 255 +++++++++++++++++++++++++++++++++++++++++++++++ ipgeo/ipgeo.go | 2 + 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 ipgeo/ipdbone.go diff --git a/cmd/cmd.go b/cmd/cmd.go index 0012f1c..133c228 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -39,7 +39,7 @@ func Excute() { numMeasurements := parser.Int("q", "queries", &argparse.Options{Default: 3, Help: "Set the number of probes per each hop"}) parallelRequests := parser.Int("", "parallel-requests", &argparse.Options{Default: 18, Help: "Set ParallelRequests number. It should be 1 when there is a multi-routing"}) maxHops := parser.Int("m", "max-hops", &argparse.Options{Default: 30, Help: "Set the max number of hops (max TTL to be reached)"}) - dataOrigin := parser.Selector("d", "data-provider", []string{"Ip2region", "ip2region", "IP.SB", "ip.sb", "IPInfo", "ipinfo", "IPInsight", "ipinsight", "IPAPI.com", "ip-api.com", "IPInfoLocal", "ipinfolocal", "chunzhen", "LeoMoeAPI", "leomoeapi", "disable-geoip"}, &argparse.Options{Default: "LeoMoeAPI", + dataOrigin := parser.Selector("d", "data-provider", []string{"Ip2region", "ip2region", "IP.SB", "ip.sb", "IPInfo", "ipinfo", "IPInsight", "ipinsight", "IPAPI.com", "ip-api.com", "IPInfoLocal", "ipinfolocal", "chunzhen", "LeoMoeAPI", "leomoeapi", "ipdb.one", "disable-geoip"}, &argparse.Options{Default: "LeoMoeAPI", Help: "Choose IP Geograph Data Provider [IP.SB, IPInfo, IPInsight, IP-API.com, Ip2region, IPInfoLocal, CHUNZHEN, disable-geoip]"}) powProvider := parser.Selector("", "pow-provider", []string{"api.nxtrace.org", "sakura"}, &argparse.Options{Default: "api.nxtrace.org", Help: "Choose PoW Provider [api.nxtrace.org, sakura] For China mainland users, please use sakura"}) diff --git a/ipgeo/ipdbone.go b/ipgeo/ipdbone.go new file mode 100644 index 0000000..059b638 --- /dev/null +++ b/ipgeo/ipdbone.go @@ -0,0 +1,255 @@ +package ipgeo + +import ( + "errors" + "io" + "net/http" + "strconv" + "sync" + "time" + + "github.com/nxtrace/NTrace-core/config" + "github.com/nxtrace/NTrace-core/util" + + "github.com/tidwall/gjson" +) + +// Language mapping for IPDB.One API +var LangMap = map[string]string{ + "en": "en", + "cn": "zh", +} + +// IPDBOneConfig holds the configuration for IPDB.One service +type IPDBOneConfig struct { + BaseURL string + ApiID string + ApiKey string +} + +// GetDefaultConfig returns the default configuration with fallback values +func GetDefaultConfig() *IPDBOneConfig { + return &IPDBOneConfig{ + BaseURL: util.GetenvDefault("IPDBONE_BASE_URL", "https://api.ipdb.one"), + ApiID: util.GetenvDefault("IPDBONE_API_ID", ""), + ApiKey: util.GetenvDefault("IPDBONE_API_KEY", ""), + } +} + +// IPDBOneTokenCache manages the caching of auth tokens +type IPDBOneTokenCache struct { + token string + expiresAt time.Time + mutex sync.RWMutex +} + +// GetToken retrieves cached token if valid, otherwise returns empty string +func (c *IPDBOneTokenCache) GetToken() string { + c.mutex.RLock() + defer c.mutex.RUnlock() + + if c.token == "" || time.Now().After(c.expiresAt) { + return "" + } + return c.token +} + +// SetToken updates the token with its expiration time +func (c *IPDBOneTokenCache) SetToken(token string, expiresIn time.Duration) { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.token = token + c.expiresAt = time.Now().Add(expiresIn) +} + +// IPDBOneClient handles communication with IPDB.One API +type IPDBOneClient struct { + config *IPDBOneConfig + tokenCache *IPDBOneTokenCache + tokenInit sync.Once + httpClient *http.Client +} + +// NewIPDBOneClient creates a new client for IPDB.One with default configuration +func NewIPDBOneClient() *IPDBOneClient { + return &IPDBOneClient{ + config: GetDefaultConfig(), + tokenCache: &IPDBOneTokenCache{}, + httpClient: &http.Client{ + Timeout: 3 * time.Second, + }, + } +} + +// fetchToken requests a new authentication token from the API +func (c *IPDBOneClient) fetchToken() error { + authURL := c.config.BaseURL + "/auth/requestToken/query" + + req, err := http.NewRequest("GET", authURL, nil) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "NextTrace/"+config.Version) + req.Header.Set("x-api-id", c.config.ApiID) + req.Header.Set("x-api-key", c.config.ApiKey) + + resp, err := c.httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + statusCode := gjson.Get(string(body), "code").Int() + statusMessage := gjson.Get(string(body), "message").String() + + if statusCode != 200 { + return errors.New("failed to authenticate: " + statusMessage) + } + + token := gjson.Get(string(body), "data.token").String() + if token == "" { + return errors.New("authentication failed: empty token received") + } + + // Cache token with a 30-second expiration + c.tokenCache.SetToken(token, 30*time.Second) + return nil +} + +// ensureToken makes sure a valid token is available, fetching a new one if needed +func (c *IPDBOneClient) ensureToken() error { + var initErr error + + // Ensure API credentials are set + if c.config.ApiID == "" || c.config.ApiKey == "" { + return errors.New("api id or api key is not set") + } + + // Initialize token the first time this is called + c.tokenInit.Do(func() { + initErr = c.fetchToken() + }) + + if initErr != nil { + return initErr + } + + // If token expired or not available, get a new one + if c.tokenCache.GetToken() == "" { + return c.fetchToken() + } + + return nil +} + +// LookupIP queries the IP information from IPDB.One +func (c *IPDBOneClient) LookupIP(ip string, lang string) (*IPGeoData, error) { + + // Ensure we have a valid token + if err := c.ensureToken(); err != nil { + return &IPGeoData{}, nil + } + + // Map language code if needed + langCode, ok := LangMap[lang] + if !ok { + langCode = "en" // Default to English + } + + // Query the IP information + queryURL := c.config.BaseURL + "/query/" + ip + "?lang=" + langCode + + req, err := http.NewRequest("GET", queryURL, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "NextTrace/"+config.Version) + req.Header.Set("Authorization", "Bearer "+c.tokenCache.GetToken()) + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + statusCode := gjson.Get(string(body), "code").Int() + if statusCode != 200 { + return nil, errors.New("failed to get IP info: " + gjson.Get(string(body), "message").String()) + } + + return parseIPDBOneResponse(ip, body) +} + +// parseIPDBOneResponse converts the API response to an IPGeoData struct +func parseIPDBOneResponse(ip string, responseBody []byte) (*IPGeoData, error) { + data := gjson.Get(string(responseBody), "data") + geoData := data.Get("geo") + routingData := data.Get("routing") + + result := &IPGeoData{ + IP: ip, + } + + // Parse geo information if available + if geoData.Exists() { + coordinate := geoData.Get("coordinate") + if coordinate.Exists() && coordinate.Type != gjson.Null && coordinate.IsArray() && len(coordinate.Array()) >= 2 { + result.Lat = coordinate.Array()[0].Float() + result.Lng = coordinate.Array()[1].Float() + } + + if geoData.Get("country").Exists() && geoData.Get("country").Type != gjson.Null { + result.Country = geoData.Get("country").String() + } + + if geoData.Get("region").Exists() && geoData.Get("region").Type != gjson.Null { + result.Prov = geoData.Get("region").String() + } + + if geoData.Get("city").Exists() && geoData.Get("city").Type != gjson.Null { + result.City = geoData.Get("city").String() + } + } + + // Parse routing information if available + if routingData.Exists() { + asnData := routingData.Get("asn") + if asnData.Get("number").Exists() && asnData.Get("number").Type != gjson.Null { + result.Asnumber = strconv.FormatInt(asnData.Get("number").Int(), 10) + } + + if routingData.Get("asn.name").Exists() && routingData.Get("asn.name").Type != gjson.Null { + result.Owner = routingData.Get("asn.name").String() + } + } + + return result, nil +} + +// Global client instance for backward compatibility +var defaultClient = NewIPDBOneClient() + +// IPDBOne looks up IP information from IPDB.One (maintains backward compatibility) +func IPDBOne(ip string, timeout time.Duration, lang string, _ bool) (*IPGeoData, error) { + // Override timeout if specified + if timeout > 0 { + defaultClient.httpClient.Timeout = timeout + } + + return defaultClient.LookupIP(ip, lang) +} diff --git a/ipgeo/ipgeo.go b/ipgeo/ipgeo.go index ec6923e..11943ee 100644 --- a/ipgeo/ipgeo.go +++ b/ipgeo/ipgeo.go @@ -52,6 +52,8 @@ func GetSource(s string) Source { return Chunzhen case "DISABLE-GEOIP": return disableGeoIP + case "IPDB.ONE": + return IPDBOne default: return LeoIP } From 7bfccf9da4bb3c585652c47b5d1430655e20cd3c Mon Sep 17 00:00:00 2001 From: SaltyFishEd Date: Sun, 27 Apr 2025 00:25:47 +0800 Subject: [PATCH 5/6] feat(geoip): overwrite the owner field when asn.domain exists for ipdb.one datasource feat(geoip): add asn.asname as whois field for ipdb.one datasource --- ipgeo/ipdbone.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ipgeo/ipdbone.go b/ipgeo/ipdbone.go index 059b638..f900742 100644 --- a/ipgeo/ipdbone.go +++ b/ipgeo/ipdbone.go @@ -236,6 +236,16 @@ func parseIPDBOneResponse(ip string, responseBody []byte) (*IPGeoData, error) { if routingData.Get("asn.name").Exists() && routingData.Get("asn.name").Type != gjson.Null { result.Owner = routingData.Get("asn.name").String() } + + // Get domain, override owner + if routingData.Get("asn.domain").Exists() && routingData.Get("asn.domain").Type != gjson.Null { + result.Owner = routingData.Get("asn.domain").String() + } + + // Get asname as Whois + if routingData.Get("asn.asname").Exists() && routingData.Get("asn.asname").Type != gjson.Null { + result.Whois = routingData.Get("asn.asname").String() + } } return result, nil From 93c0f8558b14cfbc3947bcd43b740b35525fc741 Mon Sep 17 00:00:00 2001 From: tsosunchia <59512455+tsosunchia@users.noreply.github.com> Date: Mon, 28 Apr 2025 10:02:47 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20nt=5Finstall.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/nxtrace/NTrace-core/issues/300 --- nt_install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nt_install.sh b/nt_install.sh index 9cb26f1..a6e90cb 100644 --- a/nt_install.sh +++ b/nt_install.sh @@ -60,7 +60,7 @@ checkSystemDistribution() { downloadBinrayFile() { echo -e "${Info} 获取最新版的 NextTrace 发行版文件信息" for i in {1..3}; do - downloadUrls=$(curl -sLf ${protocol}://www.nxtrace.org/api/dist/core/nexttrace_${osDistribution}_${archParam} --connect-timeout 1.5) + downloadUrls=$(curl -sLf ${protocol}://www.nxtrace.org/api/dist/core/nexttrace_${osDistribution}_${archParam} --connect-timeout 2) if [ $? -eq 0 ]; then break fi @@ -70,7 +70,7 @@ downloadBinrayFile() { backupUrl=$(echo ${downloadUrls} | awk -F '|' '{print $2}') echo -e "${Info} 正在尝试从 Primary 节点下载 NextTrace" for i in {1..3}; do - curl -sLf ${primaryUrl} -o ${Temp_path} --connect-timeout 1.5 + curl -sLf ${primaryUrl} -o ${Temp_path} --connect-timeout 2 if [ $? -eq 0 ]; then changeMode mv ${Temp_path} ${downPath} @@ -84,7 +84,7 @@ downloadBinrayFile() { fi echo -e "${Error} 从 Primary 节点下载失败,正在尝试从 Backup 节点下载 NextTrace" for i in {1..3}; do - curl -sLf ${backupUrl} -o ${Temp_path} --connect-timeout 1.5 + curl -sLf ${backupUrl} -o ${Temp_path} --connect-timeout 2 if [ $? -eq 0 ]; then changeMode mv ${Temp_path} ${downPath}