mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cefed2a481 | ||
|
|
bc5a370f3b | ||
|
|
96e06e306c | ||
|
|
ac14ef4a7a | ||
|
|
bb192699ef | ||
|
|
b0b05f476b | ||
|
|
53de171bdb | ||
|
|
547906525a | ||
|
|
b4d2038895 | ||
|
|
adf8bb7076 | ||
|
|
3defedd27c | ||
|
|
4eeef8ddb8 | ||
|
|
35b67d9502 | ||
|
|
a5f888d1bc | ||
|
|
4d38c38cf7 | ||
|
|
d0e8e67d0a | ||
|
|
26b176ccb0 | ||
|
|
e2b23bb8e0 | ||
|
|
8c3a682e15 | ||
|
|
21e1c03596 | ||
|
|
ef57db1c09 | ||
|
|
93c0f8558b | ||
|
|
7bfccf9da4 | ||
|
|
b5673b65a1 | ||
|
|
946095458b | ||
|
|
28b329e365 | ||
|
|
c27dd3703a | ||
|
|
b20c4b74cc | ||
|
|
deca060a9d | ||
|
|
fedf2946e0 | ||
|
|
d0f5862258 | ||
|
|
0f8a646d71 | ||
|
|
dccc41b995 | ||
|
|
9af629b6f9 | ||
|
|
d6de649e60 |
19
README.md
19
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
|
||||
@@ -210,7 +210,7 @@ nexttrace --dev eth0 2606:4700:4700::1111
|
||||
nexttrace --source 204.98.134.56 9.9.9.9
|
||||
```
|
||||
|
||||
`NextTrace` can also use `TCP` and `UDP` protocols to perform `Traceroute` requests, but `UDP` protocols only supports `IPv4` now
|
||||
`NextTrace` can also use `TCP` and `UDP` protocols to perform `Traceroute` requests
|
||||
|
||||
```bash
|
||||
# TCP SYN Trace
|
||||
@@ -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
|
||||
@@ -453,6 +460,12 @@ The LeoMoeAPI data is subject to copyright restrictions from multiple data sourc
|
||||
We hope you can give us as much feedback as possible on IP geolocation errors (see issue) so that it can be calibrated in the first place and others can benefit from it.
|
||||
|
||||
|
||||
## AIWEN TECH Support
|
||||
|
||||
This project is sponsored by [AIWEN TECH](https://www.ipplus360.com). We’re pleased to enhance the accuracy and completeness of this project’s GEOIP lookups using `AIWEN TECH City-Level IP Database`, and to make it freely available to the public.
|
||||
|
||||
<img src="https://www.ipplus360.com/img/LOGO.c86cd0e1.svg" title="" alt="AIWEN TECH IP Geolocation Data" width="331">
|
||||
|
||||
## JetBrain Support
|
||||
|
||||
This Project uses [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport). We Proudly Develop By `Goland`.
|
||||
|
||||
@@ -65,7 +65,7 @@ Document Language: [English](README.md) | 简体中文
|
||||
* Linux
|
||||
* 一键安装脚本
|
||||
```shell
|
||||
curl nxtrace.org/nt | bash
|
||||
curl -sL nxtrace.org/nt | bash
|
||||
```
|
||||
|
||||
* 从 nxtrace的APT源安装
|
||||
@@ -220,20 +220,23 @@ nexttrace --dev eth0 2606:4700:4700::1111
|
||||
nexttrace --source 204.98.134.56 9.9.9.9
|
||||
```
|
||||
|
||||
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求,不过目前`UDP`只支持`IPv4`
|
||||
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求
|
||||
|
||||
```bash
|
||||
# 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
|
||||
@@ -452,6 +457,12 @@ nexttrace --pow-provider sakura
|
||||
[https://github.com/nxtrace/NextTraceroute](https://github.com/nxtrace/NextTraceroute)
|
||||
<a href='https://play.google.com/store/apps/details?id=com.surfaceocean.nexttraceroute&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' width="128" height="48" src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/></a>
|
||||
|
||||
## AIWEN TECH Support
|
||||
|
||||
本项目受 [埃文科技](https://www.ipplus360.com) 赞助。 很高兴使用`埃文科技城市级IP库`增强本项目 GEOIP 查询的准确性与完整性,并免费提供给公众。
|
||||
|
||||
<img src="https://www.ipplus360.com/img/LOGO.c86cd0e1.svg" title="" alt="埃文科技 IP 定位数据" width="331">
|
||||
|
||||
## JetBrain Support
|
||||
|
||||
本项目受 [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport) 支持。 很高兴使用`Goland`作为我们的开发工具。
|
||||
|
||||
10
cmd/cmd.go
10
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"})
|
||||
@@ -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"})
|
||||
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,
|
||||
|
||||
@@ -116,7 +116,7 @@ func (f *FastTracer) testEDU_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFast_v6() {
|
||||
func (f *FastTracer) testFastBJ_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
@@ -124,12 +124,24 @@ func (f *FastTracer) testFast_v6() {
|
||||
//f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFastSH_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFastGZ_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
}
|
||||
|
||||
func FastTestv6(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
var c string
|
||||
|
||||
oe = outEnable
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 北京三网快速测试\n2. 全国电信\n3. 全国联通\n4. 全国移动\n5. 全国教育网\n6. 全国五网")
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 北京三网快速测试\n2. 上海三网快速测试\n3. 广州三网快速测试\n4. 全国电信\n5. 全国联通\n6. 全国移动\n7. 全国教育网\n8. 全国五网")
|
||||
fmt.Print("请选择选项:")
|
||||
_, err := fmt.Scanln(&c)
|
||||
if err != nil {
|
||||
@@ -159,18 +171,22 @@ func FastTestv6(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFa
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testFast_v6()
|
||||
ft.testFastBJ_v6()
|
||||
case "2":
|
||||
ft.testCT_v6()
|
||||
ft.testFastSH_v6()
|
||||
case "3":
|
||||
ft.testCU_v6()
|
||||
ft.testFastGZ_v6()
|
||||
case "4":
|
||||
ft.testCM_v6()
|
||||
ft.testCT_v6()
|
||||
case "5":
|
||||
ft.testEDU_v6()
|
||||
ft.testCU_v6()
|
||||
case "6":
|
||||
ft.testCM_v6()
|
||||
case "7":
|
||||
ft.testEDU_v6()
|
||||
case "8":
|
||||
ft.testAll_v6()
|
||||
default:
|
||||
ft.testFast_v6()
|
||||
ft.testFastBJ_v6()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ func FastTest(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFast
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 北京三网快速测试\n2. 全国电信\n3. 全国联通\n4. 全国移动\n5. 全国教育网\n6. 全国五网")
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 北京三网快速测试\n2. 上海三网快速测试\n3. 广州三网快速测试\n4. 全国电信\n5. 全国联通\n6. 全国移动\n7. 全国教育网\n8. 全国五网")
|
||||
fmt.Print("请选择选项:")
|
||||
_, err = fmt.Scanln(&c)
|
||||
if err != nil {
|
||||
@@ -192,19 +192,23 @@ func FastTest(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFast
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testFast()
|
||||
ft.testFastBJ()
|
||||
case "2":
|
||||
ft.testCT()
|
||||
ft.testFastSH()
|
||||
case "3":
|
||||
ft.testCU()
|
||||
ft.testFastGZ()
|
||||
case "4":
|
||||
ft.testCM()
|
||||
ft.testCT()
|
||||
case "5":
|
||||
ft.testEDU()
|
||||
ft.testCU()
|
||||
case "6":
|
||||
ft.testCM()
|
||||
case "7":
|
||||
ft.testEDU()
|
||||
case "8":
|
||||
ft.testAll()
|
||||
default:
|
||||
ft.testFast()
|
||||
ft.testFastBJ()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,10 +434,22 @@ func (f *FastTracer) testEDU() {
|
||||
f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.CST)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFast() {
|
||||
func (f *FastTracer) testFastBJ() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
//f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFastSH() {
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFastGZ() {
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
}
|
||||
|
||||
16
go.mod
16
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/nxtrace/NTrace-core
|
||||
|
||||
go 1.24.1
|
||||
go 1.24.5
|
||||
|
||||
require (
|
||||
github.com/akamensky/argparse v1.4.0
|
||||
@@ -10,14 +10,14 @@ require (
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
github.com/tsosunchia/powclient v0.1.5
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/net v0.42.0
|
||||
golang.org/x/sync v0.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
@@ -25,11 +25,11 @@ require (
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/cast v1.9.2 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -40,6 +40,6 @@ require (
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
28
go.sum
28
go.sum
@@ -10,8 +10,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
@@ -49,10 +49,10 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -86,19 +86,19 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
|
||||
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.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
265
ipgeo/ipdbone.go
Normal file
265
ipgeo/ipdbone.go
Normal file
@@ -0,0 +1,265 @@
|
||||
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()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
@@ -52,6 +52,8 @@ func GetSource(s string) Source {
|
||||
return Chunzhen
|
||||
case "DISABLE-GEOIP":
|
||||
return disableGeoIP
|
||||
case "IPDB.ONE":
|
||||
return IPDBOne
|
||||
default:
|
||||
return LeoIP
|
||||
}
|
||||
|
||||
10
ipgeo/leo.go
10
ipgeo/leo.go
@@ -83,6 +83,11 @@ func receiveParse() {
|
||||
}
|
||||
}
|
||||
|
||||
// 当前的实现中,每次调用 receiveParse() 都会锁定 WebSocket 连接
|
||||
// 当前为单例模式,只启动一个 receiveParse 协程
|
||||
|
||||
var receiveParseOnce sync.Once
|
||||
|
||||
func LeoIP(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeoData, error) {
|
||||
// TODO: 根据lang的值请求中文/英文API
|
||||
// TODO: 根据maptrace的值决定是否请求经纬度信息
|
||||
@@ -100,7 +105,10 @@ func LeoIP(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeo
|
||||
// 发送请求
|
||||
sendIPRequest(ip)
|
||||
// 同步开启监听
|
||||
go receiveParse()
|
||||
// 确保 receiveParse 只启动一次
|
||||
receiveParseOnce.Do(func() {
|
||||
go receiveParse()
|
||||
})
|
||||
|
||||
// 拥塞,等待数据返回
|
||||
select {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -13,12 +13,17 @@ type ReceivedMessage struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// PacketListener 负责监听网络数据包并通过通道传递接收到的消息
|
||||
type PacketListener struct {
|
||||
ctx context.Context
|
||||
Conn net.PacketConn
|
||||
Messages chan ReceivedMessage
|
||||
}
|
||||
|
||||
// NewPacketListener 创建一个新的数据包监听器
|
||||
// conn: 用于接收数据包的连接
|
||||
// ctx: 用于控制监听器生命周期的上下文
|
||||
// 返回初始化好的 PacketListener 实例
|
||||
func NewPacketListener(conn net.PacketConn, ctx context.Context) *PacketListener {
|
||||
results := make(chan ReceivedMessage, 50)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -23,6 +23,7 @@ var (
|
||||
|
||||
type Config struct {
|
||||
SrcAddr string
|
||||
SrcPort int
|
||||
BeginHop int
|
||||
MaxHops int
|
||||
NumMeasurements int
|
||||
|
||||
@@ -32,6 +32,7 @@ type UDPTracer struct {
|
||||
|
||||
sem *semaphore.Weighted
|
||||
fetchLock sync.Mutex
|
||||
udpMutex sync.Mutex
|
||||
}
|
||||
|
||||
func (t *UDPTracer) Execute() (*Result, error) {
|
||||
@@ -148,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
|
||||
@@ -192,6 +196,11 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if util.EnvRandomPort == "" {
|
||||
t.udpMutex.Lock()
|
||||
defer t.udpMutex.Unlock()
|
||||
}
|
||||
|
||||
srcIP, srcPort, udpConn, err := t.getUDPConn(0)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -32,6 +32,7 @@ type UDPTracerIPv6 struct {
|
||||
|
||||
sem *semaphore.Weighted
|
||||
fetchLock sync.Mutex
|
||||
udpMutex sync.Mutex
|
||||
}
|
||||
|
||||
func (t *UDPTracerIPv6) Execute() (*Result, error) {
|
||||
@@ -117,16 +118,77 @@ func (t *UDPTracerIPv6) listenICMP() {
|
||||
}
|
||||
}
|
||||
|
||||
// handleICMPMessage 处理 ICMPv6 消息并提取 UDP 源端口
|
||||
//
|
||||
// ICMPv6 错误消息格式:
|
||||
// - ICMPv6 头部 (8 字节)
|
||||
// - 原始 IPv6 包 (包含 IPv6 头部和 UDP 头部)
|
||||
//
|
||||
// 处理步骤:
|
||||
// 1. 验证消息长度
|
||||
// 2. 解析 ICMPv6 头部
|
||||
// 3. 提取原始 IPv6 包
|
||||
// 4. 处理可能的扩展头部
|
||||
// 5. 提取 UDP 源端口
|
||||
// 6. 发送结果到对应通道
|
||||
func (t *UDPTracerIPv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
// 对于IPv6,我们需要从ICMP消息中提取UDP源端口
|
||||
// 这里假设UDP头部在ICMP消息的固定位置
|
||||
if len(msg.Msg) < 48+8 {
|
||||
// ICMPv6 错误消息至少需要包含 IPv6 头部(40字节)和部分 UDP 头部
|
||||
if len(msg.Msg) < 48 {
|
||||
return
|
||||
}
|
||||
|
||||
// IPv6 ICMP错误消息包含原始数据包的IPv6头(40字节)和UDP头(8字节)
|
||||
// 从UDP头部提取源端口(前2字节)
|
||||
srcPort := int(uint16(msg.Msg[48])<<8 | uint16(msg.Msg[49]))
|
||||
// 尝试解析 ICMPv6 消息中包含的原始数据包
|
||||
var offset int = 8 // ICMPv6 头部长度
|
||||
|
||||
// 检查剩余长度是否足够包含 IPv6 头部
|
||||
if len(msg.Msg) < offset+40 {
|
||||
return
|
||||
}
|
||||
|
||||
// 验证 IPv6 版本 (前4位应该是6)
|
||||
if (msg.Msg[offset] >> 4) != 6 {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取下一个头部类型
|
||||
nextHeader := msg.Msg[offset+6]
|
||||
|
||||
// 跳过 IPv6 基本头部
|
||||
offset += 40
|
||||
|
||||
// 处理可能的扩展头部
|
||||
for nextHeader != 17 && offset+2 < len(msg.Msg) { // 17 是 UDP 协议号
|
||||
switch nextHeader {
|
||||
case 0: // Hop-by-Hop Options
|
||||
case 43: // Routing
|
||||
case 44: // Fragment
|
||||
case 50: // ESP
|
||||
case 51: // AH
|
||||
case 60: // Destination Options
|
||||
if offset+2 >= len(msg.Msg) {
|
||||
return // 不够长,无法读取扩展头部长度
|
||||
}
|
||||
nextHeader = msg.Msg[offset]
|
||||
headerLen := int(msg.Msg[offset+1])*8 + 8
|
||||
offset += headerLen
|
||||
default:
|
||||
// 未知或不支持的扩展头部类型
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 确认下一个头部是 UDP (17)
|
||||
if nextHeader != 17 {
|
||||
return
|
||||
}
|
||||
|
||||
// 确保有足够的数据来读取 UDP 源端口
|
||||
if offset+2 > len(msg.Msg) {
|
||||
return
|
||||
}
|
||||
|
||||
// 从 UDP 头部提取源端口(前2字节)
|
||||
srcPort := int(uint16(msg.Msg[offset])<<8 | uint16(msg.Msg[offset+1]))
|
||||
|
||||
t.inflightRequestLock.Lock()
|
||||
defer t.inflightRequestLock.Unlock()
|
||||
@@ -152,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
|
||||
@@ -196,6 +261,11 @@ func (t *UDPTracerIPv6) send(ttl int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if util.EnvRandomPort == "" {
|
||||
t.udpMutex.Lock()
|
||||
defer t.udpMutex.Unlock()
|
||||
}
|
||||
|
||||
srcIP, srcPort, udpConn, err := t.getUDPConn(0)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -23,7 +23,7 @@ func GetMapUrl(r string) (string, error) {
|
||||
if len(strings.Split(fastIp, ":")) > 1 {
|
||||
fastIp = "[" + fastIp + "]"
|
||||
}
|
||||
host = "origin-fallback.nxtrace.org"
|
||||
host = "api.nxtrace.org"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
fastIp = util.GetFastIP(host, port, false)
|
||||
|
||||
@@ -30,7 +30,7 @@ var FastIpCache = ""
|
||||
func GetFastIP(domain string, port string, enableOutput bool) string {
|
||||
proxyUrl := GetProxy()
|
||||
if proxyUrl != nil {
|
||||
return "origin-fallback.nxtrace.org"
|
||||
return "api.nxtrace.org"
|
||||
}
|
||||
if FastIpCache != "" {
|
||||
return FastIpCache
|
||||
@@ -38,7 +38,7 @@ func GetFastIP(domain string, port string, enableOutput bool) string {
|
||||
|
||||
var ips []net.IP
|
||||
var err error
|
||||
if domain == "origin-fallback.nxtrace.org" {
|
||||
if domain == "api.nxtrace.org" {
|
||||
ips, err = net.LookupIP("api.nxtrace.org")
|
||||
} else {
|
||||
ips, err = net.LookupIP(domain)
|
||||
@@ -122,6 +122,7 @@ func checkLatency(domain string, ip string, port string) {
|
||||
return
|
||||
}
|
||||
req.Host = domain
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
|
||||
|
||||
26
util/util.go
26
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)
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func GetenvDefault(key, defVal string) string {
|
||||
}
|
||||
|
||||
func GetHostAndPort() (host string, port string) {
|
||||
var hostP = GetenvDefault("NEXTTRACE_HOSTPORT", "origin-fallback.nxtrace.org")
|
||||
var hostP = GetenvDefault("NEXTTRACE_HOSTPORT", "api.nxtrace.org")
|
||||
// 解析域名
|
||||
hostArr := strings.Split(hostP, ":")
|
||||
// 判断是否有指定端口
|
||||
|
||||
@@ -193,7 +193,7 @@ func createWsConn() *WsConn {
|
||||
if len(strings.Split(fastIp, ":")) > 1 {
|
||||
fastIp = "[" + fastIp + "]"
|
||||
}
|
||||
host = "origin-fallback.nxtrace.org"
|
||||
host = "api.nxtrace.org"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
fastIp = util.GetFastIP(host, port, true)
|
||||
|
||||
Reference in New Issue
Block a user