Compare commits

..

134 Commits

Author SHA1 Message Date
tsosunchia
dccc41b995 udp mode增加mutex 以避免固定端口时的端口竞争问题 2025-04-15 15:43:41 +08:00
tsosunchia
9af629b6f9 ipgeo/leo.go 优化 receiveParse 的单例调用(WebSocket 连接的并发控制) 2025-04-15 13:36:42 +08:00
tsosunchia
d6de649e60 handleICMPMessage 采用更健壮的方案处理ICMPv6头部,之前使用了一个固定的偏移量来从 ICMPv6 错误消息中提取 UDP 源端口,现在改为动态计算偏移量 2025-04-15 13:16:15 +08:00
tsosunchia
bcd430c231 IPv6下也支持UDP Mode
Co-authored-by: Claude-3.7-Sonnet <ai@anthropic.com>
Co-authored-by: tsosunchia <59512455+tsosunchia@users.noreply.github.com>

 要提交的变更:
	修改:     cmd/cmd.go
	修改:     fast_trace/fast_trace ipv6.go
	修改:     trace/tcp_ipv6.go
	修改:     trace/trace.go
	重命名:   trace/udp.go -> trace/udp_ipv4.go
	新文件:   trace/udp_ipv6.go

Signed-off-by: tsosunchia <59512455+tsosunchia@users.noreply.github.com>
2025-04-14 23:25:18 +08:00
tsosunchia
314a4b3015 修正udp mode一些情况下的显示错误 2025-04-14 22:46:23 +08:00
tsosunchia
4d8e7e322b 固定UDP模式的源端口,改善在一些负载均衡网络环境下的效果,在使用ENV "NEXTTRACE_RANDOMPORT" 支持继续随机分配源端口。
https://github.com/nxtrace/NTrace-core/issues/296
https://github.com/nxtrace/NTrace-core/pull/297

Co-authored-by: Yunlq <vinculo025@gmail.com>
Co-authored-by: tsosunchia <59512455+tsosunchia@users.noreply.github.com>
2025-04-14 22:04:22 +08:00
tsosunchia
8fb1220f1b 更改 wshandle/client.go 超时/手动终端log显示
Signed-off-by: tsosunchia <59512455+tsosunchia@users.noreply.github.com>
2025-04-13 20:36:10 +08:00
tsosunchia
90b1a3c1ad 固定TCP模式的源端口,改善在一些负载均衡网络环境下的效果,在使用ENV "NEXTTRACE_RANDOMPORT" 支持继续随机分配源端口。
https://github.com/nxtrace/NTrace-core/issues/296
2025-04-13 20:26:54 +08:00
tsosunchia
608a2904d4 若干修改,方便其他程序调用
https://github.com/nxtrace/NTrace-V1/issues/84
 要提交的变更:
	修改:     cmd/cmd.go
	修改:     go.mod
	修改:     go.sum
	修改:     wshandle/client.go
2025-04-13 12:13:08 +08:00
tsosunchia
62ab23bdeb termux安装方法描述增加root-repo安装引导 2025-04-13 12:13:08 +08:00
tsosunchia
c095599400 upgrade go to verison 1.24
# Conflicts:
#	go.mod
2025-04-13 12:12:55 +08:00
dependabot[bot]
76d841f670 chore(deps): bump golang.org/x/sync from 0.12.0 to 0.13.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/sync/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-09 21:38:48 +08:00
dependabot[bot]
9dd36e9625 chore(deps): bump golang.org/x/net from 0.37.0 to 0.38.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-09 21:33:24 +08:00
dependabot[bot]
e1f4052518 chore(deps): bump github.com/spf13/viper from 1.19.0 to 1.20.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.19.0 to 1.20.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.19.0...v1.20.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-29 10:32:27 +08:00
dependabot[bot]
b5df3efd1b chore(deps): bump golang.org/x/net from 0.35.0 to 0.37.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.35.0 to 0.37.0.
- [Commits](https://github.com/golang/net/compare/v0.35.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-29 10:27:37 +08:00
tsosunchia
3f9803680e 更新 README_zh_CN.md 2025-03-10 14:13:52 +08:00
tsosunchia
db2b02d5f8 更新 README.md 2025-03-10 14:13:31 +08:00
dependabot[bot]
ec634fffb3 chore(deps): bump golang.org/x/net from 0.34.0 to 0.35.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.34.0 to 0.35.0.
- [Commits](https://github.com/golang/net/compare/v0.34.0...v0.35.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-19 20:47:09 +08:00
dependabot[bot]
a3404cebac chore(deps): bump golang.org/x/sync from 0.10.0 to 0.11.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/sync/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-19 10:53:18 +08:00
tsosunchia
667285a8c3 Merge pull request #291 from nxtrace/main
SYNC
2025-02-03 22:12:45 +08:00
tsosunchia
c52d5a5414 调整x-cmd位置 2025-02-03 22:11:53 +08:00
tsosunchia
6c009602b5 update readme 2025-02-03 22:09:52 +08:00
tsosunchia
a179608da0 Merge pull request #290 from nxtrace/main
Some checks failed
Build & Release / build (amd64, freebsd) (push) Has been cancelled
Build & Release / build (amd64, linux) (push) Has been cancelled
Build & Release / build (amd64, openbsd) (push) Has been cancelled
Build & Release / build (amd64, windows) (push) Has been cancelled
Build & Release / build (arm, 5, linux) (push) Has been cancelled
Build & Release / build (arm, 6, linux) (push) Has been cancelled
Build & Release / build (arm, 7, freebsd) (push) Has been cancelled
Build & Release / build (arm, 7, linux) (push) Has been cancelled
Build & Release / build (arm, 7, openbsd) (push) Has been cancelled
Build & Release / build (arm, 7, windows) (push) Has been cancelled
Build & Release / build (arm64, android) (push) Has been cancelled
Build & Release / build (arm64, darwin) (push) Has been cancelled
Build & Release / build (arm64, freebsd) (push) Has been cancelled
Build & Release / build (arm64, linux) (push) Has been cancelled
Build & Release / build (arm64, openbsd) (push) Has been cancelled
Build & Release / build (arm64, windows) (push) Has been cancelled
Build & Release / build (mips, linux) (push) Has been cancelled
Build & Release / build (mips, softfloat, linux) (push) Has been cancelled
Build & Release / build (mips64, linux) (push) Has been cancelled
Build & Release / build (mips64le, linux) (push) Has been cancelled
Build & Release / build (mipsle, linux) (push) Has been cancelled
Build & Release / build (mipsle, softfloat, linux) (push) Has been cancelled
Build & Release / build (ppc64, linux) (push) Has been cancelled
Build & Release / build (ppc64le, linux) (push) Has been cancelled
Build & Release / build (riscv64, linux) (push) Has been cancelled
Build & Release / build (s390x, linux) (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Build & Release / publish-new-formula (push) Has been cancelled
feat: 添加触发 deb 仓库的推送
2025-02-03 19:34:07 +08:00
wcbing
5cd2962a2b feat: 添加触发 deb 仓库的推送
Signed-off-by: tsosunchia <59512455+tsosunchia@users.noreply.github.com>
2025-02-03 19:32:32 +08:00
tsosunchia
f774c0d29f Merge pull request #285 from nxtrace/main
Some checks failed
Build & Release / build (amd64, freebsd) (push) Has been cancelled
Build & Release / build (amd64, linux) (push) Has been cancelled
Build & Release / build (amd64, openbsd) (push) Has been cancelled
Build & Release / build (amd64, windows) (push) Has been cancelled
Build & Release / build (arm, 5, linux) (push) Has been cancelled
Build & Release / build (arm, 6, linux) (push) Has been cancelled
Build & Release / build (arm, 7, freebsd) (push) Has been cancelled
Build & Release / build (arm, 7, linux) (push) Has been cancelled
Build & Release / build (arm, 7, openbsd) (push) Has been cancelled
Build & Release / build (arm, 7, windows) (push) Has been cancelled
Build & Release / build (arm64, android) (push) Has been cancelled
Build & Release / build (arm64, darwin) (push) Has been cancelled
Build & Release / build (arm64, freebsd) (push) Has been cancelled
Build & Release / build (arm64, linux) (push) Has been cancelled
Build & Release / build (arm64, openbsd) (push) Has been cancelled
Build & Release / build (arm64, windows) (push) Has been cancelled
Build & Release / build (mips, linux) (push) Has been cancelled
Build & Release / build (mips, softfloat, linux) (push) Has been cancelled
Build & Release / build (mips64, linux) (push) Has been cancelled
Build & Release / build (mips64le, linux) (push) Has been cancelled
Build & Release / build (mipsle, linux) (push) Has been cancelled
Build & Release / build (mipsle, softfloat, linux) (push) Has been cancelled
Build & Release / build (ppc64, linux) (push) Has been cancelled
Build & Release / build (ppc64le, linux) (push) Has been cancelled
Build & Release / build (riscv64, linux) (push) Has been cancelled
Build & Release / build (s390x, linux) (push) Has been cancelled
Test / test (macos-latest) (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
Test / test (windows-latest) (push) Has been cancelled
Build & Release / publish-new-formula (push) Has been cancelled
SYNC
2025-01-12 11:41:06 +08:00
tsosunchia
69588b0d14 更新Golang到v1.23,此处需注意之后版本编译时需要加"-ldflags=-checklinkname=0"参数
修改:     .cross_compile.sh
	修改:     .github/workflows/build.yml
	修改:     .github/workflows/test.yml
	修改:     README.md
	修改:     README_zh_CN.md
	修改:     go.mod
	修改:     go.sum
2025-01-11 23:09:27 +08:00
tsosunchia
6c49957be8 解决网络不通时的卡死问题 2025-01-05 12:24:58 +08:00
tsosunchia
5cc08151f4 Merge pull request #279 from nxtrace/main
SYNC
2024-12-21 21:40:34 +08:00
tsosunchia
d233e0e38d fasttrace和filetrace支持udpmode,且增加fasttrace和filetrace的port自定义参数 2024-12-21 21:37:54 +08:00
tsosunchia
183516b14c 修改部分UI表达 2024-12-21 21:00:45 +08:00
tsosunchia
00695f32b4 Merge pull request #278 from nxtrace/main
SYNC
2024-12-20 23:14:03 +08:00
tsosunchia
77ae2d1ef0 紧急更新:CVE-2024-45338 2024-12-20 23:12:28 +08:00
tsosunchia
6c97ae8ea6 go 1.22.8 -> 1.22.10 2024-12-16 16:30:11 +08:00
tsosunchia
2e02a2b53f 一些依赖更新 2024-12-16 16:26:54 +08:00
dependabot[bot]
670654864f chore(deps): bump github.com/stretchr/testify from 1.9.0 to 1.10.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 16:25:45 +08:00
dependabot[bot]
7f9c0fcb32 chore(deps): bump golang.org/x/net from 0.30.0 to 0.32.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.30.0 to 0.32.0.
- [Commits](https://github.com/golang/net/compare/v0.30.0...v0.32.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 16:19:23 +08:00
lunrenyi
a0e4d68d8d Update README_zh_CN.md 2024-11-28 16:26:50 +08:00
lunrenyi
13360aefff docs(install): add x-cmd method to install nexttrace 2024-11-28 16:26:50 +08:00
tsosunchia
6096047c08 一些依赖更新 2024-11-04 21:51:17 +08:00
tsosunchia
4ae9d8ece1 Merge pull request #263 from nxtrace/main
更新依赖
2024-10-06 11:01:29 +08:00
tsosunchia
970893fe52 更新依赖 2024-10-06 11:01:02 +08:00
tsosunchia
fa35005bf2 Merge pull request #262 from nxtrace/main
SYNC
2024-10-06 11:00:34 +08:00
tsosunchia
b0c0f8d3ce macOS下编译时,无论uid==0,icmpPktListen均使用fakeboboliu的实现方式 2024-10-06 10:55:17 +08:00
john xu
8774e8cd67 fix: close when createWsConn failed 2024-10-06 10:55:03 +08:00
tsosunchia
6a3ea6acb3 Merge pull request #258 from nxtrace/main
SYNC
2024-09-10 11:19:06 +08:00
dependabot[bot]
42e4a23233 chore(deps): bump golang.org/x/net from 0.28.0 to 0.29.0 (#67)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.28.0 to 0.29.0.
- [Commits](https://github.com/golang/net/compare/v0.28.0...v0.29.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: tsosunchia <59512455+tsosunchia@users.noreply.github.com>
2024-09-10 11:01:53 +08:00
tsosunchia
2f1e086c7d update: FT mode增加 CAN.CN2/CMIN2/CNC PEK.CN2 2024-09-10 10:58:27 +08:00
tsosunchia
b507dea8b1 Merge pull request #256 from nxtrace/main
SYNC
2024-09-02 14:27:51 +08:00
tsosunchia
fa8f6687de FT:杭州CU Ipv6 2024-09-02 06:06:41 +00:00
tsosunchia
f47e742d81 FT应用dot参数 2024-09-02 04:33:04 +00:00
tsosunchia
e81400bd1d 更新依赖 2024-09-02 08:22:58 +08:00
tsosunchia
e072b0dda8 Merge pull request #255 from nxtrace/main
SYNC
2024-09-01 15:38:48 +08:00
tsosunchia
868bf3d691 FT提示修改 2024-09-01 15:37:45 +08:00
tsosunchia
79ac0bc456 FT增加北京CST和广州EDU 2024-09-01 15:24:10 +08:00
tsosunchia
f13889f48f Merge pull request #254 from nxtrace/main
SYNC
2024-08-31 13:08:16 +08:00
kernelcrashdump
a5641b5530 Update nt_install.sh 2024-08-31 12:48:03 +08:00
breakertt
eea77b1f0d Enhance ipinfoLocal.mmdb file lookup (#66)
* Enhance ipinfoLocal.mmdb file lookup
* Refactor get NEXTTRACE_IPINFOLOCALPATH env variable into util
2024-08-15 21:26:57 +08:00
tsosunchia
3e0a086961 Merge pull request #65 from nxtrace/dependabot/go_modules/golang.org/x/net-0.28.0
chore(deps): bump golang.org/x/net from 0.27.0 to 0.28.0
2024-08-13 07:39:17 +08:00
tsosunchia
c0332ff4d5 Merge pull request #64 from nxtrace/dependabot/go_modules/github.com/tidwall/gjson-1.17.3
chore(deps): bump github.com/tidwall/gjson from 1.17.1 to 1.17.3
2024-08-13 07:38:36 +08:00
dependabot[bot]
7fa5181062 chore(deps): bump golang.org/x/net from 0.27.0 to 0.28.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.27.0 to 0.28.0.
- [Commits](https://github.com/golang/net/compare/v0.27.0...v0.28.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 23:38:23 +00:00
tsosunchia
f26e818764 Merge branch 'main' into dependabot/go_modules/github.com/tidwall/gjson-1.17.3 2024-08-13 07:37:59 +08:00
tsosunchia
31e3375462 Merge pull request #63 from nxtrace/dependabot/go_modules/github.com/rodaine/table-1.3.0
chore(deps): bump github.com/rodaine/table from 1.2.0 to 1.3.0
2024-08-13 07:35:52 +08:00
tsosunchia
d12c4fcd57 Merge pull request #62 from nxtrace/dependabot/go_modules/golang.org/x/sync-0.8.0
chore(deps): bump golang.org/x/sync from 0.7.0 to 0.8.0
2024-08-13 07:35:43 +08:00
dependabot[bot]
b70db0883c chore(deps): bump github.com/tidwall/gjson from 1.17.1 to 1.17.3
Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.17.1 to 1.17.3.
- [Commits](https://github.com/tidwall/gjson/compare/v1.17.1...v1.17.3)

---
updated-dependencies:
- dependency-name: github.com/tidwall/gjson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 14:38:50 +00:00
dependabot[bot]
913a4f99b3 chore(deps): bump github.com/rodaine/table from 1.2.0 to 1.3.0
Bumps [github.com/rodaine/table](https://github.com/rodaine/table) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/rodaine/table/releases)
- [Commits](https://github.com/rodaine/table/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/rodaine/table
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 14:38:48 +00:00
dependabot[bot]
6369745859 chore(deps): bump golang.org/x/sync from 0.7.0 to 0.8.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.7.0 to 0.8.0.
- [Commits](https://github.com/golang/sync/compare/v0.7.0...v0.8.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 14:38:45 +00:00
tsosunchia
756b51e6da update readme 2024-07-21 21:10:56 +08:00
tsosunchia
ea2a6b596a Merge pull request #249 from nxtrace/main
chore: SYNC
2024-07-16 19:05:05 +08:00
tsosunchia
2e9428ce76 fix:ftv6 color mode some bugs 2024-07-16 19:03:12 +08:00
tsosunchia
052fe2dc06 Merge pull request #58 from nxtrace/dependabot/go_modules/github.com/oschwald/maxminddb-golang-1.13.1
chore(deps): bump github.com/oschwald/maxminddb-golang from 1.13.0 to 1.13.1
2024-07-15 19:01:37 +08:00
tsosunchia
24b7dae2d1 Merge pull request #59 from nxtrace/dependabot/go_modules/golang.org/x/net-0.27.0
chore(deps): bump golang.org/x/net from 0.26.0 to 0.27.0
2024-07-15 19:01:28 +08:00
tsosunchia
849cf488aa Merge pull request #60 from FyZhu97/bugfix/inflightRequest-deadlock
[bugfix] fix inflightRequestLock deadlock in tcp/udp tracing
2024-07-15 19:01:11 +08:00
方鸻
a539a4e079 fix(trace): limit channel buffer size in TCP and UDP tracing to avoid deadlock 2024-07-12 17:53:55 +08:00
dependabot[bot]
efc19c4b7d chore(deps): bump golang.org/x/net from 0.26.0 to 0.27.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/net/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-08 14:51:32 +00:00
dependabot[bot]
f0a6c826fc chore(deps): bump github.com/oschwald/maxminddb-golang
Bumps [github.com/oschwald/maxminddb-golang](https://github.com/oschwald/maxminddb-golang) from 1.13.0 to 1.13.1.
- [Release notes](https://github.com/oschwald/maxminddb-golang/releases)
- [Commits](https://github.com/oschwald/maxminddb-golang/compare/v1.13.0...v1.13.1)

---
updated-dependencies:
- dependency-name: github.com/oschwald/maxminddb-golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 14:45:38 +00:00
tsosunchia
9e05065a79 fmt 2024-06-29 01:13:19 +08:00
tsosunchia
ade34e964a 修正udp mode不应用起始hop参数设置的问题 2024-06-27 12:23:00 +08:00
tsosunchia
486cdfdc03 Merge pull request #57 from fakeboboliu/main
provide ability to override baseurl of APIs
2024-06-22 20:21:58 +08:00
bobo liu
3260753a66 provide ability to override baseurl of APIs 2024-06-22 20:16:29 +08:00
tsosunchia
0c44e39a20 更新依赖 2024-06-11 19:24:34 +08:00
tsosunchia
47870b1d9a 增加一个在aur包维护的安装方式 2024-06-01 22:28:52 +08:00
tsosunchia
57346f9db8 remove illegal link 2024-06-01 22:20:07 +08:00
kernelcrashdump
026460545f Merge pull request #51 from Canary233/patch-1
README_zh_CN.md: Fix typo
2024-06-01 16:49:58 +08:00
Canary233
29b81d00ae README_zh_CN.md: Fix typo 2024-06-01 14:07:38 +08:00
tsosunchia
c1cd80232a Merge pull request #245 from nxtrace/main
SYNC
2024-05-31 09:58:35 +08:00
tsosunchia
9c4505f9c7 update README 2024-05-31 09:57:31 +08:00
tsosunchia
e27c8bea77 更新依赖 2024-05-30 04:29:25 +00:00
tsosunchia
f303397ce1 Merge pull request #244 from nxtrace/main
增加关于DF功能的描述
2024-05-30 12:26:46 +08:00
tsosunchia
33239a78d1 增加关于DF功能的描述 2024-05-30 04:26:01 +00:00
tsosunchia
e4fa907ed4 Merge pull request #243 from nxtrace/main
SYNC
2024-05-30 12:19:23 +08:00
tsosunchia
d0fb43e947 add ipv4 tcp dontFragment option 2024-05-30 03:23:10 +00:00
tsosunchia
9dda330543 workflow-test.yaml: set go version 2024-05-30 02:30:54 +00:00
tsosunchia
94372d9605 Merge pull request #242 from nxtrace/main
SYNC
2024-05-30 00:33:29 +08:00
tsosunchia
f50ca1f7f8 修正之前对于payload size设置的错误表述 2024-05-30 00:29:53 +08:00
tsosunchia
f06dba7458 ipv6 tcp应用pktsize 2024-05-30 00:01:11 +08:00
tsosunchia
d1e87c8a77 tcp应用pktsize 2024-05-29 23:25:20 +08:00
tsosunchia
57d31a38f7 修改默认udptrace端口为符合RFC8487 2024-05-29 22:50:48 +08:00
tsosunchia
84b709de44 udptrace pktsize问题修正且默认发包间隔调小 2024-05-29 22:39:35 +08:00
tsosunchia
dad9282078 TYPO: IPProtocolUDP 2024-05-29 17:37:13 +08:00
tsosunchia
85b01ebfc6 Merge pull request #50 from Liu-WeiHu/tracer-send-fix
关闭udp链接
2024-05-29 17:17:18 +08:00
Liu-WeiHu
a2b5cde829 release udpConn 2024-05-29 15:55:33 +08:00
tsosunchia
f2536980b7 Merge pull request #241 from nxtrace/main
SYNC
2024-05-28 10:30:54 +08:00
tsosunchia
fae4b7b71a 更新NextTraceroute说明 2024-05-28 02:29:30 +00:00
tsosunchia
791dd45086 Merge pull request #49 from nxtrace/dependabot/go_modules/github.com/tsosunchia/powclient-0.1.5
chore(deps): bump github.com/tsosunchia/powclient from 0.1.4 to 0.1.5
2024-05-22 11:05:46 +08:00
dependabot[bot]
3751d9ce12 chore(deps): bump github.com/tsosunchia/powclient from 0.1.4 to 0.1.5
Bumps [github.com/tsosunchia/powclient](https://github.com/tsosunchia/powclient) from 0.1.4 to 0.1.5.
- [Commits](https://github.com/tsosunchia/powclient/compare/v0.1.4...v0.1.5)

---
updated-dependencies:
- dependency-name: github.com/tsosunchia/powclient
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-20 14:31:40 +00:00
tsosunchia
39169291e8 Merge pull request #240 from nxtrace/main
SYNC
2024-05-13 21:26:35 +08:00
tsosunchia
6937b54cdf fix typo 2024-05-13 21:13:23 +08:00
tsosunchia
742035cc0c 更新依赖 2024-05-13 21:08:39 +08:00
tsosunchia
7350d13850 update readme 2024-05-13 20:54:05 +08:00
tsosunchia
970cff3b72 优化代码 2024-05-13 20:42:56 +08:00
tsosunchia
b053ee646b 优化代码 2024-05-13 20:39:22 +08:00
tsosunchia
f6f90f3a5b 更新依赖 2024-05-13 20:15:56 +08:00
tsosunchia
e128fe1893 Merge pull request #237 from nxtrace/main
SYNC
2024-04-17 19:59:25 +08:00
tsosunchia
2cb13be378 更新部分依赖
upgraded github.com/pelletier/go-toml/v2 v2.1.1 => v2.2.1
upgraded golang.org/x/exp v0.0.0-20240119083558-1b970713d09a => v0.0.0-20240416160154-fe59bbe5cc7f
2024-04-17 19:38:26 +08:00
tsosunchia
7ae4eb13c9 由于部分网络截断icmp data故ttl改为在seq读取 2024-04-17 19:34:01 +08:00
tsosunchia
d1a72458a6 Merge pull request #48 from nxtrace/dependabot/go_modules/github.com/rodaine/table-1.2.0
chore(deps): bump github.com/rodaine/table from 1.1.1 to 1.2.0
2024-04-11 13:45:33 +08:00
tsosunchia
5e3474d029 Merge pull request #47 from nxtrace/dependabot/go_modules/golang.org/x/net-0.24.0
chore(deps): bump golang.org/x/net from 0.22.0 to 0.24.0
2024-04-11 13:44:48 +08:00
dependabot[bot]
a93b7658b7 chore(deps): bump golang.org/x/net from 0.22.0 to 0.24.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.24.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.24.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 05:44:40 +00:00
tsosunchia
bbb905f113 Merge pull request #46 from nxtrace/dependabot/go_modules/golang.org/x/sync-0.7.0
chore(deps): bump golang.org/x/sync from 0.6.0 to 0.7.0
2024-04-11 13:43:56 +08:00
dependabot[bot]
5f44fc704d chore(deps): bump github.com/rodaine/table from 1.1.1 to 1.2.0
Bumps [github.com/rodaine/table](https://github.com/rodaine/table) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/rodaine/table/releases)
- [Commits](https://github.com/rodaine/table/compare/v1.1.1...v1.2.0)

---
updated-dependencies:
- dependency-name: github.com/rodaine/table
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-08 14:30:49 +00:00
dependabot[bot]
1257fe87b5 chore(deps): bump golang.org/x/sync from 0.6.0 to 0.7.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.6.0 to 0.7.0.
- [Commits](https://github.com/golang/sync/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-08 14:30:34 +00:00
tsosunchia
ea60a671f2 Merge pull request #235 from nxtrace/main
SYNC
2024-04-06 20:24:01 +08:00
tsosunchia
0d6e894a4b Merge pull request #42 from nxtrace/dependabot/go_modules/github.com/stretchr/testify-1.9.0
chore(deps): bump github.com/stretchr/testify from 1.8.4 to 1.9.0
2024-04-06 20:20:20 +08:00
tsosunchia
20069ab9f3 Merge pull request #43 from nxtrace/dependabot/github_actions/softprops/action-gh-release-2
chore(deps): bump softprops/action-gh-release from 1 to 2
2024-04-06 20:20:11 +08:00
tsosunchia
4f05b57fc2 Merge pull request #44 from nxtrace/dependabot/go_modules/golang.org/x/net-0.22.0
chore(deps): bump golang.org/x/net from 0.21.0 to 0.22.0
2024-04-06 20:19:59 +08:00
tsosunchia
190111f6da 修正在ECMP网络下的表现
之前ECMP网络可能会导致一次trace的结果来自不同flow(按icmp id区分的策略下),目前id不再携带TTL信息,因此一次trace的id将保持一致。
 要提交的变更:
	修改:     trace/icmp_ipv4.go
	修改:     trace/icmp_ipv6.go
2024-04-06 20:07:40 +08:00
tsosunchia
c6eb9bbd2e 修复ft时个别地方未应用色彩参数的问题
交互式变基操作正在进行中;至 cbc511f
 最后完成的命令(1 条命令被执行):
    pick 356e3db 修复ft时个别地方未应用色彩参数的问题
 接下来要执行的命令(剩余 1 条命令):
    pick 9ee7c3f 修正在ECMP网络下的表现 之前ECMP网络可能会导致一次trace的结果来自不同flow(按icmp id区分的策略下),目前id不再携带TTL信息,因此一次trace的id将保持一致。  要提交的变更: 	修改:     trace/icmp_ipv4.go 	修改:     trace/icmp_ipv6.go
 您在执行将分支 'main' 变基到 'cbc511f' 的操作。

 要提交的变更:
	修改:     fast_trace/fast_trace.go
2024-04-06 20:07:17 +08:00
tsosunchia
cbc511f097 修改一些显示问题,对于FASTTRACE/TESTFILE功能应用NO_COLOR属性,对于TESTFILE应用IP隐匿功能。
修改:     fast_trace/fast_trace.go
2024-03-31 12:42:20 +08:00
tsosunchia
66422c4661 Merge pull request #233 from nxtrace/main
update README.md
2024-03-31 00:14:56 +08:00
tsosunchia
b6828b4db1 update README.md 2024-03-31 00:13:32 +08:00
dependabot[bot]
0a750bc6d0 chore(deps): bump golang.org/x/net from 0.21.0 to 0.22.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.21.0 to 0.22.0.
- [Commits](https://github.com/golang/net/compare/v0.21.0...v0.22.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 14:49:20 +00:00
dependabot[bot]
47612c022d chore(deps): bump softprops/action-gh-release from 1 to 2
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 14:08:40 +00:00
dependabot[bot]
42bf45c2c8 chore(deps): bump github.com/stretchr/testify from 1.8.4 to 1.9.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.4 to 1.9.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.4...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 14:27:52 +00:00
tsosunchia
8ac1a6bacd Merge pull request #231 from nxtrace/main
SYNC
2024-02-21 09:33:09 +08:00
38 changed files with 1274 additions and 535 deletions

View File

@@ -29,13 +29,13 @@ for pl in ${PLATFORMS}; do
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
-w -s"
-w -s -checklinkname=0"
else
go build -trimpath -o ${TARGET} \
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
-w -s"
-w -s -checklinkname=0"
fi
done
export CGO_ENABLED=0
@@ -49,13 +49,13 @@ done
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
-w -s"
-w -s -checklinkname=0"
else
go build -trimpath -o ${TARGET} \
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
-w -s"
-w -s -checklinkname=0"
fi

View File

@@ -135,7 +135,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version: '1.23'
- name: Get project dependencies
run: go mod download
- name: Build
@@ -144,7 +144,7 @@ jobs:
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
-w -s"
-checklinkname=0 -w -s"
- name: Upload files to Artifacts
uses: actions/upload-artifact@v4
with:
@@ -153,7 +153,7 @@ jobs:
dist/${{ env.ASSET_NAME }}
- name: Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with: # 将下述可执行文件 release 上去
draft: true # Release草稿
files: |

View File

@@ -31,13 +31,12 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
check-latest: true
go-version: '1.23'
- name: Checkout codebase
uses: actions/checkout@v4
- name: Test with unix
if: ${{ matrix.os != 'windows-latest' }}
run: sudo go test -v -coverprofile='coverage.out' -covermode=count ./...
run: sudo go test -v -ldflags=-checklinkname=0 -coverprofile='coverage.out' -covermode=count ./...
- name: Test with windows
if: ${{ matrix.os == 'windows-latest' }}
run: go test -v -coverprofile='coverage.out' -covermode=count ./...
run: go test -v -ldflags=-checklinkname=0 -coverprofile='coverage.out' -covermode=count ./...

15
.github/workflows/triggerDebRepo.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Trigger Deb Repo
on:
release:
types: [published]
jobs:
trigger-deb-repo:
runs-on: ubuntu-latest
steps:
- env:
GITHUB_TOKEN: ${{ secrets.GT_Token }} # 操作 deb 仓库的 PAT
run: |
curl -X POST -H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/nxtrace/nexttrace-debs/actions/workflows/build.yaml/dispatches \
-d '{"ref": "main", "inputs": {"tag": "${{ github.event.release.tag_name }}"}}'

2
.gitignore vendored
View File

@@ -163,3 +163,5 @@ Temporary Items
# compile target directory
dist/
NTrace-core

141
README.md
View File

@@ -24,9 +24,6 @@
<a href="https://www.nxtrace.org/downloads">
<img src="https://img.shields.io/github/release/nxtrace/Ntrace-V1/all.svg?style=flat-square">
</a>
<a href="https://telegram.dog/sjprojects">
<img src="https://img.shields.io/endpoint?color=neon&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fnexttrace">
</a>
</p>
## IAAS Sponsor
@@ -60,56 +57,79 @@ Please note, there are exceptions to this synchronization. If a version of NTrac
* Linux
* One-click installation script
```shell
curl nxtrace.org/nt |bash
```
* Install nxtrace from the APT repository
* Supports AMD64/ARM64 architectures
```shell
echo "deb [trusted=yes] https://github.com/nxtrace/nexttrace-debs/releases/latest/download ./" |
sudo tee /etc/apt/sources.list.d/nexttrace.list
sudo apt update
sudo apt install nexttrace
```
* APT repository maintained by wcbing and nxtrace
* Arch Linux AUR installation command
* Directly download bin package (only supports amd64)
```shell
yay -S nexttrace-bin
```
* Build from source (only supports amd64)
```shell
yay -S nexttrace
```
* The AUR builds are maintained by ouuan, huyz
```shell
yay -S nexttrace-bin
```
* The AUR builds are maintained by ouuan
* Linuxbrew's installation command
* Linuxbrew's installation command
Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64)
Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64)
* Deepin installation command
```shell
apt install nexttrace
```
* Termux installation command
* [x-cmd](https://www.x-cmd.com/pkg/nexttrace) installation command
```shell
pkg install nexttrace-enhanced
x env use nexttrace
```
* Termux installation command
```shell
pkg install root-repo
pkg install nexttrace
```
* macOS
* macOS Homebrew's installation command
* Homebrew-core version
```shell
brew install nexttrace
```
```shell
brew install nexttrace
```
* This repository's ACTIONS automatically built version (updates faster)
```shell
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
```
```shell
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
```
* The homebrew-core build is maintained by chenrui333, please note that this version's updates may lag behind the repository Action automatically version
* Windows
* Windows WinGet installation command
* WinGet version
```powershell
winget install nexttrace
```
* WinGet build maintained by Dragon1573
* Windows Scoop installation command
* Scoop-extras version
```powershell
scoop bucket add extras && scoop install extras/nexttrace
```
```powershell
scoop bucket add extras && scoop install extras/nexttrace
```
* Scoop-extra is maintained by soenggam
Please note, the repositories for all of the above installation methods are maintained by open source enthusiasts. Availability and timely updates are not guaranteed. If you encounter problems, please contact the repository maintainer to solve them, or use the binary packages provided by the official build of this project.
Please note, the repositories for all of the above installation methods are maintained by open source enthusiasts. Availability and timely updates are not guaranteed. If you encounter problems, please contact the repository maintainer to solve them, or use the binary packages provided by the official build of this project.
### Manual Install
* Download the precompiled executable
@@ -119,21 +139,6 @@ Please note, there are exceptions to this synchronization. If a version of NTrac
* `Release` provides compiled binary executables for many systems and different architectures. If none are available, you can compile it yourself.
* Some essential dependencies of this project are not fully implemented on `Windows` by `Golang`, so currently, `NextTrace` is in an experimental support phase on the `Windows` platform.
* Install from source
After installing Go >= 1.20 yourself, you can use the following command to install
```shell
go install github.com/nxtrace/NTrace-core@latest
```
*because of the version constraints conflict, you can not install `NTrace-V1` by this*
After installation, the executable is in the `$GOPATH/bin` directory. If you have not set `GOPATH`, it is in the `$HOME/go/bin` directory.
The binary file name is consistent with the project name. You need to replace the `nexttrace` command below with `NTrace-core`.
If you want to be consistent with the commands below, you can rename the binary after executing the `go install` command
```shell
mv $GOPATH/bin/NTrace-core $GOPATH/bin/nexttrace
```
### Get Started
`NextTrace` uses the `ICMP` protocol to perform TraceRoute requests by default, which supports both `IPv4` and `IPv6`
@@ -240,6 +245,9 @@ nexttrace --no-rdns www.bbix.net
# Set the payload size to 1024 bytes
nexttrace --psize 1024 example.com
# Set the payload size and DF flag for TCP Trace
nexttrace --psize 1024 --dont-fragment --tcp example.com
# Feature: print Route-Path diagram
# Route-Path diagram example:
# AS6453 Tata Communication「Singapore『Singapore』」
@@ -264,9 +272,13 @@ export NO_COLOR=1
nexttrace --data-provider ip-api.com
## Note There are frequency limits for free queries of the ipinfo and IPInsight APIs. You can purchase services from these providers to remove the limits
## If necessary, you can clone this project, add the token provided by ipinfo or IPInsight and compile it yourself
## Fill the token to: ipgeo/tokens.go
## Note For the offline database IPInfoLocal, please download it manually and rename it to ipinfoLocal.mmdb. (You can download it from here: https://ipinfo.io/signup?ref=free-database-downloads)
## Current directory, nexttrace binary directory and FHS directories (Unix-like) will be searched.
## To customize it, please use environment variables,
export NEXTTRACE_IPINFOLOCALPATH=/xxx/yyy.mmdb
## For the offline database Ip2region, you can download it manually and rename it to ip2region.db, or let NextTrace download it automatically
## Fill the token to: ipgeo/tokens.go
## Please be aware: Due to the serious abuse of IP.SB, you will often be not able to query IP data from this source
## IP-API.com has a stricter restiction on API calls, if you can't query IP data from this source, please try again in a few minutes
@@ -292,7 +304,7 @@ nexttrace -T -q 2 --parallel-requests 1 -t -P 2001:4860:4860::8888
### IP Database
#### We use [bgp.tools](https://bgp.tools) as a data provider for routing tables.
We use [bgp.tools](https://bgp.tools) as a data provider for routing tables.
NextTrace BackEnd is now open-source.
@@ -332,16 +344,10 @@ Arguments:
-T --tcp Use TCP SYN for tracerouting (default port
is 80)
-U --udp Use UDP SYN for tracerouting (default port
is 53)
is 33494)
-F --fast-trace One-Key Fast Trace to China ISPs
-p --port Set the destination port to use. It is
either initial udp port value for
"default"method (incremented by each
probe, default is 33434), or initial seq
for "icmp" (incremented as well, default
from 1), or some constantdestination port
for other methods (with default of 80 for
"tcp", 53 for "udp", etc.)
-p --port Set the destination port to use. With
default of 80 for "tcp", 33494 for "udp"
-q --queries Set the number of probes per each hop.
Default: 3
--parallel-requests Set ParallelRequests number. It should be
@@ -353,9 +359,9 @@ Arguments:
IPInfo, IPInsight, IP-API.com, Ip2region,
IPInfoLocal, CHUNZHEN, disable-geoip].
Default: LeoMoeAPI
--pow-provider Choose PoW Provider [api.nxtrace.org, sakura]
For China mainland users, please use
sakura. Default: api.nxtrace.org
--pow-provider Choose PoW Provider [api.nxtrace.org,
sakura] For China mainland users, please
use sakura. Default: api.nxtrace.org
-n --no-rdns Do not resolve IP addresses to their
domain names
-a --always-rdns Always resolve IP addresses to their
@@ -382,16 +388,15 @@ Arguments:
-z --send-time Set how many [milliseconds] between
sending each packet.. Useful when some
routers use rate-limit for ICMP messages.
Default: 100
Default: 50
-i --ttl-time Set how many [milliseconds] between
sending packets groups by TTL. Useful when
some routers use rate-limit for ICMP
messages. Default: 500
messages. Default: 50
--timeout The number of [milliseconds] to keep probe
sockets open before giving up on the
connection.. Default: 1000
--psize Set the packet size (payload size).
Default: 52
--psize Set the payload size. Default: 52
--_positionalArg_nexttrace_32 IP Address or domain name
--dot-server Use DoT Server for DNS Parse [dnssb,
aliyun, dnspod, google, cloudflare]
@@ -399,6 +404,8 @@ Arguments:
cn]. Default: cn
--file Read IP Address or domain name from file
-C --nocolor Disable Colorful Output
--dont-fragment Set the Don't Fragment bit (IPv4 TCP
only). Default: false
```
## Project screenshot
@@ -421,6 +428,14 @@ This software is still in the early stages of development and may have many flaw
[https://github.com/nxtrace/nexttracewebapi](https://github.com/nxtrace/nexttracewebapi)
## NextTraceroute
`NextTraceroute` is a root-free Android route tracing application that defaults to using the `NextTrace API`, developed by @surfaceocean.
Thank you to all the test users for your enthusiastic support. This app has successfully passed the closed testing phase and is now officially available on the Google Play Store.
[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>
## LeoMoeAPI Credits
NextTrace focuses on Golang Traceroute implementations, and its LeoMoeAPI geolocation information is not supported by raw data, so a commercial version is not possible.
@@ -440,12 +455,14 @@ We hope you can give us as much feedback as possible on IP geolocation errors (s
## JetBrain Support
#### This Project uses [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport). We Proudly Develop By Goland.
This Project uses [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport). We Proudly Develop By `Goland`.
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" title="" alt="GoLand logo" width="331">
## Credits
[Gubo](https://www.gubo.org) Reliable Host Recommendation Website
[IPInfo](https://ipinfo.io) Provided most of the data support for this project free of charge
[BGP.TOOLS](https://bgp.tools) Provided some data support for this project free of charge

View File

@@ -25,9 +25,6 @@
<a href="https://www.nxtrace.org/downloads">
<img src="https://img.shields.io/github/release/nxtrace/Ntrace-V1/all.svg?style=flat-square">
</a>
<a href="https://telegram.dog/sjprojects">
<img src="https://img.shields.io/endpoint?color=neon&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fnexttrace">
</a>
</p>
## IAAS Sponsor
@@ -63,64 +60,84 @@ Document Language: [English](README.md) | 简体中文
使用 NextTrace 之前,我们建议您先阅读 [#IP 数据以及精准度说明](https://github.com/nxtrace/NTrace-core/blob/main/README_zh_CN.md#ip-%E6%95%B0%E6%8D%AE%E4%BB%A5%E5%8F%8A%E7%B2%BE%E5%87%86%E5%BA%A6%E8%AF%B4%E6%98%8E),在了解您自己的对数据精准度需求以后再进行抉择。
[NextTrace 的Telegram频道](https://t.me/nexttrace)由项目成员负责,会传递一部分通知,也会发布一些成员自己分享的小工具。项目成员的意见可作为未来项目发展的可能方向,随着开发进度变化可能会有所改动,不代表未来一定会实装,正式定稿公告会发布于 Issue 中。
### Automated Install
* Linux
* 一键安装脚本
```shell
curl nxtrace.org/nt | bash
```
* Arch Linux AUR 安装命令
* 直接下载bin包(仅支持amd64)
* 从 nxtrace的APT源安装
* 支持 AMD64/ARM64 架构
```shell
echo "deb [trusted=yes] https://github.com/nxtrace/nexttrace-debs/releases/latest/download ./" |
sudo tee /etc/apt/sources.list.d/nexttrace.list
sudo apt update
sudo apt install nexttrace
```
* APT源由 wcbing, nxtrace 维护
* Arch Linux AUR 安装命令
* 直接下载bin包(仅支持amd64)
```shell
yay -S nexttrace-bin
```
* 从源码构建(仅支持amd64)
```shell
yay -S nexttrace
```
* AUR 的构建分别由 ouuan, huyz 维护
```shell
yay -S nexttrace-bin`
```
* AUR 的构建分别由 ouuan 维护
* Linuxbrew 安装命令
同macOS Homebrew安装方法(homebrew-core版仅支持amd64)
* Deepin 安装命令
```shell
apt install nexttrace
```
* Deepin 安装命令
```shell
apt install nexttrace
```
* [x-cmd](https://cn.x-cmd.com/pkg/nexttrace) 安装命令
```shell
x env use nexttrace
```
* Termux 安装命令
```shell
pkg install nexttrace-enhanced
```
```shell
pkg install root-repo
pkg install nexttrace
```
* macOS
* macOS Homebrew 安装命令
* homebrew-core版
```shell
brew install nexttrace
```
* 本仓库ACTIONS自动构建版(更新更快)
```shell
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
```
* homebrew-core 构建由 chenrui333 维护请注意该版本更新可能会落后仓库Action自动构建版本
* homebrew-core版
```shell
brew install nexttrace
```
* 本仓库ACTIONS自动构建版(更新更快)
```shell
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
```
* homebrew-core 构建由 chenrui333 维护请注意该版本更新可能会落后仓库Action自动构建版本
* Windows
* Windows WinGet 安装命令
* WinGet 版
```powershell
winget install nexttrace
```
* WinGet 构建由 Dragon1573 维护
* Windows Scoop 安装命令
* scoop-extras版
* scoop-extras
```powershell
scoop bucket add extras && scoop install extras/nexttrace
```
* scoop-extra 由 soenggam 维护
```powershell
scoop bucket add extras && scoop install extras/nexttrace
```
* scoop-extra 由 soenggam 维护
请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。
请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。
### Manual Install
* 下载预编译的可执行程序
@@ -130,23 +147,6 @@ Document Language: [English](README.md) | 简体中文
* `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
* 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。
* 从源码安装
您可在自行安装Go >= 1.20后,使用以下命令安装
```shell
go install github.com/nxtrace/NTrace-core@latest
```
*由于go.mod文件声明和文件目录冲突的问题你不能用go install命令安装 `NTrace-V1` 版本*
安装后可执行文件在`$GOPATH/bin`目录下,如果您没有设置`GOPATH`,则在`$HOME/go/bin`目录下。
安装后二进制文件名称与项目名称保持一致,你需要将下文中的 `nexttrace` 命令替换为 `NTrace-core` 使用
如果你希望与下文命令保持一致,可以在执行 `go install` 命令后重命名二进制文件
```shell
mv $GOPATH/bin/NTrace-core $GOPATH/bin/nexttrace
```
### Get Started
`NextTrace` 默认使用`ICMP`协议发起`TraceRoute`请求,该协议同时支持`IPv4`和`IPv6`
@@ -232,7 +232,7 @@ nexttrace --tcp --port 443 2001:4860:4860::8888
# UDP Trace
nexttrace --udp 1.0.0.1
# 可以自行指定端口[此处为5353],默认53端口
# 可以自行指定端口[此处为5353]默认33494端口
nexttrace --udp --port 5353 1.0.0.1
```
@@ -256,6 +256,9 @@ nexttrace --no-rdns www.bbix.net
# 设置载荷大小为1024字节
nexttrace --psize 1024 example.com
# 设置载荷大小以及DF标志进行TCP Trace
nexttrace --psize 1024 --dont-fragment --tcp example.com
# 特色功能打印Route-Path图
# Route-Path图示例
# AS6453 塔塔通信「Singapore『Singapore』」
@@ -279,11 +282,15 @@ export NO_COLOR=1
nexttrace --data-provider ip-api.com
## 特别的: 其中 ipinfo 和 IPInsight API 对于免费版查询有频率限制,可从这些服务商自行购买服务以解除限制,如有需要可以 clone 本项目添加其提供的 token 自行编译
## TOKEN填写路径ipgeo/tokens.go
## 特别的: 对于离线库 IPInfoLocal请自行下载并命名为 ipinfoLocal.mmdb (可以从这里下载https://ipinfo.io/signup?ref=free-database-downloads)
## 特别的: 对于离线库 IPInfoLocal请自行下载并命名为 ipinfoLocal.mmdb
## (可以从这里下载https://ipinfo.io/signup?ref=free-database-downloads)
## 默认搜索用户当前路径、程序所在路径、和 FHS 路径Unix-like
## 如果需要自定义路径,请设置环境变量
export NEXTTRACE_IPINFOLOCALPATH=/xxx/yyy.mmdb
## 对于离线库 Ip2region 可NextTrace自动下载也可自行下载并命名为 ip2region.db
## 另外由于IP.SB被滥用比较严重会经常出现无法查询的问题请知悉。
## IP-API.com限制调用较为严格如有查询不到的情况请几分钟后再试。
# 纯真IP数据库默认使用 http://127.0.0.1:2060 作为查询接口,如需自定义请使用环境变量
export NEXTTRACE_CHUNZHENURL=http://127.0.0.1:2060
## 可使用 https://github.com/freshcn/qqwry 自行搭建纯真IP数据库服务
@@ -331,16 +338,10 @@ Arguments:
-T --tcp Use TCP SYN for tracerouting (default port
is 80)
-U --udp Use UDP SYN for tracerouting (default port
is 53)
is 33494)
-F --fast-trace One-Key Fast Trace to China ISPs
-p --port Set the destination port to use. It is
either initial udp port value for
"default"method (incremented by each
probe, default is 33434), or initial seq
for "icmp" (incremented as well, default
from 1), or some constantdestination port
for other methods (with default of 80 for
"tcp", 53 for "udp", etc.)
-p --port Set the destination port to use. With
default of 80 for "tcp", 33494 for "udp"
-q --queries Set the number of probes per each hop.
Default: 3
--parallel-requests Set ParallelRequests number. It should be
@@ -352,9 +353,9 @@ Arguments:
IPInfo, IPInsight, IP-API.com, Ip2region,
IPInfoLocal, CHUNZHEN, disable-geoip].
Default: LeoMoeAPI
--pow-provider Choose PoW Provider [api.nxtrace.org, sakura]
For China mainland users, please use
sakura. Default: api.nxtrace.org
--pow-provider Choose PoW Provider [api.nxtrace.org,
sakura] For China mainland users, please
use sakura. Default: api.nxtrace.org
-n --no-rdns Do not resolve IP addresses to their
domain names
-a --always-rdns Always resolve IP addresses to their
@@ -381,16 +382,15 @@ Arguments:
-z --send-time Set how many [milliseconds] between
sending each packet.. Useful when some
routers use rate-limit for ICMP messages.
Default: 100
Default: 50
-i --ttl-time Set how many [milliseconds] between
sending packets groups by TTL. Useful when
some routers use rate-limit for ICMP
messages. Default: 500
messages. Default: 50
--timeout The number of [milliseconds] to keep probe
sockets open before giving up on the
connection.. Default: 1000
--psize Set the packet size (payload size).
Default: 52
--psize Set the payload size. Default: 52
--_positionalArg_nexttrace_32 IP Address or domain name
--dot-server Use DoT Server for DNS Parse [dnssb,
aliyun, dnspod, google, cloudflare]
@@ -398,6 +398,8 @@ Arguments:
cn]. Default: cn
--file Read IP Address or domain name from file
-C --nocolor Disable Colorful Output
--dont-fragment Set the Don't Fragment bit (IPv4 TCP
only). Default: false
```
## 项目截图
@@ -431,8 +433,7 @@ nexttrace --pow-provider sakura
## OpenTrace
`OpenTrace`是 @Archeb 开发的`NextTrace`的跨平台`GUI`版本,带来您熟悉但更强大的用户体验。
`OpenTrace`是 @Archeb 开发的`NextTrace`的跨平台`GUI`版本,带来您熟悉但更强大的用户体验。
该软件仍然处于早期开发阶段,可能存在许多缺陷和错误,需要您宝贵的使用反馈。
[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace)
@@ -443,8 +444,24 @@ nexttrace --pow-provider sakura
[https://github.com/nxtrace/nexttracewebapi](https://github.com/nxtrace/nexttracewebapi)
## NextTraceroute
`NextTraceroute`,一款默认使用`NextTrace API`的免`root`安卓版路由跟踪应用,由 @surfaceocean 开发。
感谢所有测试用户的热情支持,本应用已经通过封闭测试,正式进入 Google Play 商店。
[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>
## JetBrain Support
本项目受 [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport) 支持。 很高兴使用`Goland`作为我们的开发工具。
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" title="" alt="GoLand logo" width="331">
## Credits
[Gubo](https://www.gubo.org) 靠谱主机推荐
[IPInfo](https://ipinfo.io) 无偿提供了本项目大部分数据支持
[BGP.TOOLS](https://bgp.tools) 无偿提供了本项目的一些数据支持

View File

@@ -3,7 +3,6 @@ package cmd
import (
"encoding/json"
"fmt"
"github.com/fatih/color"
"log"
"net"
"os"
@@ -12,6 +11,8 @@ import (
"strings"
"time"
"github.com/fatih/color"
"github.com/akamensky/argparse"
"github.com/nxtrace/NTrace-core/config"
fastTrace "github.com/nxtrace/NTrace-core/fast_trace"
@@ -32,11 +33,9 @@ func Excute() {
ipv4Only := parser.Flag("4", "ipv4", &argparse.Options{Help: "Use IPv4 only"})
ipv6Only := parser.Flag("6", "ipv6", &argparse.Options{Help: "Use IPv6 only"})
tcp := parser.Flag("T", "tcp", &argparse.Options{Help: "Use TCP SYN for tracerouting (default port is 80)"})
udp := parser.Flag("U", "udp", &argparse.Options{Help: "Use UDP SYN for tracerouting (default port is 53)"})
udp := parser.Flag("U", "udp", &argparse.Options{Help: "Use UDP SYN for tracerouting (default port is 33494)"})
fast_trace := parser.Flag("F", "fast-trace", &argparse.Options{Help: "One-Key Fast Trace to China ISPs"})
port := parser.Int("p", "port", &argparse.Options{Help: "Set the destination port to use. It is either initial udp port value for \"default\"" +
"method (incremented by each probe, default is 33434), or initial seq for \"icmp\" (incremented as well, default from 1), or some constant" +
"destination port for other methods (with default of 80 for \"tcp\", 53 for \"udp\", etc.)"})
port := parser.Int("p", "port", &argparse.Options{Help: "Set the destination port to use. With default of 80 for \"tcp\", 33494 for \"udp\"", Default: 80})
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)"})
@@ -61,10 +60,10 @@ func Excute() {
srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr 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: 100, 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: 500, Help: "Set how many [milliseconds] between sending packets groups by TTL. 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."})
packetSize := parser.Int("", "psize", &argparse.Options{Default: 52, Help: "Set the packet size (payload size)"})
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{
Help: "Use DoT Server for DNS Parse [dnssb, aliyun, dnspod, google, cloudflare]"})
@@ -72,6 +71,7 @@ func Excute() {
Help: "Choose the language for displaying [en, cn]"})
file := parser.String("", "file", &argparse.Options{Help: "Read IP Address or domain name from file"})
nocolor := parser.Flag("C", "nocolor", &argparse.Options{Help: "Disable Colorful Output"})
dontFragment := parser.Flag("", "dont-fragment", &argparse.Options{Default: false, Help: "Set the Don't Fragment bit (IPv4 TCP only)"})
err := parser.Parse(os.Args)
if err != nil {
@@ -96,16 +96,28 @@ func Excute() {
os.Exit(0)
}
if !*tcp && *port == 80 {
*port = 33494
}
domain := *str
if *port == 0 {
*port = 80
var m trace.Method
switch {
case *tcp:
m = trace.TCPTrace
case *udp:
m = trace.UDPTrace
default:
m = trace.ICMPTrace
}
if *fast_trace || *file != "" {
var paramsFastTrace = fastTrace.ParamsFastTrace{
SrcDev: *srcDev,
SrcAddr: *srcAddr,
DestPort: *port,
BeginHop: *beginHop,
MaxHops: *maxHops,
RDns: !*noRdns,
@@ -114,9 +126,11 @@ func Excute() {
PktSize: *packetSize,
Timeout: time.Duration(*timeout) * time.Millisecond,
File: *file,
DontFragment: *dontFragment,
Dot: *dot,
}
fastTrace.FastTest(*tcp, *output, paramsFastTrace)
fastTrace.FastTest(m, *output, paramsFastTrace)
if *output {
fmt.Println("您的追踪日志已经存放在 /tmp/trace.log 中")
}
@@ -131,6 +145,7 @@ func Excute() {
}
if strings.Contains(domain, "/") {
domain = "n" + domain
parts := strings.Split(domain, "/")
if len(parts) < 3 {
fmt.Println("Invalid input")
@@ -148,7 +163,7 @@ func Excute() {
}
// DOMAIN处理结束
capabilities_check()
capabilitiesCheck()
// return
var ip net.IP
@@ -185,7 +200,9 @@ func Excute() {
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
w.Conn.Close()
if w.Conn != nil {
w.Conn.Close()
}
}()
}
}
@@ -193,24 +210,19 @@ func Excute() {
//
//go func() {
// defer wg.Done()
if *udp {
if *ipv6Only {
fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.")
os.Exit(0)
}
if *ipv6Only {
ip, err = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
} else if *ipv4Only {
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
} else {
if *ipv6Only {
ip, err = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
} else if *ipv4Only {
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
} else {
ip, err = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
}
ip, err = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
}
if err != nil {
fmt.Println(err)
os.Exit(1)
//fmt.Println(err)
//os.Exit(1)
panic(err)
}
//}()
//
@@ -236,22 +248,7 @@ func Excute() {
}
if !*jsonPrint {
printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize)
}
var m trace.Method
switch {
case *tcp:
m = trace.TCPTrace
case *udp:
m = trace.UDPTrace
default:
m = trace.ICMPTrace
}
if !*tcp && *port == 80 {
*port = 53
printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize, *srcAddr, string(m))
}
util.DestIP = ip.String()
@@ -272,6 +269,7 @@ func Excute() {
IPGeoSource: ipgeo.GetSource(*dataOrigin),
Timeout: time.Duration(*timeout) * time.Millisecond,
PktSize: *packetSize,
DontFragment: *dontFragment,
}
// 暂时弃用
@@ -357,7 +355,7 @@ func Excute() {
}
}
func capabilities_check() {
func capabilitiesCheck() {
// Windows 判断放在前面,防止遇到一些奇奇怪怪的问题
if runtime.GOOS == "windows" {

View File

@@ -31,10 +31,11 @@ const (
CT163 string = "电信 163 AS4134"
CTCN2 string = "电信 CN2 AS4809"
CU169 string = "联通 169 AS4837"
CU9929 string = "联通 A网 AS9929"
CM string = "移动 骨干网 AS9808"
CU9929 string = "联通 A网(CNC) AS9929"
CM string = "移动 CMNET AS9808"
CMIN2 string = "移动 CMIN2 AS58807"
EDU string = "教育网 CERNET AS4538"
CST string = "科技网 CSTNET AS7497"
)
var TestIPsCollection = AllLocationCollection{
@@ -53,6 +54,11 @@ var Beijing = BackBoneCollection{
IPv6: "ipv6.pek-4134.endpoint.nxtrace.org.",
},
CTCN2: ISPCollection{
ISPName: CTCN2,
IP: "ipv4.pek-4809.endpoint.nxtrace.org.",
},
CU169: ISPCollection{
ISPName: CU169,
IP: "ipv4.pek-4837.endpoint.nxtrace.org.",
@@ -80,6 +86,13 @@ var Beijing = BackBoneCollection{
IP: "ipv4.pek-4538.endpoint.nxtrace.org.",
IPv6: "ipv6.pek-4538.endpoint.nxtrace.org.",
},
// 中科院
CST: ISPCollection{
ISPName: CST,
IP: "ipv4.pek-7497.endpoint.nxtrace.org.",
IPv6: "ipv6.pek-7497.endpoint.nxtrace.org.",
},
}
var Shanghai = BackBoneCollection{
@@ -133,17 +146,39 @@ var Guangzhou = BackBoneCollection{
IPv6: "ipv6.can-4134.endpoint.nxtrace.org.",
},
CTCN2: ISPCollection{
ISPName: CTCN2,
IP: "ipv4.can-4809.endpoint.nxtrace.org.",
},
CU169: ISPCollection{
ISPName: CU169,
IP: "ipv4.can-4837.endpoint.nxtrace.org.",
IPv6: "ipv6.can-4837.endpoint.nxtrace.org.",
},
CU9929: ISPCollection{
ISPName: CU9929,
IP: "ipv4.can-9929.endpoint.nxtrace.org.",
},
CM: ISPCollection{
ISPName: CM,
IP: "ipv4.can-9808.endpoint.nxtrace.org.",
IPv6: "ipv6.can-9808.endpoint.nxtrace.org.",
},
CMIN2: ISPCollection{
ISPName: CMIN2,
IP: "ipv4.can-58807.endpoint.nxtrace.org.",
},
// 中山大学
EDU: ISPCollection{
ISPName: EDU,
IP: "ipv4.can-4538.endpoint.nxtrace.org.",
IPv6: "ipv6.can-4538.endpoint.nxtrace.org.",
},
}
var Hangzhou = BackBoneCollection{
@@ -156,6 +191,7 @@ var Hangzhou = BackBoneCollection{
CU169: ISPCollection{
ISPName: CU169,
IP: "ipv4.hgh-4837.endpoint.nxtrace.org.",
IPv6: "ipv6.hgh-4837.endpoint.nxtrace.org.",
},
CM: ISPCollection{
ISPName: CM,
@@ -180,7 +216,7 @@ var Hefei = BackBoneCollection{
},
// 中国科学技术大学 科技网
CST: ISPCollection{
ISPName: "中国科学技术大学 科技网 AS7497",
ISPName: CST,
IP: "ipv4.hfe-7497.endpoint.nxtrace.org.",
},
}

View File

@@ -2,6 +2,7 @@ package fastTrace
import (
"fmt"
"github.com/fatih/color"
"github.com/nxtrace/NTrace-core/ipgeo"
"github.com/nxtrace/NTrace-core/printer"
"github.com/nxtrace/NTrace-core/trace"
@@ -11,22 +12,24 @@ import (
"log"
"os"
"os/signal"
"strings"
)
//var pFastTracer ParamsFastTrace
func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX)
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
fmt.Fprintf(color.Output, "%s\n", color.New(color.FgYellow, color.Bold).Sprintf("『%s %s 』", location, ispCollection.ISPName))
fmt.Printf("traceroute to %s, %d hops max, %d byte packets, %s mode\n", ispCollection.IPv6, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize, strings.ToUpper(string(f.TracerouteMethod)))
ip, err := util.DomainLookUp(ispCollection.IPv6, "6", "", true)
// ip, err := util.DomainLookUp(ispCollection.IPv6, "6", "", true)
ip, err := util.DomainLookUp(ispCollection.IPv6, "6", f.ParamsFastTrace.Dot, true)
if err != nil {
log.Fatal(err)
}
var conf = trace.Config{
BeginHop: f.ParamsFastTrace.BeginHop,
DestIP: ip,
DestPort: 80,
DestPort: f.ParamsFastTrace.DestPort,
MaxHops: f.ParamsFastTrace.MaxHops,
NumMeasurements: 3,
ParallelRequests: 18,
@@ -39,6 +42,7 @@ func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
SrcAddr: f.ParamsFastTrace.SrcAddr,
PktSize: f.ParamsFastTrace.PktSize,
Lang: f.ParamsFastTrace.Lang,
DontFragment: f.ParamsFastTrace.DontFragment,
}
if oe {
@@ -55,7 +59,7 @@ func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
log.SetOutput(fp)
log.SetFlags(0)
log.Printf("『%s %s 』\n", location, ispCollection.ISPName)
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
log.Printf("traceroute to %s, %d hops max, %d byte packets, %s mode\n", ispCollection.IPv6, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize, strings.ToUpper(string(f.TracerouteMethod)))
conf.RealtimePrinter = tracelog.RealtimePrinter
} else {
conf.RealtimePrinter = printer.RealtimePrinter
@@ -91,6 +95,7 @@ func (f *FastTracer) testCU_v6() {
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929)
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169)
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
}
@@ -105,21 +110,26 @@ func (f *FastTracer) testEDU_v6() {
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.EDU)
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.EDU)
f.tracert_v6(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.EDU)
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.EDU)
// 科技网暂时算在EDU里面等拿到了足够多的数据再分离出去单独用于测试
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
}
func (f *FastTracer) testFast_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)
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
//f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
//f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
}
func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
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. 全国五网")
fmt.Print("请选择选项:")
_, err := fmt.Scanln(&c)
if err != nil {
@@ -138,11 +148,13 @@ func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
w.Conn.Close()
}()
if !tm {
switch traceMode {
case trace.ICMPTrace:
ft.TracerouteMethod = trace.ICMPTrace
fmt.Println("您将默认使用ICMP协议进行路由跟踪如果您想使用TCP SYN进行路由跟踪可以加入 -T 参数")
} else {
case trace.TCPTrace:
ft.TracerouteMethod = trace.TCPTrace
case trace.UDPTrace:
ft.TracerouteMethod = trace.UDPTrace
}
switch c {

View File

@@ -3,6 +3,7 @@ package fastTrace
import (
"bufio"
"fmt"
"github.com/fatih/color"
"github.com/nxtrace/NTrace-core/ipgeo"
"github.com/nxtrace/NTrace-core/printer"
"github.com/nxtrace/NTrace-core/trace"
@@ -25,6 +26,7 @@ type FastTracer struct {
type ParamsFastTrace struct {
SrcDev string
SrcAddr string
DestPort int
BeginHop int
MaxHops int
RDns bool
@@ -33,6 +35,8 @@ type ParamsFastTrace struct {
PktSize int
Timeout time.Duration
File string
DontFragment bool
Dot string
}
type IpListElement struct {
@@ -44,17 +48,18 @@ type IpListElement struct {
var oe = false
func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX)
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
fmt.Fprintf(color.Output, "%s\n", color.New(color.FgYellow, color.Bold).Sprintf("『%s %s 』", location, ispCollection.ISPName))
fmt.Printf("traceroute to %s, %d hops max, %d byte packets, %s mode\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize, strings.ToUpper(string(f.TracerouteMethod)))
ip, err := util.DomainLookUp(ispCollection.IP, "4", "", true)
// ip, err := util.DomainLookUp(ispCollection.IP, "4", "", true)
ip, err := util.DomainLookUp(ispCollection.IP, "4", f.ParamsFastTrace.Dot, true)
if err != nil {
log.Fatal(err)
}
var conf = trace.Config{
BeginHop: f.ParamsFastTrace.BeginHop,
DestIP: ip,
DestPort: 80,
DestPort: f.ParamsFastTrace.DestPort,
MaxHops: f.ParamsFastTrace.MaxHops,
NumMeasurements: 3,
ParallelRequests: 18,
@@ -67,6 +72,7 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
SrcAddr: f.ParamsFastTrace.SrcAddr,
PktSize: f.ParamsFastTrace.PktSize,
Lang: f.ParamsFastTrace.Lang,
DontFragment: f.ParamsFastTrace.DontFragment,
}
if oe {
@@ -84,7 +90,7 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
log.SetOutput(fp)
log.SetFlags(0)
log.Printf("『%s %s 』\n", location, ispCollection.ISPName)
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
log.Printf("traceroute to %s, %d hops max, %d byte packets, %s mode\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize, strings.ToUpper(string(f.TracerouteMethod)))
conf.RealtimePrinter = tracelog.RealtimePrinter
} else {
conf.RealtimePrinter = printer.RealtimePrinter
@@ -98,13 +104,13 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
fmt.Println()
}
func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
func FastTest(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFastTrace) {
// tm means tcp mode
var c string
oe = outEnable
if paramsFastTrace.File != "" {
testFile(paramsFastTrace, tm)
testFile(paramsFastTrace, traceMode)
return
}
@@ -134,7 +140,7 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
}
}
}
FastTestv6(tm, outEnable, paramsFastTrace)
FastTestv6(traceMode, outEnable, paramsFastTrace)
return
}
if paramsFastTrace.SrcDev != "" {
@@ -156,7 +162,7 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
}
}
fmt.Println("您想测试哪些ISP的路由\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网\n6. 全")
fmt.Println("您想测试哪些ISP的路由\n1. 北京三网快速测试\n2. 全国电信\n3. 全国联通\n4. 全国移动\n5. 全国教育网\n6. 全国五网")
fmt.Print("请选择选项:")
_, err = fmt.Scanln(&c)
if err != nil {
@@ -175,11 +181,13 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
w.Conn.Close()
}()
if !tm {
switch traceMode {
case trace.ICMPTrace:
ft.TracerouteMethod = trace.ICMPTrace
fmt.Println("您将默认使用ICMP协议进行路由跟踪如果您想使用TCP SYN进行路由跟踪可以加入 -T 参数")
} else {
case trace.TCPTrace:
ft.TracerouteMethod = trace.TCPTrace
case trace.UDPTrace:
ft.TracerouteMethod = trace.UDPTrace
}
switch c {
@@ -200,7 +208,7 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
}
}
func testFile(paramsFastTrace ParamsFastTrace, tm bool) {
func testFile(paramsFastTrace ParamsFastTrace, traceMode trace.Method) {
// 建立 WebSocket 连接
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
@@ -210,11 +218,13 @@ func testFile(paramsFastTrace ParamsFastTrace, tm bool) {
}()
var tracerouteMethod trace.Method
if !tm {
switch traceMode {
case trace.ICMPTrace:
tracerouteMethod = trace.ICMPTrace
fmt.Println("您将默认使用ICMP协议进行路由跟踪如果您想使用TCP SYN进行路由跟踪可以加入 -T 参数")
} else {
case trace.TCPTrace:
tracerouteMethod = trace.TCPTrace
case trace.UDPTrace:
tracerouteMethod = trace.UDPTrace
}
filePath := paramsFastTrace.File
@@ -274,8 +284,14 @@ func testFile(paramsFastTrace ParamsFastTrace, tm bool) {
}
for _, ip := range ipList {
fmt.Printf("%s『%s』%s\n", printer.YELLOW_PREFIX, ip.Desc, printer.RESET_PREFIX)
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ip.Ip, paramsFastTrace.MaxHops, paramsFastTrace.PktSize)
fmt.Fprintf(color.Output, "%s\n",
color.New(color.FgYellow, color.Bold).Sprint("『 "+ip.Desc+"』"),
)
if util.EnableHidDstIP == "" {
fmt.Printf("traceroute to %s, %d hops max, %d bytes payload, %s mode\n", ip.Ip, paramsFastTrace.MaxHops, paramsFastTrace.PktSize, strings.ToUpper(string(tracerouteMethod)))
} else {
fmt.Printf("traceroute to %s, %d hops max, %d bytes payload, %s mode\n", util.HideIPPart(ip.Ip), paramsFastTrace.MaxHops, paramsFastTrace.PktSize, strings.ToUpper(string(tracerouteMethod)))
}
var srcAddr string
if ip.Version4 {
if paramsFastTrace.SrcDev != "" {
@@ -320,7 +336,7 @@ func testFile(paramsFastTrace ParamsFastTrace, tm bool) {
var conf = trace.Config{
BeginHop: paramsFastTrace.BeginHop,
DestIP: net.ParseIP(ip.Ip),
DestPort: 80,
DestPort: paramsFastTrace.DestPort,
MaxHops: paramsFastTrace.MaxHops,
NumMeasurements: 3,
ParallelRequests: 18,
@@ -343,7 +359,7 @@ func testFile(paramsFastTrace ParamsFastTrace, tm bool) {
log.SetOutput(fp)
log.SetFlags(0)
log.Printf("『%s』\n", ip.Desc)
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ip.Ip, paramsFastTrace.MaxHops, paramsFastTrace.PktSize)
log.Printf("traceroute to %s, %d hops max, %d byte packets, %s mode\n", ip.Ip, paramsFastTrace.MaxHops, paramsFastTrace.PktSize, strings.ToUpper(string(tracerouteMethod)))
conf.RealtimePrinter = tracelog.RealtimePrinter
err = fp.Close()
if err != nil {
@@ -374,10 +390,12 @@ func (f *FastTracer) testAll() {
func (f *FastTracer) testCT() {
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CTCN2)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CTCN2)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163)
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CTCN2)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163)
}
func (f *FastTracer) testCU() {
@@ -385,8 +403,10 @@ func (f *FastTracer) testCU() {
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU9929)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169)
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU9929)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169)
}
func (f *FastTracer) testCM() {
@@ -394,15 +414,19 @@ func (f *FastTracer) testCM() {
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CMIN2)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CMIN2)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CMIN2)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
}
func (f *FastTracer) testEDU() {
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.EDU)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.EDU)
f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.EDU)
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.EDU)
// 科技网暂时算在EDU里面等拿到了足够多的数据再分离出去单独用于测试
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.CST)
}
@@ -410,5 +434,6 @@ func (f *FastTracer) testFast() {
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.EDU)
//f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
}

47
go.mod
View File

@@ -1,50 +1,45 @@
module github.com/nxtrace/NTrace-core
go 1.22
go 1.24.1
require (
github.com/akamensky/argparse v1.4.0
github.com/google/gopacket v1.1.19
github.com/oschwald/maxminddb-golang v1.12.0
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.8.4
github.com/oschwald/maxminddb-golang v1.13.1
github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.10.0
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/tsosunchia/powclient v0.1.4
golang.org/x/net v0.21.0
golang.org/x/sync v0.6.0
github.com/tsosunchia/powclient v0.1.5
golang.org/x/net v0.39.0
golang.org/x/sync v0.13.0
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // 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/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
golang.org/x/text v0.24.0 // indirect
)
require (
github.com/fatih/color v1.16.0
github.com/gorilla/websocket v1.5.1
github.com/fatih/color v1.18.0
github.com/gorilla/websocket v1.5.3
github.com/lionsoul2014/ip2region v2.11.2+incompatible
github.com/rodaine/table v1.1.1
github.com/tidwall/gjson v1.17.1
github.com/rodaine/table v1.3.0
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.17.0 // indirect
golang.org/x/sys v0.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

105
go.sum
View File

@@ -4,115 +4,106 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
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/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=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lionsoul2014/ip2region v2.11.2+incompatible h1:+VRsGcrHz8ewXI/2UzTptJlACsxD/p4xCxuql4u2nKU=
github.com/lionsoul2014/ip2region v2.11.2+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rodaine/table v1.1.1 h1:zBliy3b4Oj6JRmncse2Z85WmoQvDrXOYuy0JXCt8Qz8=
github.com/rodaine/table v1.1.1/go.mod h1:iqTRptjn+EVcrVBYtNMlJ2wrJZa3MpULUmcXFpfcziA=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/rodaine/table v1.3.0 h1:4/3S3SVkHnVZX91EHFvAMV7K42AnJ0XuymRR2C5HlGE=
github.com/rodaine/table v1.3.0/go.mod h1:47zRsHar4zw0jgxGxL9YtFfs7EGN6B/TaS+/Dmk4WxU=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
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/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=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tsosunchia/powclient v0.1.4 h1:iAXUOiGPLJJTnzVSD0PY+4kBWm+SEMQZzd5ntEk1t0c=
github.com/tsosunchia/powclient v0.1.4/go.mod h1:Pm4MP3QqN74SfNskPpFIEyT+NQrcABGoXkkeRwjlMEE=
github.com/tsosunchia/powclient v0.1.5 h1:hpixFWoPbWSEC0zc9osSltyjtr1+SnhCueZVLkEpyyU=
github.com/tsosunchia/powclient v0.1.5/go.mod h1:yNlzyq+w9llYZV+0q7nrX83ULy4ghq2mCjpTLJFJ2pg=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
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/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
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.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
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=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -14,7 +14,7 @@ import (
)
func IPApiCom(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
url := "http://ip-api.com/json/" + ip + "?fields=status,message,country,regionName,city,isp,district,as,lat,lon"
url := token.BaseOrDefault("http://ip-api.com/json/") + ip + "?fields=status,message,country,regionName,city,isp,district,as,lat,lon"
client := &http.Client{
// 2 秒超时
Timeout: timeout,

View File

@@ -16,13 +16,13 @@ import (
// )
func TestXxx(t *testing.T) {
const ID_FIXED_HEADER = "10"
const IdFixedHeader = "10"
var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位
var ttl = fmt.Sprintf("%06b", 95) //取TTL的后6位
fmt.Println(os.Getpid()&0x7f, 95)
var parity int
id := ID_FIXED_HEADER + processID + ttl
id := IdFixedHeader + processID + ttl
for _, c := range id {
if c == '1' {
parity++
@@ -33,8 +33,8 @@ func TestXxx(t *testing.T) {
} else {
id += "0"
}
process_id, ttl_r, _ := reverseID(id)
log.Println(process_id, ttl_r)
processId, ttlR, _ := reverseID(id)
log.Println(processId, ttlR)
}
func TestFilter(t *testing.T) {

View File

@@ -12,7 +12,7 @@ import (
)
func IPInfo(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
url := "http://ipinfo.io/" + ip + "?token=" + token.ipinfo
url := token.BaseOrDefault("http://ipinfo.io/") + ip + "?token=" + token.ipinfo
client := &http.Client{
// 2 秒超时
Timeout: timeout,

View File

@@ -2,24 +2,71 @@ package ipgeo
import (
"errors"
"github.com/oschwald/maxminddb-golang"
"net"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/nxtrace/NTrace-core/util"
"github.com/oschwald/maxminddb-golang"
)
const (
ipinfoDataBasePath = "./ipinfoLocal.mmdb"
ipinfoDataBaseFilename = "ipinfoLocal.mmdb"
)
// Cache the path of the ipinfoLocal.mmdb file
var ipinfoDataBasePath = ""
// We will try to get the path of the ipinfoLocal.mmdb file in the following order:
// 1. Use the value of the environment variable NEXTTRACE_IPINFOLOCALPATH
// 2. Search in the current folder and the executable folder
// 3. Search in /usr/local/share/nexttrace/ and /usr/share/nexttrace/ (for Unix/Linux)
// If the file is found, the path will be stored in the ipinfoDataBasePath variable
func getIPInfoLocalPath() (error) {
if ipinfoDataBasePath != "" {
return nil
}
// NEXTTRACE_IPINFOLOCALPATH
if util.EnvIPInfoLocalPath != "" {
if _, err := os.Stat(util.EnvIPInfoLocalPath); err == nil {
ipinfoDataBasePath = util.EnvIPInfoLocalPath
return nil
} else {
return errors.New("NEXTTRACE_IPINFOLOCALPATH is set but the file does not exist")
}
}
folders := []string{}
// current folder
if cur, err := os.Getwd(); err == nil {
folders = append(folders, cur + string(filepath.Separator))
}
// exeutable folder
if exe, err := os.Executable(); err == nil {
folders = append(folders, filepath.Dir(exe) + string(filepath.Separator))
}
if runtime.GOOS != "windows" {
folders = append(folders, "/usr/local/share/nexttrace/")
folders = append(folders, "/usr/share/nexttrace/")
}
for _, folder := range folders {
if _, err := os.Stat(folder + ipinfoDataBaseFilename); err == nil {
ipinfoDataBasePath = folder + ipinfoDataBaseFilename
return nil
}
}
return errors.New("no ipinfoLocal.mmdb found")
}
func IPInfoLocal(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) {
if _, err := os.Stat(ipinfoDataBasePath); os.IsNotExist(err) {
if err := getIPInfoLocalPath(); err != nil {
panic("Cannot find ipinfoLocal.mmdb")
}
region, err := maxminddb.Open(ipinfoDataBasePath)
if err != nil {
panic("Cannot find ipinfoLocal.mmdb")
panic("Cannot open ipinfoLocal.mmdb at " + ipinfoDataBasePath)
}
defer func(region *maxminddb.Reader) {
err := region.Close()
@@ -33,19 +80,19 @@ func IPInfoLocal(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, erro
return &IPGeoData{}, errors.New("no results")
}
recordMap := record.(map[string]interface{})
country_name := recordMap["country_name"].(string)
countryName := recordMap["country_name"].(string)
prov := ""
if recordMap["country"].(string) == "HK" {
country_name = "China"
countryName = "China"
prov = "Hong Kong"
}
if recordMap["country"].(string) == "TW" {
country_name = "China"
countryName = "China"
prov = "Taiwan"
}
return &IPGeoData{
Asnumber: strings.TrimPrefix(recordMap["asn"].(string), "AS"),
Country: country_name,
Country: countryName,
City: "",
Prov: prov,
Owner: recordMap["as_name"].(string),

View File

@@ -13,7 +13,7 @@ func IPInSight(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData,
// 2 秒超时
Timeout: timeout,
}
resp, err := client.Get("https://api.ipinsight.io/ip/" + ip + "?token=" + token.ipinsight)
resp, err := client.Get(token.BaseOrDefault("https://api.ipinsight.io/ip/") + ip + "?token=" + token.ipinsight)
if err != nil {
return nil, err
}

View File

@@ -11,7 +11,7 @@ import (
)
func IPSB(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
url := "https://api.ip.sb/geoip/" + ip
url := token.BaseOrDefault("https://api.ip.sb/geoip/") + ip
client := &http.Client{
// 2 秒超时
Timeout: timeout,

View File

@@ -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 {

View File

@@ -6,10 +6,19 @@ type tokenData struct {
ipinsight string
ipinfo string
ipleo string
baseUrl string
}
func (t *tokenData) BaseOrDefault(def string) string {
if t.baseUrl == "" {
return def
}
return t.baseUrl
}
var token = tokenData{
ipinsight: util.GetenvDefault("NEXTTRACE_IPINSIGHT_TOKEN", ""),
ipinfo: util.GetenvDefault("NEXTTRACE_IPINFO_TOKEN", ""),
baseUrl: util.GetenvDefault("NEXTTRACE_IPAPI_BASE", ""),
ipleo: "NextTraceDemo",
}

View File

@@ -60,7 +60,7 @@ checkSystemDistribution() {
downloadBinrayFile() {
echo -e "${Info} 获取最新版的 NextTrace 发行版文件信息"
for i in {1..3}; do
downloadUrls=$(curl -sL ${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 1.5)
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 -sL ${primaryUrl} -o ${Temp_path} --connect-timeout 1.5
curl -sLf ${primaryUrl} -o ${Temp_path} --connect-timeout 1.5
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 -sL ${backupUrl} -o ${Temp_path} --connect-timeout 1.5
curl -sLf ${backupUrl} -o ${Temp_path} --connect-timeout 1.5
if [ $? -eq 0 ]; then
changeMode
mv ${Temp_path} ${downPath}

View File

@@ -6,6 +6,9 @@ import (
"github.com/tsosunchia/powclient"
"net/url"
"os"
"os/signal"
"syscall"
"time"
)
const (
@@ -13,6 +16,9 @@ const (
)
func GetToken(fastIp string, host string, port string) (string, error) {
// 捕获中断信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
getTokenParams := powclient.NewGetTokenParams()
u := url.URL{Scheme: "https", Host: fastIp + ":" + port, Path: baseURL}
getTokenParams.BaseUrl = u.String()
@@ -26,20 +32,30 @@ func GetToken(fastIp string, host string, port string) (string, error) {
var (
token string
err error
done = make(chan bool)
)
// 尝试三次RetToken如果都失败了异常退出
for i := 0; i < 3; i++ {
token, err = powclient.RetToken(getTokenParams)
if err != nil {
continue
// 在 goroutine 中处理阻塞调用
go func() {
for i := 0; i < 3; i++ {
token, err = powclient.RetToken(getTokenParams)
if err != nil {
continue // 如果失败则重试
}
done <- true // 成功后通知主线程
return
}
//fmt.Println("GetToken success", token, getTokenParams.UserAgent)
return token, nil
done <- false // 失败后通知主线程
}()
select {
case <-sigChan: // 监听中断信号
return "", fmt.Errorf("Program interrupted by user ") // 添加返回值
case success := <-done: // 等待 goroutine 完成
if success {
return token, nil
}
return "", fmt.Errorf("RetToken failed 3 times, please try again later")
case <-time.After(10 * time.Second): // 超时处理
return "", fmt.Errorf("RetToken timed out(10s), please check your network") // 添加返回值
}
if err != nil {
fmt.Println(err)
}
fmt.Println("RetToken failed 3 times, please try again after a while, exit")
os.Exit(1)
return "", err
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/nxtrace/NTrace-core/trace"
"github.com/nxtrace/NTrace-core/util"
"net"
"strings"
"github.com/fatih/color"
)
@@ -76,16 +77,21 @@ func sponsor() {
// )
//}
func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string, maxHops int, packetSize int) {
func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string, maxHops int, packetSize int, srcAddr string, mode string) {
fmt.Println("IP Geo Data Provider: " + dataOrigin)
if srcAddr == "" {
srcAddr = "traceroute to"
} else {
srcAddr += " ->"
}
if util.EnableHidDstIP == "" {
if ip.String() == domain {
fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", ip.String(), maxHops, packetSize)
fmt.Printf("%s %s, %d hops max, %d bytes payload, %s mode\n", srcAddr, ip.String(), maxHops, packetSize, strings.ToUpper(mode))
} else {
fmt.Printf("traceroute to %s (%s), %d hops max, %d bytes packets\n", ip.String(), domain, maxHops, packetSize)
fmt.Printf("%s %s (%s), %d hops max, %d bytes payload, %s mode\n", srcAddr, ip.String(), domain, maxHops, packetSize, strings.ToUpper(mode))
}
} else {
fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", util.HideIPPart(ip.String()), maxHops, packetSize)
fmt.Printf("%s %s, %d hops max, %d bytes payload, %s mode\n", srcAddr, util.HideIPPart(ip.String()), maxHops, packetSize, strings.ToUpper(mode))
}
}

View File

@@ -69,7 +69,7 @@ func (t *ICMPTracer) Execute() (*Result, error) {
var err error
t.icmpListen, err = internal.ListenICMP("ip4:1", t.SrcAddr)
t.icmpListen, err = internal.ListenICMP("ip4:icmp", t.SrcAddr)
if err != nil {
return &t.res, err
}
@@ -150,9 +150,10 @@ func (t *ICMPTracer) listenICMP() {
}
continue
}
packet_id := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[32:34])), 2)
if process_id, ttl, err := reverseID(packet_id); err == nil {
if process_id == int64(os.Getpid()&0x7f) {
ttl := int64(binary.BigEndian.Uint16(msg.Msg[34:36]))
packetId := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[32:34])), 2)
if processId, _, err := reverseID(packetId); err == nil {
if processId == int64(os.Getpid()&0x7f) {
dstip := net.IP(msg.Msg[24:28])
if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv4zero) {
// 匹配再继续解析包,否则直接丢弃
@@ -201,13 +202,13 @@ func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data
}
}
func gernerateID(ttl_int int) int {
const ID_FIXED_HEADER = "10"
func gernerateID(ttlInt int) int {
const IdFixedHeader = "10"
var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位
var ttl = fmt.Sprintf("%06b", ttl_int) //取TTL的后6位
var ttl = fmt.Sprintf("%06b", ttlInt) //取TTL的后6位
var parity int
id := ID_FIXED_HEADER + processID + ttl
id := IdFixedHeader + processID + ttl
for _, c := range id {
if c == '1' {
parity++
@@ -266,20 +267,29 @@ func (t *ICMPTracer) send(ttl int) error {
return nil
}
id := gernerateID(ttl)
//id := gernerateID(ttl)
id := gernerateID(0)
// log.Println("发送的", id)
//data := []byte{byte(ttl)}
data := []byte{byte(0)}
data = append(data, bytes.Repeat([]byte{1}, t.Config.PktSize-5)...)
data = append(data, 0x00, 0x00, 0x4f, 0xff)
icmpHeader := icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: id,
//Data: []byte("HELLO-R-U-THERE"),
Data: append(bytes.Repeat([]byte{1}, t.Config.PktSize-4), 0x00, 0x00, 0x4f, 0xff),
Data: data,
Seq: ttl,
},
}
ipv4.NewPacketConn(t.icmpListen).SetTTL(ttl)
err := ipv4.NewPacketConn(t.icmpListen).SetTTL(ttl)
if err != nil {
return err
}
wb, err := icmpHeader.Marshal(nil)
if err != nil {
@@ -321,7 +331,10 @@ func (t *ICMPTracer) send(ttl int) error {
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
h.fetchIPData(t.Config)
err := h.fetchIPData(t.Config)
if err != nil {
return err
}
t.res.add(h)
case <-time.After(t.Timeout):

View File

@@ -171,9 +171,10 @@ func (t *ICMPTracerv6) listenICMP() {
}
}
packet_id := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[52:54])), 2)
if process_id, ttl, err := reverseID(packet_id); err == nil {
if process_id == int64(os.Getpid()&0x7f) {
ttl := int64(binary.BigEndian.Uint16(msg.Msg[54:56]))
packetId := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[52:54])), 2)
if processId, _, err := reverseID(packetId); err == nil {
if processId == int64(os.Getpid()&0x7f) {
dstip := net.IP(msg.Msg[32:48])
// 无效包本地环回包
if dstip.String() == "::" {
@@ -260,14 +261,20 @@ func (t *ICMPTracerv6) send(ttl int) error {
if t.final != -1 && ttl > t.final {
return nil
}
id := gernerateID(ttl)
//id := gernerateID(ttl)
id := gernerateID(0)
//data := []byte{byte(ttl)}
data := []byte{byte(0)}
data = append(data, bytes.Repeat([]byte{1}, t.Config.PktSize-5)...)
data = append(data, 0x00, 0x00, 0x4f, 0xff)
icmpHeader := icmp.Message{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: id,
//Data: []byte("HELLO-R-U-THERE"),
Data: append(bytes.Repeat([]byte{1}, t.Config.PktSize-4), 0x00, 0x00, 0x4f, 0xff),
Data: data,
Seq: ttl,
},
}
@@ -275,7 +282,10 @@ func (t *ICMPTracerv6) send(ttl int) error {
p := ipv6.NewPacketConn(t.icmpListen)
icmpHeader.Body.(*icmp.Echo).Seq = ttl
p.SetHopLimit(ttl)
err := p.SetHopLimit(ttl)
if err != nil {
return err
}
wb, err := icmpHeader.Marshal(nil)
if err != nil {
@@ -317,7 +327,10 @@ func (t *ICMPTracerv6) send(ttl int) error {
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
h.fetchIPData(t.Config)
err := h.fetchIPData(t.Config)
if err != nil {
return err
}
t.res.add(h)

View File

@@ -6,7 +6,6 @@ import (
"context"
"errors"
"net"
"os"
"syscall"
"unsafe"
)
@@ -30,62 +29,69 @@ var (
)
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
if os.Getuid() == 0 { // root
return net.ListenPacket(network, laddr)
} else {
if nw, ok := networkMap[network]; ok {
proto := syscall.IPPROTO_ICMP
if nw == "udp6" {
proto = syscall.IPPROTO_ICMPV6
}
// 为兼容NE需要注释掉
//if os.Getuid() == 0 { // root
// return net.ListenPacket(network, laddr)
//} else {
if nw, ok := networkMap[network]; ok {
proto := syscall.IPPROTO_ICMP
if nw == "udp6" {
proto = syscall.IPPROTO_ICMPV6
}
var ifIndex int = -1
if laddr != "" {
la := net.ParseIP(laddr)
if ifaces, err := net.Interfaces(); err == nil {
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.Equal(la) {
ifIndex = iface.Index
break
}
var ifIndex = -1
if laddr != "" {
la := net.ParseIP(laddr)
if ifaces, err := net.Interfaces(); err == nil {
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.Equal(la) {
ifIndex = iface.Index
break
}
}
}
if ifIndex == -1 {
return nil, errUnknownIface
}
} else {
return nil, err
}
if ifIndex == -1 {
return nil, errUnknownIface
}
} else {
return nil, err
}
isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen",
func(ctx context.Context, network, address string, c syscall.RawConn) error {
if ifIndex != -1 {
if proto == syscall.IPPROTO_ICMP {
return c.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, ifIndex)
})
} else {
return c.Control(func(fd uintptr) {
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, ifIndex)
})
}
}
return nil
})
if err != nil {
panic(err)
}
return newIPConn(isock), nil
} else {
return nil, errUnknownNetwork
}
isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen",
func(ctx context.Context, network, address string, c syscall.RawConn) error {
if ifIndex != -1 {
if proto == syscall.IPPROTO_ICMP {
return c.Control(func(fd uintptr) {
err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, ifIndex)
if err != nil {
return
}
})
} else {
return c.Control(func(fd uintptr) {
err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, ifIndex)
if err != nil {
return
}
})
}
}
return nil
})
if err != nil {
panic(err)
}
return newIPConn(isock), nil
} else {
return nil, errUnknownNetwork
}
//}
}

View File

@@ -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)

View File

@@ -217,6 +217,10 @@ func (t *TCPTracer) send(ttl int) error {
DstIP: t.DestIP,
Protocol: layers.IPProtocolTCP,
TTL: uint8(ttl),
//Flags: layers.IPv4DontFragment, // 我感觉没必要
}
if t.DontFragment {
ipHeader.Flags = layers.IPv4DontFragment
}
// 使用Uint16兼容32位系统防止在rand的时候因使用int32而溢出
sequenceNumber := uint32(r.Intn(math.MaxUint16))
@@ -237,9 +241,16 @@ func (t *TCPTracer) send(ttl int) error {
desiredPayloadSize := t.Config.PktSize
payload := make([]byte, desiredPayloadSize)
copy(buf.Bytes(), payload)
// 设置随机种子
rand.Seed(time.Now().UnixNano())
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
// 填充随机数
for i := range payload {
payload[i] = byte(rand.Intn(256))
}
//copy(buf.Bytes(), payload)
if err := gopacket.SerializeLayers(buf, opts, tcpHeader, gopacket.Payload(payload)); err != nil {
return err
}
@@ -253,7 +264,7 @@ func (t *TCPTracer) send(ttl int) error {
return err
}
t.inflightRequestLock.Lock()
hopCh := make(chan Hop)
hopCh := make(chan Hop, 1)
t.inflightRequest[int(sequenceNumber)] = hopCh
t.inflightRequestLock.Unlock()
/*
@@ -293,7 +304,10 @@ func (t *TCPTracer) send(ttl int) error {
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
h.fetchIPData(t.Config)
err := h.fetchIPData(t.Config)
if err != nil {
return err
}
t.res.add(h)

View File

@@ -17,7 +17,7 @@ import (
"golang.org/x/sync/semaphore"
)
type TCPTracerv6 struct {
type TCPTracerIPv6 struct {
Config
wg sync.WaitGroup
res Result
@@ -35,7 +35,7 @@ type TCPTracerv6 struct {
fetchLock sync.Mutex
}
func (t *TCPTracerv6) Execute() (*Result, error) {
func (t *TCPTracerIPv6) Execute() (*Result, error) {
if len(t.res.Hops) > 0 {
return &t.res, ErrTracerouteExecuted
}
@@ -47,7 +47,7 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
if err != nil {
return nil, err
}
t.icmp, err = icmp.ListenPacket("ip6:58", "::")
t.icmp, err = icmp.ListenPacket("ip6:ipv6-icmp", "::")
if err != nil {
return &t.res, err
}
@@ -100,7 +100,7 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
return &t.res, nil
}
func (t *TCPTracerv6) listenICMP() {
func (t *TCPTracerIPv6) listenICMP() {
lc := NewPacketListener(t.icmp, t.ctx)
go lc.Start()
for {
@@ -133,7 +133,7 @@ func (t *TCPTracerv6) listenICMP() {
// @title listenTCP
// @description 监听TCP的响应数据包
func (t *TCPTracerv6) listenTCP() {
func (t *TCPTracerIPv6) listenTCP() {
lc := NewPacketListener(t.tcp, t.ctx)
go lc.Start()
@@ -170,7 +170,7 @@ func (t *TCPTracerv6) listenTCP() {
}
}
func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) {
func (t *TCPTracerIPv6) handleICMPMessage(msg ReceivedMessage) {
var sequenceNumber = binary.BigEndian.Uint32(msg.Msg[52:56])
t.inflightRequestLock.Lock()
@@ -187,7 +187,7 @@ func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) {
// log.Println("发送成功")
}
func (t *TCPTracerv6) send(ttl int) error {
func (t *TCPTracerIPv6) send(ttl int) error {
err := t.sem.Acquire(context.Background(), 1)
if err != nil {
return err
@@ -227,13 +227,20 @@ func (t *TCPTracerv6) send(ttl int) error {
desiredPayloadSize := t.Config.PktSize
payload := make([]byte, desiredPayloadSize)
copy(buf.Bytes(), payload)
// 设置随机种子
rand.Seed(time.Now().UnixNano())
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
// 填充随机数
for i := range payload {
payload[i] = byte(rand.Intn(256))
}
//copy(buf.Bytes(), payload)
if err := gopacket.SerializeLayers(buf, opts, tcpHeader, gopacket.Payload(payload)); err != nil {
return err
}
ipv6.NewPacketConn(t.tcp).SetHopLimit(ttl)
err = ipv6.NewPacketConn(t.tcp).SetHopLimit(ttl)
if err != nil {
return err
}
@@ -244,7 +251,7 @@ func (t *TCPTracerv6) send(ttl int) error {
}
// log.Println(ttl, sequenceNumber)
t.inflightRequestLock.Lock()
hopCh := make(chan Hop)
hopCh := make(chan Hop, 1)
t.inflightRequest[int(sequenceNumber)] = hopCh
t.inflightRequestLock.Unlock()

View File

@@ -42,6 +42,7 @@ type Config struct {
AsyncPrinter func(res *Result)
PktSize int
Maptrace bool
DontFragment bool
}
type Method string
@@ -81,14 +82,13 @@ func Traceroute(method Method, config Config) (*Result, error) {
if config.DestIP.To4() != nil {
tracer = &UDPTracer{Config: config}
} else {
return nil, errors.New("IPv6 UDP Traceroute is not supported")
tracer = &UDPTracerIPv6{Config: config}
}
case TCPTrace:
if config.DestIP.To4() != nil {
tracer = &TCPTracer{Config: config}
} else {
tracer = &TCPTracerv6{Config: config}
// return nil, errors.New("IPv6 TCP Traceroute is not supported")
tracer = &TCPTracerIPv6{Config: config}
}
default:
return &Result{}, ErrInvalidMethod

View File

@@ -2,7 +2,9 @@ package trace
import (
"log"
"math/rand"
"net"
"strconv"
"sync"
"time"
@@ -30,6 +32,7 @@ type UDPTracer struct {
sem *semaphore.Weighted
fetchLock sync.Mutex
udpMutex sync.Mutex
}
func (t *UDPTracer) Execute() (*Result, error) {
@@ -53,7 +56,7 @@ func (t *UDPTracer) Execute() (*Result, error) {
go t.listenICMP()
t.sem = semaphore.NewWeighted(int64(t.ParallelRequests))
for ttl := 1; ttl <= t.MaxHops; ttl++ {
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
// 如果到达最终跳,则退出
if t.final != -1 && ttl > t.final {
break
@@ -134,9 +137,10 @@ func (t *UDPTracer) handleICMPMessage(msg ReceivedMessage, data []byte) {
}
}
func (t *UDPTracer) getUDPConn(try int) (net.IP, int, net.PacketConn) {
srcIP, _ := util.LocalIPPort(t.DestIP)
var cachedLocalPort int
func (t *UDPTracer) getUDPConn(try int) (net.IP, int, net.PacketConn, error) {
srcIP, _ := util.LocalIPPort(t.DestIP)
var ipString string
if srcIP == nil {
ipString = ""
@@ -144,14 +148,37 @@ func (t *UDPTracer) getUDPConn(try int) (net.IP, int, net.PacketConn) {
ipString = srcIP.String()
}
udpConn, err := net.ListenPacket("udp", ipString+":0")
if err != nil {
if try > 3 {
log.Fatal(err)
// Check environment variable to decide caching behavior
if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" {
// Use cached random port logic
if cachedLocalPort == 0 {
// First time: listen on a random port
udpConn, err := net.ListenPacket("udp", ipString+":0")
if err != nil {
if try > 3 {
log.Fatal(err)
}
return srcIP, 0, nil, err
}
cachedLocalPort = udpConn.LocalAddr().(*net.UDPAddr).Port
// Close the initial connection after obtaining the port
udpConn.Close()
}
return t.getUDPConn(try + 1)
// Use the cached local port to establish a new connection
udpConn, err := net.ListenPacket("udp", ipString+":"+strconv.Itoa(cachedLocalPort))
if err != nil {
return srcIP, cachedLocalPort, nil, err
}
return srcIP, cachedLocalPort, udpConn, nil
} else {
// Without caching: create a new connection each time using a new random port
udpConn, err := net.ListenPacket("udp", ipString+":0")
if err != nil {
return srcIP, 0, nil, err
}
localPort := udpConn.LocalAddr().(*net.UDPAddr).Port
return srcIP, localPort, udpConn, nil
}
return srcIP, udpConn.LocalAddr().(*net.UDPAddr).Port, udpConn
}
func (t *UDPTracer) send(ttl int) error {
@@ -166,54 +193,73 @@ func (t *UDPTracer) send(ttl int) error {
return nil
}
srcIP, srcPort, udpConn := t.getUDPConn(0)
var payload []byte
if t.Quic {
payload = GenerateQuicPayloadWithRandomIds()
} else {
ipHeader := &layers.IPv4{
SrcIP: srcIP,
DstIP: t.DestIP,
Protocol: layers.IPProtocolTCP,
TTL: uint8(ttl),
}
udpHeader := &layers.UDP{
SrcPort: layers.UDPPort(srcPort),
DstPort: layers.UDPPort(t.DestPort),
}
_ = udpHeader.SetNetworkLayerForChecksum(ipHeader)
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
desiredPayloadSize := t.Config.PktSize
payload := make([]byte, desiredPayloadSize)
copy(buf.Bytes(), payload)
if err := gopacket.SerializeLayers(buf, opts, udpHeader); err != nil {
return err
}
payload = buf.Bytes()
if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" {
t.udpMutex.Lock()
defer t.udpMutex.Unlock()
}
srcIP, srcPort, udpConn, err := t.getUDPConn(0)
if err != nil {
return err
}
defer udpConn.Close()
//var payload []byte
//if t.Quic {
// payload = GenerateQuicPayloadWithRandomIds()
//} else {
ipHeader := &layers.IPv4{
SrcIP: srcIP,
DstIP: t.DestIP,
Protocol: layers.IPProtocolUDP,
TTL: uint8(ttl),
}
udpHeader := &layers.UDP{
SrcPort: layers.UDPPort(srcPort),
DstPort: layers.UDPPort(t.DestPort),
}
_ = udpHeader.SetNetworkLayerForChecksum(ipHeader)
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
desiredPayloadSize := t.Config.PktSize
if desiredPayloadSize-8 > 0 {
desiredPayloadSize -= 8
}
payload := make([]byte, desiredPayloadSize)
// 设置随机种子
rand.Seed(time.Now().UnixNano())
// 填充随机数
for i := range payload {
payload[i] = byte(rand.Intn(256))
}
//copy(buf.Bytes(), payload)
if err := gopacket.SerializeLayers(buf, opts, udpHeader, gopacket.Payload(payload)); err != nil {
return err
}
//payload = buf.Bytes()
//}
err = ipv4.NewPacketConn(udpConn).SetTTL(ttl)
if err != nil {
return err
}
start := time.Now()
if _, err := udpConn.WriteTo(payload, &net.UDPAddr{IP: t.DestIP, Port: t.DestPort}); err != nil {
if _, err := udpConn.WriteTo(buf.Bytes(), &net.UDPAddr{IP: t.DestIP, Port: t.DestPort}); err != nil {
return err
}
// 在对inflightRequest进行写操作的时候应该加锁保护以免多个goroutine协程试图同时写入造成panic
t.inflightRequestLock.Lock()
hopCh := make(chan Hop)
hopCh := make(chan Hop, 1)
t.inflightRequest[srcPort] = hopCh
t.inflightRequestLock.Unlock()
defer func() {
@@ -232,7 +278,7 @@ func (t *UDPTracer) send(ttl int) error {
}
hopCh <- Hop{
Success: true,
Address: peer,
Address: &net.IPAddr{IP: peer.(*net.UDPAddr).IP},
}
}()
@@ -244,7 +290,6 @@ func (t *UDPTracer) send(ttl int) error {
if t.final != -1 && ttl > t.final {
return nil
}
if addr, ok := h.Address.(*net.IPAddr); ok && addr.IP.Equal(t.DestIP) {
t.finalLock.Lock()
if t.final == -1 || ttl < t.final {
@@ -264,7 +309,10 @@ func (t *UDPTracer) send(ttl int) error {
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
h.fetchIPData(t.Config)
err := h.fetchIPData(t.Config)
if err != nil {
return err
}
t.res.add(h)

391
trace/udp_ipv6.go Normal file
View File

@@ -0,0 +1,391 @@
package trace
import (
"log"
"math/rand"
"net"
"strconv"
"sync"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/nxtrace/NTrace-core/util"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
"golang.org/x/sync/semaphore"
)
type UDPTracerIPv6 struct {
Config
wg sync.WaitGroup
res Result
ctx context.Context
inflightRequest map[int]chan Hop
inflightRequestLock sync.Mutex
icmp net.PacketConn
final int
finalLock sync.Mutex
sem *semaphore.Weighted
fetchLock sync.Mutex
udpMutex sync.Mutex
}
func (t *UDPTracerIPv6) Execute() (*Result, error) {
if len(t.res.Hops) > 0 {
return &t.res, ErrTracerouteExecuted
}
var err error
t.icmp, err = icmp.ListenPacket("ip6:ipv6-icmp", "::")
if err != nil {
return &t.res, err
}
defer t.icmp.Close()
var cancel context.CancelFunc
t.ctx, cancel = context.WithCancel(context.Background())
defer cancel()
t.inflightRequest = make(map[int]chan Hop)
t.final = -1
go t.listenICMP()
t.sem = semaphore.NewWeighted(int64(t.ParallelRequests))
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
// 如果到达最终跳,则退出
if t.final != -1 && ttl > t.final {
break
}
for i := 0; i < t.NumMeasurements; i++ {
t.wg.Add(1)
go t.send(ttl)
<-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval))
}
if t.RealtimePrinter != nil {
// 对于实时模式应该按照TTL进行并发请求
t.wg.Wait()
t.RealtimePrinter(&t.res, ttl-1)
}
<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
}
go func() {
if t.AsyncPrinter != nil {
for {
t.AsyncPrinter(&t.res)
time.Sleep(200 * time.Millisecond)
}
}
}()
// 如果是表格模式,则一次性并发请求
if t.AsyncPrinter != nil {
t.wg.Wait()
}
t.res.reduce(t.final)
return &t.res, nil
}
func (t *UDPTracerIPv6) listenICMP() {
lc := NewPacketListener(t.icmp, t.ctx)
go lc.Start()
for {
select {
case <-t.ctx.Done():
return
case msg := <-lc.Messages:
if msg.N == nil {
continue
}
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
if err != nil {
log.Println(err)
continue
}
switch rm.Type {
case ipv6.ICMPTypeTimeExceeded:
t.handleICMPMessage(msg)
case ipv6.ICMPTypeDestinationUnreachable:
t.handleICMPMessage(msg)
default:
// log.Println("received icmp message of unknown type", rm.Type)
}
}
}
}
// handleICMPMessage 处理 ICMPv6 消息并提取 UDP 源端口
//
// ICMPv6 错误消息格式:
// - ICMPv6 头部 (8 字节)
// - 原始 IPv6 包 (包含 IPv6 头部和 UDP 头部)
//
// 处理步骤:
// 1. 验证消息长度
// 2. 解析 ICMPv6 头部
// 3. 提取原始 IPv6 包
// 4. 处理可能的扩展头部
// 5. 提取 UDP 源端口
// 6. 发送结果到对应通道
func (t *UDPTracerIPv6) handleICMPMessage(msg ReceivedMessage) {
// ICMPv6 错误消息至少需要包含 IPv6 头部(40字节)和部分 UDP 头部
if len(msg.Msg) < 48 {
return
}
// 尝试解析 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()
ch, ok := t.inflightRequest[srcPort]
if !ok {
return
}
ch <- Hop{
Success: true,
Address: msg.Peer,
}
}
var cachedLocalPortv6 int
func (t *UDPTracerIPv6) getUDPConn(try int) (net.IP, int, net.PacketConn, error) {
srcIP, _ := util.LocalIPPortv6(t.DestIP)
var ipString string
if srcIP == nil {
ipString = "::"
} else {
ipString = srcIP.String()
}
// Check environment variable to decide caching behavior
if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" {
// Use cached random port logic
if cachedLocalPortv6 == 0 {
// First time: listen on a random port
udpConn, err := net.ListenPacket("udp6", "["+ipString+"]:0")
if err != nil {
if try > 3 {
log.Fatal(err)
}
return srcIP, 0, nil, err
}
cachedLocalPortv6 = udpConn.LocalAddr().(*net.UDPAddr).Port
// Close the initial connection after obtaining the port
udpConn.Close()
}
// Use the cached local port to establish a new connection
udpConn, err := net.ListenPacket("udp6", "["+ipString+"]:"+strconv.Itoa(cachedLocalPortv6))
if err != nil {
return srcIP, cachedLocalPortv6, nil, err
}
return srcIP, cachedLocalPortv6, udpConn, nil
} else {
// Without caching: create a new connection each time using a new random port
udpConn, err := net.ListenPacket("udp6", "["+ipString+"]:0")
if err != nil {
return srcIP, 0, nil, err
}
localPort := udpConn.LocalAddr().(*net.UDPAddr).Port
return srcIP, localPort, udpConn, nil
}
}
func (t *UDPTracerIPv6) send(ttl int) error {
err := t.sem.Acquire(context.Background(), 1)
if err != nil {
return err
}
defer t.sem.Release(1)
defer t.wg.Done()
if t.final != -1 && ttl > t.final {
return nil
}
if util.GetenvDefault("NEXTTRACE_RANDOMPORT", "") == "" {
t.udpMutex.Lock()
defer t.udpMutex.Unlock()
}
srcIP, srcPort, udpConn, err := t.getUDPConn(0)
if err != nil {
return err
}
defer udpConn.Close()
ipHeader := &layers.IPv6{
SrcIP: srcIP,
DstIP: t.DestIP,
NextHeader: layers.IPProtocolUDP,
HopLimit: uint8(ttl),
}
udpHeader := &layers.UDP{
SrcPort: layers.UDPPort(srcPort),
DstPort: layers.UDPPort(t.DestPort),
}
_ = udpHeader.SetNetworkLayerForChecksum(ipHeader)
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
desiredPayloadSize := t.Config.PktSize
if desiredPayloadSize-8 > 0 {
desiredPayloadSize -= 8
}
payload := make([]byte, desiredPayloadSize)
// 设置随机种子
rand.Seed(time.Now().UnixNano())
// 填充随机数
for i := range payload {
payload[i] = byte(rand.Intn(256))
}
if err := gopacket.SerializeLayers(buf, opts, udpHeader, gopacket.Payload(payload)); err != nil {
return err
}
err = ipv6.NewPacketConn(udpConn).SetHopLimit(ttl)
if err != nil {
return err
}
start := time.Now()
if _, err := udpConn.WriteTo(buf.Bytes(), &net.UDPAddr{IP: t.DestIP, Port: t.DestPort}); err != nil {
return err
}
// 在对inflightRequest进行写操作的时候应该加锁保护以免多个goroutine协程试图同时写入造成panic
t.inflightRequestLock.Lock()
hopCh := make(chan Hop, 1)
t.inflightRequest[srcPort] = hopCh
t.inflightRequestLock.Unlock()
defer func() {
t.inflightRequestLock.Lock()
close(hopCh)
delete(t.inflightRequest, srcPort)
t.inflightRequestLock.Unlock()
}()
go func() {
reply := make([]byte, 1500)
_, peer, err := udpConn.ReadFrom(reply)
if err != nil {
// probably because we closed the connection
return
}
hopCh <- Hop{
Success: true,
Address: &net.IPAddr{IP: peer.(*net.UDPAddr).IP},
}
}()
select {
case <-t.ctx.Done():
return nil
case h := <-hopCh:
rtt := time.Since(start)
if t.final != -1 && ttl > t.final {
return nil
}
if addr, ok := h.Address.(*net.IPAddr); ok && addr.IP.Equal(t.DestIP) {
t.finalLock.Lock()
if t.final == -1 || ttl < t.final {
t.final = ttl
}
t.finalLock.Unlock()
} else if addr, ok := h.Address.(*net.UDPAddr); ok && addr.IP.Equal(t.DestIP) {
t.finalLock.Lock()
if t.final == -1 || ttl < t.final {
t.final = ttl
}
t.finalLock.Unlock()
}
h.TTL = ttl
h.RTT = rtt
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
err := h.fetchIPData(t.Config)
if err != nil {
return err
}
t.res.add(h)
case <-time.After(t.Timeout):
if t.final != -1 && ttl > t.final {
return nil
}
t.res.add(Hop{
Success: false,
Address: nil,
TTL: ttl,
RTT: 0,
Error: ErrHopLimitTimeout,
})
}
return nil
}

View File

@@ -27,8 +27,8 @@ func RealtimePrinter(res *trace.Result, ttl int) {
multiWriter := io.MultiWriter(os.Stdout, f)
log.SetOutput(multiWriter)
log.SetFlags(0)
var res_str string
res_str += fmt.Sprintf("%-2d ", ttl+1)
var resStr string
resStr += fmt.Sprintf("%-2d ", ttl+1)
// 去重
var latestIP string
@@ -56,28 +56,28 @@ func RealtimePrinter(res *trace.Result, ttl int) {
}
if latestIP == "" {
res_str += fmt.Sprintf("%s\n", "*")
log.Print(res_str)
resStr += fmt.Sprintf("%s\n", "*")
log.Print(resStr)
return
}
var blockDisplay = false
for ip, v := range tmpMap {
if blockDisplay {
res_str += fmt.Sprintf("%4s", "")
resStr += fmt.Sprintf("%4s", "")
}
if net.ParseIP(ip).To4() == nil {
res_str += fmt.Sprintf("%-25s ", ip)
resStr += fmt.Sprintf("%-25s ", ip)
} else {
res_str += fmt.Sprintf("%-15s ", ip)
resStr += fmt.Sprintf("%-15s ", ip)
}
i, _ := strconv.Atoi(v[0])
if res.Hops[ttl][i].Geo.Asnumber != "" {
res_str += fmt.Sprintf("AS%-7s", res.Hops[ttl][i].Geo.Asnumber)
resStr += fmt.Sprintf("AS%-7s", res.Hops[ttl][i].Geo.Asnumber)
} else {
res_str += fmt.Sprintf(" %-8s", "*")
resStr += fmt.Sprintf(" %-8s", "*")
}
if net.ParseIP(ip).To4() != nil {
@@ -89,7 +89,7 @@ func RealtimePrinter(res *trace.Result, ttl int) {
if whoisFormat[0] != "" {
whoisFormat[0] = "[" + whoisFormat[0] + "]"
}
res_str += fmt.Sprintf("%-16s", whoisFormat[0])
resStr += fmt.Sprintf("%-16s", whoisFormat[0])
}
if res.Hops[ttl][i].Geo.Country == "" {
@@ -98,19 +98,19 @@ func RealtimePrinter(res *trace.Result, ttl int) {
if net.ParseIP(ip).To4() != nil {
res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-39s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
resStr += fmt.Sprintf(" %s %s %s %s %-6s\n %-39s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
} else {
res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-35s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
resStr += fmt.Sprintf(" %s %s %s %s %-6s\n %-35s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
}
for j := 1; j < len(v); j++ {
if len(v) == 2 || j == 1 {
res_str += v[j]
resStr += v[j]
} else {
res_str += fmt.Sprintf("/ %s", v[j])
resStr += fmt.Sprintf("/ %s", v[j])
}
}
log.Print(res_str)
log.Print(resStr)
blockDisplay = true
}
}

View File

@@ -14,7 +14,7 @@ func newDoTResolver(serverName string, addrs string) *net.Resolver {
Timeout: 1000 * time.Millisecond,
}
tls_config := &tls.Config{
tlsConfig := &tls.Config{
// 设置 TLS Server Name 以确保证书能和域名对应
ServerName: serverName,
}
@@ -26,7 +26,7 @@ func newDoTResolver(serverName string, addrs string) *net.Resolver {
if err != nil {
return nil, err
}
return tls.Client(conn, tls_config), nil
return tls.Client(conn, tlsConfig), nil
},
}
}

View File

@@ -7,6 +7,8 @@ import (
"log"
"net"
"net/http"
"os"
"os/signal"
"strings"
"time"
@@ -57,12 +59,17 @@ func GetFastIP(domain string, port string, enableOutput bool) string {
var result ResponseInfo
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
select {
case result = <-results:
//等待5s没有结果 视为连不上API了
// 正常返回结果
case <-time.After(timeout):
log.Println("IP connection has been timeout, please check your network")
log.Println("IP connection has been timeout(5s), please check your network")
case <-sigChan: // 响应中断信号
log.Println("Program interrupted by user")
os.Exit(0)
}
//if len(ips) > 0 {

View File

@@ -18,12 +18,19 @@ import (
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 DestIP string
var cachedLocalIP net.IP
var cachedLocalPort int
var localIPOnce sync.Once
var cachedLocalIPv6 net.IP
var cachedLocalPort6 int
var localIPv6Once sync.Once
func LookupAddr(addr string) ([]string, error) {
// 如果在缓存中找到,直接返回
@@ -43,37 +50,70 @@ func LookupAddr(addr string) ([]string, error) {
return names, nil
}
// LocalIPPort get the local ip and port based on our destination ip
func LocalIPPort(dstip net.IP) (net.IP, int) {
// getLocalIPPort encapsulates the logic to get local IP and port via a UDP connection
func getLocalIPPort(dstip net.IP) (net.IP, int) {
serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345")
if err != nil {
log.Fatal(err)
}
con, err := net.DialUDP("udp", nil, serverAddr)
if err != nil {
return nil, -1
}
defer con.Close()
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
return udpaddr.IP, udpaddr.Port
}
return nil, -1
}
// We don't actually connect to anything, but we can determine
// based on our destination ip what source ip we should use.
if con, err := net.DialUDP("udp", nil, serverAddr); err == nil {
defer con.Close()
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
return udpaddr.IP, udpaddr.Port
}
// getLocalIPPortv6 encapsulates the logic to get local IPv6 and port via a UDP connection
func getLocalIPPortv6(dstip net.IP) (net.IP, int) {
serverAddr, err := net.ResolveUDPAddr("udp", "["+dstip.String()+"]:12345")
if err != nil {
log.Fatal(err)
}
con, err := net.DialUDP("udp", nil, serverAddr)
if err != nil {
return nil, -1
}
defer con.Close()
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
return udpaddr.IP, udpaddr.Port
}
return nil, -1
}
// LocalIPPort returns the local IP and port based on our destination IP, with caching unless NEXTTRACE_RANDOMPORT 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", "") != "" {
return getLocalIPPort(dstip)
}
// Otherwise, use the cached value (computed only once).
localIPOnce.Do(func() {
cachedLocalIP, cachedLocalPort = getLocalIPPort(dstip)
})
if cachedLocalIP != nil {
return cachedLocalIP, cachedLocalPort
}
return nil, -1
}
func LocalIPPortv6(dstip net.IP) (net.IP, int) {
serverAddr, err := net.ResolveUDPAddr("udp", "["+dstip.String()+"]:12345")
if err != nil {
log.Fatal(err)
// If NEXTTRACE_RANDOMPORT is set, bypass caching and return a new port every time.
// 该ENV仅对TCP Mode有效UDP Mode暂无办法固定Port
if GetenvDefault("NEXTTRACE_RANDOMPORT", "") != "" {
return getLocalIPPortv6(dstip)
}
// We don't actually connect to anything, but we can determine
// based on our destination ip what source ip we should use.
if con, err := net.DialUDP("udp", nil, serverAddr); err == nil {
defer con.Close()
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
return udpaddr.IP, udpaddr.Port
}
// Otherwise, use the cached value (computed only once).
localIPv6Once.Do(func() {
cachedLocalIPv6, cachedLocalPort6 = getLocalIPPortv6(dstip)
})
if cachedLocalIPv6 != nil {
return cachedLocalIPv6, cachedLocalPort6
}
return nil, -1
}

View File

@@ -104,16 +104,16 @@ func (c *WsConn) messageSendHandler() {
err := c.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
// log.Println("write close:", err)
os.Exit(1)
//os.Exit(1)
panic(err)
}
select {
// 等到了结果,直接退出
case <-c.Done:
// 如果等待 1s 还是拿不到结果,不再等待,超时退出
case <-time.After(time.Second):
case <-time.After(1 * time.Second):
}
os.Exit(1)
// return
os.Exit(0)
}
}
}
@@ -134,8 +134,9 @@ func (c *WsConn) recreateWsConn() {
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
}
if err != nil {
log.Println(err)
os.Exit(1)
//log.Println(err)
//os.Exit(1)
panic(err)
}
} else {
// 使用 cacheToken
@@ -206,8 +207,9 @@ func createWsConn() *WsConn {
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
}
if err != nil {
log.Println(err)
os.Exit(1)
//log.Println(err)
//os.Exit(1)
panic(err)
}
ua = []string{util.UserAgent}
}