Compare commits

..

542 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
tsosunchia
f96c3e5139 Merge pull request #41 from nxtrace/dependabot/go_modules/github.com/tidwall/gjson-1.17.1
chore(deps): bump github.com/tidwall/gjson from 1.17.0 to 1.17.1
2024-02-19 23:57:22 +08:00
dependabot[bot]
c46907b881 chore(deps): bump github.com/tidwall/gjson from 1.17.0 to 1.17.1
Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.17.0 to 1.17.1.
- [Commits](https://github.com/tidwall/gjson/compare/v1.17.0...v1.17.1)

---
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-02-19 14:27:38 +00:00
tsosunchia
7afc4c5104 fix issue: https://github.com/nxtrace/NTrace-V1/issues/40
要提交的变更:
	修改:     ipgeo/ip2region.go
2024-02-15 18:00:31 +08:00
tsosunchia
27e560fbde golang 1.22 2024-02-12 22:51:23 +08:00
tsosunchia
1dc18cfff8 Merge pull request #39 from nxtrace/dependabot/go_modules/golang.org/x/net-0.21.0
chore(deps): bump golang.org/x/net from 0.20.0 to 0.21.0
2024-02-12 23:42:25 +09:00
dependabot[bot]
b16dabac7c chore(deps): bump golang.org/x/net from 0.20.0 to 0.21.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.20.0 to 0.21.0.
- [Commits](https://github.com/golang/net/compare/v0.20.0...v0.21.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-02-12 14:39:04 +00:00
tsosunchia
760e751076 设置latency测试时的fallback 2024-02-05 16:31:07 +08:00
tsosunchia
a82be1b5cf 更新依赖 2024-02-05 16:26:34 +08:00
tsosunchia
4ba76c97fe Merge pull request #38 from nxtrace/dependabot/go_modules/github.com/rodaine/table-1.1.1
chore(deps): bump github.com/rodaine/table from 1.1.0 to 1.1.1
2024-01-22 22:42:34 +08:00
dependabot[bot]
9900bff41a chore(deps): bump github.com/rodaine/table from 1.1.0 to 1.1.1
Bumps [github.com/rodaine/table](https://github.com/rodaine/table) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/rodaine/table/releases)
- [Commits](https://github.com/rodaine/table/compare/v1.1.0...v1.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 14:29:57 +00:00
tsosunchia
e81a8f9fdc Merge pull request #36 from nxtrace/dependabot/go_modules/golang.org/x/net-0.20.0
chore(deps): bump golang.org/x/net from 0.19.0 to 0.20.0
2024-01-15 22:41:21 +08:00
dependabot[bot]
e3346ff901 chore(deps): bump golang.org/x/net from 0.19.0 to 0.20.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.19.0 to 0.20.0.
- [Commits](https://github.com/golang/net/compare/v0.19.0...v0.20.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-01-15 14:36:46 +00:00
tsosunchia
70034a81f8 Merge pull request #34 from nxtrace/dependabot/go_modules/golang.org/x/sync-0.6.0 2024-01-09 10:07:05 +08:00
dependabot[bot]
eb0ac2565d chore(deps): bump golang.org/x/sync from 0.5.0 to 0.6.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.5.0 to 0.6.0.
- [Commits](https://github.com/golang/sync/compare/v0.5.0...v0.6.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-01-08 14:28:20 +00:00
tsosunchia
757ff4232b Merge pull request #223 from nxtrace/main
update readme
2023-12-20 15:38:17 +08:00
tsosunchia
4dda237a57 update readme
更换ip反馈的帖子链接,从issue->discussion
2023-12-20 15:37:03 +08:00
tsosunchia
cfec8df6a4 Merge pull request #221 from nxtrace/main
chore: SYNC
2023-12-20 13:57:38 +08:00
tsosunchia
f76c9401f9 update credit 2023-12-20 13:52:08 +08:00
tsosunchia
9536cf345c 更新依赖
go: upgraded github.com/pelletier/go-toml/v2 v2.1.0 => v2.1.1
go: upgraded github.com/spf13/viper v1.18.1 => v1.18.2
go: upgraded golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb => v0.0.0-20231219180239-dc181d75b848
2023-12-20 13:45:07 +08:00
tsosunchia
f282cf8b8c Merge pull request #33 from nxtrace/dependabot/github_actions/actions/upload-artifact-4
chore(deps): bump actions/upload-artifact from 3 to 4
2023-12-18 22:49:13 +08:00
dependabot[bot]
01fb27b7d7 chore(deps): bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 14:48:33 +00:00
tsosunchia
3133978195 Merge pull request #213 from nxtrace/main
chore: SYNC
2023-12-14 15:16:55 +08:00
tsosunchia
c9dc71cd24 Merge branch 'nxtrace:main' into main 2023-12-14 15:13:43 +08:00
tsosunchia
c67e46bf4f 替换svg 2023-12-14 14:57:45 +08:00
tsosunchia
5d796ce5cd 版本信息输出时应用色彩参数 2023-12-14 14:51:10 +08:00
tsosunchia
96e0558fef Merge pull request #32 from nxtrace/dependabot/go_modules/github.com/spf13/viper-1.18.1
chore(deps): bump github.com/spf13/viper from 1.18.0 to 1.18.1
2023-12-11 23:08:39 +08:00
tsosunchia
662e482593 Merge pull request #31 from nxtrace/dependabot/github_actions/actions/setup-go-5
chore(deps): bump actions/setup-go from 4 to 5
2023-12-11 22:40:36 +08:00
dependabot[bot]
e60a5aedd9 chore(deps): bump github.com/spf13/viper from 1.18.0 to 1.18.1
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.0...v1.18.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 14:38:52 +00:00
dependabot[bot]
559cb478ab chore(deps): bump actions/setup-go from 4 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 14:18:40 +00:00
tsosunchia
2598fb4d56 686用一键脚本也指向386 2023-12-08 17:31:43 +08:00
tsosunchia
21a92e1f89 高亮显示扩充 2023-12-08 16:57:01 +08:00
tsosunchia
ef0631fdab fix: 可能发生资源泄漏,在 for 循环中调用 defer 2023-12-08 16:16:38 +08:00
tsosunchia
5c4aa0c2de 更新依赖 2023-12-08 16:11:55 +08:00
tsosunchia
d0c1459752 fmt 2023-12-08 16:06:58 +08:00
tsosunchia
156f8914d8 删除一些没必要的赋值 2023-12-08 16:01:06 +08:00
tsosunchia
de15063be1 Merge pull request #210 from nxtrace/main
homepage部分显示BUG
2023-12-06 16:30:13 +08:00
tsosunchia
af5259e484 homepage部分显示BUG 2023-12-06 16:29:39 +08:00
tsosunchia
5e95aac500 Merge pull request #205 from nxtrace/main
chore: SYNC
2023-12-06 11:53:30 +08:00
tsosunchia
bc267f5b74 update readme 2023-12-06 11:52:05 +08:00
tsosunchia
dede4ed530 主页地址改为超链接形式 2023-12-05 13:49:31 +08:00
tsosunchia
ba2354c429 Merge pull request #204 from nxtrace/main
chore: SYNC
2023-12-05 13:45:08 +08:00
tsosunchia
03394df927 更新主页地址 2023-12-05 13:44:30 +08:00
tsosunchia
1fd3288fb9 Merge pull request #29 from nxtrace/dependabot/go_modules/golang.org/x/net-0.19.0
chore(deps): bump golang.org/x/net from 0.18.0 to 0.19.0
2023-12-05 12:26:09 +08:00
tsosunchia
44eee91c31 替换下载中心至自建 2023-12-05 12:25:28 +08:00
dependabot[bot]
e593ad8fce chore(deps): bump golang.org/x/net from 0.18.0 to 0.19.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/net/compare/v0.18.0...v0.19.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>
2023-12-04 14:58:03 +00:00
tsosunchia
533dacc9ce update credit 2023-12-03 20:31:53 +08:00
tsosunchia
fa30750190 Merge pull request #201 from nxtrace/main
chore: sync
2023-12-02 13:23:47 +08:00
tsosunchia
ffa7bb1d28 update credit 2023-12-01 18:34:42 +08:00
tsosunchia
c0455ca8e7 api变更 2023-12-01 16:32:44 +08:00
tsosunchia
faa3daa0c4 更新一键安装脚本SSL 2023-12-01 11:15:17 +08:00
tsosunchia
fc88c9be5c update readme 2023-11-30 17:14:50 +08:00
tsosunchia
f5d1a606b3 add param: support disable color output 2023-11-30 17:11:14 +08:00
tsosunchia
b32947a824 update credit 2023-11-30 16:12:47 +08:00
tsosunchia
36507ad3b5 reporter.go 未检查造成的越界 2023-11-23 10:23:30 +08:00
tsosunchia
9a3f250b9b Merge pull request #199 from nxtrace/main
update credit
2023-11-21 17:42:18 +08:00
tsosunchia
8adceb8d77 update credit 2023-11-21 17:37:46 +08:00
tsosunchia
7912af8481 Merge pull request #197 from nxtrace/main
fix bug: nil pointer dereference
2023-11-17 19:08:00 +08:00
tsosunchia
4bef8b918a fix bug: nil pointer dereference 2023-11-17 19:00:34 +08:00
tsosunchia
690f7346d0 Merge pull request #195 from nxtrace/main
chore: SYNC
2023-11-17 17:29:46 +08:00
tsosunchia
d82a521954 Update bug_report.md 2023-11-17 17:26:23 +08:00
tsosunchia
0b7d01fb21 移除route参数 2023-11-15 10:19:00 +08:00
tsosunchia
2fed1d152f chore: 更新依赖 2023-11-13 17:19:05 +08:00
tsosunchia
98c0fa38ca 可设置是否隐匿目的IP 2023-11-13 11:23:07 +08:00
tsosunchia
cb63e9b61c Merge pull request #22 from fakeboboliu/main
add source ip support to macOS rootless solution
2023-11-12 00:20:18 +08:00
bobo liu
e4626c4f82 notice silver-bullet of permission issue 2023-11-12 00:11:11 +08:00
bobo liu
47698be2ed add source ip support for macOS rootless solution
do you guys not have macos devices?
2023-11-11 21:38:28 +08:00
tsosunchia
ee0ebb126c Delete CNAME 2023-11-10 09:53:54 +08:00
tsosunchia
1b59210eb1 Merge pull request #191 from nxtrace/main
chore: SYNC
2023-11-09 16:31:25 +08:00
tsosunchia
ed518d175b Merge pull request #21 from huiming23344/main
docs: Clarify the steps in the installation guide
2023-11-09 16:29:07 +08:00
Huiming
9f67ea5e9b docs: Clarify the steps in the installation guide 2023-11-09 15:33:47 +08:00
tsosunchia
d98d2f7ee5 chore(deps): bump golang.org/x/text from 0.13.0 to 0.14.0 2023-11-08 16:17:07 +08:00
tsosunchia
e0b874a687 增加异常处理,在用户输入错误时 2023-11-08 16:13:59 +08:00
tsosunchia
b39657ca5b Merge pull request #20 from nxtrace/dependabot/go_modules/github.com/fatih/color-1.16.0
chore(deps): bump github.com/fatih/color from 1.15.0 to 1.16.0
2023-11-07 08:28:47 +08:00
dependabot[bot]
c79387ff75 chore(deps): bump github.com/fatih/color from 1.15.0 to 1.16.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.15.0...v1.16.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-07 00:25:23 +00:00
tsosunchia
e6b2d975b4 Merge pull request #19 from nxtrace/dependabot/go_modules/github.com/gorilla/websocket-1.5.1
chore(deps): bump github.com/gorilla/websocket from 1.5.0 to 1.5.1
2023-11-07 08:24:45 +08:00
tsosunchia
33c1af9f50 Merge pull request #18 from nxtrace/dependabot/go_modules/golang.org/x/sync-0.5.0
chore(deps): bump golang.org/x/sync from 0.4.0 to 0.5.0
2023-11-07 08:23:38 +08:00
dependabot[bot]
da825c18cc chore(deps): bump github.com/gorilla/websocket from 1.5.0 to 1.5.1
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.5.0...v1.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 14:57:48 +00:00
dependabot[bot]
4adba2b59d chore(deps): bump golang.org/x/sync from 0.4.0 to 0.5.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.4.0 to 0.5.0.
- [Commits](https://github.com/golang/sync/compare/v0.4.0...v0.5.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>
2023-11-06 14:57:44 +00:00
tsosunchia
65d8ac5c14 修改credit 2023-11-05 16:16:59 +08:00
tsosunchia
7472398c26 Merge pull request #186 from nxtrace/main
chore: 同步
2023-11-05 15:41:12 +08:00
tsosunchia
b2870a823f 修改默认行为,当指定v4/v6时自动选择第一个IP方便脚本使用 2023-11-03 01:21:53 +08:00
tsosunchia
f82af5f9c5 ipv6 trace 正序输出 2023-11-03 00:47:42 +08:00
tsosunchia
9eda2d2a30 chore: 更新依赖 2023-10-31 04:03:50 +08:00
tsosunchia
1a4a15eb74 修复使用NEXTTRACE_HOSTPORT环境变量时不支持IPv6反代地址的问题 2023-10-31 04:03:32 +08:00
tsosunchia
f5556fea5d Merge pull request #184 from nxtrace/main
chore: sync
2023-10-27 21:16:42 +08:00
tsosunchia
d760e75810 fix bug: 当接收到由自身发出的DestinationUnreachable包时视为有效信息,此增强由 @XQZR 提出。 2023-10-20 00:46:58 +08:00
tsosunchia
99409089b2 update credit 2023-10-14 22:32:12 +08:00
tsosunchia
2040497d89 Merge pull request #182 from nxtrace/main
chore: 同步至v1.2.3
2023-10-14 15:18:22 +08:00
tsosunchia
29ce61b24b 更新readme 2023-10-14 09:21:05 +08:00
tsosunchia
701abc3447 增加file参数支持文件读取列表进行路由测试 2023-10-14 09:08:29 +08:00
tsosunchia
f08778c862 修正部分typo 2023-10-13 18:34:14 +08:00
tsosunchia
8697325193 Merge pull request #17 from 1-1-2/softfloat
add mips softfloat & mipsle softfloat
2023-10-13 18:03:21 +08:00
1-1-2
32b0f15e78 add mips softfloat & mipsle softfloat 2023-10-13 16:46:20 +08:00
tsosunchia
90e349eeed Merge pull request #181 from nxtrace/main
更新readme
2023-10-13 10:49:48 +08:00
tsosunchia
f275edba3a 更新readme 2023-10-13 10:49:20 +08:00
tsosunchia
2974002c02 Merge pull request #178 from nxtrace/main
chore: 同步V1版本
2023-10-12 19:52:57 +08:00
tsosunchia
9b2fc9b570 Merge pull request #16 from nxtrace/dependabot/go_modules/golang.org/x/net-0.17.0
chore(deps): bump golang.org/x/net from 0.16.0 to 0.17.0
2023-10-12 17:48:58 +08:00
tsosunchia
960ab9687c 修复无v6网络下的连接问题 2023-10-12 17:47:31 +08:00
dependabot[bot]
07623ce4fd chore(deps): bump golang.org/x/net from 0.16.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-11 23:17:18 +00:00
tsosunchia
3164acfccf Merge pull request #173 from nxtrace/main
fix bug: ipv6局域网地址错误显示为INVALID
2023-10-11 10:57:51 +08:00
tsosunchia
8b30ef39dd fix bug: ipv6局域网地址错误显示为INVALID 2023-10-11 10:22:14 +08:00
tsosunchia
239b1c2f8d Merge pull request #172 from nxtrace/main
update readme
2023-10-09 08:16:24 +08:00
tsosunchia
ed3c158e87 更新下载方式 2023-10-09 08:14:04 +08:00
tsosunchia
02348f08c0 skywolf image update 2023-10-09 08:14:04 +08:00
tsosunchia
9c7402accb Merge pull request #171 from nxtrace/main
仓库用途变更
2023-10-08 17:21:44 +08:00
tsosunchia
a6848f8f23 仓库用途变更
要提交的变更:
	修改:     .github/workflows/build.yml
	修改:     .github/workflows/publishNewFormula.yml
	修改:     README.md
	修改:     README_zh_CN.md
	修改:     fast_trace/fast_trace_test.go
	修改:     go.mod
	修改:     go.sum
	修改:     pow/pow.go
	修改:     printer/basic.go
	修改:     util/latency.go
	修改:     util/latency_test.go
	修改:     util/util.go
	修改:     wshandle/client.go
2023-10-08 17:15:29 +08:00
tsosunchia
29b7c668a4 rename nt_install_v1.sh 2023-10-08 17:13:57 +08:00
tsosunchia
86eacc006e 更新readme 2023-10-08 17:11:35 +08:00
tsosunchia
81d6df8b82 更新readme 2023-10-08 17:07:24 +08:00
tsosunchia
dbe42d669c 更新依赖,优化显示 2023-10-08 16:33:25 +08:00
tsosunchia
a7089e8d54 更换homebrew地址 2023-10-06 21:35:17 +08:00
tsosunchia
bcda750a66 更新homebrew仓库地址地址 2023-10-06 21:30:35 +08:00
tsosunchia
1580c6111f add mpls func 2023-10-06 21:03:38 +08:00
tsosunchia
a3ef1b2574 update credit 2023-10-06 20:55:44 +08:00
tsosunchia
ba27ff967b chore: resolve incorrect conversion between integer types
https://github.com/nxtrace/NTrace-V1/security/code-scanning/1
 要提交的变更:
	修改:     trace/icmp_ipv4.go
2023-10-06 20:30:25 +08:00
tsosunchia
b326d7bed6 更新readme 2023-10-06 06:19:24 +08:00
tsosunchia
b733ef2d82 增加对多MPLS情景的支持,支持通过ENV或参数禁用MPLS功能
要提交的变更:
	修改:     cmd/cmd.go
	修改:     printer/basic.go
	修改:     printer/printer.go
	修改:     printer/realtime_printer.go
	修改:     trace/icmp_ipv4.go
	修改:     trace/icmp_ipv6.go
	修改:     trace/trace.go
	修改:     util/util.go
2023-10-06 06:02:09 +08:00
tsosunchia
2113264336 ipv6 mpls support 2023-10-05 07:25:05 +08:00
tsosunchia
ba7d6ea87b 重构mpls icmpv4 代码 2023-10-05 06:47:28 +08:00
tsosunchia
db1e9e2b0c v4 mpls实装显示 2023-10-05 06:42:04 +08:00
tsosunchia
13aaa54067 可显示MPLS LABEL 2023-10-05 05:12:01 +08:00
tsosunchia
db9b3fed9a 已经可以检测到MPLS 2023-10-05 04:30:09 +08:00
tsosunchia
7b145f4e64 增加ip2region下载db错误处理 2023-10-05 00:55:44 +08:00
tsosunchia
d444ff3c62 fix bug:IPv4被错误识别为LAN地址 2023-10-05 00:45:06 +08:00
tsosunchia
f1ceaf7f86 rename mod 2023-10-05 00:18:18 +08:00
tsosunchia
d6536f7c69 chore:更新依赖 2023-10-04 23:58:44 +08:00
tsosunchia
ae2e6b3631 修改fasttrace默认行为 2023-10-04 23:42:35 +08:00
tsosunchia
f63ef9552e 修正过滤器 2023-10-03 01:57:11 +08:00
tsosunchia
ce1bae2125 Merge pull request #13 from nxtrace/dependabot/go_modules/github.com/tidwall/gjson-1.17.0
chore(deps): bump github.com/tidwall/gjson from 1.16.0 to 1.17.0
2023-09-25 22:34:52 +08:00
dependabot[bot]
9d3878efbe chore(deps): bump github.com/tidwall/gjson from 1.16.0 to 1.17.0
Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.16.0 to 1.17.0.
- [Commits](https://github.com/tidwall/gjson/compare/v1.16.0...v1.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-25 14:32:39 +00:00
tsosunchia
c940b9f19f 更新go及依赖 2023-09-13 00:03:35 +08:00
tsosunchia
2d6ae4ff4c Merge pull request #10 from nxtrace/dependabot/go_modules/golang.org/x/net-0.15.0
chore(deps): bump golang.org/x/net from 0.14.0 to 0.15.0
2023-09-11 23:23:51 +08:00
dependabot[bot]
7977383fec chore(deps): bump golang.org/x/net from 0.14.0 to 0.15.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/net/compare/v0.14.0...v0.15.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>
2023-09-11 14:26:49 +00:00
tsosunchia
ae16731bbe update readme 2023-09-09 17:21:50 +08:00
tsosunchia
e0ea009b2c update readme 2023-09-09 16:55:59 +08:00
sjlleo
c532dfd05c improve: plugin hook for TTLComplete 2023-09-07 22:20:55 +08:00
tsosunchia
b967ee411d tracemap支持IP-API.COM做为API,修正第三方API部分地区显示问题 2023-09-07 16:35:51 +08:00
tsosunchia
46ce56f3a7 使用ipinfo api时支持显示tracemap,正确识别ANYCAST坐标 2023-09-07 13:27:19 +08:00
sjlleo
bb522ed859 refactor && feat: add plugin system && cobra 2023-09-05 22:18:28 +08:00
tsosunchia
cdfe926c37 更改pow失败时的错误提示 2023-09-05 22:01:45 +08:00
tsosunchia
aa7cea4cf8 dod也纳入考虑 2023-09-05 21:50:23 +08:00
tsosunchia
549dc549dd 网络故障时,不再显示为局域网
@XQZR 你要的我修好了🤗(希望)
2023-09-05 21:16:07 +08:00
tsosunchia
76fa8f2019 Merge pull request #9 from nxtrace/dependabot/github_actions/actions/checkout-4
chore(deps): bump actions/checkout from 3 to 4
2023-09-04 22:57:17 +08:00
dependabot[bot]
9e17f4a24f chore(deps): bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 14:48:59 +00:00
tsosunchia
b06487e293 Update README_zh_CN.md 2023-09-04 22:10:55 +08:00
tsosunchia
a0ab83c8ed 增加指定第三方POW服务器的参数及环境变量 2023-09-02 19:45:07 +08:00
sjlleo
9e5cd736c8 doc: supplement NextTrace v1/v2 Branch content 2023-09-02 19:27:51 +08:00
tsosunchia
b11d2334c2 Update README_zh_CN.md 2023-09-01 10:45:46 +08:00
tsosunchia
84a03e8a10 Update README.md 2023-09-01 10:45:26 +08:00
tsosunchia
761d506613 Update README 2023-09-01 08:32:21 +08:00
tsosunchia
c3ab3e6c1c fix:当接口多IP时,指定接口路由导致不能正常运行的问题 2023-08-31 20:02:54 +08:00
tsosunchia
96671d263c 修复了macos下ipv6不能指定dev的问题 2023-08-31 13:19:21 +08:00
tsosunchia
9228cdf307 Update README_zh_CN.md 2023-08-31 09:01:16 +08:00
tsosunchia
12565941ab Update README.md 2023-08-31 09:00:17 +08:00
sjlleo
e33548d2ce Update README 2023-08-31 00:12:39 +08:00
tsosunchia
5d766de52f Update readme 2023-08-31 00:11:49 +08:00
tsosunchia
ce46abd232 更新golang1.21 2023-08-30 20:47:32 +08:00
tsosunchia
6179f1b8f0 Update README 2023-08-30 20:47:32 +08:00
tsosunchia
3c12d99168 更新所有依赖 2023-08-16 23:11:52 +08:00
tsosunchia
f3f99ac0aa fix bugs:--source and --dev doesn't work on macOS
https://github.com/nxtrace/Ntrace-core/issues/147
2023-08-16 22:44:18 +08:00
tsosunchia
cb7bc7450f Update basic.go 2023-08-15 17:26:13 +08:00
tsosunchia
74f46f5d24 fix bugs:proxy settings
https://github.com/nxtrace/Ntrace-V1/pull/2#issuecomment-1610979252
2023-06-28 23:41:58 +08:00
tsosunchia
2bc386355c fix bugs:proxy settings
https://github.com/nxtrace/Ntrace-V1/pull/2#issuecomment-1610979252
2023-06-28 23:06:48 +08:00
tsosunchia
1266f62aba Update README 2023-06-26 14:52:44 +08:00
Leo
8e837072c6 doc: README_zh_CN 2023-06-26 14:52:41 +08:00
sjlleo
cf0639111d fix: install fail 2023-06-26 14:52:37 +08:00
sjlleo
56cd0022f7 remove: channel link is illegal and has been removed. 2023-06-26 14:52:33 +08:00
tsosunchia
1407c499b9 Update README 2023-06-26 14:52:27 +08:00
sjlleo
f25662d481 doc: future change 2023-06-26 14:52:22 +08:00
tsosunchia
4f94f01e87 Merge pull request #128 from fakeboboliu/fix1
improve: rootless trace on macOS
2023-06-26 14:52:12 +08:00
Leo
1460ad67c0 refactor: nexttrace core 2023-06-26 14:52:05 +08:00
Leo
d6dcfc8dc5 feat: try add UDP IPv6 Support 2023-06-26 14:51:59 +08:00
Leo
190b3ab94e chore: add begin hop / srcaddr listen 2023-06-26 14:51:54 +08:00
Leo
87fa850d2d chore: sync to the latest repository name 2023-06-26 14:51:48 +08:00
Leo
c71a26d018 fix: name change caused installation failure 2023-06-26 14:51:36 +08:00
Leo
79c39b655a clean mod && delete useless files 2023-06-26 14:51:19 +08:00
tsosunchia
580612ce08 refactor: preparatory work 2023-06-26 14:49:05 +08:00
tsosunchia
141653a3e9 增加版本说明 2023-06-17 20:38:36 +08:00
tsosunchia
f59d7ff2f5 chore:更改相关url 2023-06-17 20:11:10 +08:00
tsosunchia
87f153cdf1 Update README.md 2023-06-17 03:23:14 +08:00
tsosunchia
ebe2a0f8e9 update readme 2023-06-15 12:42:28 +08:00
tsosunchia
ca2a7a8dc8 Update build/test action & readme
update readme

Update test.yml
2023-06-15 11:43:17 +08:00
tsosunchia
b68b768a65 Merge pull request #138 from tsosunchia/main
支持通过设置SOCKS5/HTTP代理访问我们的API
2023-06-14 23:03:24 +08:00
tsosunchia
efbede8ca1 Update build.yml
actions运作发版时,先存储为draft
2023-06-14 22:16:38 +08:00
tsosunchia
b1edef305f Merge pull request #2 from tsosunchia/proxyenv
增加支持SOCKS5/HTTP代理,通过环境变量NEXTTRACE_PROXY配置.
2023-06-07 03:21:12 +08:00
tsosunchia
54242cfa8b 增加支持SOCKS5/HTTP代理,通过环境变量NEXTTRACE_PROXY配置.
如export NEXTTRACE_PROXY=socks5://127.0.0.1:10808
2023-06-07 03:15:29 +08:00
tsosunchia
2d4de9e4c1 Update .cross_compile.sh 2023-06-04 20:34:17 +08:00
tsosunchia
d75a384d70 Update nt_install.sh 2023-06-04 13:12:40 +08:00
tsosunchia
f6fbf3803f UPDATE README 2023-06-04 12:38:24 +08:00
tsosunchia
2c86f86204 fix bugs:ipv6下fasttrace异常退出的问题 2023-06-04 05:27:21 +08:00
tsosunchia
dcce91614f fix bugs:ft下maxhops、pktsize不正常显示、tracemap在配置环境变量时不能正常使用、trace时无法显示第一跳 2023-06-04 04:12:57 +08:00
tsosunchia
85df0121fd UPDATE README 2023-06-03 20:45:47 +08:00
tsosunchia
4a749285be 将DNS解析与WS握手同步进行 2023-06-03 20:19:01 +08:00
tsosunchia
a92cfc7783 删除无意义文件 2023-06-03 19:52:25 +08:00
tsosunchia
268971e85b Merge pull request #128 from fakeboboliu/fix1
improve: rootless trace on macOS
2023-06-03 17:26:52 +08:00
bobo liu
2bac716bd7 improve: rootless trace on macOS 2023-06-03 16:50:21 +08:00
tsosunchia
eb64f68663 修补timeout参数 2023-06-03 06:57:44 +08:00
tsosunchia
e9ca9cf388 fixbug:tcp ipv6 trace时遵从BeginHop 2023-06-03 05:47:38 +08:00
tsosunchia
364be22383 tcp,udp模式也适用ttl-time,send-time了 2023-06-03 05:25:14 +08:00
tsosunchia
2f6a2573ae fetchipdata加mutex,以后同时仅进行一次的查询 2023-06-03 04:28:10 +08:00
tsosunchia
a25157867c fixbug: fasttrace指定网卡异常退出 2023-06-03 03:33:36 +08:00
tsosunchia
7c37598804 将GeoIPInformatinDataCache推广到所有数据源 2023-06-02 18:13:27 +08:00
tsosunchia
8d30c59c17 fasttrace增加支持SrcDev,SrcAddr,BeginHop,MaxHops,RDns,AlwaysWaitRDNS,Lang,PktSize 2023-06-02 17:32:07 +08:00
tsosunchia
e8e5c1438e Merge remote-tracking branch 'refs/remotes/origin/main' 2023-06-02 14:20:48 +08:00
tsosunchia
cb0c988eda Update bug_report.md 2023-06-02 14:20:21 +08:00
tsosunchia
b1cabbc6d4 pow_test增加计时 2023-06-02 13:57:04 +08:00
tsosunchia
2493f471dc 对pow_token也进行了缓存 2023-06-02 04:37:40 +08:00
tsosunchia
8578109243 对rdns也进行了缓存 2023-06-02 02:12:35 +08:00
tsosunchia
089250fee1 提供不间断运行环境变量 2023-06-02 01:41:11 +08:00
tsosunchia
0e87592537 增加IP查询缓存机制 2023-06-02 01:04:32 +08:00
tsosunchia
14552cd853 推介OpenTrace 2023-06-01 18:43:43 +08:00
tsosunchia
44b70cf7e7 fix typo 2023-06-01 18:24:55 +08:00
tsosunchia
3f8b043821 format readme 2023-06-01 18:12:13 +08:00
tsosunchia
fa8eb050af 更新依赖 2023-06-01 17:29:35 +08:00
tsosunchia
50582f0fd0 重构UA存放位置 2023-06-01 16:18:28 +08:00
tsosunchia
64371fb41a GetTracemap也适用优选IP 2023-06-01 15:40:04 +08:00
tsosunchia
6476c3aff3 pow模块解耦合完成 2023-06-01 14:46:52 +08:00
tsosunchia
4d7831fd29 pow使用的ip也是优选IP 2023-06-01 14:08:32 +08:00
tsosunchia
d602941722 对pow模块解耦合 2023-06-01 12:30:36 +08:00
tsosunchia
2d548fcca0 增加一个设置POWtoken的环境变量 2023-06-01 00:46:45 +08:00
tsosunchia
8a79a5dd1c 调整POW超时时间为5s 2023-05-31 22:30:31 +08:00
tsosunchia
390efdd75d Merge remote-tracking branch 'origin/main' 2023-05-31 22:20:49 +08:00
tsosunchia
d0d1511c29 增加POW机制 2023-05-31 22:20:08 +08:00
Leo
7013fb6a41 doc: maintainer 2023-05-31 20:46:10 +08:00
tsosunchia
0513ab2a1b 更新README 2023-05-31 03:19:23 +08:00
tsosunchia
11489d7027 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	README.md
2023-05-31 03:12:11 +08:00
tsosunchia
97880049f7 更新README安装指引 2023-05-31 03:10:00 +08:00
tsosunchia
781c6ccaee Merge pull request #124 from chenrui333/docs/add-homebrew-installation-note
docs: update to use homebrew-core formula
2023-05-31 03:09:34 +08:00
Rui Chen
9e84a3da65 docs: update to use homebrew-core formula
Signed-off-by: Rui Chen <rui@chenrui.dev>
2023-05-30 12:47:50 -04:00
tsosunchia
4e5cd2d053 fix bug:重构版本信息的位置解决包引用环路问题 2023-05-30 14:55:47 +08:00
tsosunchia
288aee254f 变更:获取GEOIP信息适用于timeout参数 2023-05-30 14:38:42 +08:00
tsosunchia
2d235baa53 变更:获取GEOIP信息适用于timeout参数 2023-05-30 14:37:51 +08:00
sjlleo
d23f90dd19 Merge pull request #123 from sjlleo/newargs
增加timeout和psize参数
2023-05-30 12:21:38 +08:00
tsosunchia
fdc6145087 添加一个psize参数 2023-05-30 03:37:00 +08:00
tsosunchia
1d6ce5de88 添加一个timeout参数 2023-05-30 02:24:43 +08:00
tsosunchia
86b817291d Merge pull request #122 from sjlleo/resovle_v4_v6_only
增加指定解析IPv4/IPv6的功能
2023-05-29 21:58:28 +08:00
tsosunchia
b38fc18bb4 增加指定解析IPv4/IPv6的功能 2023-05-29 20:53:07 +08:00
tsosunchia
23d20de7c8 fix bug:fasttrace也进行nslookup 2023-05-29 19:51:16 +08:00
tsosunchia
bf4ec99323 fasttraceIP改为由dns获取 2023-05-29 18:34:15 +08:00
tsosunchia
4bfe85df1b 更改为仅在使用LEOMOEAPI时尝试获取tracemap 2023-05-28 16:26:22 +08:00
tsosunchia
e48b30c47a 增加json输出模式,修正使用ipinfo API查询的IP无ASN时的异常
PS: json输出模式主要目的是方便开发者调用NEXTTRACE
2023-05-28 13:58:27 +08:00
tsosunchia
f2fbce0358 增加不输出GEOIP信息的方法
PS: 为外部程序调用预留
2023-05-26 11:13:59 +08:00
tsosunchia
98bd95d046 update readme 2023-05-25 20:54:22 +08:00
tsosunchia
b622a40022 fix bug:raw和classic模式增加语言个性化输出
注意:classic模式将会被废弃,请不要在此模式基础上开发
2023-05-25 20:40:12 +08:00
tsosunchia
6bc74e14cb 增加关于IP信息纠错的联系方式 2023-05-25 18:24:05 +08:00
tsosunchia
8233c7c303 更新新模式说明 2023-05-24 22:50:11 +08:00
tsosunchia
3084effd78 修改输出提示,显示为当前真实的maxhops设置
之前无论maxhops设置为多少,都输出为 30 hops max
2023-05-24 16:34:30 +08:00
tsosunchia
ac1ee5e08c 增加tracemap的异常处理
防止与tracemap连接出现问题时报错
2023-05-24 16:08:59 +08:00
tsosunchia
723f5c4c5e raw模式使用新参数--raw
classic模式参数保持v1.1.3版本的设置
2023-05-23 22:22:14 +08:00
Leo
308ae73728 chore: add latency 2023-05-23 21:41:16 +08:00
Leo
b88d5292da chore: Classic Printer Change 2023-05-23 20:00:42 +08:00
tsosunchia
903b6f232d Update README_zh_CN.md 2023-05-23 02:39:59 +08:00
tsosunchia
5211daf814 Update README.md 2023-05-23 02:38:56 +08:00
tsosunchia
62f817af91 Merge pull request #111 from sjlleo/dependabot/go_modules/golang.org/x/net-0.10.0 2023-05-15 20:53:18 +08:00
dependabot[bot]
5907ebcf84 chore(deps): bump golang.org/x/net from 0.9.0 to 0.10.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/net/compare/v0.9.0...v0.10.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>
2023-05-15 10:02:10 +00:00
tsosunchia
825eccf986 Merge pull request #109 from sjlleo/dependabot/go_modules/golang.org/x/sync-0.2.0
chore(deps): bump golang.org/x/sync from 0.1.0 to 0.2.0
2023-05-08 19:02:51 +08:00
dependabot[bot]
a33307d746 chore(deps): bump golang.org/x/sync from 0.1.0 to 0.2.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.1.0 to 0.2.0.
- [Commits](https://github.com/golang/sync/compare/v0.1.0...v0.2.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>
2023-05-08 10:04:38 +00:00
sjlleo
eee741d848 doc: add donate link 2023-05-04 21:08:21 +08:00
sjlleo
98344193e7 doc: add donate link 2023-05-04 21:06:12 +08:00
tsosunchia
7a74bb83c2 Update basic.go
更换北京9929IP
2023-05-02 01:21:47 +08:00
sjlleo
3e010bd9da update readme 2023-04-21 19:22:47 +08:00
tsosunchia
abc1ecd035 Merge pull request #94 from sjlleo/dependabot/go_modules/golang.org/x/net-0.9.0
chore(deps): bump golang.org/x/net from 0.8.0 to 0.9.0
2023-04-10 18:38:33 +08:00
dependabot[bot]
989dc29931 chore(deps): bump golang.org/x/net from 0.8.0 to 0.9.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.8.0 to 0.9.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.8.0...v0.9.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>
2023-04-10 10:02:16 +00:00
tsosunchia
1f2b4cde1f Merge pull request #91 from sjlleo/dependabot/github_actions/actions/setup-go-4
chore(deps): bump actions/setup-go from 3 to 4
2023-03-20 18:15:33 +08:00
dependabot[bot]
939a4c7aab chore(deps): bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 10:08:31 +00:00
tsosunchia
4890615a2b 增加了关于IPInfo和IPInsight API TOKEN的环境变量 2023-03-14 00:20:42 +08:00
tsosunchia
9b8fd51ef2 Merge pull request #90 from sjlleo/dependabot/go_modules/github.com/fatih/color-1.15.0
chore(deps): bump github.com/fatih/color from 1.14.1 to 1.15.0
2023-03-13 20:46:22 +08:00
dependabot[bot]
73c9d91637 chore(deps): bump github.com/fatih/color from 1.14.1 to 1.15.0
Bumps [github.com/fatih/color](https://github.com/fatih/color) from 1.14.1 to 1.15.0.
- [Release notes](https://github.com/fatih/color/releases)
- [Commits](https://github.com/fatih/color/compare/v1.14.1...v1.15.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-13 12:40:57 +00:00
tsosunchia
06e85c11c6 修改GitHub Action 2023-03-13 20:36:55 +08:00
tsosunchia
d151416566 Merge pull request #86 from sjlleo/dependabot/go_modules/golang.org/x/net-0.8.0
chore(deps): bump golang.org/x/net from 0.7.0 to 0.8.0
2023-03-12 04:03:04 +08:00
tsosunchia
b9d9853458 Merge pull request #85 from sjlleo/dependabot/github_actions/actions/setup-go-3
chore(deps): bump actions/setup-go from 2 to 3
2023-03-12 04:00:26 +08:00
dependabot[bot]
3f177c87d2 chore(deps): bump golang.org/x/net from 0.7.0 to 0.8.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.7.0 to 0.8.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.7.0...v0.8.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>
2023-03-06 04:52:00 +00:00
dependabot[bot]
d3a00335a1 chore(deps): bump actions/setup-go from 2 to 3
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 04:51:41 +00:00
tsosunchia
aa38ed374f Create .github/dependabot.yml 2023-03-06 12:51:25 +08:00
tsosunchia
08f87cc229 Update publishNewFormula.yml 2023-03-03 16:15:31 +08:00
tsosunchia
ead7ebbc25 Update fast_trace_test.go
减少TEST耗时
2023-03-03 13:42:32 +08:00
tsosunchia
7a048fa976 Update README.md 2023-03-03 13:32:55 +08:00
sjlleo
dbb844eb44 Merge pull request #84 from sjlleo/dev
多个API增加支持
2023-03-03 13:30:20 +08:00
tsosunchia
916ac3cb69 add CHUNZHEN api 2023-03-03 12:07:37 +08:00
tsosunchia
bdfba172d1 revert 2023-03-03 12:02:48 +08:00
tsosunchia
a95a741ce2 增加scanf时的默认值 2023-03-02 22:12:34 +08:00
tsosunchia
362317e95e format 2023-03-02 21:47:59 +08:00
tsosunchia
dc735b0f97 Update ipinfo.go 转换ISO3166 2023-03-02 21:21:43 +08:00
tsosunchia
fe0fce07ec Update README 2023-03-02 19:11:26 +08:00
tsosunchia
60a4454802 Update README 2023-03-02 18:56:25 +08:00
tsosunchia
525cfd8c8f merge 2023-03-02 18:47:25 +08:00
tsosunchia
c2eebca25c 修正IPInfo BUG 和增加IPInfoLocal 2023-03-02 18:38:42 +08:00
tsosunchia
110bdc27ff 修改一些第三方API信息
Update README_zh_CN.md
Update README.md
Update ipinsight.go
Update ipgeo.go
2023-03-02 12:05:32 +08:00
tsosunchia
510b0f6c32 可使用别名调用第三方API 2023-03-02 11:48:49 +08:00
tsosunchia
8a01bccbff Update ipinsight.go 2023-03-01 18:04:06 +08:00
tsosunchia
64224c905e Update ipinsight.go 2023-03-01 18:01:18 +08:00
tsosunchia
2403308c1b Update build.yml 2023-02-23 14:53:58 +08:00
sjlleo
560a1693af chore: add more detail 2023-02-20 17:11:21 +08:00
Leo
45e9fe604d chore: clean default args setting 2023-02-20 13:16:08 +08:00
sjlleo
88e69f7dc2 feat: enable pure Go and add DoT support 2023-02-20 03:49:10 +00:00
sjlleo
b1a66546a0 Update CNAME 2023-02-17 12:48:40 +08:00
sjlleo
5c46e4eddb chore: 补充配置文件默认存放位置 2023-02-16 22:39:43 +08:00
sjlleo
30d550753c chore: DN42 配置文件教程 2023-02-16 22:17:08 +08:00
Leo
50b3e442da dump golang.org/x/net -> 0.7.0 2023-02-16 21:18:11 +08:00
Leo
ba133e3c73 feat: add DN42 Mode 2023-02-16 21:16:47 +08:00
Leo
3a5184b06c dump to Go 1.20 2023-02-16 21:16:25 +08:00
Leo
e825b8d092 fix: IPv6 no message in fast_trace 2023-02-15 18:46:00 +08:00
Leo
f64934bfde Merge branch 'main' of https://github.com/sjlleo/nexttrace 2023-02-15 18:42:36 +08:00
Leo
9871449b30 chore: IPv6 TCP Tracert Support for Fast Trace 2023-02-15 18:41:59 +08:00
tsosunchia
31a9c356e1 修正readme 2023-02-15 18:41:49 +08:00
Leo
5c0c490ae8 feat: add TCP IPv6 Support 2023-02-15 18:16:09 +08:00
tsosunchia
22660143b0 更新截图 2023-02-13 23:52:56 +08:00
tsosunchia
f4b5de08ca 更换traceMap截图 2023-02-13 23:35:56 +08:00
sjlleo
b0e1329d66 simplify build.yml 2023-02-13 09:19:48 +08:00
sjlleo
805e827e45 try add: mips64 mips64le support 2023-02-13 09:00:13 +08:00
tsosunchia
ed2250f811 fix: ipv6地址分割问题 2023-02-11 01:53:30 +08:00
tsosunchia
fe18c80f83 支持直接传入未经修剪的URL 2023-02-11 01:03:02 +08:00
tsosunchia
d1c9fd6c3a 小修改 2023-02-10 00:53:53 +08:00
tsosunchia
c67e2d6384 更新MapTrace在readme的指引 2023-02-06 23:44:31 +08:00
tsosunchia
309ae9d74a fix:macOS下报错异常 2023-02-06 09:22:27 +08:00
sjlleo
cf65ff5e7e chore: translation 2023-02-01 22:12:54 +08:00
sjlleo
eae92ebfae chore: update English doc 2023-02-01 22:10:39 +08:00
Leo
afe18fc4c0 feat: add English support 2023-02-01 21:34:33 +08:00
Leo
cc1d6177ca 重构关于地理位置以及 PTR 记录获取的模块,大幅提升路由跟踪性能 2023-02-01 18:22:02 +08:00
tsosunchia
c8077919ce 优化rdns体验,默认开启traceMap 2023-02-01 15:56:12 +08:00
tsosunchia
cfc79489d4 fix: fmt error 2023-02-01 12:45:06 +08:00
Leo
f60e6fbc99 feat: LeoMoeAPI 优选 IP 2023-01-31 11:58:55 +08:00
Leo
de0f49d01b fix: homebrew git fail 2023-01-30 10:19:37 +08:00
Leo
7ae47fdb6f fix: Fast Trace Bug && Performance Improvement 2023-01-30 09:59:10 +08:00
YekongTAT
471412b740 Merge pull request #70 from haima3/main
fix: Fixed invalid test IP for Shanghai CT (163)
2023-01-29 22:57:34 +08:00
Haima
8aca06fe24 fix: Fixed invalid test IP for Shanghai CT (163)
Minor fix: Fixed invalid Shanghai CT (163) test IPv4 IP
2023-01-29 22:55:52 +08:00
Leo
003db157a9 try fix: #67 fatal error: concurrent map read and map write 2023-01-29 21:32:33 +08:00
Leo
6e88706d62 Merge branch 'main' of https://github.com/sjlleo/nexttrace 2023-01-25 18:05:17 +08:00
Leo
0940014b09 修改默认发包间隔为100 2023-01-25 18:04:48 +08:00
sjlleo
5a7d04ab1e chore: 添加 IP 骨干网准度说明 2023-01-25 14:18:16 +08:00
tsosunchia
c6b7db8d59 解决brew包总落后release一个版本的问题
以后publishnewformula集成到build中,
publishnewformula保留,但只能手动启用
2023-01-25 12:33:06 +08:00
Leo
95f8cef54c small improvement 2023-01-24 14:47:02 +08:00
Leo
99ad5649a0 fix: filter switch-case condition 2023-01-24 08:51:53 +08:00
sjlleo
4bc4f1c176 Merge pull request #64 from tsosunchia/main
improve: 在本地对部分IP做了过滤
2023-01-24 05:37:59 +08:00
tsosunchia
b663e69343 improve: 在本地对部分IP做了过滤
IANA Reserved Address Space
2023-01-24 01:37:29 +08:00
Leo
7619e74152 feat: 添加 report 模式 2023-01-23 21:58:22 +08:00
Leo
c682f90856 feat: 小彩蛋 2023-01-23 21:25:19 +08:00
Leo
68c84488ae fix a bug caused table crash when using IPv6 2023-01-23 20:55:31 +08:00
sjlleo
deaebfa383 Merge pull request #61 from tsosunchia/patch-2
减少actions报错
2023-01-22 10:27:13 +08:00
tsosunchia
b467128c24 减少actions报错
在代码没有变化时,git commit会报错:
位于分支 main
您的分支与上游分支 'origin/main' 一致。

无文件要提交,干净的工作区
2023-01-22 09:22:43 +08:00
sjlleo
de93a2b3cb Merge pull request #60 from tsosunchia/patch-1
add feature: Read ENV to replace default endpoint
2023-01-22 04:45:07 +08:00
tsosunchia
66de7df351 add feature: Read ENV to replace default endpoint
ENV:  "NEXTTRACE_HOSTPORT"
default endpoint:  "api.leo.moe:443"
2023-01-22 00:49:33 +08:00
sjlleo
9685b80820 chore: 更新一些已经与旧版本有所不同的部分 2023-01-20 20:14:06 +08:00
sjlleo
8a3a9d5e38 chore: 调整一下模板 2023-01-20 20:06:32 +08:00
sjlleo
c826dd50ec chore: 调整一下排版 2023-01-20 19:53:25 +08:00
sjlleo
4664bc727a Update README_zh_CN.md 2023-01-20 12:23:14 +08:00
sjlleo
b66b7edc46 chore: add report 2023-01-20 10:59:30 +08:00
sjlleo
457f25035c chore: 为什么说 BestTrace 是普通人更好的选择?您何时应该选择 NextTrace? 2023-01-19 20:34:32 +08:00
sjlleo
8220bd7920 Merge pull request #59 from tsosunchia/patch-1
Update README
2023-01-19 11:37:26 +08:00
tsosunchia
372ac9d383 Update README
Update README.md, README_zh_CN.md
2023-01-19 10:30:08 +08:00
sjlleo
916a5dc9d0 fix: 不能正常显示版本号 2023-01-18 19:24:15 +08:00
Leo
b9b18f5efa feat: 增加了对发包的时间管理控制 2023-01-18 19:19:19 +08:00
Leo
326034b41e fix: panic on IPv6 2023-01-18 13:44:47 +08:00
Leo
09a5b42443 fix: 在 Table 模式下,无法自动退出的问题 2023-01-18 13:26:48 +08:00
Leo
b8542489b6 fix: 修复使用 -f 导致打印卡住的问题 2023-01-18 13:19:27 +08:00
Leo
a73a306d0a chore: sync document 2023-01-17 20:30:49 +08:00
Leo
801f42801a fix: Fast Trace 无法使用 2023-01-17 20:10:35 +08:00
Leo
08c4a5ceae fix: Fast Trace 无法使用 2023-01-17 20:09:53 +08:00
Leo
ce1c773996 improve: rDNS 解析等待时间过长 2023-01-17 20:08:57 +08:00
Leo
cdec6cbd8d feat: refactor flag 2023-01-17 19:08:32 +08:00
sjlleo
aa891ebd7c Typo 2023-01-15 15:07:29 +08:00
sjlleo
5d132e73ab chore: add announcement 2023-01-15 14:13:09 +08:00
Leo
63ca4d0418 fix: 一些包合法性判断上的错误 2023-01-14 20:22:07 +08:00
Leo
a6da078eb0 feat: 重构整个 ICMP 包校验模块,增加了对 ICMP 异步发包的支持 2023-01-14 18:15:23 +08:00
Leo
7ee76591b4 fix: Error printing method called 2023-01-13 13:48:46 +08:00
Leo
2d95fed6b2 fix: Test #342 nil pointer 2023-01-13 13:41:20 +08:00
Leo
29b1d7b283 feat: 为 table 模式增加屏幕的实时刷新功能 2023-01-13 13:32:20 +08:00
sjlleo
1e654d1400 Merge pull request #53 from tsosunchia/patch-1
fix: 当有多个路由跟踪实例运行,对输出结果造成干扰的问题 (IPv6,ICMPv6)
2023-01-13 10:35:14 +08:00
tsosunchia
1746068302 fix: 当有多个路由跟踪实例运行,对输出结果造成干扰的问题 (IPv6,ICMPv6) 2023-01-12 23:21:41 +08:00
sjlleo
45c30ddb8d Merge branch 'main' of https://github.com/xgadget-lab/nexttrace 2023-01-10 06:26:44 -05:00
sjlleo
88fef52e71 feat: add IPv6 Fast Test 2023-01-10 06:24:14 -05:00
sjlleo
491f774336 Merge pull request #51 from isyekong/main
补充架构检测
2023-01-08 21:06:58 +08:00
YekongTAT
b435a36ee4 downPath 增加判断
部分奇怪的系统可能没有 `/usr/local` 目录,如果目录不存在就把 `nexttrace` 放到 `/usr/local`
2023-01-08 20:56:36 +08:00
YekongTAT
ba87933580 补充系统架构检测 2023-01-08 19:32:22 +08:00
sjlleo
6bf243794b chore: update readme about LeoMoeAPI 2023-01-04 12:30:35 +08:00
sjlleo
44d7d5a024 chore: add traceMap readme 2022-12-18 04:13:38 -05:00
sjlleo
5f096964dc Update README_zh_CN.md 2022-12-18 17:00:13 +08:00
sjlleo
69b893a587 chore: readme_EN 2022-12-18 16:58:33 +08:00
sjlleo
ab2462bead chore: readme 2022-12-18 03:43:59 -05:00
sjlleo
d9d60d09b2 Merge branch 'main' of https://github.com/xgadget-lab/nexttrace 2022-12-17 04:14:54 -05:00
sjlleo
91cd4fb8f4 chore: add traceMap (new) 2022-12-17 04:14:39 -05:00
sjlleo
c592c14f28 chore: readme_zh-CN 2022-12-14 13:11:30 +08:00
sjlleo
15829c7041 chore: screenshot 2022-12-12 15:29:59 +08:00
sjlleo
336151dc1b chore: 调整一下判断顺序 2022-12-11 22:11:04 -05:00
sjlleo
66ee62f22b chore: 在对于普通用户运行在检测到对应 Linux Capability 以后也给予放行,不再拦截 2022-12-11 22:07:51 -05:00
sjlleo
3afd28cb89 chore: screenshots update 2022-12-10 12:36:12 +08:00
sjlleo
690f546ff0 chore: add copyright 2022-12-08 09:49:40 +08:00
sjlleo
d561063a7c add: 对 Hop 的路由表展示 2022-11-18 04:41:47 -05:00
sjlleo
473ce3c5f2 add: windows support information 2022-10-19 03:14:34 -04:00
sjlleo
a1783e3563 fix: github action cannot find windows release file 2022-10-19 03:03:47 -04:00
sjlleo
7b9912f23f fix: 修正 log 输出功能在 Fast Trace 不可用的问题 2022-10-19 02:52:25 -04:00
sjlleo
fdeaf112f5 add: Windows 系统支持, log 日志输出 2022-10-19 02:46:44 -04:00
sjlleo
07a2aac7c7 fix: project shell 2022-10-04 15:58:52 +08:00
sjlleo
08f8daf9ce remove: donate info 2022-10-04 11:57:45 +08:00
sjlleo
7872e9ee0f remove: ipdata info 2022-10-04 11:55:23 +08:00
sjlleo
991f66cfe4 fix: realtime_printer 2022-09-20 05:12:20 -04:00
sjlleo
5b91fac860 fix: 使用第三方 API 不显示 ISP 或域名 2022-09-20 04:40:59 -04:00
sjlleo
62e877e248 add: 新增离线数据库支持 2022-09-20 04:34:03 -04:00
sjlleo
9e4b2ae577 fix: → →又手滑了... rDNS 在同一个 Hop 下完全相同的神奇 Bug 2022-09-20 03:47:43 -04:00
sjlleo
b02572d6ff update: English / Chinese Screenshot 2022-09-01 21:42:37 -04:00
sjlleo
e5742e1603 update: 软件截图更新至最新版 2022-09-01 21:40:13 -04:00
sjlleo
70a727bee6 update: 关于指定网卡进行路由跟踪的说明文档 2022-09-01 21:27:00 -04:00
sjlleo
1261e243f2 add: 指定网卡进行路由跟踪 2022-09-01 21:02:15 -04:00
sjlleo
49ce0cba8e add & fix: IPWhois 功能 / IPv6 显示对齐 2022-09-01 21:01:14 -04:00
sjlleo
9764533c8e update: 依赖更新 2022-09-01 21:00:30 -04:00
sjlleo
d752385c29 add: 添加爱发电创作者捐助平台 2022-08-19 17:19:44 +08:00
sjlleo
bf005fb37a update: screenshot 2022-08-17 10:54:40 +08:00
sjlleo
8dc5960d98 修改 brew 更新触发状态 2022-08-09 17:08:24 +08:00
80 changed files with 6543 additions and 1123 deletions

View File

@@ -5,7 +5,7 @@ set -e
DIST_PREFIX="nexttrace"
DEBUG_MODE=${2}
TARGET_DIR="dist"
PLATFORMS="darwin/amd64 darwin/arm64 linux/386 linux/amd64 linux/arm64 linux/mips openbsd/amd64 openbsd/arm64 freebsd/amd64 freebsd/arm64"
PLATFORMS="darwin/amd64 darwin/arm64 linux/386 linux/amd64 linux/arm64 linux/mips linux/mips64 linux/mipsle linux/mips64le windows/amd64 windows/arm64 openbsd/amd64 openbsd/arm64 freebsd/amd64 freebsd/arm64"
BUILD_VERSION="$(git describe --tags --always)"
BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
@@ -26,16 +26,16 @@ for pl in ${PLATFORMS}; do
echo "build => ${TARGET}"
if [ "${DEBUG_MODE}" == "debug" ]; then
go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
-w -s"
-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"
else
go build -trimpath -o ${TARGET} \
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
-w -s"
-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"
fi
done
export CGO_ENABLED=0
@@ -46,16 +46,16 @@ done
echo "build => ${TARGET}"
if [ "${DEBUG_MODE}" == "debug" ]; then
go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
-w -s"
-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"
else
go build -trimpath -o ${TARGET} \
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
-w -s"
-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"
fi

View File

@@ -15,8 +15,13 @@ copyright: [v2fly](https://github.com/v2fly)
<!--
除非特殊情况,请完整填写所有问题。不按模板发的 issue 将直接被关闭。
如果你遇到的问题不是 nexttrace 的 bug比如你不清楚如何配置请在 https://github.com/xgadget-lab/nexttrace/discussions 进行讨论。
如果你遇到的问题不是 nexttrace 的 bug比如你不清楚如何配置请在 https://github.com/nxtrace/NTrace-core/discussions 进行讨论。
-->
## 本项目是基于Linux/macOS的请确认您遇到的问题是否在Linux或macOS上存在。
<!-- 是/否 -->
<!-- 对于只出现在Windows上的问题本项目有时无法解决请知悉 -->
## 你正在使用哪个版本的 nexttrace
@@ -38,3 +43,9 @@ copyright: [v2fly](https://github.com/v2fly)
## 请附上出错时软件输出的错误信息
<!-- 是/否 -->
## 是否查询过本仓库wiki有没有类似错误
<!-- wiki: https://github.com/nxtrace/NTrace-core/wiki -->

15
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@@ -1,49 +1,200 @@
name: Build & Release
on:
workflow_dispatch:
push:
branches:
- main
tags:
- "v*"
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
pull_request:
name: Test & Build Release
types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
jobs:
Test:
build:
runs-on: ubuntu-latest
strategy:
matrix:
# Include amd64 on all platforms.
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
goarch: [amd64, 386]
exclude:
# Exclude i386 on darwin and dragonfly.
- goarch: 386
goos: dragonfly
- goarch: 386
goos: darwin
include:
# BEIGIN MacOS ARM64
- goos: darwin
goarch: arm64
# END macOS ARM64
# BEGIN Linux ARM 5 6 7
- goos: linux
goarch: arm
goarm: 7
- goos: linux
goarch: arm
goarm: 6
- goos: linux
goarch: arm
goarm: 5
# END Linux ARM 5 6 7
# BEGIN Android ARM 8
- goos: android
goarch: arm64
# END Android ARM 8
# Windows ARM
- goos: windows
goarch: arm64
- goos: windows
goarch: arm
goarm: 7
# BEGIN Other architectures
# BEGIN riscv64 & ARM64
- goos: linux
goarch: arm64
- goos: linux
goarch: riscv64
# END riscv64 & ARM64
# BEGIN MIPS
- goos: linux
goarch: mips64
- goos: linux
goarch: mips64le
- goos: linux
goarch: mipsle
- goos: linux
goarch: mips
- goos: linux
goarch: mipsle
gomips: softfloat
- goos: linux
goarch: mips
gomips: softfloat
# END MIPS
# BEGIN PPC
- goos: linux
goarch: ppc64
- goos: linux
goarch: ppc64le
# END PPC
# BEGIN FreeBSD ARM
- goos: freebsd
goarch: arm64
- goos: freebsd
goarch: arm
goarm: 7
# END FreeBSD ARM
# BEGIN S390X
- goos: linux
goarch: s390x
# END S390X
# END Other architectures
# BEGIN OPENBSD ARM
- goos: openbsd
goarch: arm64
- goos: openbsd
goarch: arm
goarm: 7
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
GOARM: ${{ matrix.goarm }}
GOMIPS: ${{ matrix.gomips }}
CGO_ENABLED: 0
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
- name: Checkout codebase
uses: actions/checkout@v4
- name: Show workflow information
run: |
if [ ! -z $GOARM ]; then
export GOARM=v$GOARM
fi
export _NAME="nexttrace_${GOOS}_${GOARCH}${GOARM}"
if [ "$GOOS" == "windows" ]; then
export _NAME="$_NAME.exe"
fi
if [ "$GOMIPS" == "softfloat" ]; then
export _NAME="${_NAME}_softfolat"
fi
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
echo "BUILD_VERSION=$(git describe --tags --always)" >> $GITHUB_ENV
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
echo "COMMIT_SHA1=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.18"
- name: Test
run: sudo go test -v -coverprofile='coverage.out' -covermode=count ./...
Build:
needs: test
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
go-version: '1.23'
- name: Get project dependencies
run: go mod download
- name: Build
run: |
go build -trimpath -o dist/${ASSET_NAME} \
-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}'\
-checklinkname=0 -w -s"
- name: Upload files to Artifacts
uses: actions/upload-artifact@v4
with:
go-version: "1.18"
- run: bash .cross_compile.sh
name: ${{ env.ASSET_NAME }}
path: |
dist/${{ env.ASSET_NAME }}
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v2
with: # 将下述可执行文件 release 上去
draft: false # Release草稿
draft: true # Release草稿
files: |
dist/nexttrace_darwin_amd64
dist/nexttrace_darwin_arm64
dist/nexttrace_linux_386
dist/nexttrace_linux_amd64
dist/nexttrace_linux_arm64
dist/nexttrace_linux_armv7
dist/nexttrace_linux_mips
dist/nexttrace_openbsd_amd64
dist/nexttrace_openbsd_arm64
dist/nexttrace_freebsd_amd64
dist/nexttrace_freebsd_arm64
dist/*
env:
GITHUB_TOKEN: ${{ secrets.GT_Token }}
GITHUB_TOKEN: ${{ secrets.GT_Token }}
publish-new-formula:
needs: build
# The type of runner that the job will run on
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Runs a single command using the runners shell
- name: config git
run: |
git config --global user.email "${{ secrets.git_mail }}"
git config --global user.name "${{ secrets.git_name }}"
- name: Clone repo
run: |
git clone https://github.com/nxtrace/homebrew-nexttrace.git
- name: Exec scipt
run: |
cd homebrew-nexttrace
bash genFormula.sh
# - name: setup SSH keys and known_hosts
# run: |
# mkdir -p ~/.ssh
# ssh-keyscan github.com >> ~/.ssh/known_hosts
# ssh-agent -a $SSH_AUTH_SOCK > /dev/null
# ssh-add - <<< "${{ secrets.ID_RSA }}"
# env:
# SSH_AUTH_SOCK: /tmp/ssh_agent.sock
- name: Git Push
run: |
cd homebrew-nexttrace
git commit -am 'Publish a new version with Formula' || true
git remote set-url origin https://${{ secrets.gt_token }}@github.com/nxtrace/homebrew-nexttrace.git
git push
# env:
# SSH_AUTH_SOCK: /tmp/ssh_agent.sock
- run: echo "🍏 This job's status is ${{ job.status }}."

View File

@@ -21,7 +21,7 @@ jobs:
git config --global user.name "${{ secrets.git_name }}"
- name: Clone repo
run: |
git clone https://github.com/xgadget-lab/homebrew-nexttrace.git
git clone https://github.com/nxtrace/homebrew-nexttrace.git
- name: Exec scipt
run: |
cd homebrew-nexttrace
@@ -37,9 +37,9 @@ jobs:
- name: Git Push
run: |
cd homebrew-nexttrace
git commit -am 'Publish a new version with Formula'
git remote set-url origin https://${{ secrets.gt_token }}@github.com/xgadget-lab/homebrew-nexttrace.git
git push || 1
git commit -am 'Publish a new version with Formula' || true
git remote set-url origin https://${{ secrets.gt_token }}@github.com/nxtrace/homebrew-nexttrace.git
git push
# env:
# SSH_AUTH_SOCK: /tmp/ssh_agent.sock
- run: echo "🍏 This job's status is ${{ job.status }}."

42
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Test
on:
push:
branches:
- main
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
pull_request:
types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/*.yml"
workflow_dispatch:
jobs:
test:
permissions:
contents: read
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Set up Go
uses: actions/setup-go@v5
with:
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 -ldflags=-checklinkname=0 -coverprofile='coverage.out' -covermode=count ./...
- name: Test with windows
if: ${{ matrix.os == 'windows-latest' }}
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 }}"}}'

4
.gitignore vendored
View File

@@ -161,3 +161,7 @@ Network Trash Folder
Temporary Items
.apdisk
# compile target directory
dist/
NTrace-core

1
CNAME
View File

@@ -1 +0,0 @@
trace.ac

492
README.md
View File

@@ -4,30 +4,140 @@
</div>
## NextTrace Lite
<h1 align="center">
<br>NextTrace<br>
</h1>
Document Language: English | [简体中文](README_zh_CN.md)
<h4 align="center">An open source visual routing tool that pursues light weight, developed using Golang.</h4>
An open source visual routing tool that pursues light weight, developed using Golang.
---------------------------------------
NextTrace has a total of 2 versions, the Lite version focusing on lightweight and the [Enhanced version](#nexttrace-enhanced) which is more enthusiast-oriented.
<h6 align="center">HomePage: www.nxtrace.org</h6>
PS: Our Lite version does not provide OSM based geolocation visualization, we provide this parameter in the enhanced version if needed.
<p align="center">
<a href="https://github.com/nxtrace/Ntrace-V1/actions">
<img src="https://img.shields.io/github/actions/workflow/status/nxtrace/Ntrace-V1/build.yml?branch=main&style=flat-square" alt="Github Actions">
</a>
<a href="https://goreportcard.com/report/github.com/nxtrace/Ntrace-V1">
<img src="https://goreportcard.com/badge/github.com/nxtrace/Ntrace-V1?style=flat-square">
</a>
<a href="https://www.nxtrace.org/downloads">
<img src="https://img.shields.io/github/release/nxtrace/Ntrace-V1/all.svg?style=flat-square">
</a>
</p>
## IAAS Sponsor
<div style="text-align: center;">
<a href="https://dmit.io">
<img src="https://assets.nxtrace.org/dmit.svg" width="170.7" height="62.9">
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://misaka.io" >
<img src="https://assets.nxtrace.org/misaka.svg" width="170.7" height="62.9">
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://portal.saltyfish.io" >
<img src="https://assets.nxtrace.org/snapstack.svg" width="170.7" height="62.9">
</a>
</div>
We are extremely grateful to [DMIT](https://dmit.io), [Misaka](https://misaka.io) and [SnapStack](https://portal.saltyfish.io) for providing the network infrastructure that powers this project.
## How To Use
### Automated Installation
Document Language: English | [简体中文](README_zh_CN.md)
```bash
# Linux one-click install script
bash <(curl -Ls https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
⚠️ Please note: We welcome PR submissions from the community, but please submit your PRs to the [NTrace-V1](https://github.com/nxtrace/NTrace-V1) repository instead of [NTrace-core](https://github.com/nxtrace/NTrace-core) repository.<br>
Regarding the NTrace-V1 and NTrace-core repositories:<br>
Both will largely remain consistent with each other. All development work is done within the NTrace-V1 repository. The NTrace-V1 repository releases new versions first. After running stably for an undetermined period, we will synchronize that version to NTrace-core. This means that the NTrace-V1 repository serves as a "beta" or "testing" version.<br>
Please note, there are exceptions to this synchronization. If a version of NTrace-V1 encounters a serious bug, NTrace-core will skip that flawed version and synchronize directly to the next version that resolves the issue.
# macOS brew install command
brew tap xgadget-lab/nexttrace && brew install nexttrace
```
### Automated Install
- `Release` provides compiled executables for many systems and architectures, if not, you can compile it yourself.
- Some of the necessary dependencies of this project are not fully implemented in `Golang` on `Windows`, so currently `NextTrace` is not available on `Windows` platform.
* 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
* Linuxbrew's installation command
Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64)
* Deepin installation command
```shell
apt install nexttrace
```
* [x-cmd](https://www.x-cmd.com/pkg/nexttrace) installation command
```shell
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
```
* This repository's ACTIONS automatically built version (updates faster)
```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
```
* 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.
### Manual Install
* Download the precompiled executable
For users not covered by the above methods, please go directly to [Release](https://www.nxtrace.org/downloads) to download the compiled binary executable.
* `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.
### Get Started
@@ -36,53 +146,107 @@ brew tap xgadget-lab/nexttrace && brew install nexttrace
```bash
# IPv4 ICMP Trace
nexttrace 1.0.0.1
# URL
nexttrace http://example.com:8080/index.html?q=1
# Form printing (output all hops at one time, wait 20-40 seconds)
nexttrace -table 1.0.0.1
# Form printing
nexttrace --table 1.0.0.1
# An Output Easy to Parse
nexttrace --raw 1.0.0.1
nexttrace --json 1.0.0.1
# IPv4/IPv6 Resolve Only, and automatically select the first IP when there are multiple IPs
nexttrace --ipv4 g.co
nexttrace --ipv6 g.co
# IPv6 ICMP Trace
nexttrace 2606:4700:4700::1111
# Disable Path Visualization With the -M parameter
nexttrace koreacentral.blob.core.windows.net
# MapTrace URL: https://api.nxtrace.org/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
# Disable MPLS display using the --disable-mpls / -e parameter or the NEXTTRACE_DISABLEMPLS environment variable
nexttrace --disable-mpls example.com
export NEXTTRACE_DISABLEMPLS=1
```
PS: The routing visualization drawing module was written by [@tsosunchia](https://github.com/tsosunchia), and the specific code can be viewed at [tsosunchia/traceMap](https://github.com/tsosunchia/traceMap).
Note that in LeoMoeAPI 2.0, due to the addition of geographical location data, **we have deprecated the online query part of the OpenStreetMap API in the traceMap plugin and are using location information from our own database**.
The routing visualization function requires the geographical coordinates of each Hop, but third-party APIs generally do not provide this information, so this function is currently only supported when used with LeoMoeAPI.
`NextTrace` now supports quick testing, and friends who have a one-time backhaul routing test requirement can use it
```bash
# IPv4 ICMP Fast Test (Beijing + Shanghai + Guangzhou + Hangzhou) in China Telecom / Unicom / Mobile / Education Network
nexttrace -f
nexttrace --fast-trace
# You can also use TCP SYN for testing
nexttrace -f -T
nexttrace --fast-trace --tcp
# You can also quickly test through a customized IP/DOMAIN list file
nexttrace --file /path/to/your/iplist.txt
# CUSTOMIZED IP DOMAIN LIST FILE FORMAT
## One IP/DOMAIN per line + space + description information (optional)
## forExample:
## 106.37.67.1 BEIJING-TELECOM
## 240e:928:101:31a::1 BEIJING-TELECOM
## bj.10086.cn BEIJING-MOBILE
## 2409:8080:0:1::1
## 223.5.5.5
```
`NextTrace` can also use `TCP` and `UDP` protocols to perform `Traceroute` requests, but these protocols only supports `IPv4` now
`NextTrace` already supports route tracing for specified Network Devices
```bash
# Use eth0 network interface
nexttrace --dev eth0 2606:4700:4700::1111
# Use eth0 network interface's IP
# When using the network interface's IP for route tracing, note that the IP type to be traced should be the same as network interface's IP type (e.g. both IPv4)
nexttrace --source 204.98.134.56 9.9.9.9
```
`NextTrace` can also use `TCP` and `UDP` protocols to perform `Traceroute` requests, but `UDP` protocols only supports `IPv4` now
```bash
# TCP SYN Trace
nexttrace -T www.bing.com
nexttrace --tcp www.bing.com
# You can specify the port by yourself [here is 443], the default port is 80
nexttrace -T -p 443 1.0.0.1
nexttrace --tcp --port 443 2001:4860:4860::8888
# UDP Trace
nexttrace -U 1.0.0.1
nexttrace --udp 1.0.0.1
nexttrace -U -p 53 1.0.0.1
nexttrace --udp --port 53 1.0.0.1
```
`NextTrace` also supports some advanced functions, such as ttl control, concurrent probe packet count control, mode switching, etc.
```bash
# Send 2 probe packets per hop
nexttrace -q 2 www.hkix.net
nexttrace --queries 2 www.hkix.net
# No concurrent probe packets, only one probe packet is sent at a time
nexttrace -r 1 www.hkix.net
nexttrace --parallel-requests 1 www.hkix.net
# Start Trace with TTL of 5, end at TTL of 10
nexttrace -b 5 -m 10 www.decix.net
nexttrace --first 5 --max-hops 10 www.decix.net
# In addition, an ENV is provided to set whether to hide the destination IP
export NEXTTRACE_ENABLEHIDDENDSTIP=1
# Turn off the IP reverse parsing function
nexttrace -n www.bbix.net
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:
@@ -93,145 +257,247 @@ nexttrace -n www.bbix.net
# ╰AS36776 Five9 Inc.「Philippines『Metro Manila』」
# ╭╯
# ╰AS37963 Aliyun「ALIDNS.COM『ALIDNS.COM』」
nexttrace -report www.time.com.my
nexttrace --route-path www.time.com.my
# Disable color output
nexttrace --nocolor 1.1.1.1
# or use ENV
export NO_COLOR=1
```
`NextTrace` supports users to select their own IP API (currently supports: `LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`)
`NextTrace` supports users to select their own IP API (currently supports: `LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`)
```bash
# You can specify the IP database by yourself [IP.SB here], if not specified, LeoMoeAPI will be used
nexttrace -d IP.SB
## Note that the ipinfo API needs users to purchase services from ipinfo. If necessary, you can clone this project, add the token provided by ipinfo and compile it yourself
## Fill the token to: ipgeo/tokens.go
# You can specify the IP database by yourself [IP-API.com here], if not specified, LeoMoeAPI will be used
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
## Please be aware: Due to the serious abuse of IP.SB, you will often be not able to query IP data from this source
## IPAPI.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.
## 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
# The Pure-FTPd IP database defaults to using http://127.0.0.1:2060 as the query interface. To customize it, please use environment variables
export NEXTTRACE_CHUNZHENURL=http://127.0.0.1:2060
## You can use https://github.com/freshcn/qqwry to build your own Pure-FTPd IP database service
# You can also specify the default IP database by setting an environment variable
export NEXTTRACE_DATAPROVIDER=ipinfo
```
`NextTrace` supports mixed parameters
`NextTrace` supports mixed parameters and shortened parameters
```bash
Example:
nexttrace -d IPInsight -m 20 -p 443 -q 5 -r 20 -rdns 1.1.1.1
nexttrace -T -q 2 -r 1 -table -report 2001:4860:4860::8888
nexttrace --data-provider IPAPI.com --max-hops 20 --tcp --port 443 --queries 5 --no-rdns 1.1.1.1
nexttrace -tcp --queries 2 --parallel-requests 1 --table --route-path 2001:4860:4860::8888
Equivalent to:
nexttrace -d ip-api.com -m 20 -T -p 443 -q 5 -n 1.1.1.1
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.
NextTrace BackEnd is now open-source.
https://github.com/sjlleo/nexttrace-backend
All NextTrace IP geolocation `API DEMO` can refer to [here](https://github.com/xgadget-lab/nexttrace/blob/main/ipgeo/)
NextTrace `LeoMoeAPI` now utilizes the Proof of Work (POW) mechanism to prevent abuse, where NextTrace introduces the powclient library as a client-side component. Both the POW CLIENT and SERVER are open source, and everyone is welcome to use them. (Please direct any POW module-related questions to the corresponding repositories)
- [GitHub - tsosunchia/powclient: Proof of Work CLIENT for NextTrace](https://github.com/tsosunchia/powclient)
- [GitHub - tsosunchia/powserver: Proof of Work SERVER for NextTrace](https://github.com/tsosunchia/powserver)
All NextTrace IP geolocation `API DEMO` can refer to [here](https://github.com/nxtrace/NTrace-core/blob/main/ipgeo/)
### For full usage list, please refer to the usage menu
```shell
Usage of nexttrace:
'nexttrace [options] <hostname>' or 'nexttrace <hostname> [option...]'
Options:
-T Use TCP SYN for tracerouting (default port is 80)
-U Use UDP Package for tracerouting (default port is 53 in UDP)
-V Print Version
-b int
Set The Begin TTL (default 1)
-d string
Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight, IPAPI.com] (default "LeoMoeAPI")
-f One-Key Fast Traceroute
-m int
Set the max number of hops (max TTL to be reached). (default 30)
-n Disable IP Reverse DNS lookup
-p int
Set SYN Traceroute Port (default 80)
-q int
Set the number of probes per each hop. (default 3)
-r int
Set ParallelRequests number. It should be 1 when there is a multi-routing. (default 18)
-report
Route Path
-table
Output trace results as table
Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
[-F|--fast-trace] [-p|--port <integer>] [-q|--queries
<integer>] [--parallel-requests <integer>] [-m|--max-hops
<integer>] [-d|--data-provider
(Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)]
[--pow-provider (api.nxtrace.org|sakura)] [-n|--no-rdns]
[-a|--always-rdns] [-P|--route-path] [-r|--report] [--dn42]
[-o|--output] [-t|--table] [--raw] [-j|--json] [-c|--classic]
[-f|--first <integer>] [-M|--map] [-e|--disable-mpls]
[-v|--version] [-s|--source "<value>"] [-D|--dev "<value>"]
[-z|--send-time <integer>] [-i|--ttl-time <integer>]
[--timeout <integer>] [--psize <integer>]
[_positionalArg_nexttrace_32 "<value>"] [--dot-server
(dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language
(en|cn)] [--file "<value>"] [-C|--nocolor]
Arguments:
-h --help Print help information
-4 --ipv4 Use IPv4 only
-6 --ipv6 Use IPv6 only
-T --tcp Use TCP SYN for tracerouting (default port
is 80)
-U --udp Use UDP SYN for tracerouting (default port
is 33494)
-F --fast-trace One-Key Fast Trace to China ISPs
-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
1 when there is a multi-routing. Default:
18
-m --max-hops Set the max number of hops (max TTL to be
reached). Default: 30
-d --data-provider Choose IP Geograph Data Provider [IP.SB,
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
-n --no-rdns Do not resolve IP addresses to their
domain names
-a --always-rdns Always resolve IP addresses to their
domain names
-P --route-path Print traceroute hop path by ASN and
location
-r --report output using report mode
--dn42 DN42 Mode
-o --output Write trace result to file
(RealTimePrinter ONLY)
-t --table Output trace results as table
--raw An Output Easy to Parse
-j --json Output trace results as JSON
-c --classic Classic Output trace results like
BestTrace
-f --first Start from the first_ttl hop (instead from
1). Default: 1
-M --map Disable Print Trace Map
-e --disable-mpls Disable MPLS
-v --version Print version info and exit
-s --source Use source src_addr for outgoing packets
-D --dev Use the following Network Devices as the
source address in outgoing packets
-z --send-time Set how many [milliseconds] between
sending each packet.. Useful when some
routers use rate-limit for ICMP messages.
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: 50
--timeout The number of [milliseconds] to keep probe
sockets open before giving up on the
connection.. Default: 1000
--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]
-g --language Choose the language for displaying [en,
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
![NextTrace Screenshot](asset/screenshot.png)
![image](https://user-images.githubusercontent.com/13616352/216064486-5e0a4ad5-01d6-4b3c-85e9-2e6d2519dc5d.png)
## NextTrace Enhanced
![image](https://user-images.githubusercontent.com/59512455/218501311-1ceb9b79-79e6-4eb6-988a-9d38f626cdb8.png)
`NextTrace Enhanced` is an enhanced version for enthusiasts, `Enhanced` provides trace route calls in the form of Web API and a simple Looking Glass webpage with built-in visualization.
## OpenTrace
The `Enhanced` version supports many functions that the `lite` version does not have, such as the ability to customize the timeout period, and the ability to specify TTL as the starting point for route tracking, etc. For ordinary users, the `lite` version is usually enough.
`OpenTrace` is the cross-platform `GUI` version of `NextTrace` developed by @Archeb, bringing a familiar but more powerful user experience.
https://github.com/OwO-Network/nexttrace-enhanced
This software is still in the early stages of development and may have many flaws and errors. We value your feedback.
## FAQ Frequently Asked Questions
[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace)
If you encounter problems while installing or using it, we do not recommend you to choose creating an `issue` as a preference
## NEXTTRACE WEB API
Here is our recommended troubleshooting process:
`NextTraceWebApi` is a web-based server implementation of `NextTrace` in the `MTR` style, offering various deployment options including `Docker`.
[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.
The LeoMoeAPI data is subject to copyright restrictions from multiple data sources, and is only used for the purpose of displaying the geolocation of route tracing.
1. We would like to credit samleong123 for providing nodes in Malaysia, TOHUNET Looking Glass for global nodes, and Ping.sx from Misaka, where more than 80% of reliable calibration data comes from ping/mtr reports.
2. At the same time, we would like to credit isyekong for their contribution to rDNS-based calibration ideas and data. LeoMoeAPI is accelerating the development of rDNS resolution function, and has already achieved automated geolocation resolution for some backbone networks, but there are some misjudgments. We hope that NextTrace will become a One-Man ISP-friendly traceroute tool in the future, and we are working on improving the calibration of these ASN micro-backbones as much as possible.
3. In terms of development, I would like to credit missuo and zhshch for their help with Go cross-compilation, design concepts and TCP/UDP Traceroute refactoring, and tsosunchia for their support on TraceMap.
4. I would also like to credit FFEE_CO, TheresaQWQ, stydxm and others for their help. LeoMoeAPI has received a lot of support since its first release, so I would like to credit them all!
We hope you can give us as much feedback as possible on IP geolocation errors (see issue) so that it can be calibrated in the first place and others can benefit from it.
1. Check if it is already in FAQ -> [Go to Github Wiki](https://github.com/xgadget-lab/nexttrace/wiki/FAQ---%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94)
2. Suspected bug or feature suggestion -> [Go to Github Issues](https://github.com/xgadget-lab/nexttrace/issues)
## 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`.
![GoLand logo](https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png)
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" title="" alt="GoLand logo" width="331">
## Credits
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
[Gubo](https://www.gubo.org) Reliable Host Recommendation Website
[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
[IPInfo](https://ipinfo.io) Provided most of the data support for this project free of charge
[tsosunchia](https://github.com/tsosunchia)
[BGP.TOOLS](https://bgp.tools) Provided some data support for this project free of charge
[PeeringDB](https://www.peeringdb.com) Provided some data support for this project free of charge
[sjlleo](https://github.com/sjlleo) The perpetual leader, founder, and core contributors
[tsosunchia](https://github.com/tsosunchia) The project chair, infra maintainer, and core contributors
[Vincent Young](https://github.com/missuo)
[zhshch2002](https://github.com/zhshch2002)
[Sam Sam](https://github.com/samleong123)
[waiting4new](https://github.com/waiting4new)
[FFEE_CO](https://github.com/fkx4-p)
## IP Database Copyright
[bobo liu](https://github.com/fakeboboliu)
### IPv4 Database
#### China
| ISP | Type | Data Source | Proportion |
|:---------------------------:|:--------:|:--------------------:|:----------:|
| China Telecom/Unicom/Mobile | Backbone | Internet Enthusiasts | 10% |
| China Telecom/Unicom/Mobile | Local | Avon Technology | 90% |
#### WorldWide
##### Tier 1
| ISP | Type | Data Source | Proportion |
|:------:|:--------:|:---------------:|:----------:|
| Tier 1 | Backbone | IPInfo | 2% |
| Tier 1 | Backbone | Avon Technology | 3% |
| Tier 1 | Backbone | IPInSight | 5% |
| Tier 1 | Local | IPInSight | 90% |
##### General
| ISP | Type | Data Source | Proportion |
|:-------:|:--------:|:-----------:|:----------:|
| General | Backbone | IPInSight | 5% |
| General | Local | IPInSight | 95% |
### IPv6 Database
| ISP | Type | Data Source | Proportion |
|:---:|:----:|:----------------:|:----------:|
| All | All | IP2Location Lite | 100% |
This product includes IP2Location LITE data available from <a href="https://lite.ip2location.com">https://lite.ip2location.com</a>.
[YekongTAT](https://github.com/isyekong)
### Others
Although other third-party APIs are integrated in this project, please refer to the official website of the third-party APIs for specific TOS and AUP. If you encounter IP data errors, please contact them directly to correct them.
For feedback related to corrections about IP information, we currently have two channels available:
>- [IP 错误报告汇总帖](https://github.com/orgs/nxtrace/discussions/222) in the GITHUB ISSUES section of this project (Recommended)
>- This project's dedicated correction email: `correction@nxtrace.org` (Please note that this email is only for correcting IP-related information. For other feedback, please submit an ISSUE)
How to obtain the freshly baked binary executable of the latest commit?
> Please go to the most recent [Build & Release](https://github.com/nxtrace/Ntrace-V1/actions/workflows/build.yml) workflow in GitHub Actions.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=xgadget-lab/nexttrace&type=Date)](https://star-history.com/#xgadget-lab/nexttrace&Date)
[![Star History Chart](https://api.star-history.com/svg?repos=nxtrace/NTrace-core&type=Date)](https://star-history.com/#nxtrace/NTrace-core&Date)

View File

@@ -4,31 +4,148 @@
</div>
## NextTrace Lite
<h1 align="center">
<br>NextTrace<br>
</h1>
一款追求轻量的开源可视化路由跟踪工具,使用 Golang 开发。
NextTrace 一共有2个版本专注于轻量的 Lite 版本以及更面向发烧友的 [Enhanced 版本](#nexttrace-enhanced)。
<h4 align="center">一款追求轻量化的开源可视化路由跟踪工具。</h4>
PS: Lite 版本追求轻量化,并不提供基于高德地图 / OpenStreetMap 的路由可视化功能,如有需要,请使用 Enhanced 版本。
---------------------------------------
<h6 align="center">主页www.nxtrace.org</h6>
<p align="center">
<a href="https://github.com/nxtrace/Ntrace-V1/actions">
<img src="https://img.shields.io/github/actions/workflow/status/nxtrace/Ntrace-V1/build.yml?branch=main&style=flat-square" alt="Github Actions">
</a>
<a href="https://goreportcard.com/report/github.com/nxtrace/Ntrace-V1">
<img src="https://goreportcard.com/badge/github.com/nxtrace/Ntrace-V1?style=flat-square">
</a>
<a href="https://www.nxtrace.org/downloads">
<img src="https://img.shields.io/github/release/nxtrace/Ntrace-V1/all.svg?style=flat-square">
</a>
</p>
## IAAS Sponsor
<div style="text-align: center;">
<a href="https://dmit.io">
<img src="https://assets.nxtrace.org/dmit.svg" width="170.7" height="62.9">
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://misaka.io" >
<img src="https://assets.nxtrace.org/misaka.svg" width="170.7" height="62.9">
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://portal.saltyfish.io" >
<img src="https://assets.nxtrace.org/snapstack.svg" width="170.7" height="62.9">
</a>
</div>
我们非常感谢 [DMIT](https://dmit.io)、 [Misaka](https://misaka.io) 和 [SnapStack](https://portal.saltyfish.io) 提供了支持本项目所需的网络基础设施。
## How To Use
Document Language: [English](README.md) | 简体中文
⚠️ 请注意我们欢迎来自社区的PR提交但是请将您的PR提交至 [NTrace-V1](https://github.com/nxtrace/NTrace-V1) 仓库,而不是 [NTrace-core](https://github.com/nxtrace/NTrace-core) 仓库。<br>
关于NTrace-V1和NTrace-core两个仓库的说明<br>
二者将大体上保持一致。所有的开发工作均在NTrace-V1仓库中进行。NTrace-V1仓库首先发布新版本在稳定运行一段时间后时长不定我们会把版本同步至NTrace-core。这意味着NTrace-V1仓库充当了一个“测试版”的角色。<br>
请注意版本同步也存在例外。如果NTrace-V1的某个版本出现了严重的bugNTrace-core会跳过这一有缺陷的版本直接同步到下一个修复了该问题的版本。
### Before Using
使用 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),在了解您自己的对数据精准度需求以后再进行抉择。
### Automated Install
```bash
# Linux 一键安装脚本
bash <(curl -Ls https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
* Linux
* 一键安装脚本
```shell
curl nxtrace.org/nt | bash
```
* 从 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 维护
# GHPROXY 镜像(国内使用)
bash <(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
* Arch Linux AUR 安装命令
* 直接下载bin包(仅支持amd64)
```shell
yay -S nexttrace-bin
```
* 从源码构建(仅支持amd64)
```shell
yay -S nexttrace
```
* AUR 的构建分别由 ouuan, huyz 维护
# macOS brew 安装命令
brew tap xgadget-lab/nexttrace && brew install nexttrace
```
* Linuxbrew 安装命令
- `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
- 一些本项目的必要依赖在`Windows``Golang`底层实现不完全,所以目前`NextTrace``Windows`平台不可用。
同macOS Homebrew安装方法(homebrew-core版仅支持amd64)
* Deepin 安装命令
```shell
apt install nexttrace
```
* [x-cmd](https://cn.x-cmd.com/pkg/nexttrace) 安装命令
```shell
x env use nexttrace
```
* Termux 安装命令
```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自动构建版本
* Windows
* Windows WinGet 安装命令
* WinGet 版
```powershell
winget install nexttrace
```
* WinGet 构建由 Dragon1573 维护
* Windows Scoop 安装命令
* scoop-extras 版
```powershell
scoop bucket add extras && scoop install extras/nexttrace
```
* scoop-extra 由 soenggam 维护
请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。
### Manual Install
* 下载预编译的可执行程序
对于以上方法没有涵盖的用户,请直接前往 [Release](https://www.nxtrace.org/downloads) 下载编译后的二进制可执行文件。
* `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
* 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。
### Get Started
@@ -37,53 +154,110 @@ brew tap xgadget-lab/nexttrace && brew install nexttrace
```bash
# IPv4 ICMP Trace
nexttrace 1.0.0.1
# URL
nexttrace http://example.com:8080/index.html?q=1
# 表格打印一次性输出全部跳数需等待20-40秒
nexttrace -table 1.0.0.1
# 表格打印,使用 --table / -t 参数,将实时显示结果
nexttrace --table 1.0.0.1
# 一个方便供机器读取转化的模式
nexttrace --raw 1.0.0.1
nexttrace --json 1.0.0.1
# 只进行IPv4/IPv6解析且当多个IP时自动选择第一个IP
nexttrace --ipv4 g.co
nexttrace --ipv6 g.co
# IPv6 ICMP Trace
nexttrace 2606:4700:4700::1111
# 禁用路径可视化 使用 --map / -M 参数
nexttrace koreacentral.blob.core.windows.net
# MapTrace URL: https://api.nxtrace.org/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
# 禁用MPLS显示 使用 --disable-mpls / -e 参数 或 NEXTTRACE_DISABLEMPLS 环境变量
nexttrace --disable-mpls example.com
export NEXTTRACE_DISABLEMPLS=1
```
PS: 路由可视化的绘制模块由 [@tsosunchia](https://github.com/tsosunchia) 同学编写,具体代码可在 [tsosunchia/traceMap](https://github.com/tsosunchia/traceMap) 查看
需要注意的是,在 LeoMoeAPI 2.0 中,由于新增了了地理位置数据,**我们已经弃用 traceMap 插件中 OpenStreetMap API 的在线查询的部分,并且使用自己数据库内的位置信息**。
路由可视化功能因为需要每个 Hop 的地理位置坐标,而第三方 API 通常不提供此类信息,所以此功能目前只支持搭配 LeoMoeAPI 使用。
`NextTrace` 现已经支持快速测试,有一次性测试回程路由需求的朋友可以使用
```bash
# 北上广(电信+联通+移动+教育网IPv4 ICMP 快速测试
nexttrace -f
# 北上广(电信+联通+移动+教育网IPv4 / IPv6 ICMP 快速测试
nexttrace --fast-trace
# 也可以使用 TCP SYN 而非 ICMP 进行测试
nexttrace -f -T
nexttrace --fast-trace --tcp
# 也可以通过自定义的IP/DOMAIN列表文件进行快速测试
nexttrace --file /path/to/your/iplist.txt
# 自定义的IP/DOMAIN列表文件格式
## 一行一个IP/DOMAIN + 空格 + 描述信息(可选)
## 例如:
## 106.37.67.1 北京电信
## 240e:928:101:31a::1 北京电信
## bj.10086.cn 北京移动
## 2409:8080:0:1::1
## 223.5.5.5
```
`NextTrace` 也可以使用`TCP``UDP`协议发起`Traceroute`请求,不过目前只支持`IPv4`
`NextTrace` 已支持指定网卡进行路由跟踪
```bash
# 请注意 Lite 版本此参数不能和快速测试联用,如有需要请使用 enhanced 版本
# 使用 eth0 网卡
nexttrace --dev eth0 2606:4700:4700::1111
# 使用 eth0 网卡IP
# 网卡 IP 可以使用 ip a 或者 ifconfig 获取
# 使用网卡IP进行路由跟踪时需要注意跟踪的IP类型应该和网卡IP类型一致如都为 IPv4
nexttrace --source 204.98.134.56 9.9.9.9
```
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求,不过目前`UDP`只支持`IPv4`
```bash
# TCP SYN Trace
nexttrace -T www.bing.com
nexttrace --tcp www.bing.com
# 可以自行指定端口[此处为443]默认80端口
nexttrace -T -p 443 1.0.0.1
nexttrace --tcp --port 443 2001:4860:4860::8888
# UDP Trace
nexttrace -U 1.0.0.1
nexttrace --udp 1.0.0.1
nexttrace -U -p 53 1.0.0.1
# 可以自行指定端口[此处为5353]默认33494端口
nexttrace --udp --port 5353 1.0.0.1
```
`NextTrace`也同样支持一些进阶功能,如 TTL 控制、并发数控制、模式切换等
```bash
# 每一跳发送2个探测包
nexttrace -q 2 www.hkix.net
nexttrace --queries 2 www.hkix.net
# 无并发,每次只发送一个探测包
nexttrace -r 1 www.hkix.net
nexttrace --parallel-requests 1 www.hkix.net
# 从TTL为5开始发送探测包直到TTL为10结束
nexttrace -b 5 -m 10 www.decix.net
nexttrace --first 5 --max-hops 10 www.decix.net
# 此外还提供了一个ENV可以设置是否隐匿目的IP
export NEXTTRACE_ENABLEHIDDENDSTIP=1
# 关闭IP反向解析功能
nexttrace -n www.bbix.net
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图示例
@@ -94,138 +268,293 @@ nexttrace -n www.bbix.net
# ╰AS36776 Five9 Inc.「Philippines『Metro Manila』」
# ╭╯
# ╰AS37963 阿里云「ALIDNS.COM『ALIDNS.COM』」
nexttrace -report www.time.com.my
nexttrace --route-path www.time.com.my
# 禁止色彩输出
nexttrace --nocolor 1.1.1.1
# 或者使用环境变量
export NO_COLOR=1
```
`NextTrace`支持用户自主选择 IP 数据库(目前支持:`LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`
`NextTrace`支持用户自主选择 IP 数据库(目前支持:`LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`)
```bash
# 可以自行指定IP数据库[此处为IP.SB]不指定则默认为LeoMoeAPI
nexttrace -d IP.SB
## 特别的其中 ipinfo API 需要从ipinfo自行购买服务如有需要可以clone本项目添加其提供的token自行编译
# 可以自行指定IP数据库[此处为IP-API.com]不指定则默认为LeoMoeAPI
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)
## 默认搜索用户当前路径、程序所在路径、和 FHS 路径Unix-like
## 如果需要自定义路径,请设置环境变量
export NEXTTRACE_IPINFOLOCALPATH=/xxx/yyy.mmdb
## 对于离线库 Ip2region 可NextTrace自动下载也可自行下载并命名为 ip2region.db
## 另外由于IP.SB被滥用比较严重会经常出现无法查询的问题请知悉。
## IPAPI.com限制调用较为严格如有查询不到的情况请几分钟后再试。
## 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数据库服务
# 也可以通过设置环境变量来指定默认IP数据库
export NEXTTRACE_DATAPROVIDER=ipinfo
```
`NextTrace`支持参数混合使用
`NextTrace`支持使用混合参数和简略参数
```bash
Example:
nexttrace -d IPInsight -m 20 -p 443 -q 5 -r 20 -rdns 1.1.1.1
nexttrace -T -q 2 -r 1 -table -report 2001:4860:4860::8888
nexttrace --data-provider ip-api.com --max-hops 20 --tcp --port 443 --queries 5 --no-rdns 1.1.1.1
nexttrace -tcp --queries 2 --parallel-requests 1 --table --route-path 2001:4860:4860::8888
Equivalent to:
nexttrace -d ip-api.com -m 20 -T -p 443 -q 5 -n 1.1.1.1
nexttrace -T -q 2 --parallel-requests 1 -t -P 2001:4860:4860::8888
```
### IP 数据库
✨NextTrace `LeoMoeAPI` 的后端也开源啦
[GitHub - sjlleo/nexttrace-backend: NextTrace BackEnd](https://github.com/sjlleo/nexttrace-backend)
NextTrace 所有的的 IP 地理位置`API DEMO`可以参考[这里](https://github.com/xgadget-lab/nexttrace/blob/main/ipgeo/)
### 全部用法详见 Usage 菜单
```shell
Usage of nexttrace:
'nexttrace [options] <hostname>' or 'nexttrace <hostname> [option...]'
Options:
-T Use TCP SYN for tracerouting (default port is 80)
-U Use UDP Package for tracerouting (default port is 53 in UDP)
-V Print Version
-b int
Set The Begin TTL (default 1)
-d string
Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight, IPAPI.com] (default "LeoMoeAPI")
-f One-Key Fast Traceroute
-m int
Set the max number of hops (max TTL to be reached). (default 30)
-n Disable IP Reverse DNS lookup
-p int
Set SYN Traceroute Port (default 80)
-q int
Set the number of probes per each hop. (default 3)
-r int
Set ParallelRequests number. It should be 1 when there is a multi-routing. (default 18)
-report
Route Path
-table
Output trace results as table
Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
[-F|--fast-trace] [-p|--port <integer>] [-q|--queries
<integer>] [--parallel-requests <integer>] [-m|--max-hops
<integer>] [-d|--data-provider
(Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)]
[--pow-provider (api.nxtrace.org|sakura)] [-n|--no-rdns]
[-a|--always-rdns] [-P|--route-path] [-r|--report] [--dn42]
[-o|--output] [-t|--table] [--raw] [-j|--json] [-c|--classic]
[-f|--first <integer>] [-M|--map] [-e|--disable-mpls]
[-v|--version] [-s|--source "<value>"] [-D|--dev "<value>"]
[-z|--send-time <integer>] [-i|--ttl-time <integer>]
[--timeout <integer>] [--psize <integer>]
[_positionalArg_nexttrace_32 "<value>"] [--dot-server
(dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language
(en|cn)] [--file "<value>"] [-C|--nocolor]
Arguments:
-h --help Print help information
-4 --ipv4 Use IPv4 only
-6 --ipv6 Use IPv6 only
-T --tcp Use TCP SYN for tracerouting (default port
is 80)
-U --udp Use UDP SYN for tracerouting (default port
is 33494)
-F --fast-trace One-Key Fast Trace to China ISPs
-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
1 when there is a multi-routing. Default:
18
-m --max-hops Set the max number of hops (max TTL to be
reached). Default: 30
-d --data-provider Choose IP Geograph Data Provider [IP.SB,
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
-n --no-rdns Do not resolve IP addresses to their
domain names
-a --always-rdns Always resolve IP addresses to their
domain names
-P --route-path Print traceroute hop path by ASN and
location
-r --report output using report mode
--dn42 DN42 Mode
-o --output Write trace result to file
(RealTimePrinter ONLY)
-t --table Output trace results as table
--raw An Output Easy to Parse
-j --json Output trace results as JSON
-c --classic Classic Output trace results like
BestTrace
-f --first Start from the first_ttl hop (instead from
1). Default: 1
-M --map Disable Print Trace Map
-e --disable-mpls Disable MPLS
-v --version Print version info and exit
-s --source Use source src_addr for outgoing packets
-D --dev Use the following Network Devices as the
source address in outgoing packets
-z --send-time Set how many [milliseconds] between
sending each packet.. Useful when some
routers use rate-limit for ICMP messages.
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: 50
--timeout The number of [milliseconds] to keep probe
sockets open before giving up on the
connection.. Default: 1000
--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]
-g --language Choose the language for displaying [en,
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
```
## 项目截图
![NextTrace Screenshot](asset/screenshot.png)
![image](https://user-images.githubusercontent.com/59512455/218505939-287727ce-7207-43c4-8e31-fcda7df0b872.png)
## NextTrace Enhanced
![image](https://user-images.githubusercontent.com/59512455/218504874-06b9fa4b-48e0-420a-a195-08a1200d65a7.png)
`NextTrace Enhanced` 是面向发烧友的增强版,`Enhanced`提供Web API形式的路由跟踪调用以及一个简单的自带可视化的Looking Glass网页。
## 第三方 IP 数据库 API 开发接口
`Enhanced` 版本支持很多`lite`版本没有的功能如能够自定义设置超时时间也能指定TTL作为起点进行路由跟踪等对于普通用户来说通常`lite`版本已经足够完成大部分需要。
NextTrace 所有的的 IP 地理位置 `API DEMO` 可以参考[这里](https://github.com/nxtrace/NTrace-core/blob/main/ipgeo/)
https://github.com/OwO-Network/nexttrace-enhanced
你可以在这里添加你自己的 API 接口,为了 NextTrace 能够正确显示你接口中的内容,请参考 `leo.go` 中所需要的信息
## FAQ 常见问题
✨NextTrace `LeoMoeAPI` 的后端 Demo
如果你在安装或者使用的时候遇到了问题,我们建议你不要把新建一个 `issue` 作为首选项
[GitHub - sjlleo/nexttrace-backend: NextTrace BackEnd](https://github.com/sjlleo/nexttrace-backend)
以下是我们推荐的排错流程:
NextTrace `LeoMoeAPI`现已使用Proof of Work(POW)机制来防止滥用其中NextTrace作为客户端引入了powclient库POW CLIENT/SERVER均已开源欢迎大家使用。(POW模块相关问题请发到对应的仓库)
- [GitHub - tsosunchia/powclient: Proof of Work CLIENT for NextTrace](https://github.com/tsosunchia/powclient)
- [GitHub - tsosunchia/powserver: Proof of Work SERVER for NextTrace](https://github.com/tsosunchia/powserver)
1. 查看是否为常见问题 -> [前往 Github Wiki](https://github.com/xgadget-lab/nexttrace/wiki/FAQ---%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94)
2. 疑似 BUG、或者功能建议 -> [前往 Github Issues](https://github.com/xgadget-lab/nexttrace/issues)
对于中国大陆用户,可以使用 [Nya Labs](https://natfrp.com) 提供的位于大陆的POW服务器优化访问速度
```shell
#使用方法任选其一
#1. 在环境变量中设置
export NEXTTRACE_POWPROVIDER=sakura
#2. 在命令行中设置
nexttrace --pow-provider sakura
```
## Thanks
## OpenTrace
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
`OpenTrace`是 @Archeb 开发的`NextTrace`的跨平台`GUI`版本,带来您熟悉但更强大的用户体验。
该软件仍然处于早期开发阶段,可能存在许多缺陷和错误,需要您宝贵的使用反馈。
[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace)
[tsosunchia](https://github.com/tsosunchia)
## NEXTTRACE WEB API
`NextTraceWebApi`是一个`MTR`风格的`NextTrace`网页版服务端实现,提供了包括`Docker`在内多种部署方式。
[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) 无偿提供了本项目的一些数据支持
[PeeringDB](https://www.peeringdb.com) 无偿提供了本项目的一些数据支持
[sjlleo](https://github.com/sjlleo) 项目永远的领导者、创始人及核心贡献者
[tsosunchia](https://github.com/tsosunchia) 项目现任管理、基础设施运维及核心贡献者
[Vincent Young](https://github.com/missuo)
[zhshch2002](https://github.com/zhshch2002)
[Sam Sam](https://github.com/samleong123)
[waiting4new](https://github.com/waiting4new)
[FFEE_CO](https://github.com/fkx4-p)
## IP Database Copyright
[bobo liu](https://github.com/fakeboboliu)
### IPv4 Database
[YekongTAT](https://github.com/isyekong)
#### China
| ISP | 类型 | 数据源 | 占比 |
|:--------:|:---:|:-----:|:---:|
| 电信/联通/移动 | 骨干网 | 网络爱好者 | 10% |
| 电信/联通/移动 | 城域网 | 埃文科技 | 90% |
- 参与骨干网维护的朋友都是网络爱好者群体,尽管我们多名志愿者通过自己的网络进行了大量的勘测,但是由于信息不足,依旧存在很多错误。
- 对于更高精度的朋友我们依旧强烈推荐IPIP.NET他们开发的Besttrace是目前质量最好的路由可视化软件我们多数爱好者能有今天这样的骨干网初步认知都是归功于他们在此特表感谢。
#### WorldWide
##### Tier 01
| ISP | 类型 | 数据源 | 占比 |
|:-------:|:---:|:---------:|:---:|
| Tier-01 | 骨干网 | IPInfo | 2% |
| Tier-01 | 骨干网 | 埃文科技 | 3% |
| Tier-01 | 骨干网 | IPInSight | 5% |
| Tier-01 | 城域网 | IPInSight | 90% |
##### Other ISP
| ISP | 类型 | 数据源 | 占比 |
|:------:|:---:|:---------:|:---:|
| Others | 骨干网 | IPInSight | 5% |
| Others | 城域网 | IPInSight | 95% |
### IPv6 Database
| ISP | 类型 | 数据源 | 占比 |
|:---:|:---:|:----------------:|:----:|
| All | 全部 | IP2Location Lite | 100% |
This product includes IP2Location LITE data available from <a href="https://lite.ip2location.com">https://lite.ip2location.com</a>.
### Others
## Others
其他第三方 API 尽管集成在本项目内,但是具体的 TOS 以及 AUP请详见第三方 API 官网。如遇到 IP 数据错误,也请直接联系他们纠错。
如何获取最新commit的新鲜出炉的二进制可执行文件
>请前往GitHub Actions中最新一次 [Build & Release](https://github.com/nxtrace/Ntrace-V1/actions/workflows/build.yml) workflow.
## IP 数据以及精准度说明
对于IP相关信息的纠错反馈我们目前开放了两个渠道
>- 本项目的GITHUB ISSUES区中的[IP 错误报告汇总帖](https://github.com/orgs/nxtrace/discussions/222)
>- 本项目的纠错专用邮箱: `correction@nxtrace.org` 请注意此邮箱仅供IP相关信息纠错专用其他反馈请发送ISSUE
NextTrace 有多个数据源可以选择,目前默认使用的 LeoMoeAPI 为我们项目维护的数据源。
该项目由 OwO Network 的 [Missuo](https://github.com/missuo) && [Leo](https://github.com/sjlleo) 发起,由 [Zhshch](https://github.com/zhshch2002/) 完成最早期架构的编写和指导,后由 Leo 完成了大部分开发工作,现主要交由 [tsosunchia](https://github.com/tsosunchia) 完成后续的二开和维护工作。
LeoMoeAPI 是 [Leo](https://github.com/sjlleo) 的作品,归属于 Leo Network由 [Leo](https://github.com/sjlleo) 完成整套后端 API 编写,该接口未经允许不可用于任何第三方用途。
LeoMoeAPI 早期数据主要来自 IPInsight、IPInfo随着项目发展越来越多的志愿者参与进了这个项目。目前 LeoMoeAPI 有近一半的数据是社区提供的,而另外一半主要来自于包含 IPInfo、IPData、BigDataCloud、IPGeoLocation 在内的多个第三方数据。
LeoMoeAPI 的骨干网数据有近 70% 是社区自发反馈又或者是项目组成员校准的,这给本项目的路由跟踪基础功能带来了一定的保证,但是全球骨干网的体量庞大,我们并无能力如 IPIP 等商业公司拥有海量监测节点,这使得 LeoMoeAPI 的数据精准度无法和形如 BestTraceIPIP相提并论。
LeoMoeAPI 已经尽力校准了比较常见的骨干网路由,这部分在测试的时候经常会命中,但是如果遇到封闭型 ISP 的路由,大概率可以遇到错误,此类数据不仅是我们,哪怕 IPInsight、IPInfo 也无法正确定位,目前只有 IPIP 能够标记正确,如对此类数据的精确性有着非常高的要求,请务必使用 BestTrace 作为首选。
我们不保证我们的数据一定会及时更新,也不保证数据的精确性,我们希望您在发现数据错误的时候可以前往 issue 页面提交错误报告,谢谢。
当您使用 LeoMoeAPI 即视为您已经完全了解 NextTrace LeoMoeAPI 的数据精确性,并且同意如果您引用 LeoMoeAPI 其中的数据从而引发的一切问题,均由您自己承担。
## DN42 模式使用说明
使用这个模式需要您配置 2 个文件,分别是 geofeed.csv 以及 ptr.csv
当您初次运行 DN42 模式NT 会为您生成 nt_config.yaml 文件,您可以自定义 2 个文件的存放位置,默认应该存放在 NT 的运行目录下
### GeoFeed
对于 geofeed.csv 来说,格式如下:
```
IP_CDIR,LtdCode,ISO3166-2,CityName,ASN,IPWhois
```
比如,您可以这么写:
```
58.215.96.0/20,CN,CN-JS,Wuxi,23650,CHINANET-JS
```
如果您有一个大段作为骨干网使用,您也可以不写地理位置信息,如下:
```
202.97.0.0/16,,,4134,CHINANET-BACKBONE
```
### PTR
对于 ptr.csv 来说,格式如下:
```
IATA_CODE,LtdCode,RegionName,CityName
```
比如对于美国洛杉矶,您可以这么写
```
LAX,US,California,Los Anegles
```
需要注意的是NextTrace 支持自动匹配 CSV 中的城市名,如果您的 PTR 记录中有 `losangeles`,您可以只添加上面一条记录就可以正常识别并读取。
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=nxtrace/NTrace-core&type=Date)](https://star-history.com/#nxtrace/NTrace-core&Date)

BIN
asset/nexttrace021.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

410
cmd/cmd.go Normal file
View File

@@ -0,0 +1,410 @@
package cmd
import (
"encoding/json"
"fmt"
"log"
"net"
"os"
"os/signal"
"runtime"
"strings"
"time"
"github.com/fatih/color"
"github.com/akamensky/argparse"
"github.com/nxtrace/NTrace-core/config"
fastTrace "github.com/nxtrace/NTrace-core/fast_trace"
"github.com/nxtrace/NTrace-core/ipgeo"
"github.com/nxtrace/NTrace-core/printer"
"github.com/nxtrace/NTrace-core/reporter"
"github.com/nxtrace/NTrace-core/trace"
"github.com/nxtrace/NTrace-core/tracelog"
"github.com/nxtrace/NTrace-core/tracemap"
"github.com/nxtrace/NTrace-core/util"
"github.com/nxtrace/NTrace-core/wshandle"
"github.com/syndtr/gocapability/capability"
)
func Excute() {
parser := argparse.NewParser("nexttrace", "An open source visual route tracking CLI tool")
// Create string flag
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 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. 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)"})
dataOrigin := parser.Selector("d", "data-provider", []string{"Ip2region", "ip2region", "IP.SB", "ip.sb", "IPInfo", "ipinfo", "IPInsight", "ipinsight", "IPAPI.com", "ip-api.com", "IPInfoLocal", "ipinfolocal", "chunzhen", "LeoMoeAPI", "leomoeapi", "disable-geoip"}, &argparse.Options{Default: "LeoMoeAPI",
Help: "Choose IP Geograph Data Provider [IP.SB, IPInfo, IPInsight, IP-API.com, Ip2region, IPInfoLocal, CHUNZHEN, disable-geoip]"})
powProvider := parser.Selector("", "pow-provider", []string{"api.nxtrace.org", "sakura"}, &argparse.Options{Default: "api.nxtrace.org",
Help: "Choose PoW Provider [api.nxtrace.org, sakura] For China mainland users, please use sakura"})
noRdns := parser.Flag("n", "no-rdns", &argparse.Options{Help: "Do not resolve IP addresses to their domain names"})
alwaysRdns := parser.Flag("a", "always-rdns", &argparse.Options{Help: "Always resolve IP addresses to their domain names"})
routePath := parser.Flag("P", "route-path", &argparse.Options{Help: "Print traceroute hop path by ASN and location"})
report := parser.Flag("r", "report", &argparse.Options{Help: "output using report mode"})
dn42 := parser.Flag("", "dn42", &argparse.Options{Help: "DN42 Mode"})
output := parser.Flag("o", "output", &argparse.Options{Help: "Write trace result to file (RealTimePrinter ONLY)"})
tablePrint := parser.Flag("t", "table", &argparse.Options{Help: "Output trace results as table"})
rawPrint := parser.Flag("", "raw", &argparse.Options{Help: "An Output Easy to Parse"})
jsonPrint := parser.Flag("j", "json", &argparse.Options{Help: "Output trace results as JSON"})
classicPrint := parser.Flag("c", "classic", &argparse.Options{Help: "Classic Output trace results like BestTrace"})
beginHop := parser.Int("f", "first", &argparse.Options{Default: 1, Help: "Start from the first_ttl hop (instead from 1)"})
disableMaptrace := parser.Flag("M", "map", &argparse.Options{Help: "Disable Print Trace Map"})
disableMPLS := parser.Flag("e", "disable-mpls", &argparse.Options{Help: "Disable MPLS"})
ver := parser.Flag("v", "version", &argparse.Options{Help: "Print version info and exit"})
srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr for outgoing packets"})
srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the following Network Devices as the source address in outgoing packets"})
//router := parser.Flag("R", "route", &argparse.Options{Help: "Show Routing Table [Provided By BGP.Tools]"})
packetInterval := parser.Int("z", "send-time", &argparse.Options{Default: 50, Help: "Set how many [milliseconds] between sending each packet.. Useful when some routers use rate-limit for ICMP messages"})
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 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]"})
lang := parser.Selector("g", "language", []string{"en", "cn"}, &argparse.Options{Default: "cn",
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 {
// In case of error print error and print usage
// This can also be done by passing -h or --help flags
fmt.Print(parser.Usage(err))
return
}
if *nocolor {
color.NoColor = true
} else {
color.NoColor = false
}
if !*jsonPrint {
printer.Version()
}
if *ver {
printer.CopyRight()
os.Exit(0)
}
if !*tcp && *port == 80 {
*port = 33494
}
domain := *str
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,
AlwaysWaitRDNS: *alwaysRdns,
Lang: *lang,
PktSize: *packetSize,
Timeout: time.Duration(*timeout) * time.Millisecond,
File: *file,
DontFragment: *dontFragment,
Dot: *dot,
}
fastTrace.FastTest(m, *output, paramsFastTrace)
if *output {
fmt.Println("您的追踪日志已经存放在 /tmp/trace.log 中")
}
os.Exit(0)
}
// DOMAIN处理开始
if domain == "" {
fmt.Print(parser.Usage(err))
return
}
if strings.Contains(domain, "/") {
domain = "n" + domain
parts := strings.Split(domain, "/")
if len(parts) < 3 {
fmt.Println("Invalid input")
return
}
domain = parts[2]
}
if strings.Contains(domain, "]") {
domain = strings.Split(strings.Split(domain, "]")[0], "[")[1]
} else if strings.Contains(domain, ":") {
if strings.Count(domain, ":") == 1 {
domain = strings.Split(domain, ":")[0]
}
}
// DOMAIN处理结束
capabilitiesCheck()
// return
var ip net.IP
if runtime.GOOS == "windows" && (*tcp || *udp) {
fmt.Println("NextTrace 基于 Windows 的路由跟踪还在早期开发阶段目前还存在诸多问题TCP/UDP SYN 包请求可能不能正常运行")
}
if *dn42 {
// 初始化配置
config.InitConfig()
*dataOrigin = "DN42"
*disableMaptrace = true
}
/**
* 此处若使用goroutine同时运行ws的建立与nslookup
* 会导致第一跳的IP信息无法获取原因不明。
*/
//var wg sync.WaitGroup
//wg.Add(2)
//
//go func() {
// defer wg.Done()
if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
val, ok := os.LookupEnv("NEXTTRACE_DATAPROVIDER")
if strings.ToUpper(*powProvider) != "API.NXTRACE.ORG" {
util.PowProviderParam = *powProvider
}
if ok {
*dataOrigin = val
} else {
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
if w.Conn != nil {
w.Conn.Close()
}
}()
}
}
//}()
//
//go func() {
// defer wg.Done()
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)
}
if err != nil {
//fmt.Println(err)
//os.Exit(1)
panic(err)
}
//}()
//
//wg.Wait()
if *srcDev != "" {
dev, _ := net.InterfaceByName(*srcDev)
if addrs, err := dev.Addrs(); err == nil {
for _, addr := range addrs {
if (addr.(*net.IPNet).IP.To4() == nil) == (ip.To4() == nil) {
*srcAddr = addr.(*net.IPNet).IP.String()
// 检查是否是内网IP
if !(net.ParseIP(*srcAddr).IsPrivate() ||
net.ParseIP(*srcAddr).IsLoopback() ||
net.ParseIP(*srcAddr).IsLinkLocalUnicast() ||
net.ParseIP(*srcAddr).IsLinkLocalMulticast()) {
// 若不是则跳出
break
}
}
}
}
}
if !*jsonPrint {
printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize, *srcAddr, string(m))
}
util.DestIP = ip.String()
var conf = trace.Config{
DN42: *dn42,
SrcAddr: *srcAddr,
BeginHop: *beginHop,
DestIP: ip,
DestPort: *port,
MaxHops: *maxHops,
PacketInterval: *packetInterval,
TTLInterval: *ttlInterval,
NumMeasurements: *numMeasurements,
ParallelRequests: *parallelRequests,
Lang: *lang,
RDns: !*noRdns,
AlwaysWaitRDNS: *alwaysRdns,
IPGeoSource: ipgeo.GetSource(*dataOrigin),
Timeout: time.Duration(*timeout) * time.Millisecond,
PktSize: *packetSize,
DontFragment: *dontFragment,
}
// 暂时弃用
router := new(bool)
*router = false
if !*tablePrint {
if *classicPrint {
conf.RealtimePrinter = printer.ClassicPrinter
} else if *rawPrint {
conf.RealtimePrinter = printer.EasyPrinter
} else {
if *output {
conf.RealtimePrinter = tracelog.RealtimePrinter
} else if *router {
conf.RealtimePrinter = printer.RealtimePrinterWithRouter
fmt.Println("路由表数据源由 BGP.Tools 提供,在此特表感谢")
} else {
conf.RealtimePrinter = printer.RealtimePrinter
}
}
} else {
if !*report {
conf.AsyncPrinter = printer.TracerouteTablePrinter
}
}
if *jsonPrint {
conf.RealtimePrinter = nil
conf.AsyncPrinter = nil
}
if util.Uninterrupted != "" && *rawPrint {
for {
_, err := trace.Traceroute(m, conf)
if err != nil {
fmt.Println(err)
}
}
}
if *disableMPLS {
util.DisableMPLS = "1"
}
res, err := trace.Traceroute(m, conf)
if err != nil {
log.Fatalln(err)
}
if *tablePrint {
printer.TracerouteTablePrinter(res)
}
if *routePath {
r := reporter.New(res, ip.String())
r.Print()
}
r, err := json.Marshal(res)
if err != nil {
fmt.Println(err)
return
}
if !*disableMaptrace &&
(util.StringInSlice(strings.ToUpper(*dataOrigin), []string{"LEOMOEAPI", "IPINFO", "IPINFO", "IP-API.COM", "IPAPI.COM"})) {
url, err := tracemap.GetMapUrl(string(r))
if err != nil {
log.Fatalln(err)
}
res.TraceMapUrl = url
if !*jsonPrint {
tracemap.PrintMapUrl(url)
}
}
r, err = json.Marshal(res)
if err != nil {
fmt.Println(err)
return
}
if *jsonPrint {
fmt.Println(string(r))
}
}
func capabilitiesCheck() {
// Windows 判断放在前面,防止遇到一些奇奇怪怪的问题
if runtime.GOOS == "windows" {
// Running on Windows, skip checking capabilities
return
}
uid := os.Getuid()
if uid == 0 {
// Running as root, skip checking capabilities
return
}
/***
* 检查当前进程是否有两个关键的权限
==== 看不到我 ====
* 没办法啦
* 自己之前承诺的坑补全篇
* 被迫填坑系列 qwq
==== 看不到我 ====
***/
// NewPid 已经被废弃了,这里改用 NewPid2 方法
caps, err := capability.NewPid2(0)
if err != nil {
// 判断是否为macOS
if runtime.GOOS == "darwin" {
// macOS下报错有问题
} else {
fmt.Println(err)
}
return
}
// load 获取全部的 caps 信息
err = caps.Load()
if err != nil {
fmt.Println(err)
return
}
// 判断一下权限有木有
if caps.Get(capability.EFFECTIVE, capability.CAP_NET_RAW) && caps.Get(capability.EFFECTIVE, capability.CAP_NET_ADMIN) {
// 有权限啦
return
} else {
// 没权限啦
fmt.Println("您正在以普通用户权限运行 NextTrace但 NextTrace 未被赋予监听网络套接字的ICMP消息包、修改IP头信息TTL等路由跟踪所需的权限")
fmt.Println("请使用管理员用户执行 `sudo setcap cap_net_raw,cap_net_admin+eip ${your_nexttrace_path}/nexttrace` 命令,赋予相关权限后再运行~")
fmt.Println("什么?为什么 ping 普通用户执行不要 root 权限?因为这些工具在管理员安装时就已经被赋予了一些必要的权限,具体请使用 `getcap /usr/bin/ping` 查看")
}
}

7
cmd/cmd_test.go Normal file
View File

@@ -0,0 +1,7 @@
package cmd
import "testing"
func TestCMD(t *testing.T) {
Excute()
}

5
config/basic.go Normal file
View File

@@ -0,0 +1,5 @@
package config
var Version = "v0.0.0.alpha"
var BuildDate = ""
var CommitID = ""

40
config/viper.go Normal file
View File

@@ -0,0 +1,40 @@
package config
import (
"fmt"
"github.com/spf13/viper"
)
func InitConfig() {
// 配置文件名, 不加扩展
viper.SetConfigName("nt_config") // name of config file (without extension)
// 设置文件的扩展名
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
// 查找配置文件所在路径
viper.AddConfigPath("/etc/bin/nexttrace/")
viper.AddConfigPath("/usr/local/bin/nexttrace/")
// 在当前路径进行查找
viper.AddConfigPath(".")
// viper.AddConfigPath("./config/")
// 配置默认值
viper.SetDefault("ptrPath", "./ptr.csv")
viper.SetDefault("geoFeedPath", "./geofeed.csv")
// 开始查找并读取配置文件
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
fmt.Println("未能找到配置文件,我们将在您的运行目录为您创建 nt_config.yaml 默认配置")
err := viper.SafeWriteConfigAs("./nt_config.yaml")
if err != nil {
return
}
}
err = viper.ReadInConfig()
if err != nil {
return
}
}

7
dn42/dn42.go Normal file
View File

@@ -0,0 +1,7 @@
package dn42
/***
[DN42 Package]
谨献给 DN42 所有的小伙伴们,祝你们终有一天能有自己的公网 ASN ~
By Leo
***/

101
dn42/geofeed.go Normal file
View File

@@ -0,0 +1,101 @@
package dn42
import (
"encoding/csv"
"net"
"os"
"sort"
"github.com/spf13/viper"
)
type GeoFeedRow struct {
IPNet *net.IPNet
CIDR string
LtdCode string
ISO3166 string
City string
ASN string
IPWhois string
}
func GetGeoFeed(ip string) (GeoFeedRow, bool) {
rows, err := ReadGeoFeed()
if err != nil {
// 处理错误
panic(err)
}
row, find := FindGeoFeedRow(ip, rows)
return row, find
}
func ReadGeoFeed() ([]GeoFeedRow, error) {
path := viper.Get("geoFeedPath").(string)
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
r := csv.NewReader(f)
rows, err := r.ReadAll()
if err != nil {
return nil, err
}
// 将 CSV 中的每一行转换为 GeoFeedRow 类型,并保存到 rowsSlice 中
var rowsSlice []GeoFeedRow
for _, row := range rows {
cidr := row[0] // 假设第一列是 CIDR 字段
_, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
// 如果解析 CIDR 失败,跳过这一行
continue
}
if len(row) == 4 {
rowsSlice = append(rowsSlice, GeoFeedRow{
IPNet: ipnet,
CIDR: cidr,
LtdCode: row[1],
ISO3166: row[2],
City: row[3],
})
} else {
rowsSlice = append(rowsSlice, GeoFeedRow{
IPNet: ipnet,
CIDR: cidr,
LtdCode: row[1],
ISO3166: row[2],
City: row[3],
ASN: row[4],
IPWhois: row[5],
})
}
}
// 根据 CIDR 范围从小到大排序,方便后面查找
sort.Slice(rowsSlice, func(i, j int) bool {
return rowsSlice[i].IPNet.Mask.String() > rowsSlice[j].IPNet.Mask.String()
})
return rowsSlice, nil
}
func FindGeoFeedRow(ipStr string, rows []GeoFeedRow) (GeoFeedRow, bool) {
ip := net.ParseIP(ipStr)
if ip == nil {
// 如果传入的 IP 无效,直接返回
return GeoFeedRow{}, false
}
// 遍历每个 CIDR 范围,找到第一个包含传入的 IP 的 CIDR
for _, row := range rows {
if row.IPNet.Contains(ip) {
return row, true
}
}
return GeoFeedRow{}, false
}

17
dn42/geofeed_test.go Normal file
View File

@@ -0,0 +1,17 @@
package dn42
// func TestGeoFeed(t *testing.T) {
// rows, err := ReadGeoFeed()
// if err != nil {
// // 处理错误
// }
// row, found := FindGeoFeedRow("2001:0418:1403:8080::6fff", rows)
// if found {
// // 处理符合条件的 row
// log.Println(row)
// } else {
// // 处理未找到的情况
// }
// }

83
dn42/ptr.go Normal file
View File

@@ -0,0 +1,83 @@
package dn42
import (
"encoding/csv"
"errors"
"fmt"
"os"
"regexp"
"strings"
"github.com/spf13/viper"
)
type PtrRow struct {
IATACode string
LtdCode string
Region string
City string
}
func matchesPattern(prefix string, s string) bool {
pattern := fmt.Sprintf(`^(.*[-.\d]|^)%s[-.\d].*$`, prefix)
r, err := regexp.Compile(pattern)
if err != nil {
fmt.Println("Invalid regular expression:", err)
return false
}
return r.MatchString(s)
}
func FindPtrRecord(ptr string) (PtrRow, error) {
path := viper.Get("ptrPath").(string)
f, err := os.Open(path)
if err != nil {
return PtrRow{}, err
}
defer f.Close()
r := csv.NewReader(f)
rows, err := r.ReadAll()
if err != nil {
return PtrRow{}, err
}
// 转小写
ptr = strings.ToLower(ptr)
// 先查城市名
for _, row := range rows {
city := row[3]
if city == "" {
continue
}
city = strings.ReplaceAll(city, " ", "")
city = strings.ToLower(city)
if matchesPattern(city, ptr) {
return PtrRow{
LtdCode: row[1],
Region: row[2],
City: row[3],
}, nil
}
}
// 查 IATA Code
for _, row := range rows {
iata := row[0]
if iata == "" {
continue
}
iata = strings.ToLower(iata)
if matchesPattern(iata, ptr) {
return PtrRow{
IATACode: iata,
LtdCode: row[1],
Region: row[2],
City: row[3],
}, nil
}
}
return PtrRow{}, errors.New("ptr not found")
}

50
dn42/ptr_test.go Normal file
View File

@@ -0,0 +1,50 @@
package dn42
import (
"testing"
)
func TestPTR(t *testing.T) {
// example_mis := []string{
// "sloutravel.com",
// "memeslou.org",
// "followsloucity.net",
// "slouslou.slou",
// "slouslou8.slou",
// }
// examples := []string{
// "1ge.slou.as1299.net",
// "1ge.slou2.as1299.net",
// "1ge-slou.as1299.net",
// "slou-1.as1299.net",
// "slou.as1299.com",
// "1ge-snge-6.as1299.net",
// "c-1.sin.sg.atlas.moeqing.com",
// "core.hkg1.hk.atlas.moeqing.com",
// "core.losangles.us.atlas.moeqing.com",
// }
// fmt.Println("容易误匹配的 PTR")
// for _, s := range example_mis {
// if r, err := FindPtrRecord("ptr.csv"); err == nil {
// fmt.Println(s, r)
// } else {
// fmt.Println(s, err)
// }
// }
// fmt.Println("\n应该正常匹配的 PTR")
// for _, s := range examples {
// if r, err := FindPtrRecord("ptr.csv"); err == nil {
// fmt.Println(s, r)
// } else {
// fmt.Println(s, err)
// }
// }
}

View File

@@ -16,6 +16,7 @@ type BackBoneCollection struct {
CU169 ISPCollection
CU9929 ISPCollection
CM ISPCollection
CMIN2 ISPCollection
EDU ISPCollection
CST ISPCollection
}
@@ -23,15 +24,18 @@ type BackBoneCollection struct {
type ISPCollection struct {
ISPName string
IP string
IPv6 string
}
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{
@@ -46,22 +50,48 @@ var Beijing = BackBoneCollection{
Location: "北京",
CT163: ISPCollection{
ISPName: CT163,
IP: "106.37.67.1",
IP: "ipv4.pek-4134.endpoint.nxtrace.org.",
IPv6: "ipv6.pek-4134.endpoint.nxtrace.org.",
},
CTCN2: ISPCollection{
ISPName: CTCN2,
IP: "ipv4.pek-4809.endpoint.nxtrace.org.",
},
CU169: ISPCollection{
ISPName: CU169,
IP: "123.125.96.156",
IP: "ipv4.pek-4837.endpoint.nxtrace.org.",
IPv6: "ipv6.pek-4837.endpoint.nxtrace.org.",
},
CU9929: ISPCollection{
ISPName: CU9929,
IP: "ipv4.pek-9929.endpoint.nxtrace.org.",
},
CM: ISPCollection{
ISPName: CM,
IP: "211.136.25.153",
IP: "ipv4.pek-9808.endpoint.nxtrace.org.",
IPv6: "ipv6.pek-9808.endpoint.nxtrace.org.",
},
CMIN2: ISPCollection{
ISPName: CMIN2,
IP: "ipv4.pek-58807.endpoint.nxtrace.org.",
},
EDU: ISPCollection{
ISPName: EDU,
IP: "101.6.15.130",
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.",
},
}
@@ -69,32 +99,42 @@ var Shanghai = BackBoneCollection{
Location: "上海",
CT163: ISPCollection{
ISPName: CT163,
IP: "101.226.28.198",
IP: "ipv4.sha-4134.endpoint.nxtrace.org.",
IPv6: "ipv6.sha-4134.endpoint.nxtrace.org.",
},
CTCN2: ISPCollection{
ISPName: CTCN2,
IP: "58.32.4.1",
IP: "ipv4.sha-4809.endpoint.nxtrace.org.",
},
CU169: ISPCollection{
ISPName: CU169,
IP: "139.226.206.150",
IP: "ipv4.sha-4837.endpoint.nxtrace.org.",
IPv6: "ipv6.sha-4837.endpoint.nxtrace.org.",
},
CU9929: ISPCollection{
ISPName: CU9929,
IP: "210.13.86.1",
IP: "ipv4.sha-9929.endpoint.nxtrace.org.",
IPv6: "ipv6.sha-9929.endpoint.nxtrace.org.",
},
CM: ISPCollection{
ISPName: CM,
IP: "120.204.34.85",
IP: "ipv4.sha-9808.endpoint.nxtrace.org.",
IPv6: "ipv6.sha-9808.endpoint.nxtrace.org.",
},
CMIN2: ISPCollection{
ISPName: CMIN2,
IP: "ipv4.sha-58807.endpoint.nxtrace.org.",
},
EDU: ISPCollection{
ISPName: EDU,
IP: "202.120.58.155",
IP: "ipv4.sha-4538.endpoint.nxtrace.org.",
IPv6: "ipv6.sha-4538.endpoint.nxtrace.org.",
},
}
@@ -102,17 +142,42 @@ var Guangzhou = BackBoneCollection{
Location: "广州",
CT163: ISPCollection{
ISPName: CT163,
IP: "106.37.67.1",
IP: "ipv4.can-4134.endpoint.nxtrace.org.",
IPv6: "ipv6.can-4134.endpoint.nxtrace.org.",
},
CTCN2: ISPCollection{
ISPName: CTCN2,
IP: "ipv4.can-4809.endpoint.nxtrace.org.",
},
CU169: ISPCollection{
ISPName: CU169,
IP: "157.18.0.22",
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: "120.198.26.254",
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.",
},
}
@@ -120,20 +185,24 @@ var Hangzhou = BackBoneCollection{
Location: "杭州",
CT163: ISPCollection{
ISPName: CT163,
IP: "61.164.23.196",
IP: "ipv4.hgh-4134.endpoint.nxtrace.org.",
IPv6: "ipv6.hgh-4134.endpoint.nxtrace.org.",
},
CU169: ISPCollection{
ISPName: CU169,
IP: "60.12.244.1",
IP: "ipv4.hgh-4837.endpoint.nxtrace.org.",
IPv6: "ipv6.hgh-4837.endpoint.nxtrace.org.",
},
CM: ISPCollection{
ISPName: CM,
IP: "112.17.224.98",
IP: "ipv4.hgh-9808.endpoint.nxtrace.org.",
IPv6: "ipv6.hgh-9808.endpoint.nxtrace.org.",
},
// 浙江大学 教育网
EDU: ISPCollection{
ISPName: EDU,
IP: "210.32.2.1",
IP: "ipv4.hgh-4538.endpoint.nxtrace.org.",
IPv6: "ipv6.hgh-4538.endpoint.nxtrace.org.",
},
}
@@ -142,11 +211,12 @@ var Hefei = BackBoneCollection{
// 中国科学技术大学 教育网
EDU: ISPCollection{
ISPName: EDU,
IP: "202.38.64.1",
IP: "ipv4.hfe-4538.endpoint.nxtrace.org.",
IPv6: "ipv6.hfe-4538.endpoint.nxtrace.org.",
},
// 中国科学技术大学 科技网
CST: ISPCollection{
ISPName: "中国科学技术大学 科技网 AS7497",
IP: "210.72.22.2",
ISPName: CST,
IP: "ipv4.hfe-7497.endpoint.nxtrace.org.",
},
}

View File

@@ -0,0 +1,176 @@
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"
"github.com/nxtrace/NTrace-core/tracelog"
"github.com/nxtrace/NTrace-core/util"
"github.com/nxtrace/NTrace-core/wshandle"
"log"
"os"
"os/signal"
"strings"
)
//var pFastTracer ParamsFastTrace
func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
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", f.ParamsFastTrace.Dot, true)
if err != nil {
log.Fatal(err)
}
var conf = trace.Config{
BeginHop: f.ParamsFastTrace.BeginHop,
DestIP: ip,
DestPort: f.ParamsFastTrace.DestPort,
MaxHops: f.ParamsFastTrace.MaxHops,
NumMeasurements: 3,
ParallelRequests: 18,
RDns: f.ParamsFastTrace.RDns,
AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS,
PacketInterval: 100,
TTLInterval: 500,
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
Timeout: f.ParamsFastTrace.Timeout,
SrcAddr: f.ParamsFastTrace.SrcAddr,
PktSize: f.ParamsFastTrace.PktSize,
Lang: f.ParamsFastTrace.Lang,
DontFragment: f.ParamsFastTrace.DontFragment,
}
if oe {
fp, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
return
}
defer func(fp *os.File) {
err := fp.Close()
if err != nil {
log.Fatal(err)
}
}(fp)
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, %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
}
_, err = trace.Traceroute(f.TracerouteMethod, conf)
if err != nil {
log.Fatal(err)
}
fmt.Println()
}
func (f *FastTracer) testAll_v6() {
f.testCT_v6()
println()
f.testCU_v6()
println()
f.testCM_v6()
println()
f.testEDU_v6()
}
func (f *FastTracer) testCT_v6() {
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163)
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
}
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)
}
func (f *FastTracer) testCM_v6() {
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
}
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.CST)
}
func FastTestv6(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFastTrace) {
var c string
oe = outEnable
fmt.Println("您想测试哪些ISP的路由\n1. 北京三网快速测试\n2. 全国电信\n3. 全国联通\n4. 全国移动\n5. 全国教育网\n6. 全国五网")
fmt.Print("请选择选项:")
_, err := fmt.Scanln(&c)
if err != nil {
c = "1"
}
ft := FastTracer{
ParamsFastTrace: paramsFastTrace,
}
// 建立 WebSocket 连接
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
w.Conn.Close()
}()
switch traceMode {
case trace.ICMPTrace:
ft.TracerouteMethod = trace.ICMPTrace
case trace.TCPTrace:
ft.TracerouteMethod = trace.TCPTrace
case trace.UDPTrace:
ft.TracerouteMethod = trace.UDPTrace
}
switch c {
case "1":
ft.testFast_v6()
case "2":
ft.testCT_v6()
case "3":
ft.testCU_v6()
case "4":
ft.testCM_v6()
case "5":
ft.testEDU_v6()
case "6":
ft.testAll_v6()
default:
ft.testFast_v6()
}
}

View File

@@ -1,55 +1,381 @@
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"
"github.com/nxtrace/NTrace-core/tracelog"
"github.com/nxtrace/NTrace-core/util"
"github.com/nxtrace/NTrace-core/wshandle"
"log"
"net"
"os"
"os/signal"
"strings"
"time"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/xgadget-lab/nexttrace/printer"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/xgadget-lab/nexttrace/wshandle"
)
type FastTracer struct {
TracerouteMethod trace.Method
ParamsFastTrace ParamsFastTrace
}
type ParamsFastTrace struct {
SrcDev string
SrcAddr string
DestPort int
BeginHop int
MaxHops int
RDns bool
AlwaysWaitRDNS bool
Lang string
PktSize int
Timeout time.Duration
File string
DontFragment bool
Dot string
}
type IpListElement struct {
Ip string
Desc string
Version4 bool // true for IPv4, false for IPv6
}
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, 30 hops max, 32 byte packets\n", ispCollection.IP)
ip := net.ParseIP(ispCollection.IP)
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", f.ParamsFastTrace.Dot, true)
if err != nil {
log.Fatal(err)
}
var conf = trace.Config{
BeginHop: 1,
BeginHop: f.ParamsFastTrace.BeginHop,
DestIP: ip,
DestPort: 80,
MaxHops: 30,
DestPort: f.ParamsFastTrace.DestPort,
MaxHops: f.ParamsFastTrace.MaxHops,
NumMeasurements: 3,
ParallelRequests: 18,
RDns: true,
RDns: f.ParamsFastTrace.RDns,
AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS,
PacketInterval: 100,
TTLInterval: 500,
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
Timeout: 1 * time.Second,
Timeout: f.ParamsFastTrace.Timeout,
SrcAddr: f.ParamsFastTrace.SrcAddr,
PktSize: f.ParamsFastTrace.PktSize,
Lang: f.ParamsFastTrace.Lang,
DontFragment: f.ParamsFastTrace.DontFragment,
}
if f.TracerouteMethod == trace.ICMPTrace {
if oe {
fp, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
return
}
defer func(fp *os.File) {
err := fp.Close()
if err != nil {
log.Fatal(err)
}
}(fp)
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, %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
}
res, err := trace.Traceroute(f.TracerouteMethod, conf)
_, err = trace.Traceroute(f.TracerouteMethod, conf)
if err != nil {
log.Fatal(err)
}
fmt.Println()
}
if f.TracerouteMethod == trace.TCPTrace {
printer.TracerouteTablePrinter(res)
// 单次测试结束阻塞 3 秒,仅阻塞 TCP
<-time.After(time.Second * 3)
func FastTest(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFastTrace) {
// tm means tcp mode
var c string
oe = outEnable
if paramsFastTrace.File != "" {
testFile(paramsFastTrace, traceMode)
return
}
println()
fmt.Println("Hi欢迎使用 Fast Trace 功能,请注意 Fast Trace 功能只适合新手使用\n因为国内网络复杂我们设置的测试目标有限建议普通用户自测以获得更加精准的路由情况")
fmt.Println("请您选择要测试的 IP 类型\n1. IPv4\n2. IPv6")
fmt.Print("请选择选项:")
_, err := fmt.Scanln(&c)
if err != nil {
c = "1"
}
if c == "2" {
if paramsFastTrace.SrcDev != "" {
dev, _ := net.InterfaceByName(paramsFastTrace.SrcDev)
if addrs, err := dev.Addrs(); err == nil {
for _, addr := range addrs {
if (addr.(*net.IPNet).IP.To4() == nil) == true {
paramsFastTrace.SrcAddr = addr.(*net.IPNet).IP.String()
// 检查是否是内网IP
if !(net.ParseIP(paramsFastTrace.SrcAddr).IsPrivate() ||
net.ParseIP(paramsFastTrace.SrcAddr).IsLoopback() ||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalUnicast() ||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalMulticast()) {
// 若不是则跳出
break
}
}
}
}
}
FastTestv6(traceMode, outEnable, paramsFastTrace)
return
}
if paramsFastTrace.SrcDev != "" {
dev, _ := net.InterfaceByName(paramsFastTrace.SrcDev)
if addrs, err := dev.Addrs(); err == nil {
for _, addr := range addrs {
if (addr.(*net.IPNet).IP.To4() == nil) == false {
paramsFastTrace.SrcAddr = addr.(*net.IPNet).IP.String()
// 检查是否是内网IP
if !(net.ParseIP(paramsFastTrace.SrcAddr).IsPrivate() ||
net.ParseIP(paramsFastTrace.SrcAddr).IsLoopback() ||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalUnicast() ||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalMulticast()) {
// 若不是则跳出
break
}
}
}
}
}
fmt.Println("您想测试哪些ISP的路由\n1. 北京三网快速测试\n2. 全国电信\n3. 全国联通\n4. 全国移动\n5. 全国教育网\n6. 全国五网")
fmt.Print("请选择选项:")
_, err = fmt.Scanln(&c)
if err != nil {
c = "1"
}
ft := FastTracer{
ParamsFastTrace: paramsFastTrace,
}
// 建立 WebSocket 连接
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
w.Conn.Close()
}()
switch traceMode {
case trace.ICMPTrace:
ft.TracerouteMethod = trace.ICMPTrace
case trace.TCPTrace:
ft.TracerouteMethod = trace.TCPTrace
case trace.UDPTrace:
ft.TracerouteMethod = trace.UDPTrace
}
switch c {
case "1":
ft.testFast()
case "2":
ft.testCT()
case "3":
ft.testCU()
case "4":
ft.testCM()
case "5":
ft.testEDU()
case "6":
ft.testAll()
default:
ft.testFast()
}
}
func testFile(paramsFastTrace ParamsFastTrace, traceMode trace.Method) {
// 建立 WebSocket 连接
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
w.Conn.Close()
}()
var tracerouteMethod trace.Method
switch traceMode {
case trace.ICMPTrace:
tracerouteMethod = trace.ICMPTrace
case trace.TCPTrace:
tracerouteMethod = trace.TCPTrace
case trace.UDPTrace:
tracerouteMethod = trace.UDPTrace
}
filePath := paramsFastTrace.File
file, err := os.Open(filePath)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Fatal(err)
}
}(file)
var ipList []IpListElement
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
parts := strings.SplitN(line, " ", 2)
var ip, desc string
if len(parts) == 2 {
ip = parts[0]
desc = parts[1]
} else if len(parts) == 1 {
ip = parts[0]
desc = ip // Set the description to the IP if no description is provided
} else {
fmt.Printf("Ignoring invalid line: %s\n", line)
continue
}
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
netIp, err := util.DomainLookUp(ip, "all", "", true)
if err != nil {
fmt.Printf("Ignoring invalid IP: %s\n", ip)
continue
}
if len(parts) == 1 {
desc = ip
}
ip = netIp.String()
}
ipElem := IpListElement{
Ip: ip,
Desc: desc,
Version4: strings.Contains(ip, "."),
}
ipList = append(ipList, ipElem)
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading file:", err)
}
for _, ip := range ipList {
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 != "" {
dev, _ := net.InterfaceByName(paramsFastTrace.SrcDev)
if addrs, err := dev.Addrs(); err == nil {
for _, addr := range addrs {
if (addr.(*net.IPNet).IP.To4() == nil) == false {
srcAddr = addr.(*net.IPNet).IP.String()
// 检查是否是内网IP
if !(net.ParseIP(srcAddr).IsPrivate() ||
net.ParseIP(srcAddr).IsLoopback() ||
net.ParseIP(srcAddr).IsLinkLocalUnicast() ||
net.ParseIP(srcAddr).IsLinkLocalMulticast()) {
// 若不是则跳出
break
}
}
}
}
}
} else {
if paramsFastTrace.SrcDev != "" {
dev, _ := net.InterfaceByName(paramsFastTrace.SrcDev)
if addrs, err := dev.Addrs(); err == nil {
for _, addr := range addrs {
if (addr.(*net.IPNet).IP.To4() == nil) == true {
srcAddr = addr.(*net.IPNet).IP.String()
// 检查是否是内网IP
if !(net.ParseIP(srcAddr).IsPrivate() ||
net.ParseIP(srcAddr).IsLoopback() ||
net.ParseIP(srcAddr).IsLinkLocalUnicast() ||
net.ParseIP(srcAddr).IsLinkLocalMulticast()) {
// 若不是则跳出
break
}
}
}
}
}
}
var conf = trace.Config{
BeginHop: paramsFastTrace.BeginHop,
DestIP: net.ParseIP(ip.Ip),
DestPort: paramsFastTrace.DestPort,
MaxHops: paramsFastTrace.MaxHops,
NumMeasurements: 3,
ParallelRequests: 18,
RDns: paramsFastTrace.RDns,
AlwaysWaitRDNS: paramsFastTrace.AlwaysWaitRDNS,
PacketInterval: 100,
TTLInterval: 500,
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
Timeout: paramsFastTrace.Timeout,
SrcAddr: srcAddr,
PktSize: paramsFastTrace.PktSize,
Lang: paramsFastTrace.Lang,
}
if oe {
fp, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
return
}
log.SetOutput(fp)
log.SetFlags(0)
log.Printf("『%s』\n", ip.Desc)
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 {
log.Fatal(err)
}
} else {
conf.RealtimePrinter = printer.RealtimePrinter
}
_, err := trace.Traceroute(tracerouteMethod, conf)
if err != nil {
log.Fatalln(err)
}
fmt.Println()
}
}
func (f *FastTracer) testAll() {
@@ -64,71 +390,50 @@ 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() {
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
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() {
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CMIN2)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CMIN2)
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)
}
func FastTest(tm bool) {
var c string
fmt.Println("您想测试哪些ISP的路由\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网")
fmt.Print("请选择选项:")
fmt.Scanln(&c)
ft := FastTracer{}
// 建立 WebSocket 连接
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
w.Conn.Close()
}()
if !tm {
ft.TracerouteMethod = trace.ICMPTrace
fmt.Println("您将默认使用ICMP协议进行路由跟踪如果您想使用TCP SYN进行路由跟踪可以加入 -T 参数")
} else {
ft.TracerouteMethod = trace.TCPTrace
}
switch c {
case "1":
ft.testAll()
case "2":
ft.testCT()
case "3":
ft.testCU()
case "4":
ft.testCM()
case "5":
ft.testEDU()
default:
ft.testAll()
}
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.CST)
}

View File

@@ -1,25 +1,36 @@
package fastTrace
import (
"os"
"os/signal"
"testing"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/xgadget-lab/nexttrace/wshandle"
)
// ICMP Use Too Many Time to Wait So we don't test it.
func TestTCPTrace(t *testing.T) {
ft := FastTracer{}
// 建立 WebSocket 连接
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
w.Conn.Close()
}()
ft.TracerouteMethod = trace.TCPTrace
ft.testCM()
ft.testEDU()
func TestTrace(t *testing.T) {
//pFastTrace := ParamsFastTrace{
// SrcDev: "",
// SrcAddr: "",
// BeginHop: 1,
// MaxHops: 30,
// RDns: false,
// AlwaysWaitRDNS: false,
// Lang: "",
// PktSize: 52,
//}
//ft := FastTracer{ParamsFastTrace: pFastTrace}
//// 建立 WebSocket 连接
//w := wshandle.New()
//w.Interrupt = make(chan os.Signal, 1)
//signal.Notify(w.Interrupt, os.Interrupt)
//defer func() {
// w.Conn.Close()
//}()
//fmt.Println("TCP v4")
//ft.TracerouteMethod = trace.TCPTrace
//ft.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
//fmt.Println("TCP v6")
//ft.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
//fmt.Println("ICMP v4")
//ft.TracerouteMethod = trace.ICMPTrace
//ft.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
//fmt.Println("ICMP v6")
//ft.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
}

3
geofeed.example.csv Normal file
View File

@@ -0,0 +1,3 @@
154.48.0.0/12,,,,174,COGENT-NET
200.15.12.0/22,BR,BR-SP,Sao Paulo,2914,NTT-BACKBONE
2001:0418:1403::/48,US,US-VA,Ashburn,2914,NTT-BACKBONE
1 154.48.0.0/12 174 COGENT-NET
2 200.15.12.0/22 BR BR-SP Sao Paulo 2914 NTT-BACKBONE
3 2001:0418:1403::/48 US US-VA Ashburn 2914 NTT-BACKBONE

49
go.mod
View File

@@ -1,28 +1,45 @@
module github.com/xgadget-lab/nexttrace
module github.com/nxtrace/NTrace-core
go 1.18
go 1.24.1
require (
github.com/akamensky/argparse v1.4.0
github.com/google/gopacket v1.1.19
golang.org/x/net v0.0.0-20220809012201-f428fae20770
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
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.5
golang.org/x/net v0.39.0
golang.org/x/sync v0.13.0
)
require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/text v0.24.0 // indirect
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0
github.com/gorilla/websocket v1.5.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rodaine/table v1.0.1
github.com/stretchr/testify v1.7.1
github.com/tidwall/gjson v1.14.2
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.3.0
github.com/tidwall/gjson v1.18.0
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
github.com/tidwall/pretty v1.2.1 // indirect
golang.org/x/sys v0.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

128
go.sum
View File

@@ -1,67 +1,109 @@
github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
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.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.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.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/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.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/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
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.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.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/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
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/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
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.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 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
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.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/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.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220809012201-f428fae20770 h1:dIi4qVdvjZEjiMDv7vhokAZNGnz3kepwuXqFKYDdDMs=
golang.org/x/net v0.0.0-20220809012201-f428fae20770/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs=
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=

91
ipgeo/chunzhen.go Normal file
View File

@@ -0,0 +1,91 @@
package ipgeo
import (
"encoding/json"
"github.com/nxtrace/NTrace-core/util"
"io"
"log"
"net/http"
"strings"
"time"
)
func Chunzhen(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
url := util.GetenvDefault("NEXTTRACE_CHUNZHENURL", "http://127.0.0.1:2060") + "?ip=" + ip
client := &http.Client{
// 2 秒超时
Timeout: timeout,
}
req, _ := http.NewRequest("GET", url, nil)
content, err := client.Do(req)
if err != nil {
log.Println("纯真 请求超时(2s)请切换其他API使用")
return &IPGeoData{}, err
}
body, _ := io.ReadAll(content.Body)
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
return &IPGeoData{}, err
}
city := data[ip].(map[string]interface{})["area"].(string)
region := data[ip].(map[string]interface{})["country"].(string)
var asn string
if data[ip].(map[string]interface{})["asn"] != nil {
asn = data[ip].(map[string]interface{})["asn"].(string)
}
// 判断是否前两个字为香港或台湾
var country string
provinces := []string{
"北京",
"天津",
"河北",
"山西",
"内蒙古",
"辽宁",
"吉林",
"黑龙江",
"上海",
"江苏",
"浙江",
"安徽",
"福建",
"江西",
"山东",
"河南",
"湖北",
"湖南",
"广东",
"广西",
"海南",
"重庆",
"四川",
"贵州",
"云南",
"西藏",
"陕西",
"甘肃",
"青海",
"宁夏",
"新疆",
"台湾",
"香港",
"澳门",
}
for _, province := range provinces {
if strings.Contains(region, province) {
country = "中国"
city = region + city
break
}
}
if country == "" {
country = region
}
return &IPGeoData{
Asnumber: asn,
Country: country,
City: city,
}, nil
}

63
ipgeo/dn42.go Normal file
View File

@@ -0,0 +1,63 @@
package ipgeo
import (
"strings"
"time"
"github.com/nxtrace/NTrace-core/dn42"
)
func LtdCodeToCountryOrAreaName(Code string) string {
countryName := []string{"United States", "Afghanistan", "Åland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", " Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada ", "Cape Verde", "Cayman Islands", "Central Africa", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo (Brazzaville)", "DRC", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", " Denmark", "Djibouti", "Dominica", "Dominica", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", " French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Vatican", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "British Isles of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "North Korea", "South Korea", " Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "FYROM", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", " Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia (Federated States of)", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana", "Norway", "Oman", "Pakistan", "Palau", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "St. Helena", "St. Kitts and Nevis", "St. Lucia", "St. Pierre and Miquelon", "St. Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen Islands", "Swaziland", "Sweden ", "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor-Leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", " United Kingdom", "U.S. Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Vietnam", "British Virgin Islands", "U.S. Virgin Islands", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"}
countryCode := []string{"us", "af", "ax", "al", "dz", "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "au", "at", "az", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm", "bt", "bo", "ba", "bw", "bv", "br", "io", "bn", "bg", "bf", "bi", "kh", "cm", "ca", "cv", "ky", "cf", "td", "cl", "cn", "cx", "cc", "co", "km", "cg", "cd", "ck", "cr", "ci", "hr", "cu", "cy", "cz", "dk", "dj", "dm", "do", "ec", "eg", "sv", "gq", "er", "ee", "et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm", "ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gg", "gn", "gw", "gy", "ht", "hm", "va", "hn", "hk", "hu", "is", "in", "id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo", "kz", "ke", "ki", "kp", "kr", "kw", "kg", "la", "lv", "lb", "ls", "lr", "ly", "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv", "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc", "mn", "me", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "an", "nc", "nz", "ni", "ne", "ng", "nu", "nf", "mp", "no", "om", "pk", "pw", "ps", "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re", "ro", "ru", "rw", "sh", "kn", "lc", "pm", "vc", "ws", "sm", "st", "sa", "sn", "rs", "sc", "sl", "sg", "sk", "si", "sb", "so", "za", "gs", "es", "lk", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj", "tz", "th", "tl", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", "ug", "ua", "ae", "gb", "um", "uy", "uz", "vu", "ve", "vn", "vg", "vi", "wf", "eh", "ye", "zm", "zw"}
Code = strings.ToLower(Code)
for i, v := range countryCode {
if strings.Contains(Code, v) {
return countryName[i]
}
}
return Code
}
func DN42(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) {
data := &IPGeoData{}
// 先解析传入过来的数据
ipTmp := strings.Split(ip, ",")
if len(ipTmp) > 1 {
ip = ipTmp[0]
}
// 先查找 GeoFeed
if geo, find := dn42.GetGeoFeed(ip); find {
data.Country = geo.LtdCode
data.City = geo.City
data.Asnumber = geo.ASN
data.Whois = geo.IPWhois
}
// 如果没找到,查找 PTR
if len(ipTmp) > 1 {
// 存在 PTR 记录
if res, err := dn42.FindPtrRecord(ipTmp[1]); err == nil && res.LtdCode != "" {
data.Country = res.LtdCode
data.Prov = res.Region
data.City = res.City
}
}
data.Country = LtdCodeToCountryOrAreaName(data.Country)
switch data.Country {
case "Hong Kong":
data.Country = "China"
data.Prov = "Hong Kong"
case "Taiwan":
data.Country = "China"
data.Prov = "Taiwan"
case "Macao":
data.Country = "China"
data.Prov = "Macao"
case "":
data.Country = "Unknown"
}
return data, nil
}

5
ipgeo/dn42_test.go Normal file
View File

@@ -0,0 +1,5 @@
package ipgeo
// func TestDN42(t *testing.T) {
// DN42("")
// }

88
ipgeo/ip2region.go Normal file
View File

@@ -0,0 +1,88 @@
package ipgeo
import (
"errors"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/lionsoul2014/ip2region/v1.0/binding/golang/ip2region"
)
const (
ipDataBasePath = "./ip2region.db"
defaultDownURL = "1"
originURL = "https://mirror.ghproxy.com/?q=https://github.com/bqf9979/ip2region/blob/master/data/ip2region.db?raw=true"
)
func downloadDataBase() error {
fmt.Println("Downloading DataBase...")
resp, err := http.Get(originURL)
if err != nil {
return err
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
panic(err)
}
}(resp.Body)
// Create the file
out, err := os.Create(ipDataBasePath)
if err != nil {
return err
}
defer func(out *os.File) {
err := out.Close()
if err != nil {
panic(err)
}
}(out)
// Write the body to file
_, err = io.Copy(out, resp.Body)
return err
}
func IP2Region(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) {
if _, err := os.Stat(ipDataBasePath); os.IsNotExist(err) {
if err = downloadDataBase(); err != nil {
panic("Download Failed!")
}
}
region, err := ip2region.New(ipDataBasePath)
if err != nil {
panic("Cannot find ip2region.db")
}
defer region.Close()
info, searchErr := region.MemorySearch(ip)
if searchErr != nil {
return &IPGeoData{}, errors.New("no results")
}
if info.Country == "0" {
info.Country = ""
}
if info.Province == "0" {
info.Province = ""
}
if info.City == "0" {
info.City = ""
}
if info.ISP == "0" {
info.ISP = ""
}
return &IPGeoData{
Owner: info.ISP,
Country: info.Country,
Prov: info.Province,
City: info.City,
}, nil
}

View File

@@ -2,20 +2,22 @@ package ipgeo
import (
"errors"
"io/ioutil"
"github.com/nxtrace/NTrace-core/util"
"io"
"log"
"net/http"
"regexp"
"strconv"
"time"
"github.com/tidwall/gjson"
)
func IPApiCom(ip string) (*IPGeoData, error) {
url := "http://ip-api.com/json/" + ip + "?fields=status,message,country,regionName,city,isp,as"
func IPApiCom(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
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: 2 * time.Second,
Timeout: timeout,
}
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0")
@@ -24,7 +26,7 @@ func IPApiCom(ip string) (*IPGeoData, error) {
log.Println("ip-api.com 请求超时(2s)请切换其他API使用")
return nil, err
}
body, _ := ioutil.ReadAll(content.Body)
body, _ := io.ReadAll(content.Body)
res := gjson.ParseBytes(body)
if res.Get("status").String() != "success" {
@@ -32,12 +34,27 @@ func IPApiCom(ip string) (*IPGeoData, error) {
}
re := regexp.MustCompile("[0-9]+")
var country = res.Get("country").String()
var prov = res.Get("region").String()
var city = res.Get("city").String()
var district = res.Get("district").String()
if util.StringInSlice(country, []string{"Hong Kong", "Taiwan", "Macao"}) {
district = prov + " " + city + " " + district
city = country
prov = ""
country = "China"
}
lat, _ := strconv.ParseFloat(res.Get("lat").String(), 32)
lng, _ := strconv.ParseFloat(res.Get("lon").String(), 32)
return &IPGeoData{
Asnumber: re.FindString(res.Get("as").String()),
Country: res.Get("country").String(),
City: res.Get("city").String(),
Prov: res.Get("regionName").String(),
Isp: res.Get("isp").String(),
Country: country,
City: city,
Prov: prov,
District: district,
Owner: res.Get("isp").String(),
Lat: lat,
Lng: lng,
}, nil
}

174
ipgeo/ipfilter.go Normal file
View File

@@ -0,0 +1,174 @@
package ipgeo
import (
"net"
)
func cidrRangeContains(cidrRange string, checkIP string) bool {
_, ipNet, err := net.ParseCIDR(cidrRange)
if err != nil {
return false
}
secondIP := net.ParseIP(checkIP)
return ipNet.Contains(secondIP)
}
// Filter 被选到的返回 geodata, true 否则返回 nil, false
func Filter(ip string) (*IPGeoData, bool) {
//geodata := &IPGeoData{}
asn := ""
whois := ""
isFiltered := false
switch {
case cidrRangeContains("0.0.0.0/8", ip):
asn = ""
whois = "RFC1122"
isFiltered = true
//IANA Reserved Address Space
case cidrRangeContains("100.64.0.0/10", ip):
asn = ""
whois = "RFC6598"
isFiltered = true
//127.0.0.0/8
case cidrRangeContains("127.0.0.0/8", ip):
asn = ""
whois = "RFC1122"
isFiltered = true
//169.254.0.0/16
case cidrRangeContains("169.254.0.0/16", ip):
asn = ""
whois = "RFC3927"
isFiltered = true
//192.0.0.0/24
case cidrRangeContains("192.0.0.0/24", ip):
asn = ""
whois = "RFC6890"
isFiltered = true
//192.0.2.0/24
case cidrRangeContains("192.0.2.0/24", ip):
asn = ""
whois = "RFC5737"
isFiltered = true
//192.88.99.0/24
case cidrRangeContains("192.88.99.0/24", ip):
asn = ""
whois = "RFC3068"
isFiltered = true
case cidrRangeContains("198.18.0.0/15", ip):
asn = ""
whois = "RFC2544"
isFiltered = true
case cidrRangeContains("198.51.100.0/24", ip):
fallthrough
case cidrRangeContains("203.0.113.0/24", ip):
asn = ""
whois = "RFC5737"
isFiltered = true
//224.0.0.0/4
case cidrRangeContains("224.0.0.0/4", ip):
asn = ""
whois = "RFC5771"
isFiltered = true
//255.255.255.255/32
case cidrRangeContains("255.255.255.255/32", ip):
asn = ""
whois = "RFC0919"
isFiltered = true
case cidrRangeContains("240.0.0.0/4", ip):
asn = ""
whois = "RFC1112"
isFiltered = true
case cidrRangeContains("fe80::/10", ip):
asn = ""
whois = "RFC4291"
isFiltered = true
case cidrRangeContains("ff00::/8", ip):
asn = ""
whois = "RFC4291"
isFiltered = true
case cidrRangeContains("fec0::/10", ip):
asn = ""
whois = "RFC3879"
isFiltered = true
case cidrRangeContains("fe00::/9", ip):
asn = ""
whois = "RFC4291"
isFiltered = true
case cidrRangeContains("64:ff9b::/96", ip):
asn = ""
whois = "RFC6052"
isFiltered = true
case cidrRangeContains("0::/96", ip):
asn = ""
whois = "RFC4291"
isFiltered = true
case cidrRangeContains("64:ff9b:1::/48", ip):
asn = ""
whois = "RFC6052"
isFiltered = true
case cidrRangeContains("2001:db8::/32", ip):
asn = ""
whois = "RFC3849"
isFiltered = true
case cidrRangeContains("2002::/16", ip):
asn = ""
whois = "RFC3056"
isFiltered = true
case net.ParseIP(ip).IsPrivate():
//rfc4193
if cidrRangeContains("fc00::/7", ip) {
asn = ""
whois = "RFC4193"
isFiltered = true
//rfc1918
} else {
asn = ""
whois = "RFC1918"
isFiltered = true
}
//Defense Information System Network
case cidrRangeContains("6.0.0.0/8", ip):
fallthrough
case cidrRangeContains("7.0.0.0/8", ip):
fallthrough
case cidrRangeContains("11.0.0.0/8", ip):
fallthrough
case cidrRangeContains("21.0.0.0/8", ip):
fallthrough
case cidrRangeContains("22.0.0.0/8", ip):
fallthrough
case cidrRangeContains("26.0.0.0/8", ip):
fallthrough
case cidrRangeContains("28.0.0.0/8", ip):
fallthrough
case cidrRangeContains("29.0.0.0/8", ip):
fallthrough
case cidrRangeContains("30.0.0.0/8", ip):
fallthrough
case cidrRangeContains("33.0.0.0/8", ip):
fallthrough
case cidrRangeContains("55.0.0.0/8", ip):
fallthrough
case cidrRangeContains("214.0.0.0/8", ip):
fallthrough
case cidrRangeContains("215.0.0.0/8", ip):
asn = ""
whois = "DOD"
isFiltered = true
default:
}
// 判断是否为v6 且不在2000::/3
if net.ParseIP(ip).To4() == nil && !cidrRangeContains("2000::/3", ip) && !isFiltered {
asn = ""
whois = "INVALID"
isFiltered = true
}
if !isFiltered {
return nil, false
} else {
return &IPGeoData{
Asnumber: asn,
Whois: whois,
}, true
}
}

View File

@@ -2,22 +2,36 @@ package ipgeo
import (
"strings"
"time"
)
type IPGeoData struct {
Asnumber string
Country string
Prov string
City string
District string
Owner string
Isp string
IP string `json:"ip"`
Asnumber string `json:"asnumber"`
Country string `json:"country"`
CountryEn string `json:"country_en"`
Prov string `json:"prov"`
ProvEn string `json:"prov_en"`
City string `json:"city"`
CityEn string `json:"city_en"`
District string `json:"district"`
Owner string `json:"owner"`
Isp string `json:"isp"`
Domain string `json:"domain"`
Whois string `json:"whois"`
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
Prefix string `json:"prefix"`
Router map[string][]string `json:"router"`
Source string `json:"source"`
}
type Source = func(ip string) (*IPGeoData, error)
type Source = func(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeoData, error)
func GetSource(s string) Source {
switch strings.ToUpper(s) {
case "DN42":
return DN42
case "LEOMOEAPI":
return LeoIP
case "IP.SB":
@@ -26,9 +40,23 @@ func GetSource(s string) Source {
return IPInSight
case "IPAPI.COM":
return IPApiCom
case "IP-API.COM":
return IPApiCom
case "IPINFO":
return IPInfo
case "IP2REGION":
return IP2Region
case "IPINFOLOCAL":
return IPInfoLocal
case "CHUNZHEN":
return Chunzhen
case "DISABLE-GEOIP":
return disableGeoIP
default:
return LeoIP
}
}
func disableGeoIP(string, time.Duration, string, bool) (*IPGeoData, error) {
return &IPGeoData{}, nil
}

View File

@@ -1,11 +1,79 @@
package ipgeo
import (
"errors"
"fmt"
"log"
"os"
"strconv"
"testing"
)
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// )
func TestXxx(t *testing.T) {
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 := IdFixedHeader + processID + ttl
for _, c := range id {
if c == '1' {
parity++
}
}
if parity%2 == 0 {
id += "1"
} else {
id += "0"
}
processId, ttlR, _ := reverseID(id)
log.Println(processId, ttlR)
}
func TestFilter(t *testing.T) {
res, err := Filter("fd11::1")
//打印whois信息
fmt.Println(res.Whois)
print(err)
}
func reverseID(id string) (int64, int64, error) {
ttl, _ := strconv.ParseInt(id[9:15], 2, 32)
//process ID
processID, _ := strconv.ParseInt(id[2:9], 2, 32)
parity := 0
for i := 0; i < len(id)-1; i++ {
if id[i] == '1' {
parity++
}
}
if parity%2 == 1 {
if id[len(id)-1] == '0' {
fmt.Println("Parity check passed.")
} else {
fmt.Println("Parity check failed.")
return 0, 0, errors.New("err")
}
} else {
if id[len(id)-1] == '1' {
fmt.Println("Parity check passed.")
} else {
fmt.Println("Parity check failed.")
return 0, 0, errors.New("err")
}
}
return processID, ttl, nil
}
// func TestLeoIP(t *testing.T) {
// // res, err := LeoIP("1.1.1.1")
// // assert.Nil(t, err)

View File

@@ -1,36 +1,339 @@
package ipgeo
import (
"io/ioutil"
"github.com/nxtrace/NTrace-core/util"
"io"
"net/http"
"strconv"
"strings"
"time"
"github.com/tidwall/gjson"
)
func IPInfo(ip string) (*IPGeoData, error) {
resp, err := http.Get("https://ipinfo.io/" + ip + "?token=" + token.ipinfo)
func IPInfo(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
url := token.BaseOrDefault("http://ipinfo.io/") + ip + "?token=" + token.ipinfo
client := &http.Client{
// 2 秒超时
Timeout: timeout,
}
resp, err := client.Get(url)
//resp, err := http.Get("https://ipinfo.io/" + ip + "?token=" + token.ipinfo)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
res := gjson.ParseBytes(body)
var country string
if res.Get("country").String() == "HK" || res.Get("country").String() == "TW" {
// ISO-3166 转换
var countryMap = map[string]string{
"AF": "Afghanistan",
"AX": "Åland Islands",
"AL": "Albania",
"DZ": "Algeria",
"AS": "American Samoa",
"AD": "Andorra",
"AO": "Angola",
"AI": "Anguilla",
"AQ": "Antarctica",
"AG": "Antigua and Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AW": "Aruba",
"AU": "Australia",
"AT": "Austria",
"AZ": "Azerbaijan",
"BH": "Bahrain",
"BS": "Bahamas",
"BD": "Bangladesh",
"BB": "Barbados",
"BY": "Belarus",
"BE": "Belgium",
"BZ": "Belize",
"BJ": "Benin",
"BM": "Bermuda",
"BT": "Bhutan",
"BO": "Bolivia",
"BQ": "Bonaire",
"BA": "Bosnia and Herzegovina",
"BW": "Botswana",
"BV": "Bouvet Island",
"BR": "Brazil",
"IO": "British Indian Ocean Territory",
"BN": "Brunei Darussalam",
"BG": "Bulgaria",
"BF": "Burkina Faso",
"BI": "Burundi",
"KH": "Cambodia",
"CM": "Cameroon",
"CA": "Canada",
"CV": "Cape Verde",
"KY": "Cayman Islands",
"CF": "Central African Republic",
"TD": "Chad",
"CL": "Chile",
"CN": "China",
"CX": "Christmas Island",
"CC": "Cocos (Keeling) Islands",
"CO": "Colombia",
"KM": "Comoros",
"CG": "Congo",
"CD": "Congo",
"CK": "Cook Islands",
"CR": "Costa Rica",
"CI": "Côte d'Ivoire",
"HR": "Croatia",
"CU": "Cuba",
"CW": "Curaçao",
"CY": "Cyprus",
"CZ": "Czech Republic",
"DK": "Denmark",
"DJ": "Djibouti",
"DM": "Dominica",
"DO": "Dominican Republic",
"EC": "Ecuador",
"EG": "Egypt",
"SV": "El Salvador",
"GQ": "Equatorial Guinea",
"ER": "Eritrea",
"EE": "Estonia",
"ET": "Ethiopia",
"FK": "Falkland Islands (Malvinas)",
"FO": "Faroe Islands",
"FJ": "Fiji",
"FI": "Finland",
"FR": "France",
"GF": "French Guiana",
"PF": "French Polynesia",
"TF": "French Southern Territories",
"GA": "Gabon",
"GM": "Gambia",
"GE": "Georgia",
"DE": "Germany",
"GH": "Ghana",
"GI": "Gibraltar",
"GR": "Greece",
"GL": "Greenland",
"GD": "Grenada",
"GP": "Guadeloupe",
"GU": "Guam",
"GT": "Guatemala",
"GG": "Guernsey",
"GN": "Guinea",
"GW": "Guinea-Bissau",
"GY": "Guyana",
"HT": "Haiti",
"HM": "Heard Island and McDonald Islands",
"VA": "Holy See (Vatican City State)",
"HN": "Honduras",
"HK": "Hong Kong",
"HU": "Hungary",
"IS": "Iceland",
"IN": "India",
"ID": "Indonesia",
"IR": "Iran",
"IQ": "Iraq",
"IE": "Ireland",
"IM": "Isle of Man",
"IL": "Israel",
"IT": "Italy",
"JM": "Jamaica",
"JP": "Japan",
"JE": "Jersey",
"JO": "Jordan",
"KZ": "Kazakhstan",
"KE": "Kenya",
"KI": "Kiribati",
"KP": "Korea",
"KR": "Korea",
"KW": "Kuwait",
"KG": "Kyrgyzstan",
"LA": "Lao People's Democratic Republic",
"LV": "Latvia",
"LB": "Lebanon",
"LS": "Lesotho",
"LR": "Liberia",
"LY": "Libya",
"LI": "Liechtenstein",
"LT": "Lithuania",
"LU": "Luxembourg",
"MO": "Macao",
"MK": "Macedonia",
"MG": "Madagascar",
"MW": "Malawi",
"MY": "Malaysia",
"MV": "Maldives",
"ML": "Mali",
"MT": "Malta",
"MH": "Marshall Islands",
"MQ": "Martinique",
"MR": "Mauritania",
"MU": "Mauritius",
"YT": "Mayotte",
"MX": "Mexico",
"FM": "Micronesia",
"MD": "Moldova",
"MC": "Monaco",
"MN": "Mongolia",
"ME": "Montenegro",
"MS": "Montserrat",
"MA": "Morocco",
"MZ": "Mozambique",
"MM": "Myanmar",
"NA": "Namibia",
"NR": "Nauru",
"NP": "Nepal",
"NL": "Netherlands",
"NC": "New Caledonia",
"NZ": "New Zealand",
"NI": "Nicaragua",
"NE": "Niger",
"NG": "Nigeria",
"NU": "Niue",
"NF": "Norfolk Island",
"MP": "Northern Mariana Islands",
"NO": "Norway",
"OM": "Oman",
"PK": "Pakistan",
"PW": "Palau",
"PS": "Palestine",
"PA": "Panama",
"PG": "Papua New Guinea",
"PY": "Paraguay",
"PE": "Peru",
"PH": "Philippines",
"PN": "Pitcairn",
"PL": "Poland",
"PT": "Portugal",
"PR": "Puerto Rico",
"QA": "Qatar",
"RE": "Réunion",
"RO": "Romania",
"RU": "Russian Federation",
"RW": "Rwanda",
"BL": "Saint Barthélemy",
"SH": "Saint Helena",
"KN": "Saint Kitts and Nevis",
"LC": "Saint Lucia",
"MF": "Saint Martin (French part)",
"PM": "Saint Pierre and Miquelon",
"VC": "Saint Vincent and the Grenadines",
"WS": "Samoa",
"SM": "San Marino",
"ST": "Sao Tome and Principe",
"SA": "Saudi Arabia",
"SN": "Senegal",
"RS": "Serbia",
"SC": "Seychelles",
"SL": "Sierra Leone",
"SG": "Singapore",
"SX": "Sint Maarten (Dutch part)",
"SK": "Slovakia",
"SI": "Slovenia",
"SB": "Solomon Islands",
"SO": "Somalia",
"ZA": "South Africa",
"GS": "South Georgia and the South Sandwich Islands",
"SS": "South Sudan",
"ES": "Spain",
"LK": "Sri Lanka",
"SD": "Sudan",
"SR": "Suriname",
"SJ": "Svalbard and Jan Mayen",
"SZ": "Swaziland",
"SE": "Sweden",
"CH": "Switzerland",
"SY": "Syrian Arab Republic",
"TW": "Taiwan",
"TJ": "Tajikistan",
"TZ": "Tanzania",
"TH": "Thailand",
"TL": "Timor-Leste",
"TG": "Togo",
"TK": "Tokelau",
"TO": "Tonga",
"TT": "Trinidad and Tobago",
"TN": "Tunisia",
"TR": "Turkey",
"TM": "Turkmenistan",
"TC": "Turks and Caicos Islands",
"TV": "Tuvalu",
"UG": "Uganda",
"UA": "Ukraine",
"AE": "United Arab Emirates",
"GB": "United Kingdom",
"US": "United States of America",
"UM": "United States Minor Outlying Islands",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"VU": "Vanuatu",
"VE": "Venezuela",
"VN": "Viet Nam",
"VG": "Virgin Islands",
"VI": "Virgin Islands",
"WF": "Wallis and Futuna",
"EH": "Western Sahara",
"YE": "Yemen",
"ZM": "Zambia",
"ZW": "Zimbabwe",
}
var country = res.Get("country").String()
var prov = res.Get("region").String()
var city = res.Get("city").String()
var district = ""
if util.StringInSlice(country, []string{"TW", "MO", "HK"}) {
district = prov + " " + city
city = countryMap[country]
prov = ""
country = "CN"
}
country = countryMap[country]
var anycast = false
if res.Get("anycast").String() == "true" {
country = "ANYCAST"
prov = "ANYCAST"
city = ""
anycast = true
}
i := strings.Index(res.Get("org").String(), " ")
var owner string
if i == -1 {
owner = ""
} else {
owner = res.Get("org").String()[i:]
}
var asnumber = ""
// 有时候不返回asn或其本身没有asn
if strings.HasPrefix(res.Get("org").String(), "AS") {
asnumber = strings.Fields(strings.TrimPrefix(res.Get("org").String(), "AS"))[0]
}
//"loc": "34.0522,-118.2437",
var lat, lng float64
if res.Get("loc").String() != "" {
lat, _ = strconv.ParseFloat(strings.Split(res.Get("loc").String(), ",")[0], 32)
lng, _ = strconv.ParseFloat(strings.Split(res.Get("loc").String(), ",")[1], 32)
}
if anycast {
lat, lng = 0, 0
}
return &IPGeoData{
Asnumber: res.Get("asn").Get("asn").String(),
Asnumber: asnumber,
Country: country,
City: res.Get("city").String(),
Prov: res.Get("region").String(),
Isp: res.Get("asn").Get("domain").String(),
City: city,
Prov: prov,
District: district,
Owner: owner,
Lat: lat,
Lng: lng,
}, nil
}

100
ipgeo/ipinfoLocal.go Normal file
View File

@@ -0,0 +1,100 @@
package ipgeo
import (
"errors"
"net"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/nxtrace/NTrace-core/util"
"github.com/oschwald/maxminddb-golang"
)
const (
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 := getIPInfoLocalPath(); err != nil {
panic("Cannot find ipinfoLocal.mmdb")
}
region, err := maxminddb.Open(ipinfoDataBasePath)
if err != nil {
panic("Cannot open ipinfoLocal.mmdb at " + ipinfoDataBasePath)
}
defer func(region *maxminddb.Reader) {
err := region.Close()
if err != nil {
panic(err)
}
}(region)
var record interface{}
searchErr := region.Lookup(net.ParseIP(ip), &record)
if searchErr != nil {
return &IPGeoData{}, errors.New("no results")
}
recordMap := record.(map[string]interface{})
countryName := recordMap["country_name"].(string)
prov := ""
if recordMap["country"].(string) == "HK" {
countryName = "China"
prov = "Hong Kong"
}
if recordMap["country"].(string) == "TW" {
countryName = "China"
prov = "Taiwan"
}
return &IPGeoData{
Asnumber: strings.TrimPrefix(recordMap["asn"].(string), "AS"),
Country: countryName,
City: "",
Prov: prov,
Owner: recordMap["as_name"].(string),
}, nil
}

View File

@@ -1,18 +1,23 @@
package ipgeo
import (
"io/ioutil"
"io"
"net/http"
"time"
"github.com/tidwall/gjson"
)
func IPInSight(ip string) (*IPGeoData, error) {
resp, err := http.Get("https://ipinsight.io/query?ip=" + ip)
func IPInSight(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
client := &http.Client{
// 2 秒超时
Timeout: timeout,
}
resp, err := client.Get(token.BaseOrDefault("https://api.ipinsight.io/ip/") + ip + "?token=" + token.ipinsight)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

View File

@@ -1,7 +1,7 @@
package ipgeo
import (
"io/ioutil"
"io"
"log"
"net/http"
"os"
@@ -10,11 +10,11 @@ import (
"github.com/tidwall/gjson"
)
func IPSB(ip string) (*IPGeoData, error) {
url := "https://api.ip.sb/geoip/" + ip
func IPSB(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
url := token.BaseOrDefault("https://api.ip.sb/geoip/") + ip
client := &http.Client{
// 2 秒超时
Timeout: 2 * time.Second,
Timeout: timeout,
}
req, _ := http.NewRequest("GET", url, nil)
// 设置 UAip.sb 默认禁止 go-client User-Agent 的 api 请求
@@ -24,7 +24,7 @@ func IPSB(ip string) (*IPGeoData, error) {
log.Println("api.ip.sb 请求超时(2s)请切换其他API使用")
return nil, err
}
body, _ := ioutil.ReadAll(content.Body)
body, _ := io.ReadAll(content.Body)
res := gjson.ParseBytes(body)
if res.Get("country").String() == "" {
@@ -37,6 +37,6 @@ func IPSB(ip string) (*IPGeoData, error) {
Country: res.Get("country").String(),
City: res.Get("city").String(),
Prov: res.Get("region").String(),
Isp: res.Get("isp").String(),
Owner: res.Get("isp").String(),
}, nil
}

View File

@@ -1,12 +1,14 @@
package ipgeo
import (
"encoding/json"
"errors"
"strconv"
"sync"
"time"
"github.com/nxtrace/NTrace-core/wshandle"
"github.com/tidwall/gjson"
"github.com/xgadget-lab/nexttrace/wshandle"
)
/***
@@ -17,7 +19,7 @@ import (
* 运作模型可以理解为一个 Node 一直在等待数据,当获得一个新的任务后,转交给下一个协程,不再关注这个 Node 的下一步处理过程,并且回到空闲状态继续等待新的任务
***/
// IP 查询池 map - ip - ip channel
// IPPool IP 查询池 map - ip - ip channel
type IPPool struct {
pool map[string]chan IPGeoData
poolMux sync.Mutex
@@ -46,47 +48,75 @@ func receiveParse() {
// json解析 -> data
res := gjson.Parse(data)
// 根据返回的IP信息发送给对应等待回复的IP通道上
var domain string = res.Get("domain").String()
var domain = res.Get("domain").String()
if res.Get("domain").String() == "" {
domain = res.Get("owner").String()
}
m := make(map[string][]string)
err := json.Unmarshal([]byte(res.Get("router").String()), &m)
if err != nil {
// 此处是正常的因为有些IP没有路由信息
}
lat, _ := strconv.ParseFloat(res.Get("lat").String(), 32)
lng, _ := strconv.ParseFloat(res.Get("lng").String(), 32)
IPPools.pool[gjson.Parse(data).Get("ip").String()] <- IPGeoData{
Asnumber: res.Get("asnumber").String(),
Country: res.Get("country").String(),
Prov: res.Get("prov").String(),
City: res.Get("city").String(),
District: res.Get("district").String(),
Owner: domain,
Isp: res.Get("isp").String(),
Asnumber: res.Get("asnumber").String(),
Country: res.Get("country").String(),
CountryEn: res.Get("country_en").String(),
Prov: res.Get("prov").String(),
ProvEn: res.Get("prov_en").String(),
City: res.Get("city").String(),
CityEn: res.Get("city_en").String(),
District: res.Get("district").String(),
Owner: domain,
Lat: lat,
Lng: lng,
Isp: res.Get("isp").String(),
Whois: res.Get("whois").String(),
Prefix: res.Get("prefix").String(),
Router: m,
}
}
}
func LeoIP(ip string) (*IPGeoData, error) {
// 初始化通道 - 向池子里添加IP的Channel返回IP数据是通过字典中对应键为IP的Channel来获取的
// 当前的实现中,每次调用 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的值决定是否请求经纬度信息
if timeout < 5*time.Second {
timeout = 5 * time.Second
}
// 缓存中没有找到IP信息需要请求API获取
IPPools.poolMux.Lock()
defer IPPools.poolMux.Unlock()
// 如果之前已经被别的协程初始化过了就不用初始化了
if IPPools.pool[ip] == nil {
IPPools.pool[ip] = make(chan IPGeoData)
}
IPPools.poolMux.Unlock()
// 发送请求
sendIPRequest(ip)
// 同步开启监听
go receiveParse()
// 确保 receiveParse 只启动一次
receiveParseOnce.Do(func() {
go receiveParse()
})
// 拥塞,等待数据返回
select {
case res := <-IPPools.pool[ip]:
return &res, nil
// 5秒后依旧没有接收到返回的IP数据不再等待超时异常处理
case <-time.After(5 * time.Second):
// default:
case <-time.After(timeout):
// 这里不可以返回一个 nil否则在访问对象内部的键值的时候会报空指针的 Fatal Error
return &IPGeoData{}, errors.New("TimeOut")
}
}

View File

@@ -1,13 +1,24 @@
package ipgeo
import "github.com/nxtrace/NTrace-core/util"
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: "",
ipinfo: "",
ipinsight: util.GetenvDefault("NEXTTRACE_IPINSIGHT_TOKEN", ""),
ipinfo: util.GetenvDefault("NEXTTRACE_IPINFO_TOKEN", ""),
baseUrl: util.GetenvDefault("NEXTTRACE_IPAPI_BASE", ""),
ipleo: "NextTraceDemo",
}

150
main.go
View File

@@ -1,155 +1,9 @@
package main
import (
"flag"
"fmt"
"log"
"net"
"os"
"os/signal"
"strings"
"time"
fastTrace "github.com/xgadget-lab/nexttrace/fast_trace"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/xgadget-lab/nexttrace/printer"
"github.com/xgadget-lab/nexttrace/reporter"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/xgadget-lab/nexttrace/util"
"github.com/xgadget-lab/nexttrace/wshandle"
"github.com/nxtrace/NTrace-core/cmd"
)
var fSet = flag.NewFlagSet("", flag.ExitOnError)
var fastTest = fSet.Bool("f", false, "One-Key Fast Traceroute")
var tcpSYNFlag = fSet.Bool("T", false, "Use TCP SYN for tracerouting (default port is 80)")
var udpPackageFlag = fSet.Bool("U", false, "Use UDP Package for tracerouting (default port is 53 in UDP)")
var port = fSet.Int("p", 80, "Set SYN Traceroute Port")
var numMeasurements = fSet.Int("q", 3, "Set the number of probes per each hop.")
var parallelRequests = fSet.Int("r", 18, "Set ParallelRequests number. It should be 1 when there is a multi-routing.")
var maxHops = fSet.Int("m", 30, "Set the max number of hops (max TTL to be reached).")
var dataOrigin = fSet.String("d", "LeoMoeAPI", "Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight, IPAPI.com]")
var noRdns = fSet.Bool("n", false, "Disable IP Reverse DNS lookup")
var routePath = fSet.Bool("report", false, "Route Path")
var tablePrint = fSet.Bool("table", false, "Output trace results as table")
var classicPrint = fSet.Bool("classic", false, "Classic Output trace results like BestTrace")
var beginHop = fSet.Int("b", 1, "Set The Begin TTL")
var ver = fSet.Bool("V", false, "Print Version")
func printArgHelp() {
fmt.Println("\nArgs Error\nUsage : 'nexttrace [option...] HOSTNAME' or 'nexttrace HOSTNAME [option...]'\nOPTIONS: [-VTU] [-d DATAORIGIN.STR ] [ -m TTL ] [ -p PORT ] [ -q PROBES.COUNT ] [ -r PARALLELREQUESTS.COUNT ] [-rdns] [ -table ] -report")
fSet.PrintDefaults()
os.Exit(2)
}
func flagApply() string {
printer.Version()
target := ""
if len(os.Args) < 2 {
printArgHelp()
}
// flag parse
if !strings.HasPrefix(os.Args[1], "-") {
target = os.Args[1]
fSet.Parse(os.Args[2:])
} else {
fSet.Parse(os.Args[1:])
target = fSet.Arg(0)
}
// Print Version
if *ver {
printer.CopyRight()
os.Exit(0)
}
// -f Fast Test
if *fastTest {
fastTrace.FastTest(*tcpSYNFlag)
os.Exit(0)
}
if target == "" {
printArgHelp()
}
return target
}
func main() {
domain := flagApply()
if os.Getuid() != 0 {
log.Fatalln("Traceroute requires root/sudo privileges.")
}
var ip net.IP
if *tcpSYNFlag || *udpPackageFlag {
ip = util.DomainLookUp(domain, true)
} else {
ip = util.DomainLookUp(domain, false)
}
if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
w := wshandle.New()
w.Interrupt = make(chan os.Signal, 1)
signal.Notify(w.Interrupt, os.Interrupt)
defer func() {
w.Conn.Close()
}()
}
printer.PrintTraceRouteNav(ip, domain, *dataOrigin)
var m trace.Method = ""
switch {
case *tcpSYNFlag:
m = trace.TCPTrace
case *udpPackageFlag:
m = trace.UDPTrace
default:
m = trace.ICMPTrace
}
if !*tcpSYNFlag && *port == 80 {
*port = 53
}
var conf = trace.Config{
BeginHop: *beginHop,
DestIP: ip,
DestPort: *port,
MaxHops: *maxHops,
NumMeasurements: *numMeasurements,
ParallelRequests: *parallelRequests,
RDns: !*noRdns,
IPGeoSource: ipgeo.GetSource(*dataOrigin),
Timeout: 1 * time.Second,
}
if !*tablePrint {
if *classicPrint {
conf.RealtimePrinter = printer.ClassicPrinter
} else {
conf.RealtimePrinter = printer.RealtimePrinter
}
}
res, err := trace.Traceroute(m, conf)
if err != nil {
log.Fatalln(err)
}
if *tablePrint {
printer.TracerouteTablePrinter(res)
}
if *routePath {
r := reporter.New(res, ip.String())
r.Print()
}
cmd.Excute()
}

2
nt_config.yaml Normal file
View File

@@ -0,0 +1,2 @@
geofeedpath: ./geofeed.csv
ptrpath: ./ptr.csv

View File

@@ -1,5 +1,12 @@
#!/bin/bash
if [ "$1" = "http" ]; then
protocol="http"
else
protocol="https"
fi
Green_font="\033[32m"
Yellow_font="\033[33m"
Red_font="\033[31m"
@@ -17,18 +24,31 @@ checkSystemArch() {
arch=$(uname -m)
if [[ $arch == "x86_64" ]]; then
archParam="amd64"
fi
if [[ $arch == "aarch64" ]]; then
elif [[ $arch == "i386" ]]; then
archParam="386"
elif [[ $arch == "i686" ]]; then
archParam="386"
elif [[ $arch == "aarch64" ]]; then
archParam="arm64"
elif [[ $arch == "armv7l" ]] || [[ $arch == "armv7ml" ]]; then
archParam="armv7"
elif [[ $arch == "mips" ]]; then
archParam="mips"
fi
}
checkSystemDistribution() {
case "$OSTYPE" in
linux*)
linux*)
osDistribution="linux"
if [ ! -d "/usr/local" ];
then
downPath="/usr/bin/nexttrace"
else
downPath="/usr/local/bin/nexttrace"
fi
;;
*)
echo "unknown: $OSTYPE"
@@ -37,68 +57,46 @@ checkSystemDistribution() {
esac
}
getLocation() {
countryCode=$(curl -s "http://ip-api.com/line/?fields=countryCode")
}
installWgetPackage() {
echo -e "${Info} wget 正在安装中..."
# try apt
apt -h &> /dev/null
if [ $? -eq 0 ]; then
# 先更新一下数据源有些机器数据源比较老可能会404
apt update -y &> /dev/null
apt install wget -y &> /dev/null
fi
# try yum
yum -h &> /dev/null
if [ $? -eq 0 ]; then
yum install wget -y &> /dev/null
fi
# try dnf
dnf -h &> /dev/null
if [ $? -eq 0 ]; then
dnf install wget -y &> /dev/null
fi
# try pacman
pacman -h &> /dev/null
if [ $? -eq 0 ]; then
pacman -Sy
pacman -S wget
fi
}
checkWgetPackage() {
wget -h &> /dev/null
if [ $? -ne 0 ]; then
installWgetPackage
fi
}
downloadBinrayFile() {
echo -e "${Info} 获取最新版的 NextTrace 发行版文件信息"
# 简单说明一下Github提供了一个API可以获取最新发行版本的二进制文件下载地址对应的是browser_download_url根据刚刚测得的osDistribution、archParam获取对应的下载地址
latestURL=$(curl -s https://api.github.com/repos/xgadget-lab/nexttrace/releases/latest | grep -i "browser_download_url.*${osDistribution}.*${archParam}" | awk -F '"' '{print $4}')
if [ "$countryCode" == "CN" ]; then
echo -e "${Info} 检测到国内环境,正在使用镜像下载"
latestURL="https://ghproxy.com/"$latestURL
fi
echo -e "${Info} 正在下载 NextTrace 二进制文件..."
wget -O ${Temp_path} ${latestURL} &> /dev/null
if [ $? -eq 0 ];
then
changeMode
mv ${Temp_path} ${downPath}
echo -e "${Info} NextTrace 现在已经在您的系统中可用"
for i in {1..3}; do
downloadUrls=$(curl -sLf ${protocol}://www.nxtrace.org/api/dist/core/nexttrace_${osDistribution}_${archParam} --connect-timeout 1.5)
if [ $? -eq 0 ]; then
break
fi
done
if [ $? -eq 0 ]; then
primaryUrl=$(echo ${downloadUrls} | awk -F '|' '{print $1}')
backupUrl=$(echo ${downloadUrls} | awk -F '|' '{print $2}')
echo -e "${Info} 正在尝试从 Primary 节点下载 NextTrace"
for i in {1..3}; do
curl -sLf ${primaryUrl} -o ${Temp_path} --connect-timeout 1.5
if [ $? -eq 0 ]; then
changeMode
mv ${Temp_path} ${downPath}
echo -e "${Info} NextTrace 现在已经在您的系统中可用"
return
fi
done
if [ -z ${backupUrl} ]; then
echo -e "${Error} 从 Primary 节点下载失败,且 Backup 节点为空,无法下载 NextTrace"
exit 1
fi
echo -e "${Error} 从 Primary 节点下载失败,正在尝试从 Backup 节点下载 NextTrace"
for i in {1..3}; do
curl -sLf ${backupUrl} -o ${Temp_path} --connect-timeout 1.5
if [ $? -eq 0 ]; then
changeMode
mv ${Temp_path} ${downPath}
echo -e "${Info} NextTrace 现在已经在您的系统中可用"
return
fi
done
echo -e "${Error} NextTrace 下载失败,请检查您的网络是否正常"
exit 1
else
echo -e "${Error} NextTrace 下载失败,请检查您的网络是否正常"
exit 1
echo -e "${Error} 获取下载地址失败,请检查您的网络是否正常"
exit 1
fi
}
@@ -108,8 +106,8 @@ changeMode() {
runBinrayFileHelp() {
if [ -e ${downPath} ]; then
${downPath} -V
echo -e "${Tips} 一切准备就绪!使用命令 nexttrace 1.1.1.1 开始您的第一次路由测试吧~ 更多进阶命令玩法可以用 nexttrace -h 查看哦\n 关于软件卸载因为nexttrace是绿色版单文件卸载只需输入命令 rm /usr/local/bin/nexttrace 即可"
${downPath} --version
echo -e "${Tips} 一切准备就绪!使用命令 nexttrace 1.1.1.1 开始您的第一次路由测试吧~ 更多进阶命令玩法可以用 nexttrace -h 查看哦\n 关于软件卸载因为nexttrace是绿色版单文件卸载只需输入命令 rm ${downPath} 即可"
fi
}
@@ -117,11 +115,10 @@ runBinrayFileHelp() {
checkRootPermit
checkSystemDistribution
checkSystemArch
checkWgetPackage
# Download Procedure
getLocation
downloadBinrayFile
# Run Procedure
runBinrayFileHelp

61
pow/pow.go Normal file
View File

@@ -0,0 +1,61 @@
package pow
import (
"fmt"
"github.com/nxtrace/NTrace-core/util"
"github.com/tsosunchia/powclient"
"net/url"
"os"
"os/signal"
"syscall"
"time"
)
const (
baseURL = "/v3/challenge"
)
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()
getTokenParams.SNI = host
getTokenParams.Host = host
getTokenParams.UserAgent = util.UserAgent
proxyUrl := util.GetProxy()
if proxyUrl != nil {
getTokenParams.Proxy = proxyUrl
}
var (
token string
err error
done = make(chan bool)
)
// 在 goroutine 中处理阻塞调用
go func() {
for i := 0; i < 3; i++ {
token, err = powclient.RetToken(getTokenParams)
if err != nil {
continue // 如果失败则重试
}
done <- true // 成功后通知主线程
return
}
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") // 添加返回值
}
}

19
pow/pow_test.go Normal file
View File

@@ -0,0 +1,19 @@
package pow
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
func TestGetToken(t *testing.T) {
// 计时开始
start := time.Now()
token, err := GetToken("origin-fallback.nxtrace.org", "origin-fallback.nxtrace.org", "443")
// 计时结束
end := time.Now()
fmt.Println("耗时:", end.Sub(start))
fmt.Println("token:", token)
assert.NoError(t, err, "GetToken() returned an error")
}

View File

@@ -2,14 +2,18 @@ package printer
import (
"fmt"
"github.com/nxtrace/NTrace-core/config"
"github.com/nxtrace/NTrace-core/trace"
"github.com/nxtrace/NTrace-core/util"
"net"
"strings"
"github.com/fatih/color"
)
var version = "v0.0.0.alpha"
var buildDate = ""
var commitID = ""
var version = config.Version
var buildDate = config.BuildDate
var commitID = config.CommitID
func Version() {
fmt.Fprintf(color.Output, "%s %s %s %s\n",
@@ -21,15 +25,110 @@ func Version() {
}
func CopyRight() {
fmt.Println("XGadget-lab Leo (leo.moe) & Tso (tsosunchia@gmail.com) & Vincent (vincent.moe) & zhshch (xzhsh.ch)")
sponsor()
fmt.Fprintf(color.Output, "\n%s\n%s %s\n%s %s\n%s %s, %s, %s, %s\n%s %s\n",
color.New(color.FgCyan, color.Bold).Sprintf("%s", "NextTrace CopyRight"),
//color.New(color.FgGreen, color.Bold).Sprintf("%s", "Contact Us"),
//color.New(color.FgWhite, color.Bold).Sprintf("%s", "Feedback Email:"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "nt@moeqing.com"),
//color.New(color.FgWhite, color.Bold).Sprintf("%s", "HomePage:"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "github.com/nxtrace"),
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Honorary Founder:"),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "Leo"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@leo.moe"),
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Project Chair:"),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "Tso"),
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Core-Developer:"),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "Leo"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@leo.moe"),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "Vincent"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@vincent.moe"),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "zhshch"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "zhshch@athorx.com"),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "Tso"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"),
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Infra Maintainer:"),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "Tso"),
//color.New(color.FgWhite, color.Bold).Sprintf("%s", "NOC Manager:"),
//color.New(color.FgHiBlue, color.Bold).Sprintf("%s", "YekongTAT"),
//color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"),
)
//PluginCopyRight()
}
func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string) {
fmt.Println("IP Geo Data Provider: " + dataOrigin)
func sponsor() {
italic := "\x1b[3m%s\x1b[0m"
formatted := fmt.Sprintf(italic, "(Listed in no particular order)")
if ip.String() == domain {
fmt.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ip.String())
fmt.Fprintf(color.Output, "%s\n%s\n%s\n%s\n%s\n",
color.New(color.FgCyan, color.Bold).Sprintf("%s", "NextTrace Sponsored by"),
color.New(color.FgHiYellow, color.Bold).Sprintf("%s", "· DMIT.io"),
color.New(color.FgHiYellow, color.Bold).Sprintf("%s", "· Misaka.io"),
color.New(color.FgHiYellow, color.Bold).Sprintf("%s", "· Saltyfish.io"),
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", formatted),
)
}
//func PluginCopyRight() {
// fmt.Fprintf(color.Output, "%s\n%s %s\n\n",
// color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Map Plugin Author"),
// color.New(color.FgWhite, color.Bold).Sprintf("%s", "Tso"),
// color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"),
// )
//}
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 {
fmt.Printf("traceroute to %s (%s), 30 hops max, 32 byte packets\n", ip.String(), domain)
srcAddr += " ->"
}
if util.EnableHidDstIP == "" {
if ip.String() == domain {
fmt.Printf("%s %s, %d hops max, %d bytes payload, %s mode\n", srcAddr, ip.String(), maxHops, packetSize, strings.ToUpper(mode))
} else {
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("%s %s, %d hops max, %d bytes payload, %s mode\n", srcAddr, util.HideIPPart(ip.String()), maxHops, packetSize, strings.ToUpper(mode))
}
}
func applyLangSetting(h *trace.Hop) {
if len(h.Geo.Country) <= 1 {
//打印h.geo
if h.Geo.Whois != "" {
h.Geo.Country = h.Geo.Whois
} else {
if h.Geo.Source != "LeoMoeAPI" {
h.Geo.Country = "网络故障"
h.Geo.CountryEn = "Network Error"
} else {
h.Geo.Country = "未知"
h.Geo.CountryEn = "Unknown"
}
}
}
if h.Lang == "en" {
if h.Geo.Country == "Anycast" {
} else if h.Geo.Prov == "骨干网" {
h.Geo.Prov = "BackBone"
} else if h.Geo.ProvEn == "" {
h.Geo.Country = h.Geo.CountryEn
} else {
if h.Geo.CityEn == "" {
h.Geo.Country = h.Geo.ProvEn
h.Geo.Prov = h.Geo.CountryEn
h.Geo.City = ""
} else {
h.Geo.Country = h.Geo.CityEn
h.Geo.Prov = h.Geo.ProvEn
h.Geo.City = h.Geo.CountryEn
}
}
}
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/nxtrace/NTrace-core/trace"
)
type HopInfo int

32
printer/easy.go Normal file
View File

@@ -0,0 +1,32 @@
package printer
import (
"fmt"
"github.com/nxtrace/NTrace-core/trace"
)
func EasyPrinter(res *trace.Result, ttl int) {
for i := range res.Hops[ttl] {
if res.Hops[ttl][i].Address == nil {
fmt.Printf("%d|*||||||\n", ttl+1)
continue
}
applyLangSetting(&res.Hops[ttl][i]) // 应用语言设置
fmt.Printf(
"%d|%s|%s|%.2f|%s|%s|%s|%s|%s|%s|%.4f|%.4f\n",
ttl+1,
res.Hops[ttl][i].Address.String(),
res.Hops[ttl][i].Hostname,
float64(res.Hops[ttl][i].RTT.Microseconds())/1000,
res.Hops[ttl][i].Geo.Asnumber,
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].Geo.Lat,
res.Hops[ttl][i].Geo.Lng,
)
}
}

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"strings"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/nxtrace/NTrace-core/trace"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/nxtrace/NTrace-core/ipgeo"
)
// var dataOrigin string
@@ -20,6 +20,8 @@ import (
// }
// }
//此文件目前仅供classic_printer使用
const (
RED_PREFIX = "\033[1;31m"
GREEN_PREFIX = "\033[1;32m"
@@ -33,6 +35,7 @@ func HopPrinter(h trace.Hop, info HopInfo) {
if h.Address == nil {
fmt.Println("\t*")
} else {
applyLangSetting(&h) // 应用语言设置
txt := "\t"
if h.Hostname == "" {
@@ -44,6 +47,9 @@ func HopPrinter(h trace.Hop, info HopInfo) {
if h.Geo != nil {
txt += " " + formatIpGeoData(h.Address.String(), h.Geo)
}
for _, v := range h.MPLS {
txt += " " + v
}
switch info {
case IXP:
fmt.Print(CYAN_PREFIX)
@@ -74,12 +80,13 @@ func formatIpGeoData(ip string, data *ipgeo.IPGeoData) string {
// TODO: 判断阿里云和腾讯云内网,数据不足,有待进一步完善
// TODO: 移动IDC判断到Hop.fetchIPData函数减少API调用
if strings.HasPrefix(ip, "9.") {
res = append(res, "LAN Address")
} else if strings.HasPrefix(ip, "11.") {
res = append(res, "LAN Address")
} else if data.Country == "" {
res = append(res, "LAN Address")
//if strings.HasPrefix(ip, "9.") {
// res = append(res, "LAN Address")
//} else if strings.HasPrefix(ip, "11.") {
// res = append(res, "LAN Address")
//} else if data.Country == "" {
// res = append(res, "LAN Address")
if false {
} else {
// 有些IP的归属信息为空这个时候将ISP的信息填入
if data.Owner == "" {

View File

@@ -1,105 +1,94 @@
package printer
import (
"errors"
"net"
"testing"
"time"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/xgadget-lab/nexttrace/util"
)
func TestPrintTraceRouteNav(t *testing.T) {
PrintTraceRouteNav(util.DomainLookUp("1.1.1.1", false), "1.1.1.1", "dataOrigin")
}
var testGeo = &ipgeo.IPGeoData{
Asnumber: "TestAsnumber",
Country: "TestCountry",
Prov: "TestProv",
City: "TestCity",
District: "TestDistrict",
Owner: "TestOwner",
Isp: "TestIsp",
}
var testResult = &trace.Result{
Hops: [][]trace.Hop{
{
{
Success: true,
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
Hostname: "test",
TTL: 0,
RTT: 10 * time.Millisecond,
Error: nil,
Geo: testGeo,
},
{
Success: true,
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
Hostname: "test",
TTL: 0,
RTT: 10 * time.Millisecond,
Error: nil,
Geo: testGeo,
},
},
{
{
Success: false,
Address: nil,
Hostname: "",
TTL: 0,
RTT: 0,
Error: errors.New("test error"),
Geo: nil,
},
{
Success: true,
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
Hostname: "test",
TTL: 0,
RTT: 10 * time.Millisecond,
Error: nil,
Geo: nil,
},
},
{
{
Success: true,
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
Hostname: "test",
TTL: 0,
RTT: 0,
Error: nil,
Geo: &ipgeo.IPGeoData{},
},
{
Success: true,
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
Hostname: "",
TTL: 0,
RTT: 10 * time.Millisecond,
Error: nil,
Geo: testGeo,
},
},
},
}
// func TestTraceroutePrinter(t *testing.T) {
// TraceroutePrinter(testResult)
// func TestPrintTraceRouteNav(t *testing.T) {
// PrintTraceRouteNav(util.DomainLookUp("1.1.1.1", false), "1.1.1.1", "dataOrigin")
// }
func TestTracerouteTablePrinter(t *testing.T) {
TracerouteTablePrinter(testResult)
}
// var testGeo = &ipgeo.IPGeoData{
// Asnumber: "TestAsnumber",
// Country: "TestCountry",
// Prov: "TestProv",
// City: "TestCity",
// District: "TestDistrict",
// Owner: "TestOwner",
// Isp: "TestIsp",
// }
func TestRealtimePrinter(t *testing.T) {
RealtimePrinter(testResult, 0)
// RealtimePrinter(testResult, 1)
// RealtimePrinter(testResult, 2)
}
// var testResult = &trace.Result{
// Hops: [][]trace.Hop{
// {
// {
// Success: true,
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
// Hostname: "test",
// TTL: 0,
// RTT: 10 * time.Millisecond,
// Error: nil,
// Geo: testGeo,
// },
// {
// Success: true,
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
// Hostname: "test",
// TTL: 0,
// RTT: 10 * time.Millisecond,
// Error: nil,
// Geo: testGeo,
// },
// },
// {
// {
// Success: false,
// Address: nil,
// Hostname: "",
// TTL: 0,
// RTT: 0,
// Error: errors.New("test error"),
// Geo: nil,
// },
// {
// Success: true,
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
// Hostname: "test",
// TTL: 0,
// RTT: 10 * time.Millisecond,
// Error: nil,
// Geo: nil,
// },
// },
// {
// {
// Success: true,
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
// Hostname: "test",
// TTL: 0,
// RTT: 0,
// Error: nil,
// Geo: &ipgeo.IPGeoData{},
// },
// {
// Success: true,
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
// Hostname: "",
// TTL: 0,
// RTT: 10 * time.Millisecond,
// Error: nil,
// Geo: testGeo,
// },
// },
// },
// }
// // func TestTraceroutePrinter(t *testing.T) {
// // TraceroutePrinter(testResult)
// // }
// func TestTracerouteTablePrinter(t *testing.T) {
// TracerouteTablePrinter(testResult)
// }
// func TestRealtimePrinter(t *testing.T) {
// RealtimePrinter(testResult, 0)
// // RealtimePrinter(testResult, 1)
// // RealtimePrinter(testResult, 2)
// }

View File

@@ -2,15 +2,17 @@ package printer
import (
"fmt"
"github.com/nxtrace/NTrace-core/util"
"net"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/nxtrace/NTrace-core/trace"
)
func RealtimePrinter(res *trace.Result, ttl int) {
fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1))
// 去重
var latestIP string
tmpMap := make(map[string][]string)
@@ -48,30 +50,130 @@ func RealtimePrinter(res *trace.Result, ttl int) {
if blockDisplay {
fmt.Printf("%4s", "")
}
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
)
if net.ParseIP(ip).To4() == nil {
if util.EnableHidDstIP == "" || ip != util.DestIP {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip),
)
} else {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", util.HideIPPart(ip)),
)
}
} else {
if util.EnableHidDstIP == "" || ip != util.DestIP {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
)
} else {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", util.HideIPPart(ip)),
)
}
}
i, _ := strconv.Atoi(v[0])
if res.Hops[ttl][i].Geo.Asnumber != "" {
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber))
/*** CMIN2, CUG, CN2, CUII, CTG 改为壕金色高亮
/* 小孩子不懂事加着玩的
/* 此处的高亮不代表任何线路质量
/* 仅代表走了这部分的ASN
/* 如果使用这些ASN的IP同样会被高亮
***/
switch {
case res.Hops[ttl][i].Geo.Asnumber == "58807":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "10099":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "4809":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "9929":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "23764":
fallthrough
case res.Hops[ttl][i].Geo.Whois == "CTG-CN":
fallthrough
case res.Hops[ttl][i].Geo.Whois == "[CNC-BACKBONE]":
fallthrough
case res.Hops[ttl][i].Geo.Whois == "[CUG-BACKBONE]":
fallthrough
case res.Hops[ttl][i].Geo.Whois == "CMIN2-NET":
fallthrough
case strings.HasPrefix(res.Hops[ttl][i].Address.String(), "59.43."):
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiYellow, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber))
default:
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber))
}
} else {
fmt.Printf(" %-8s", "*")
}
if res.Hops[ttl][i].Geo.Country == "" {
res.Hops[ttl][i].Geo.Country = "LAN Address"
if net.ParseIP(ip).To4() != nil {
whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-")
if len(whoisFormat) > 1 {
whoisFormat[0] = strings.Join(whoisFormat[:2], "-")
}
if whoisFormat[0] != "" {
//如果以RFC或DOD开头那么为空
if !(strings.HasPrefix(whoisFormat[0], "RFC") ||
strings.HasPrefix(whoisFormat[0], "DOD")) {
whoisFormat[0] = "[" + whoisFormat[0] + "]"
} else {
whoisFormat[0] = ""
}
}
// CMIN2, CUII, CN2, CUG 改为壕金色高亮
switch {
case res.Hops[ttl][i].Geo.Asnumber == "58807":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "10099":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "4809":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "9929":
fallthrough
case res.Hops[ttl][i].Geo.Asnumber == "23764":
fallthrough
case whoisFormat[0] == "[CTG-CN]":
fallthrough
case whoisFormat[0] == "[CNC-BACKBONE]":
fallthrough
case whoisFormat[0] == "[CUG-BACKBONE]":
fallthrough
case whoisFormat[0] == "[CMIN2-NET]":
fallthrough
case strings.HasPrefix(res.Hops[ttl][i].Address.String(), "59.43."):
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiYellow, color.Bold).Sprintf("%-16s", whoisFormat[0]))
default:
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-16s", whoisFormat[0]))
}
}
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
color.New(color.FgHiBlack, color.Bold).Sprintf("%-22s", res.Hops[ttl][0].Hostname),
)
applyLangSetting(&res.Hops[ttl][i]) // 应用语言设置
if net.ParseIP(ip).To4() != nil {
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
color.New(color.FgHiBlack, color.Bold).Sprintf("%-39s", res.Hops[ttl][i].Hostname),
)
} else {
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
color.New(color.FgHiBlack, color.Bold).Sprintf("%-32s", res.Hops[ttl][i].Hostname),
)
}
for j := 1; j < len(v); j++ {
if len(v) == 2 || j == 1 {
@@ -84,6 +186,11 @@ func RealtimePrinter(res *trace.Result, ttl int) {
)
}
}
for _, v := range res.Hops[ttl][i].MPLS {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgHiBlack, color.Bold).Sprintf("\n %s", v),
)
}
fmt.Println()
blockDisplay = true
}

View File

@@ -0,0 +1,152 @@
package printer
import (
"fmt"
"net"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/nxtrace/NTrace-core/trace"
)
func RealtimePrinterWithRouter(res *trace.Result, ttl int) {
fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1))
// 去重
var latestIP string
tmpMap := make(map[string][]string)
for i, v := range res.Hops[ttl] {
if v.Address == nil && latestIP != "" {
tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*"))
continue
} else if v.Address == nil {
continue
}
if _, exist := tmpMap[v.Address.String()]; !exist {
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i))
// 首次进入
if latestIP == "" {
for j := 0; j < i; j++ {
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*"))
}
}
latestIP = v.Address.String()
}
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))
}
if latestIP == "" {
fmt.Fprintf(color.Output, "%s\n",
color.New(color.FgWhite, color.Bold).Sprintf("*"),
)
return
}
var blockDisplay = false
for ip, v := range tmpMap {
if blockDisplay {
fmt.Printf("%4s", "")
}
if net.ParseIP(ip).To4() == nil {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip),
)
} else {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
)
}
i, _ := strconv.Atoi(v[0])
if res.Hops[ttl][i].Geo.Asnumber != "" {
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber))
} else {
fmt.Printf(" %-8s", "*")
}
if net.ParseIP(ip).To4() != nil {
whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-")
if len(whoisFormat) > 1 {
whoisFormat[0] = strings.Join(whoisFormat[:2], "-")
}
if whoisFormat[0] != "" {
whoisFormat[0] = "[" + whoisFormat[0] + "]"
}
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-16s", whoisFormat[0]))
}
if res.Hops[ttl][i].Geo.Country == "" {
res.Hops[ttl][i].Geo.Country = "LAN Address"
}
if net.ParseIP(ip).To4() != nil {
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
color.New(color.FgHiBlack, color.Bold).Sprintf("%-39s", res.Hops[ttl][i].Hostname),
)
} else {
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
color.New(color.FgHiBlack, color.Bold).Sprintf("%-32s", res.Hops[ttl][i].Hostname),
)
}
for j := 1; j < len(v); j++ {
if len(v) == 2 || j == 1 {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]),
)
} else {
fmt.Fprintf(color.Output, " / %s",
color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]),
)
}
}
i = 0
fmt.Println()
if res.Hops[ttl][i].Geo != nil && !blockDisplay {
fmt.Fprintf(color.Output, "%s %s %s %s %s\n",
color.New(color.FgWhite, color.Bold).Sprintf("-"),
color.New(color.FgHiYellow, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prefix),
color.New(color.FgWhite, color.Bold).Sprintf("路由表"),
color.New(color.FgHiCyan, color.Bold).Sprintf("Beta"),
color.New(color.FgWhite, color.Bold).Sprintf("-"),
)
GetRouter(&res.Hops[ttl][i].Geo.Router, "AS"+res.Hops[ttl][i].Geo.Asnumber)
}
blockDisplay = true
}
}
func GetRouter(r *map[string][]string, node string) {
routeMap := *r
for _, v := range routeMap[node] {
if len(routeMap[v]) != 0 {
fmt.Fprintf(color.Output, " %s %s %s\n",
color.New(color.FgWhite, color.Bold).Sprintf("%s", routeMap[v][0]),
color.New(color.FgWhite, color.Bold).Sprintf("%s", v),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", node),
)
} else {
fmt.Fprintf(color.Output, " %s %s\n",
color.New(color.FgWhite, color.Bold).Sprintf("%s", v),
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", node),
)
}
}
}

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"strings"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/nxtrace/NTrace-core/ipgeo"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/nxtrace/NTrace-core/trace"
"github.com/fatih/color"
"github.com/rodaine/table"
@@ -37,14 +37,17 @@ func TracerouteTablePrinter(res *trace.Result) {
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, "", data.Owner)
} else {
if data.City != "" {
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country+", "+data.Prov+", "+data.City, data.Owner)
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.City+", "+data.Prov+", "+data.Country, data.Owner)
} else if data.Prov != "" {
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Prov+", "+data.Country, data.Owner)
} else {
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country + ", " + data.Prov, data.Owner)
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country, data.Owner)
}
}
}
}
fmt.Print("\033[H\033[2J")
// 打印表格
tbl.Print()
}
@@ -75,7 +78,7 @@ func tableDataGenerator(h trace.Hop) *rowData {
IP: IP,
Latency: lantency,
Country: "LAN Address",
Prov: "LAN Address",
Prov: "",
Owner: "",
}
} else if strings.HasPrefix(IP, "11.") {
@@ -84,7 +87,7 @@ func tableDataGenerator(h trace.Hop) *rowData {
IP: IP,
Latency: lantency,
Country: "LAN Address",
Prov: "LAN Address",
Prov: "",
Owner: "",
}
}
@@ -102,9 +105,9 @@ func tableDataGenerator(h trace.Hop) *rowData {
IP: IP,
Latency: lantency,
Asnumber: h.Geo.Asnumber,
Country: h.Geo.Country,
Prov: h.Geo.Prov,
City: h.Geo.City,
Country: h.Geo.CountryEn,
Prov: h.Geo.ProvEn,
City: h.Geo.CityEn,
District: h.Geo.District,
Owner: h.Geo.Owner,
}
@@ -117,9 +120,9 @@ func tableDataGenerator(h trace.Hop) *rowData {
h.Geo.Owner = h.Geo.Isp
}
r.Asnumber = h.Geo.Asnumber
r.Country = h.Geo.Country
r.Prov = h.Geo.Prov
r.City = h.Geo.City
r.Country = h.Geo.CountryEn
r.Prov = h.Geo.ProvEn
r.City = h.Geo.CityEn
r.District = h.Geo.District
r.Owner = h.Geo.Owner
return r

4
ptr.example.csv Normal file
View File

@@ -0,0 +1,4 @@
snge,SG,,Singapore
CXS,CN,Hunan,Changsha
LAX,US,California,Los Angeles
SJC,US,California,San Jose
1 snge SG Singapore
2 CXS CN Hunan Changsha
3 LAX US California Los Angeles
4 SJC US California San Jose

View File

@@ -6,8 +6,8 @@ import (
"strings"
"sync"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/nxtrace/NTrace-core/ipgeo"
"github.com/nxtrace/NTrace-core/trace"
)
type Reporter interface {
@@ -45,7 +45,7 @@ func experimentTag() {
func (r *reporter) generateRouteReportNode(ip string, ipGeoData ipgeo.IPGeoData, ttl uint16) {
var success bool = true
var success = true
defer r.wg.Done()
@@ -114,11 +114,13 @@ func (r *reporter) InitialBaseData() Reporter {
r.targetTTL = uint16(len(r.routeResult.Hops))
for i := uint16(0); i < r.targetTTL; i++ {
traceHop := r.routeResult.Hops[i][0]
if traceHop.Success {
currentIP := traceHop.Address.String()
r.wg.Add(1)
go r.generateRouteReportNode(currentIP, *traceHop.Geo, i)
if i < uint16(len(r.routeResult.Hops)) && len(r.routeResult.Hops[i]) > 0 {
traceHop := r.routeResult.Hops[i][0]
if traceHop.Success {
currentIP := traceHop.Address.String()
r.wg.Add(1)
go r.generateRouteReportNode(currentIP, *traceHop.Geo, i)
}
}
}

View File

@@ -5,8 +5,8 @@ import (
"testing"
"time"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/xgadget-lab/nexttrace/trace"
"github.com/nxtrace/NTrace-core/ipgeo"
"github.com/nxtrace/NTrace-core/trace"
)
var testResult = &trace.Result{

View File

@@ -1,37 +1,75 @@
package trace
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"log"
"net"
"os"
"strconv"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"github.com/nxtrace/NTrace-core/trace/internal"
)
type ICMPTracer struct {
Config
wg sync.WaitGroup
res Result
ctx context.Context
resCh chan Hop
icmpListen net.PacketConn
final int
finalLock sync.Mutex
wg sync.WaitGroup
res Result
ctx context.Context
inflightRequest map[int]chan Hop
inflightRequestRWLock sync.RWMutex
icmpListen net.PacketConn
final int
finalLock sync.Mutex
fetchLock sync.Mutex
}
var psize = 52
func (t *ICMPTracer) PrintFunc() {
defer t.wg.Done()
var ttl = t.Config.BeginHop - 1
for {
if t.AsyncPrinter != nil {
t.AsyncPrinter(&t.res)
}
// 接收的时候检查一下是不是 3 跳都齐了
if len(t.res.Hops)-1 > ttl {
if len(t.res.Hops[ttl]) == t.NumMeasurements {
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, ttl)
}
ttl++
if ttl == t.final-1 || ttl >= t.MaxHops-1 {
return
}
}
}
<-time.After(200 * time.Millisecond)
}
}
func (t *ICMPTracer) Execute() (*Result, error) {
t.inflightRequestRWLock.Lock()
t.inflightRequest = make(map[int]chan Hop)
t.inflightRequestRWLock.Unlock()
if len(t.res.Hops) > 0 {
return &t.res, ErrTracerouteExecuted
}
var err error
t.icmpListen, err = net.ListenPacket("ip4:1", "0.0.0.0")
t.icmpListen, err = internal.ListenICMP("ip4:icmp", t.SrcAddr)
if err != nil {
return &t.res, err
}
@@ -40,32 +78,52 @@ func (t *ICMPTracer) Execute() (*Result, error) {
var cancel context.CancelFunc
t.ctx, cancel = context.WithCancel(context.Background())
defer cancel()
t.resCh = make(chan Hop)
t.final = -1
go t.listenICMP()
t.wg.Add(1)
go t.PrintFunc()
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
t.inflightRequestRWLock.Lock()
t.inflightRequest[ttl] = make(chan Hop, t.NumMeasurements)
t.inflightRequestRWLock.Unlock()
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))
}
// 一组TTL全部退出收到应答或者超时终止以后再进行下一个TTL的包发送
t.wg.Wait()
<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
}
t.wg.Wait()
t.res.reduce(t.final)
if t.final != -1 {
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, ttl-1)
t.RealtimePrinter(&t.res, t.final-1)
}
} else {
for i := 0; i < t.NumMeasurements; i++ {
t.res.add(Hop{
Success: false,
Address: nil,
TTL: 30,
RTT: 0,
Error: ErrHopLimitTimeout,
})
}
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, t.MaxHops-1)
}
}
t.res.reduce(t.final)
return &t.res, nil
}
func (t *ICMPTracer) listenICMP() {
lc := NewPacketListener(t.icmpListen, t.ctx)
psize = t.Config.PktSize
go lc.Start()
for {
select {
@@ -75,59 +133,163 @@ func (t *ICMPTracer) listenICMP() {
if msg.N == nil {
continue
}
if binary.BigEndian.Uint16(msg.Msg[32:34]) != uint16(os.Getpid()&0xffff) {
// 如果类型为应答消息且应答消息包的进程ID与主进程相同时不跳过
if msg.Msg[0] != 0 || binary.BigEndian.Uint16(msg.Msg[4:6]) != uint16(os.Getpid()&0xffff) {
continue
}
}
dstip := net.IP(msg.Msg[24:28])
if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv4zero) {
// 匹配再继续解析包,否则直接丢弃
// log.Println(msg.Msg)
if msg.Msg[0] == 0 {
rm, err := icmp.ParseMessage(1, msg.Msg[:*msg.N])
if err != nil {
log.Println(err)
continue
}
switch rm.Type {
case ipv4.ICMPTypeTimeExceeded:
t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data)
case ipv4.ICMPTypeEchoReply:
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
default:
// log.Println("received icmp message of unknown type", rm.Type)
echoReply := rm.Body.(*icmp.Echo)
ttl := echoReply.Seq // This is the TTL value
if ttl > 100 {
continue
}
if msg.Peer.String() == t.DestIP.String() {
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, ttl)
}
continue
}
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) {
// 匹配再继续解析包,否则直接丢弃
rm, err := icmp.ParseMessage(1, msg.Msg[:*msg.N])
if err != nil {
log.Println(err)
continue
}
switch rm.Type {
case ipv4.ICMPTypeTimeExceeded:
t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data, int(ttl))
case ipv4.ICMPTypeEchoReply:
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, int(ttl))
//unreachable
case ipv4.ICMPTypeDestinationUnreachable:
t.handleICMPMessage(msg, 2, rm.Body.(*icmp.DstUnreach).Data, int(ttl))
default:
// log.Println("received icmp message of unknown type", rm.Type)
}
}
}
}
}
}
}
func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte) {
t.resCh <- Hop{
Success: true,
Address: msg.Peer,
func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte, ttl int) {
if icmpType == 2 {
if t.DestIP.String() != msg.Peer.String() {
return
}
}
t.inflightRequestRWLock.RLock()
defer t.inflightRequestRWLock.RUnlock()
mpls := extractMPLS(msg, data)
if _, ok := t.inflightRequest[ttl]; ok {
t.inflightRequest[ttl] <- Hop{
Success: true,
Address: msg.Peer,
MPLS: mpls,
}
}
}
func gernerateID(ttlInt int) int {
const IdFixedHeader = "10"
var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位
var ttl = fmt.Sprintf("%06b", ttlInt) //取TTL的后6位
var parity int
id := IdFixedHeader + processID + ttl
for _, c := range id {
if c == '1' {
parity++
}
}
if parity%2 == 0 {
id += "1"
} else {
id += "0"
}
res, _ := strconv.ParseInt(id, 2, 32)
return int(res)
}
func reverseID(id string) (int64, int64, error) {
if len(id) < 16 {
return 0, 0, errors.New("err")
}
ttl, err := strconv.ParseInt(id[9:15], 2, 32)
if err != nil {
return 0, 0, err
}
//process ID
processID, _ := strconv.ParseInt(id[2:9], 2, 32)
parity := 0
for i := 0; i < len(id)-1; i++ {
if id[i] == '1' {
parity++
}
}
if parity%2 == 1 {
if id[len(id)-1] == '0' {
// fmt.Println("Parity check passed.")
} else {
// fmt.Println("Parity check failed.")
return 0, 0, errors.New("err")
}
} else {
if id[len(id)-1] == '1' {
// fmt.Println("Parity check passed.")
} else {
// fmt.Println("Parity check failed.")
return 0, 0, errors.New("err")
}
}
return processID, ttl, nil
}
func (t *ICMPTracer) send(ttl int) error {
defer t.wg.Done()
if t.final != -1 && ttl > t.final {
return nil
}
//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: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
ID: id,
//Data: []byte("HELLO-R-U-THERE"),
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 {
@@ -141,11 +303,10 @@ func (t *ICMPTracer) send(ttl int) error {
if err := t.icmpListen.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
select {
case <-t.ctx.Done():
return nil
case h := <-t.resCh:
case h := <-t.inflightRequest[ttl]:
rtt := time.Since(start)
if t.final != -1 && ttl > t.final {
return nil
@@ -153,6 +314,7 @@ func (t *ICMPTracer) send(ttl int) error {
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()
@@ -167,10 +329,14 @@ func (t *ICMPTracer) send(ttl int) error {
h.TTL = ttl
h.RTT = rtt
h.fetchIPData(t.Config)
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
@@ -183,6 +349,7 @@ func (t *ICMPTracer) send(ttl int) error {
RTT: 0,
Error: ErrHopLimitTimeout,
})
}
return nil

View File

@@ -1,36 +1,73 @@
package trace
import (
"bytes"
"encoding/binary"
"log"
"net"
"os"
"strconv"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
"github.com/nxtrace/NTrace-core/trace/internal"
)
type ICMPTracerv6 struct {
Config
wg sync.WaitGroup
res Result
ctx context.Context
resCh chan Hop
icmpListen net.PacketConn
final int
finalLock sync.Mutex
wg sync.WaitGroup
res Result
ctx context.Context
resCh chan Hop
inflightRequest map[int]chan Hop
inflightRequestRWLock sync.RWMutex
icmpListen net.PacketConn
final int
finalLock sync.Mutex
fetchLock sync.Mutex
}
func (t *ICMPTracerv6) PrintFunc() {
defer t.wg.Done()
var ttl = t.Config.BeginHop - 1
for {
if t.AsyncPrinter != nil {
t.AsyncPrinter(&t.res)
}
// 接收的时候检查一下是不是 3 跳都齐了
if len(t.res.Hops)-1 > ttl {
if len(t.res.Hops[ttl]) == t.NumMeasurements {
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, ttl)
}
ttl++
if ttl == t.final-1 || ttl >= t.MaxHops-1 {
return
}
}
}
<-time.After(200 * time.Millisecond)
}
}
func (t *ICMPTracerv6) Execute() (*Result, error) {
t.inflightRequestRWLock.Lock()
t.inflightRequest = make(map[int]chan Hop)
t.inflightRequestRWLock.Unlock()
if len(t.res.Hops) > 0 {
return &t.res, ErrTracerouteExecuted
}
var err error
t.icmpListen, err = net.ListenPacket("ip6:58", "::")
t.icmpListen, err = internal.ListenICMP("ip6:58", t.SrcAddr)
if err != nil {
return &t.res, err
}
@@ -43,28 +80,67 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
t.final = -1
go t.listenICMP()
t.wg.Add(1)
go t.PrintFunc()
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
t.inflightRequestRWLock.Lock()
t.inflightRequest[ttl] = make(chan Hop, t.NumMeasurements)
t.inflightRequestRWLock.Unlock()
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))
}
// 一组TTL全部退出收到应答或者超时终止以后再进行下一个TTL的包发送
t.wg.Wait()
<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
}
// 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)
// }
// // 一组TTL全部退出收到应答或者超时终止以后再进行下一个TTL的包发送
// t.wg.Wait()
// if t.RealtimePrinter != nil {
// t.RealtimePrinter(&t.res, ttl-1)
// }
// if t.AsyncPrinter != nil {
// t.AsyncPrinter(&t.res)
// }
// }
t.wg.Wait()
t.res.reduce(t.final)
if t.final != -1 {
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, ttl-1)
t.RealtimePrinter(&t.res, t.final-1)
}
} else {
for i := 0; i < t.NumMeasurements; i++ {
t.res.add(Hop{
Success: false,
Address: nil,
TTL: 30,
RTT: 0,
Error: ErrHopLimitTimeout,
})
}
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, t.MaxHops-1)
}
}
t.res.reduce(t.final)
return &t.res, nil
}
func (t *ICMPTracerv6) listenICMP() {
lc := NewPacketListener(t.icmpListen, t.ctx)
psize = t.Config.PktSize
go lc.Start()
for {
select {
@@ -74,29 +150,109 @@ func (t *ICMPTracerv6) listenICMP() {
if msg.N == nil {
continue
}
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
if err != nil {
log.Println(err)
continue
if msg.Msg[0] == 129 {
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
if err != nil {
log.Println(err)
continue
}
echoReply, ok := rm.Body.(*icmp.Echo)
if ok {
ttl := echoReply.Seq // This is the TTL value
if ttl > 100 {
continue
}
if msg.Peer.String() == t.DestIP.String() {
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, ttl)
}
}
}
// log.Println(msg.Peer)
switch rm.Type {
case ipv6.ICMPTypeTimeExceeded:
t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data)
case ipv6.ICMPTypeEchoReply:
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
default:
// log.Println("received icmp message of unknown type", rm.Type)
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() == "::" {
continue
}
if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv6zero) {
// 匹配再继续解析包,否则直接丢弃
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, 0, rm.Body.(*icmp.TimeExceeded).Data, int(ttl))
case ipv6.ICMPTypeEchoReply:
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, int(ttl))
case ipv6.ICMPTypeDestinationUnreachable:
t.handleICMPMessage(msg, 2, rm.Body.(*icmp.DstUnreach).Data, int(ttl))
default:
// log.Println("received icmp message of unknown type", rm.Type)
}
}
}
}
// dstip := net.IP(msg.Msg[32:48])
// if binary.BigEndian.Uint16(msg.Msg[52:54]) != uint16(os.Getpid()&0xffff) {
// // // 如果类型为应答消息且应答消息包的进程ID与主进程相同时不跳过
// if binary.BigEndian.Uint16(msg.Msg[52:54]) != 0 {
// continue
// } else {
// if dstip.String() != "::" {
// continue
// }
// if msg.Peer.String() != t.DestIP.String() {
// continue
// }
// }
// }
// if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv6zero) {
// rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
// if err != nil {
// log.Println(err)
// continue
// }
// // log.Println(msg.Peer)
// switch rm.Type {
// case ipv6.ICMPTypeTimeExceeded:
// t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data)
// case ipv6.ICMPTypeEchoReply:
// t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
// default:
// // log.Println("received icmp message of unknown type", rm.Type)
// }
// }
}
}
}
func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte) {
t.resCh <- Hop{
Success: true,
Address: msg.Peer,
func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte, ttl int) {
if icmpType == 2 {
if t.DestIP.String() != msg.Peer.String() {
return
}
}
t.inflightRequestRWLock.RLock()
defer t.inflightRequestRWLock.RUnlock()
mpls := extractMPLS(msg, data)
if _, ok := t.inflightRequest[ttl]; ok {
t.inflightRequest[ttl] <- Hop{
Success: true,
Address: msg.Peer,
MPLS: mpls,
}
}
}
@@ -105,19 +261,31 @@ func (t *ICMPTracerv6) send(ttl int) error {
if t.final != -1 && ttl > t.final {
return nil
}
//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: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
ID: id,
//Data: []byte("HELLO-R-U-THERE"),
Data: data,
Seq: ttl,
},
}
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 {
@@ -135,7 +303,7 @@ func (t *ICMPTracerv6) send(ttl int) error {
select {
case <-t.ctx.Done():
return nil
case h := <-t.resCh:
case h := <-t.inflightRequest[ttl]:
rtt := time.Since(start)
if t.final != -1 && ttl > t.final {
return nil
@@ -157,7 +325,12 @@ func (t *ICMPTracerv6) send(ttl int) error {
h.TTL = ttl
h.RTT = rtt
h.fetchIPData(t.Config)
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
err := h.fetchIPData(t.Config)
if err != nil {
return err
}
t.res.add(h)

View File

@@ -0,0 +1,97 @@
//go:build darwin
package internal
import (
"context"
"errors"
"net"
"syscall"
"unsafe"
)
//go:linkname internetSocket net.internetSocket
func internetSocket(ctx context.Context, net string, laddr, raddr any, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
//go:linkname newIPConn net.newIPConn
func newIPConn(fd unsafe.Pointer) *net.IPConn
var (
errUnknownNetwork = errors.New("unknown network type")
errUnknownIface = errors.New("unknown network interface")
networkMap = map[string]string{
"ip4:icmp": "udp4",
"ip4:1": "udp4",
"ip6:icmp": "udp6",
"ip6:58": "udp6",
}
)
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
// 为兼容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 = -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
}
}
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

@@ -0,0 +1,9 @@
//go:build !darwin
package internal
import "net"
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
return net.ListenPacket(network, laddr)
}

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)

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,7 @@ import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/xgadget-lab/nexttrace/util"
"github.com/nxtrace/NTrace-core/util"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
@@ -31,7 +31,8 @@ type TCPTracer struct {
final int
finalLock sync.Mutex
sem *semaphore.Weighted
sem *semaphore.Weighted
fetchLock sync.Mutex
}
func (t *TCPTracer) Execute() (*Result, error) {
@@ -42,11 +43,16 @@ func (t *TCPTracer) Execute() (*Result, error) {
t.SrcIP, _ = util.LocalIPPort(t.DestIP)
var err error
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcIP.String())
if t.SrcAddr != "" {
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcAddr)
} else {
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcIP.String())
}
if err != nil {
return nil, err
}
t.icmp, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
t.icmp, err = icmp.ListenPacket("ip4:icmp", t.SrcAddr)
if err != nil {
return &t.res, err
}
@@ -55,7 +61,10 @@ func (t *TCPTracer) Execute() (*Result, error) {
var cancel context.CancelFunc
t.ctx, cancel = context.WithCancel(context.Background())
defer cancel()
t.inflightRequestLock.Lock()
t.inflightRequest = make(map[int]chan Hop)
t.inflightRequestLock.Unlock()
t.final = -1
go t.listenICMP()
@@ -71,15 +80,26 @@ func (t *TCPTracer) Execute() (*Result, error) {
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.Sleep(1 * time.Millisecond)
<-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.RealtimePrinter == nil {
t.wg.Wait()
@@ -145,7 +165,7 @@ func (t *TCPTracer) listenTCP() {
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
tcp, _ := tcpLayer.(*layers.TCP)
// 取得目标主机的Sequence Number
t.inflightRequestLock.Lock()
if ch, ok := t.inflightRequest[int(tcp.Ack-1)]; ok {
// 最后一跳
ch <- Hop{
@@ -153,6 +173,7 @@ func (t *TCPTracer) listenTCP() {
Address: msg.Peer,
}
}
t.inflightRequestLock.Unlock()
}
}
}
@@ -196,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))
@@ -213,7 +238,19 @@ func (t *TCPTracer) send(ttl int) error {
ComputeChecksums: true,
FixLengths: true,
}
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
desiredPayloadSize := t.Config.PktSize
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, tcpHeader, gopacket.Payload(payload)); err != nil {
return err
}
@@ -227,11 +264,11 @@ 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()
/*
// 这里属于 2个SenderN个Reciever的情况在哪里关闭Channel都容易导致Panic
// 这里属于 2个SenderN个Receiver的情况在哪里关闭Channel都容易导致Panic
defer func() {
t.inflightRequestLock.Lock()
close(hopCh)
@@ -265,7 +302,12 @@ func (t *TCPTracer) send(ttl int) error {
h.TTL = ttl
h.RTT = rtt
h.fetchIPData(t.Config)
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
err := h.fetchIPData(t.Config)
if err != nil {
return err
}
t.res.add(h)

View File

@@ -1,7 +1,7 @@
package trace
import (
"log"
"encoding/binary"
"math"
"math/rand"
"net"
@@ -10,14 +10,14 @@ import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/xgadget-lab/nexttrace/util"
"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 TCPTracerv6 struct {
type TCPTracerIPv6 struct {
Config
wg sync.WaitGroup
res Result
@@ -31,22 +31,23 @@ type TCPTracerv6 struct {
final int
finalLock sync.Mutex
sem *semaphore.Weighted
sem *semaphore.Weighted
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
}
t.SrcIP, _ = util.LocalIPPort(t.DestIP)
log.Println(util.LocalIPPort(t.DestIP))
t.SrcIP, _ = util.LocalIPPortv6(t.DestIP)
// log.Println(util.LocalIPPortv6(t.DestIP))
var err error
t.tcp, err = net.ListenPacket("ip6:tcp", t.SrcIP.String())
if err != nil {
return nil, err
}
t.icmp, err = icmp.ListenPacket("ip6:53", "::")
t.icmp, err = icmp.ListenPacket("ip6:ipv6-icmp", "::")
if err != nil {
return &t.res, err
}
@@ -63,21 +64,43 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
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
}
for i := 0; i < t.NumMeasurements; i++ {
t.wg.Add(1)
go t.send(ttl)
<-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval))
}
time.Sleep(1 * time.Millisecond)
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.RealtimePrinter == nil {
t.wg.Wait()
}
t.wg.Wait()
t.res.reduce(t.final)
return &t.res, nil
}
func (t *TCPTracerv6) listenICMP() {
func (t *TCPTracerIPv6) listenICMP() {
lc := NewPacketListener(t.icmp, t.ctx)
go lc.Start()
for {
@@ -85,20 +108,21 @@ func (t *TCPTracerv6) listenICMP() {
case <-t.ctx.Done():
return
case msg := <-lc.Messages:
// log.Println(msg)
if msg.N == nil {
continue
}
rm, err := icmp.ParseMessage(53, msg.Msg[:*msg.N])
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
if err != nil {
log.Println(err)
// log.Println(err)
continue
}
log.Println(msg.Peer)
switch rm.Type {
case ipv6.ICMPTypeTimeExceeded:
t.handleICMPMessage(msg, rm.Body.(*icmp.TimeExceeded).Data)
t.handleICMPMessage(msg)
case ipv6.ICMPTypeDestinationUnreachable:
t.handleICMPMessage(msg, rm.Body.(*icmp.DstUnreach).Data)
t.handleICMPMessage(msg)
default:
//log.Println("received icmp message of unknown type", rm.Type)
}
@@ -109,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()
@@ -118,6 +142,8 @@ func (t *TCPTracerv6) listenTCP() {
case <-t.ctx.Done():
return
case msg := <-lc.Messages:
// log.Println(msg)
// return
if msg.N == nil {
continue
}
@@ -144,26 +170,24 @@ func (t *TCPTracerv6) listenTCP() {
}
}
func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage, data []byte) {
header, err := util.GetICMPResponsePayload(data)
if err != nil {
return
}
sequenceNumber := util.GetTCPSeq(header)
func (t *TCPTracerIPv6) handleICMPMessage(msg ReceivedMessage) {
var sequenceNumber = binary.BigEndian.Uint32(msg.Msg[52:56])
t.inflightRequestLock.Lock()
defer t.inflightRequestLock.Unlock()
ch, ok := t.inflightRequest[int(sequenceNumber)]
if !ok {
return
}
// log.Println("发送数据", sequenceNumber)
ch <- Hop{
Success: true,
Address: msg.Peer,
}
// 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
@@ -176,7 +200,7 @@ func (t *TCPTracerv6) send(ttl int) error {
}
// 随机种子
r := rand.New(rand.NewSource(time.Now().UnixNano()))
_, srcPort := util.LocalIPPort(t.DestIP)
_, srcPort := util.LocalIPPortv6(t.DestIP)
ipHeader := &layers.IPv6{
SrcIP: t.SrcIP,
DstIP: t.DestIP,
@@ -185,6 +209,7 @@ func (t *TCPTracerv6) send(ttl int) error {
}
// 使用Uint16兼容32位系统防止在rand的时候因使用int32而溢出
sequenceNumber := uint32(r.Intn(math.MaxUint16))
tcpHeader := &layers.TCP{
SrcPort: layers.TCPPort(srcPort),
DstPort: layers.TCPPort(t.DestPort),
@@ -199,11 +224,23 @@ func (t *TCPTracerv6) send(ttl int) error {
ComputeChecksums: true,
FixLengths: true,
}
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
desiredPayloadSize := t.Config.PktSize
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, 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
}
@@ -212,8 +249,9 @@ func (t *TCPTracerv6) send(ttl int) error {
if _, err := t.tcp.WriteTo(buf.Bytes(), &net.IPAddr{IP: t.DestIP}); err != nil {
return err
}
// log.Println(ttl, sequenceNumber)
t.inflightRequestLock.Lock()
hopCh := make(chan Hop)
hopCh := make(chan Hop, 1)
t.inflightRequest[int(sequenceNumber)] = hopCh
t.inflightRequestLock.Unlock()
@@ -243,6 +281,8 @@ func (t *TCPTracerv6) send(ttl int) error {
h.TTL = ttl
h.RTT = rtt
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
h.fetchIPData(t.Config)
t.res.add(h)

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/nxtrace/NTrace-core/ipgeo"
)
func HopPrinter(h Hop) {

View File

@@ -2,20 +2,27 @@ package trace
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/nxtrace/NTrace-core/ipgeo"
"github.com/nxtrace/NTrace-core/util"
)
var (
ErrInvalidMethod = errors.New("invalid method")
ErrTracerouteExecuted = errors.New("traceroute already executed")
ErrHopLimitTimeout = errors.New("hop timeout")
geoCache = sync.Map{}
)
type Config struct {
SrcAddr string
BeginHop int
MaxHops int
NumMeasurements int
@@ -26,7 +33,16 @@ type Config struct {
Quic bool
IPGeoSource ipgeo.Source
RDns bool
AlwaysWaitRDNS bool
PacketInterval int
TTLInterval int
Lang string
DN42 bool
RealtimePrinter func(res *Result, ttl int)
AsyncPrinter func(res *Result)
PktSize int
Maptrace bool
DontFragment bool
}
type Method string
@@ -66,24 +82,28 @@ 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
}
return tracer.Execute()
result, err := tracer.Execute()
if err != nil && errors.Is(err, syscall.EPERM) {
err = fmt.Errorf("%w, please run as root", err)
}
return result, err
}
type Result struct {
Hops [][]Hop
lock sync.Mutex
Hops [][]Hop
lock sync.Mutex
TraceMapUrl string
}
func (s *Result) add(hop Hop) {
@@ -111,17 +131,211 @@ type Hop struct {
RTT time.Duration
Error error
Geo *ipgeo.IPGeoData
Lang string
MPLS []string
}
func (h *Hop) fetchIPData(c Config) (err error) {
if c.RDns && h.Hostname == "" {
ptr, err := net.LookupAddr(h.Address.String())
if err == nil && len(ptr) > 0 {
h.Hostname = ptr[0]
// DN42
if c.DN42 {
var ip string
// 首先查找 PTR 记录
r, err := util.LookupAddr(h.Address.String())
if err == nil && len(r) > 0 {
h.Hostname = r[0][:len(r[0])-1]
ip = h.Address.String() + "," + h.Hostname
}
h.Geo, _ = c.IPGeoSource(ip, c.Timeout, c.Lang, c.Maptrace)
return nil
}
if c.IPGeoSource != nil && h.Geo == nil {
h.Geo, err = c.IPGeoSource(h.Address.String())
// Debug Area
// c.AlwaysWaitRDNS = true
// Initialize a rDNS Channel
rDNSChan := make(chan []string)
fetchDoneChan := make(chan bool)
if c.RDns && h.Hostname == "" {
// Create a rDNS Query go-routine
go func() {
r, err := util.LookupAddr(h.Address.String())
if err != nil {
// No PTR Record
rDNSChan <- nil
} else {
// One PTR Record is found
rDNSChan <- r
}
}()
}
// Create Data Fetcher go-routine
go func() {
// Start to fetch IP Geolocation data
if c.IPGeoSource != nil && h.Geo == nil {
res := false
h.Lang = c.Lang
h.Geo, res = ipgeo.Filter(h.Address.String())
if !res {
timeout := c.Timeout
if c.Timeout < 2*time.Second {
timeout = 2 * time.Second
}
//h.Geo, err = c.IPGeoSource(h.Address.String(), timeout, c.Lang, c.Maptrace)
if cacheVal, ok := geoCache.Load(h.Address.String()); ok {
// 如果缓存中已有结果,直接使用
h.Geo = cacheVal.(*ipgeo.IPGeoData)
} else {
// 如果缓存中无结果,进行查询并将结果存入缓存
h.Geo, err = c.IPGeoSource(h.Address.String(), timeout, c.Lang, c.Maptrace)
if err == nil {
geoCache.Store(h.Address.String(), h.Geo)
}
}
}
}
// Fetch Done
fetchDoneChan <- true
}()
// Select Close Flag
var selectClose bool
if !c.AlwaysWaitRDNS {
select {
case <-fetchDoneChan:
// When fetch done signal received, stop waiting PTR record
case ptr := <-rDNSChan:
// process result
if err == nil && len(ptr) > 0 {
h.Hostname = ptr[0][:len(ptr[0])-1]
}
selectClose = true
}
} else {
select {
case ptr := <-rDNSChan:
// process result
if err == nil && len(ptr) > 0 {
h.Hostname = ptr[0]
}
// 1 second timeout
case <-time.After(time.Second * 1):
}
selectClose = true
}
// When Select Close, fetchDoneChan Received will also be closed
if selectClose {
// New a receiver to prevent channel congestion
<-fetchDoneChan
}
return
}
func extractMPLS(msg ReceivedMessage, data []byte) []string {
if util.DisableMPLS != "" {
return nil
}
if psize != 52 {
return nil
}
extensionOffset := 20 + 8 + psize
if len(data) <= extensionOffset {
return nil
}
extensionBody := data[extensionOffset:]
if len(extensionBody) < 8 || len(extensionBody)%8 != 0 {
return nil
}
tmp := fmt.Sprintf("%x", msg.Msg[:*msg.N])
index := strings.Index(tmp, strings.Repeat("01", psize-4)+"00004fff")
if index == -1 {
return nil
}
tmp = tmp[index+psize*2:]
//由于限制长度了
index1 := strings.Index(tmp, "00002000")
l := len(tmp[index1+4:])/8 - 2
//fmt.Printf("l:%d\n", l)
if l < 1 {
return nil
}
//去掉扩展头和MPLS头
tmp = tmp[index1+4+8*2:]
//fmt.Print(tmp)
var retStrList []string
for i := 0; i < l; i++ {
label, err := strconv.ParseInt(tmp[i*8+0:i*8+5], 16, 32)
if err != nil {
return nil
}
strSlice := fmt.Sprintf("%s", []byte(tmp[i*8+5:i*8+6]))
//fmt.Printf("\nstrSlice: %s\n", strSlice)
num, err := strconv.ParseUint(strSlice, 16, 64)
if err != nil {
return nil
}
binaryStr := fmt.Sprintf("%04s", strconv.FormatUint(num, 2))
//fmt.Printf("\nbinaryStr: %s\n", binaryStr)
tc, err := strconv.ParseInt(binaryStr[:3], 2, 32)
if err != nil {
return nil
}
s := binaryStr[3:]
ttlMpls, err := strconv.ParseInt(tmp[i*8+6:i*8+8], 16, 32)
if err != nil {
return nil
}
//if i > 0 {
// retStr += "\n "
//}
retStrList = append(retStrList, fmt.Sprintf("[MPLS: Lbl %d, TC %d, S %s, TTL %d]", label, tc, s, ttlMpls))
}
//label, err := strconv.ParseInt(tmp[len(tmp)-8:len(tmp)-3], 16, 32)
//if err != nil {
// return ""
//}
//
//strSlice := fmt.Sprintf("%s", []byte(tmp[len(tmp)-3:len(tmp)-2]))
////fmt.Printf("\nstrSlice: %s\n", strSlice)
//
//num, err := strconv.ParseUint(strSlice, 16, 64)
//if err != nil {
// return ""
//}
//binaryStr := fmt.Sprintf("%04s", strconv.FormatUint(num, 2))
//
////fmt.Printf("\nbinaryStr: %s\n", binaryStr)
//tc, err := strconv.ParseInt(binaryStr[:3], 2, 32)
//if err != nil {
// return ""
//}
//s := binaryStr[3:]
//
//ttlMpls, err := strconv.ParseInt(tmp[len(tmp)-2:], 16, 32)
//if err != nil {
// return ""
//}
//
//retStr := fmt.Sprintf("Lbl %d, TC %d, S %s, TTL %d", label, tc, s, ttlMpls)
return retStrList
}

View File

@@ -2,13 +2,15 @@ package trace
import (
"log"
"math/rand"
"net"
"strconv"
"sync"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/xgadget-lab/nexttrace/util"
"github.com/nxtrace/NTrace-core/util"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
@@ -28,7 +30,9 @@ type UDPTracer struct {
final int
finalLock sync.Mutex
sem *semaphore.Weighted
sem *semaphore.Weighted
fetchLock sync.Mutex
udpMutex sync.Mutex
}
func (t *UDPTracer) Execute() (*Result, error) {
@@ -37,7 +41,7 @@ func (t *UDPTracer) Execute() (*Result, error) {
}
var err error
t.icmp, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
t.icmp, err = icmp.ListenPacket("ip4:icmp", t.SrcAddr)
if err != nil {
return &t.res, err
}
@@ -52,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
@@ -60,17 +64,25 @@ func (t *UDPTracer) Execute() (*Result, error) {
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.Sleep(1 * time.Millisecond)
<-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.RealtimePrinter == nil {
if t.AsyncPrinter != nil {
t.wg.Wait()
}
t.res.reduce(t.final)
@@ -100,7 +112,7 @@ func (t *UDPTracer) listenICMP() {
case ipv4.ICMPTypeDestinationUnreachable:
t.handleICMPMessage(msg, rm.Body.(*icmp.DstUnreach).Data)
default:
log.Println("received icmp message of unknown type", rm.Type)
// log.Println("received icmp message of unknown type", rm.Type)
}
}
}
@@ -113,8 +125,8 @@ func (t *UDPTracer) handleICMPMessage(msg ReceivedMessage, data []byte) {
return
}
srcPort := util.GetUDPSrcPort(header)
//t.inflightRequestLock.Lock()
//defer t.inflightRequestLock.Unlock()
t.inflightRequestLock.Lock()
defer t.inflightRequestLock.Unlock()
ch, ok := t.inflightRequest[int(srcPort)]
if !ok {
return
@@ -125,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 = ""
@@ -135,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 {
@@ -157,49 +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,
}
if err := gopacket.SerializeLayers(buf, opts, udpHeader, gopacket.Payload("HAJSFJHKAJSHFKJHAJKFHKASHKFHHKAFKHFAHSJK")); 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() {
@@ -218,7 +278,7 @@ func (t *UDPTracer) send(ttl int) error {
}
hopCh <- Hop{
Success: true,
Address: peer,
Address: &net.IPAddr{IP: peer.(*net.UDPAddr).IP},
}
}()
@@ -230,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 {
@@ -248,7 +307,12 @@ func (t *UDPTracer) send(ttl int) error {
h.TTL = ttl
h.RTT = rtt
h.fetchIPData(t.Config)
t.fetchLock.Lock()
defer t.fetchLock.Unlock()
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
}

116
tracelog/log.go Normal file
View File

@@ -0,0 +1,116 @@
package tracelog
import (
"fmt"
"io"
"log"
"net"
"os"
"strconv"
"strings"
"github.com/nxtrace/NTrace-core/trace"
)
func RealtimePrinter(res *trace.Result, ttl int) {
f, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
return
}
defer func(f *os.File) {
err := f.Close()
if err != nil {
log.Fatal(err)
}
}(f)
multiWriter := io.MultiWriter(os.Stdout, f)
log.SetOutput(multiWriter)
log.SetFlags(0)
var resStr string
resStr += fmt.Sprintf("%-2d ", ttl+1)
// 去重
var latestIP string
tmpMap := make(map[string][]string)
for i, v := range res.Hops[ttl] {
if v.Address == nil && latestIP != "" {
tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*"))
continue
} else if v.Address == nil {
continue
}
if _, exist := tmpMap[v.Address.String()]; !exist {
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i))
// 首次进入
if latestIP == "" {
for j := 0; j < i; j++ {
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*"))
}
}
latestIP = v.Address.String()
}
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))
}
if latestIP == "" {
resStr += fmt.Sprintf("%s\n", "*")
log.Print(resStr)
return
}
var blockDisplay = false
for ip, v := range tmpMap {
if blockDisplay {
resStr += fmt.Sprintf("%4s", "")
}
if net.ParseIP(ip).To4() == nil {
resStr += fmt.Sprintf("%-25s ", ip)
} else {
resStr += fmt.Sprintf("%-15s ", ip)
}
i, _ := strconv.Atoi(v[0])
if res.Hops[ttl][i].Geo.Asnumber != "" {
resStr += fmt.Sprintf("AS%-7s", res.Hops[ttl][i].Geo.Asnumber)
} else {
resStr += fmt.Sprintf(" %-8s", "*")
}
if net.ParseIP(ip).To4() != nil {
whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-")
if len(whoisFormat) > 1 {
whoisFormat[0] = strings.Join(whoisFormat[:2], "-")
}
if whoisFormat[0] != "" {
whoisFormat[0] = "[" + whoisFormat[0] + "]"
}
resStr += fmt.Sprintf("%-16s", whoisFormat[0])
}
if res.Hops[ttl][i].Geo.Country == "" {
res.Hops[ttl][i].Geo.Country = "LAN Address"
}
if net.ParseIP(ip).To4() != nil {
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 {
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 {
resStr += v[j]
} else {
resStr += fmt.Sprintf("/ %s", v[j])
}
}
log.Print(resStr)
blockDisplay = true
}
}

78
tracemap/tracemap.go Normal file
View File

@@ -0,0 +1,78 @@
package tracemap
import (
"crypto/tls"
"errors"
"fmt"
"github.com/fatih/color"
"github.com/nxtrace/NTrace-core/util"
"io"
"net"
"net/http"
"net/url"
"strings"
"time"
)
func GetMapUrl(r string) (string, error) {
host, port := util.GetHostAndPort()
var fastIp string
// 如果 host 是一个 IP 使用默认域名
if valid := net.ParseIP(host); valid != nil {
fastIp = host
if len(strings.Split(fastIp, ":")) > 1 {
fastIp = "[" + fastIp + "]"
}
host = "origin-fallback.nxtrace.org"
} else {
// 默认配置完成,开始寻找最优 IP
fastIp = util.GetFastIP(host, port, false)
}
u := url.URL{Scheme: "https", Host: fastIp + ":" + port, Path: "/tracemap/api"}
tracemapUrl := u.String()
client := &http.Client{
Timeout: 5 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
ServerName: host,
},
},
}
proxyUrl := util.GetProxy()
if proxyUrl != nil {
client.Transport.(*http.Transport).Proxy = http.ProxyURL(proxyUrl)
}
req, err := http.NewRequest("POST", tracemapUrl, strings.NewReader(r))
if err != nil {
return "", errors.New("an issue occurred while connecting to the tracemap API")
}
req.Header.Add("User-Agent", util.UserAgent)
req.Host = host
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", errors.New("an issue occurred while connecting to the tracemap API")
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
return
}
}(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", errors.New("an issue occurred while connecting to the tracemap API")
}
return string(body), nil
}
func PrintMapUrl(r string) {
_, err := fmt.Fprintf(color.Output, "%s %s\n",
color.New(color.FgWhite, color.Bold).Sprintf("%s", "MapTrace URL:"),
color.New(color.FgBlue, color.Bold).Sprintf("%s", r),
)
if err != nil {
return
}
}

23
util/common.go Normal file
View File

@@ -0,0 +1,23 @@
package util
import "net"
func Dnspod() *net.Resolver {
return newDoTResolver("dot.pub", "dot.pub:853")
}
func Aliyun() *net.Resolver {
return newDoTResolver("dns.alidns.com", "dns.alidns.com:853")
}
func DNSSB() *net.Resolver {
return newDoTResolver("45.11.45.11", "dot.sb:853")
}
func Cloudflare() *net.Resolver {
return newDoTResolver("one.one.one.one", "one.one.one.one:853")
}
func Google() *net.Resolver {
return newDoTResolver("dns.google", "dns.google:853")
}

20
util/dns_test.go Normal file
View File

@@ -0,0 +1,20 @@
package util
import (
"context"
"fmt"
"testing"
)
func TestDNS(t *testing.T) {
resolver := DNSSB()
ips, _ := resolver.LookupHost(context.Background(), "www.bing.com")
fmt.Println(ips)
}
func TestDomainLookUp(t *testing.T) {
ips, _ := DomainLookUp("pek-4134.endpoint.nxtrace.org.", "all", "", false)
fmt.Println(ips)
ips, _ = DomainLookUp("pek-4134.endpoint.nxtrace.org.", "4", "", false)
fmt.Println(ips)
}

32
util/dot.go Normal file
View File

@@ -0,0 +1,32 @@
package util
import (
"context"
"crypto/tls"
"net"
"time"
)
func newDoTResolver(serverName string, addrs string) *net.Resolver {
d := &net.Dialer{
// 设置超时时间
Timeout: 1000 * time.Millisecond,
}
tlsConfig := &tls.Config{
// 设置 TLS Server Name 以确保证书能和域名对应
ServerName: serverName,
}
return &net.Resolver{
// 指定使用 Go Build-in 的 DNS Resolver 来解析
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
conn, err := d.DialContext(ctx, "tcp", addrs)
if err != nil {
return nil, err
}
return tls.Client(conn, tlsConfig), nil
},
}
}

141
util/latency.go Normal file
View File

@@ -0,0 +1,141 @@
package util
import (
"crypto/tls"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"os/signal"
"strings"
"time"
"github.com/fatih/color"
)
type ResponseInfo struct {
IP string
Latency string
Content string
}
var (
results = make(chan ResponseInfo)
timeout = 5 * time.Second
)
var FastIpCache = ""
func GetFastIP(domain string, port string, enableOutput bool) string {
proxyUrl := GetProxy()
if proxyUrl != nil {
return "origin-fallback.nxtrace.org"
}
if FastIpCache != "" {
return FastIpCache
}
var ips []net.IP
var err error
if domain == "origin-fallback.nxtrace.org" {
ips, err = net.LookupIP("api.nxtrace.org")
} else {
ips, err = net.LookupIP(domain)
}
if err != nil {
log.Fatal("DNS resolution failed, please check your system DNS Settings")
}
if len(ips) == 0 {
// 添加默认IP 45.88.195.154
ips = append(ips, net.ParseIP("45.88.195.154"))
}
for _, ip := range ips {
go checkLatency(domain, ip.String(), port)
}
var result ResponseInfo
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
select {
case result = <-results:
// 正常返回结果
case <-time.After(timeout):
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 {
if enableOutput {
_, _ = fmt.Fprintf(color.Output, "%s preferred API IP - %s - %s - %s",
color.New(color.FgWhite, color.Bold).Sprintf("[NextTrace API]"),
color.New(color.FgGreen, color.Bold).Sprintf("%s", result.IP),
color.New(color.FgCyan, color.Bold).Sprintf("%sms", result.Latency),
color.New(color.FgGreen, color.Bold).Sprintf("%s", result.Content),
)
}
//}
//有些时候真的啥都不通,还是挑一个顶上吧
if result.IP == "" {
result.IP = "45.88.195.154"
}
FastIpCache = result.IP
return result.IP
}
func checkLatency(domain string, ip string, port string) {
start := time.Now()
if !strings.Contains(ip, ".") {
ip = "[" + ip + "]"
}
// 自定义DialContext以使用指定的IP连接
transport := &http.Transport{
//DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
// return net.DialTimeout(network, addr, 1*time.Second)
//},
TLSClientConfig: &tls.Config{
ServerName: domain,
},
TLSHandshakeTimeout: timeout,
}
client := &http.Client{
Transport: transport,
Timeout: timeout,
}
//此处虽然是 https://domain/ 但是实际上会使用指定的IP连接
req, err := http.NewRequest("GET", "https://"+ip+":"+port+"/", nil)
if err != nil {
// !!! 此处不要给results返回任何值
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
return
}
req.Host = domain
resp, err := client.Do(req)
if err != nil {
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
return
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
return
}
bodyString := string(bodyBytes)
latency := fmt.Sprintf("%.2f", float64(time.Since(start))/float64(time.Millisecond))
results <- ResponseInfo{IP: ip, Latency: latency, Content: bodyString}
}

6
util/latency_test.go Normal file
View File

@@ -0,0 +1,6 @@
package util
//github action test 不支持v6 这里会报错
//func TestGetFastIP(t *testing.T) {
// GetFastIP("origin-fallback.nxtrace.org", "443", true)
//}

12
util/udp.go Normal file
View File

@@ -0,0 +1,12 @@
package util
import (
"net"
)
func newUDPResolver() *net.Resolver {
return &net.Resolver{
// 指定使用 Go Build-in 的 DNS Resolver 来解析
PreferGo: true,
}
}

View File

@@ -1,67 +1,181 @@
package util
import (
"context"
"errors"
"fmt"
"github.com/nxtrace/NTrace-core/config"
"log"
"net"
"net/url"
"os"
"runtime"
"strings"
"sync"
"github.com/fatih/color"
)
// get the local ip and port based on our destination ip
func LocalIPPort(dstip net.IP) (net.IP, int) {
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) {
// 如果在缓存中找到,直接返回
if hostname, ok := RdnsCache.Load(addr); ok {
//fmt.Println("hit RdnsCache for", addr, hostname)
return []string{hostname.(string)}, nil
}
// 如果缓存中未找到,进行 DNS 查询
names, err := net.LookupAddr(addr)
if err != nil {
return nil, err
}
// 将查询结果存入缓存
if len(names) > 0 {
RdnsCache.Store(addr, names[0])
}
return names, nil
}
// 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)
}
// 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
}
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
}
func DomainLookUp(host string, ipv4Only bool) net.IP {
ips, err := net.LookupIP(host)
// 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 {
fmt.Println("Domain " + host + " Lookup Fail.")
os.Exit(1)
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)
}
var ipSlice = []net.IP{}
var ipv6Flag = false
// Otherwise, use the cached value (computed only once).
localIPOnce.Do(func() {
cachedLocalIP, cachedLocalPort = getLocalIPPort(dstip)
})
if cachedLocalIP != nil {
return cachedLocalIP, cachedLocalPort
}
return nil, -1
}
for _, ip := range ips {
if ipv4Only {
// 仅返回ipv4的ip
if ip.To4() != nil {
ipSlice = append(ipSlice, ip)
} else {
ipv6Flag = true
func LocalIPPortv6(dstip net.IP) (net.IP, int) {
// If NEXTTRACE_RANDOMPORT is set, bypass caching and return a new port every time.
// 该ENV仅对TCP Mode有效UDP Mode暂无办法固定Port
if GetenvDefault("NEXTTRACE_RANDOMPORT", "") != "" {
return getLocalIPPortv6(dstip)
}
// Otherwise, use the cached value (computed only once).
localIPv6Once.Do(func() {
cachedLocalIPv6, cachedLocalPort6 = getLocalIPPortv6(dstip)
})
if cachedLocalIPv6 != nil {
return cachedLocalIPv6, cachedLocalPort6
}
return nil, -1
}
func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput bool) (net.IP, error) {
// ipVersion: 4, 6, all
var (
r *net.Resolver
ips []net.IP
)
switch dotServer {
case "dnssb":
r = DNSSB()
case "aliyun":
r = Aliyun()
case "dnspod":
r = Dnspod()
case "google":
r = Google()
case "cloudflare":
r = Cloudflare()
default:
r = newUDPResolver()
}
ipsStr, err := r.LookupHost(context.Background(), host)
for _, v := range ipsStr {
ips = append(ips, net.ParseIP(v))
}
if err != nil {
return nil, errors.New("DNS lookup failed")
}
//var ipv6Flag = false
//TODO: 此处代码暂无意义
//if ipv6Flag {
// fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.")
// if len(ips) == 0 {
// os.Exit(0)
// }
//}
// Filter by IPv4/IPv6
if ipVersion != "all" {
var filteredIPs []net.IP
for _, ip := range ips {
if ipVersion == "4" && ip.To4() != nil {
filteredIPs = []net.IP{ip}
break
} else if ipVersion == "6" && strings.Contains(ip.String(), ":") {
filteredIPs = []net.IP{ip}
break
}
} else {
ipSlice = append(ipSlice, ip)
}
ips = filteredIPs
}
if ipv6Flag {
fmt.Println("[Info] IPv6 TCP/UDP Traceroute is not supported right now.")
if len(ipSlice) == 0 {
os.Exit(0)
}
}
if len(ipSlice) == 1 {
return ipSlice[0]
if (len(ips) == 1) || (disableOutput) {
return ips[0], nil
} else {
fmt.Println("Please Choose the IP You Want To TraceRoute")
for i, ip := range ipSlice {
for i, ip := range ips {
fmt.Fprintf(color.Output, "%s %s\n",
color.New(color.FgHiYellow, color.Bold).Sprintf("%d.", i),
color.New(color.FgWhite, color.Bold).Sprintf("%s", ip),
@@ -69,11 +183,102 @@ func DomainLookUp(host string, ipv4Only bool) net.IP {
}
var index int
fmt.Printf("Your Option: ")
fmt.Scanln(&index)
if index >= len(ipSlice) || index < 0 {
_, err := fmt.Scanln(&index)
if err != nil {
index = 0
}
if index >= len(ips) || index < 0 {
fmt.Println("Your Option is invalid")
os.Exit(3)
}
return ipSlice[index]
return ips[index], nil
}
}
func GetenvDefault(key, defVal string) string {
val, ok := os.LookupEnv(key)
if ok {
_, ok := os.LookupEnv("NEXTTRACE_DEBUG")
if ok {
fmt.Println("ENV", key, "detected as", val)
}
return val
}
return defVal
}
func GetHostAndPort() (host string, port string) {
var hostP = GetenvDefault("NEXTTRACE_HOSTPORT", "origin-fallback.nxtrace.org")
// 解析域名
hostArr := strings.Split(hostP, ":")
// 判断是否有指定端口
if len(hostArr) > 1 {
// 判断是否为 IPv6
if strings.HasPrefix(hostP, "[") {
tmp := strings.Split(hostP, "]")
host = tmp[0]
host = host[1:]
if port = tmp[1]; port != "" {
port = port[1:]
}
} else {
host, port = hostArr[0], hostArr[1]
}
} else {
host = hostP
}
if port == "" {
// 默认端口
port = "443"
}
return
}
func GetProxy() *url.URL {
proxyURLStr := GetenvDefault("NEXTTRACE_PROXY", "")
if proxyURLStr == "" {
return nil
}
proxyURL, err := url.Parse(proxyURLStr)
if err != nil {
log.Println("Failed to parse proxy URL:", err)
return nil
}
return proxyURL
}
func GetPowProvider() string {
var powProvider string
if PowProviderParam == "" {
powProvider = GetenvDefault("NEXTTRACE_POWPROVIDER", "api.nxtrace.org")
} else {
powProvider = PowProviderParam
}
if powProvider == "sakura" {
return "pow.nexttrace.owo.13a.com"
}
return ""
}
func StringInSlice(val string, list []string) bool {
for _, v := range list {
if v == val {
return true
}
}
return false
}
func HideIPPart(ip string) string {
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
return ""
}
if parsedIP.To4() != nil {
// IPv4: 隐藏后16位
return strings.Join(strings.Split(ip, ".")[:2], ".") + ".0.0/16"
}
// IPv6: 隐藏后96位
return parsedIP.Mask(net.CIDRMask(32, 128)).String() + "/32"
}

View File

@@ -1,10 +1,16 @@
package wshandle
import (
"crypto/tls"
"github.com/nxtrace/NTrace-core/pow"
"github.com/nxtrace/NTrace-core/util"
"log"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"strings"
"sync"
"time"
@@ -24,6 +30,10 @@ type WsConn struct {
}
var wsconn *WsConn
var host, port, fastIp string
var envToken = util.EnvToken
var cacheToken string
var cacheTokenFailedTimes int
func (c *WsConn) keepAlive() {
go func() {
@@ -31,7 +41,12 @@ func (c *WsConn) keepAlive() {
for {
<-time.After(time.Second * 54)
if c.Connected {
c.Conn.WriteMessage(websocket.TextMessage, []byte("ping"))
err := c.Conn.WriteMessage(websocket.TextMessage, []byte("ping"))
if err != nil {
log.Println(err)
c.Connected = false
return
}
}
}
}()
@@ -70,12 +85,12 @@ func (c *WsConn) messageSendHandler() {
// 循环监听发送
select {
case <-c.Done:
log.Println("发送协程已经退出")
log.Println("go-routine has been returned")
return
case t := <-c.MsgSendCh:
// log.Println(t)
if !c.Connected {
c.MsgReceiveCh <- `{"ip":"` + t + `", "asnumber":"API服务端异常"}`
c.MsgReceiveCh <- `{"ip":"` + t + `", "asnumber":"API Server Error"}`
} else {
err := c.Conn.WriteMessage(websocket.TextMessage, []byte(t))
if err != nil {
@@ -89,31 +104,72 @@ 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)
}
}
}
func (c *WsConn) recreateWsConn() {
u := url.URL{Scheme: "wss", Host: "api.leo.moe", Path: "/v2/ipGeoWs"}
// 尝试重新连线
u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"}
// log.Printf("connecting to %s", u.String())
ws, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
jwtToken, ua := envToken, []string{"Privileged Client"}
err := error(nil)
if envToken == "" {
// 无环境变量 token
if cacheToken == "" {
// 无cacheToken, 重新获取 token
if util.GetPowProvider() == "" {
jwtToken, err = pow.GetToken(fastIp, host, port)
} else {
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
}
if err != nil {
//log.Println(err)
//os.Exit(1)
panic(err)
}
} else {
// 使用 cacheToken
jwtToken = cacheToken
}
ua = []string{util.UserAgent}
}
cacheToken = jwtToken
requestHeader := http.Header{
"Host": []string{host},
"User-Agent": ua,
"Authorization": []string{"Bearer " + jwtToken},
}
dialer := websocket.DefaultDialer
dialer.TLSClientConfig = &tls.Config{
ServerName: host,
}
proxyUrl := util.GetProxy()
if proxyUrl != nil {
dialer.Proxy = http.ProxyURL(proxyUrl)
}
ws, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader)
c.Conn = ws
if err != nil {
log.Println("dial:", err)
// <-time.After(time.Second * 1)
c.Connected = false
c.Connecting = false
if cacheTokenFailedTimes > 3 {
cacheToken = ""
}
cacheTokenFailedTimes += 1
//fmt.Println("重连失败", cacheTokenFailedTimes, "次")
return
} else {
c.Connected = true
@@ -125,14 +181,56 @@ func (c *WsConn) recreateWsConn() {
}
func createWsConn() *WsConn {
proxyUrl := util.GetProxy()
//fmt.Println("正在连接 WS")
// 设置终端中断通道
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
u := url.URL{Scheme: "wss", Host: "api.leo.moe", Path: "/v2/ipGeoWs"}
host, port = util.GetHostAndPort()
// 如果 host 是一个 IP 使用默认域名
if valid := net.ParseIP(host); valid != nil {
fastIp = host
if len(strings.Split(fastIp, ":")) > 1 {
fastIp = "[" + fastIp + "]"
}
host = "origin-fallback.nxtrace.org"
} else {
// 默认配置完成,开始寻找最优 IP
fastIp = util.GetFastIP(host, port, true)
}
jwtToken, ua := envToken, []string{"Privileged Client"}
err := error(nil)
if envToken == "" {
if util.GetPowProvider() == "" {
jwtToken, err = pow.GetToken(fastIp, host, port)
} else {
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
}
if err != nil {
//log.Println(err)
//os.Exit(1)
panic(err)
}
ua = []string{util.UserAgent}
}
cacheToken = jwtToken
cacheTokenFailedTimes = 0
requestHeader := http.Header{
"Host": []string{host},
"User-Agent": ua,
"Authorization": []string{"Bearer " + jwtToken},
}
dialer := websocket.DefaultDialer
dialer.TLSClientConfig = &tls.Config{
ServerName: host,
}
if proxyUrl != nil {
dialer.Proxy = http.ProxyURL(proxyUrl)
}
u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"}
// log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
c, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader)
wsconn = &WsConn{
Conn: c,