mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dccc41b995 | ||
|
|
9af629b6f9 | ||
|
|
d6de649e60 | ||
|
|
bcd430c231 | ||
|
|
314a4b3015 | ||
|
|
4d8e7e322b | ||
|
|
8fb1220f1b | ||
|
|
90b1a3c1ad | ||
|
|
608a2904d4 | ||
|
|
62ab23bdeb | ||
|
|
c095599400 | ||
|
|
76d841f670 | ||
|
|
9dd36e9625 | ||
|
|
e1f4052518 | ||
|
|
b5df3efd1b | ||
|
|
3f9803680e | ||
|
|
db2b02d5f8 | ||
|
|
ec634fffb3 | ||
|
|
a3404cebac | ||
|
|
667285a8c3 | ||
|
|
c52d5a5414 | ||
|
|
6c009602b5 | ||
|
|
a179608da0 | ||
|
|
5cd2962a2b | ||
|
|
f774c0d29f |
15
.github/workflows/triggerDebRepo.yml
vendored
Normal file
15
.github/workflows/triggerDebRepo.yml
vendored
Normal 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 }}"}}'
|
||||
81
README.md
81
README.md
@@ -57,67 +57,78 @@ Please note, there are exceptions to this synchronization. If a version of NTrac
|
||||
|
||||
* Linux
|
||||
* One-click installation script
|
||||
|
||||
```shell
|
||||
curl nxtrace.org/nt |bash
|
||||
```
|
||||
|
||||
* Install nxtrace from the APT repository
|
||||
* Supports AMD64/ARM64 architectures
|
||||
```shell
|
||||
echo "deb [trusted=yes] https://github.com/nxtrace/nexttrace-debs/releases/latest/download ./" |
|
||||
sudo tee /etc/apt/sources.list.d/nexttrace.list
|
||||
sudo apt update
|
||||
sudo apt install nexttrace
|
||||
```
|
||||
* APT repository maintained by wcbing and nxtrace
|
||||
|
||||
* Arch Linux AUR installation command
|
||||
* Directly download bin package (only supports amd64)
|
||||
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
* Build from source (only supports amd64)
|
||||
|
||||
```shell
|
||||
yay -S nexttrace
|
||||
```
|
||||
```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)
|
||||
* 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
|
||||
```
|
||||
* Termux installation command
|
||||
|
||||
|
||||
* [x-cmd](https://www.x-cmd.com/pkg/nexttrace) installation command
|
||||
```shell
|
||||
pkg install nexttrace-enhanced
|
||||
x env use nexttrace
|
||||
```
|
||||
|
||||
* Termux installation command
|
||||
```shell
|
||||
pkg install root-repo
|
||||
pkg install nexttrace
|
||||
```
|
||||
|
||||
* macOS
|
||||
* macOS Homebrew's installation command
|
||||
* Homebrew-core version
|
||||
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
* This repository's ACTIONS automatically built version (updates faster)
|
||||
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* The homebrew-core build is maintained by chenrui333, please note that this version's updates may lag behind the repository Action automatically version
|
||||
|
||||
* Windows
|
||||
* Windows WinGet installation command
|
||||
* WinGet version
|
||||
```powershell
|
||||
winget install nexttrace
|
||||
```
|
||||
* WinGet build maintained by Dragon1573
|
||||
|
||||
* Windows Scoop installation command
|
||||
* Scoop-extras version
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
* Scoop-extra is maintained by soenggam
|
||||
|
||||
* X-CMD
|
||||
* [x-cmd](https://x-cmd.com) is a lightweight cross-platform package manager implemented in posix shell. Quickly download and execute `nexttrace` with a single command: [`x nexttrace`](https://x-cmd.com/pkg/nexttrace)
|
||||
* You can also install `nexttrace` in the user level without requiring root privileges.
|
||||
```shell
|
||||
x env use nexttrace
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
103
README_zh_CN.md
103
README_zh_CN.md
@@ -64,69 +64,78 @@ Document Language: [English](README.md) | 简体中文
|
||||
|
||||
* Linux
|
||||
* 一键安装脚本
|
||||
|
||||
```shell
|
||||
curl nxtrace.org/nt | bash
|
||||
```
|
||||
|
||||
* Arch Linux AUR 安装命令
|
||||
* 直接下载bin包(仅支持amd64)
|
||||
|
||||
* 从 nxtrace的APT源安装
|
||||
* 支持 AMD64/ARM64 架构
|
||||
```shell
|
||||
echo "deb [trusted=yes] https://github.com/nxtrace/nexttrace-debs/releases/latest/download ./" |
|
||||
sudo tee /etc/apt/sources.list.d/nexttrace.list
|
||||
sudo apt update
|
||||
sudo apt install nexttrace
|
||||
```
|
||||
* APT源由 wcbing, nxtrace 维护
|
||||
|
||||
* Arch Linux AUR 安装命令
|
||||
* 直接下载bin包(仅支持amd64)
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
* 从源码构建(仅支持amd64)
|
||||
```shell
|
||||
yay -S nexttrace
|
||||
```
|
||||
* AUR 的构建分别由 ouuan, huyz 维护
|
||||
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
* 从源码构建(仅支持amd64)
|
||||
|
||||
```shell
|
||||
yay -S nexttrace
|
||||
```
|
||||
* AUR 的构建分别由 ouuan, huyz 维护
|
||||
* Linuxbrew 安装命令
|
||||
|
||||
同macOS Homebrew安装方法(homebrew-core版仅支持amd64)
|
||||
* Deepin 安装命令
|
||||
|
||||
```shell
|
||||
apt install nexttrace
|
||||
```
|
||||
* Deepin 安装命令
|
||||
```shell
|
||||
apt install nexttrace
|
||||
```
|
||||
|
||||
* [x-cmd](https://cn.x-cmd.com/pkg/nexttrace) 安装命令
|
||||
```shell
|
||||
x env use nexttrace
|
||||
```
|
||||
|
||||
* Termux 安装命令
|
||||
|
||||
```shell
|
||||
pkg install nexttrace-enhanced
|
||||
```
|
||||
```shell
|
||||
pkg install root-repo
|
||||
pkg install nexttrace
|
||||
```
|
||||
|
||||
|
||||
* macOS
|
||||
* macOS Homebrew 安装命令
|
||||
* homebrew-core版
|
||||
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
* 本仓库ACTIONS自动构建版(更新更快)
|
||||
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* homebrew-core 构建由 chenrui333 维护,请注意该版本更新可能会落后仓库Action自动构建版本
|
||||
* homebrew-core版
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
* 本仓库ACTIONS自动构建版(更新更快)
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* homebrew-core 构建由 chenrui333 维护,请注意该版本更新可能会落后仓库Action自动构建版本
|
||||
|
||||
* Windows
|
||||
* Windows WinGet 安装命令
|
||||
* WinGet 版
|
||||
```powershell
|
||||
winget install nexttrace
|
||||
```
|
||||
* WinGet 构建由 Dragon1573 维护
|
||||
|
||||
* Windows Scoop 安装命令
|
||||
* scoop-extras版
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
|
||||
* scoop-extra 由 soenggam 维护
|
||||
|
||||
* X-CMD
|
||||
* [x-cmd](https://cn.x-cmd.com) 是一个使用 posix shell 实现的轻量级跨平台包管理器。使用单个命令快速下载并执行 `nexttrace` : [`x nexttrace`](https://cn.x-cmd.com/pkg/nexttrace)
|
||||
* 您还可以在用户级安装 `nexttrace`,且不需要 root 权限
|
||||
|
||||
```shell
|
||||
x env use nexttrace
|
||||
```
|
||||
* scoop-extras 版
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
* scoop-extra 由 soenggam 维护
|
||||
|
||||
请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。
|
||||
|
||||
|
||||
23
cmd/cmd.go
23
cmd/cmd.go
@@ -210,24 +210,19 @@ func Excute() {
|
||||
//
|
||||
//go func() {
|
||||
// defer wg.Done()
|
||||
if *udp {
|
||||
if *ipv6Only {
|
||||
fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *ipv6Only {
|
||||
ip, err = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
|
||||
} else if *ipv4Only {
|
||||
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
} else {
|
||||
if *ipv6Only {
|
||||
ip, err = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
|
||||
} else if *ipv4Only {
|
||||
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
} else {
|
||||
ip, err = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
|
||||
}
|
||||
ip, err = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
//fmt.Println(err)
|
||||
//os.Exit(1)
|
||||
panic(err)
|
||||
}
|
||||
//}()
|
||||
//
|
||||
|
||||
@@ -154,8 +154,7 @@ func FastTestv6(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFa
|
||||
case trace.TCPTrace:
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
case trace.UDPTrace:
|
||||
fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.")
|
||||
os.Exit(0)
|
||||
ft.TracerouteMethod = trace.UDPTrace
|
||||
}
|
||||
|
||||
switch c {
|
||||
|
||||
29
go.mod
29
go.mod
@@ -1,40 +1,35 @@
|
||||
module github.com/nxtrace/NTrace-core
|
||||
|
||||
go 1.23.4
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/akamensky/argparse v1.4.0
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
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.34.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/sync v0.13.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // 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.7.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -45,6 +40,6 @@ require (
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
54
go.sum
54
go.sum
@@ -8,36 +8,32 @@ 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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
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.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lionsoul2014/ip2region v2.11.2+incompatible h1:+VRsGcrHz8ewXI/2UzTptJlACsxD/p4xCxuql4u2nKU=
|
||||
github.com/lionsoul2014/ip2region v2.11.2+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/oschwald/maxminddb-golang v1.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.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -47,20 +43,18 @@ 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.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/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.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
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.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -88,32 +82,28 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
10
ipgeo/leo.go
10
ipgeo/leo.go
@@ -83,6 +83,11 @@ func receiveParse() {
|
||||
}
|
||||
}
|
||||
|
||||
// 当前的实现中,每次调用 receiveParse() 都会锁定 WebSocket 连接
|
||||
// 当前为单例模式,只启动一个 receiveParse 协程
|
||||
|
||||
var receiveParseOnce sync.Once
|
||||
|
||||
func LeoIP(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeoData, error) {
|
||||
// TODO: 根据lang的值请求中文/英文API
|
||||
// TODO: 根据maptrace的值决定是否请求经纬度信息
|
||||
@@ -100,7 +105,10 @@ func LeoIP(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeo
|
||||
// 发送请求
|
||||
sendIPRequest(ip)
|
||||
// 同步开启监听
|
||||
go receiveParse()
|
||||
// 确保 receiveParse 只启动一次
|
||||
receiveParseOnce.Do(func() {
|
||||
go receiveParse()
|
||||
})
|
||||
|
||||
// 拥塞,等待数据返回
|
||||
select {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
type TCPTracerv6 struct {
|
||||
type TCPTracerIPv6 struct {
|
||||
Config
|
||||
wg sync.WaitGroup
|
||||
res Result
|
||||
@@ -35,7 +35,7 @@ type TCPTracerv6 struct {
|
||||
fetchLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
func (t *TCPTracerIPv6) Execute() (*Result, error) {
|
||||
if len(t.res.Hops) > 0 {
|
||||
return &t.res, ErrTracerouteExecuted
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
return &t.res, nil
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) listenICMP() {
|
||||
func (t *TCPTracerIPv6) listenICMP() {
|
||||
lc := NewPacketListener(t.icmp, t.ctx)
|
||||
go lc.Start()
|
||||
for {
|
||||
@@ -133,7 +133,7 @@ func (t *TCPTracerv6) listenICMP() {
|
||||
|
||||
// @title listenTCP
|
||||
// @description 监听TCP的响应数据包
|
||||
func (t *TCPTracerv6) listenTCP() {
|
||||
func (t *TCPTracerIPv6) listenTCP() {
|
||||
lc := NewPacketListener(t.tcp, t.ctx)
|
||||
go lc.Start()
|
||||
|
||||
@@ -170,7 +170,7 @@ func (t *TCPTracerv6) listenTCP() {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
func (t *TCPTracerIPv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
var sequenceNumber = binary.BigEndian.Uint32(msg.Msg[52:56])
|
||||
|
||||
t.inflightRequestLock.Lock()
|
||||
@@ -187,7 +187,7 @@ func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
// log.Println("发送成功")
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) send(ttl int) error {
|
||||
func (t *TCPTracerIPv6) send(ttl int) error {
|
||||
err := t.sem.Acquire(context.Background(), 1)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -82,15 +82,13 @@ func Traceroute(method Method, config Config) (*Result, error) {
|
||||
if config.DestIP.To4() != nil {
|
||||
tracer = &UDPTracer{Config: config}
|
||||
} else {
|
||||
//TODO: IPv6 UDP trace 在做了,指新建文件夹(
|
||||
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
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -31,6 +32,7 @@ type UDPTracer struct {
|
||||
|
||||
sem *semaphore.Weighted
|
||||
fetchLock sync.Mutex
|
||||
udpMutex sync.Mutex
|
||||
}
|
||||
|
||||
func (t *UDPTracer) Execute() (*Result, error) {
|
||||
@@ -135,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 = ""
|
||||
@@ -145,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 {
|
||||
@@ -167,7 +193,15 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
srcIP, srcPort, udpConn := t.getUDPConn(0)
|
||||
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
|
||||
@@ -244,7 +278,7 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
}
|
||||
hopCh <- Hop{
|
||||
Success: true,
|
||||
Address: peer,
|
||||
Address: &net.IPAddr{IP: peer.(*net.UDPAddr).IP},
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -256,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 {
|
||||
391
trace/udp_ipv6.go
Normal file
391
trace/udp_ipv6.go
Normal 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
|
||||
}
|
||||
77
util/util.go
77
util/util.go
@@ -25,6 +25,12 @@ 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) {
|
||||
// 如果在缓存中找到,直接返回
|
||||
@@ -44,37 +50,70 @@ func LookupAddr(addr string) ([]string, error) {
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// LocalIPPort get the local ip and port based on our destination ip
|
||||
func LocalIPPort(dstip net.IP) (net.IP, int) {
|
||||
// getLocalIPPort encapsulates the logic to get local IP and port via a UDP connection
|
||||
func getLocalIPPort(dstip net.IP) (net.IP, int) {
|
||||
serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
con, err := net.DialUDP("udp", nil, serverAddr)
|
||||
if err != nil {
|
||||
return nil, -1
|
||||
}
|
||||
defer con.Close()
|
||||
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
|
||||
return udpaddr.IP, udpaddr.Port
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
// We don't actually connect to anything, but we can determine
|
||||
// based on our destination ip what source ip we should use.
|
||||
if con, err := net.DialUDP("udp", nil, serverAddr); err == nil {
|
||||
defer con.Close()
|
||||
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
|
||||
return udpaddr.IP, udpaddr.Port
|
||||
}
|
||||
// getLocalIPPortv6 encapsulates the logic to get local IPv6 and port via a UDP connection
|
||||
func getLocalIPPortv6(dstip net.IP) (net.IP, int) {
|
||||
serverAddr, err := net.ResolveUDPAddr("udp", "["+dstip.String()+"]:12345")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
con, err := net.DialUDP("udp", nil, serverAddr)
|
||||
if err != nil {
|
||||
return nil, -1
|
||||
}
|
||||
defer con.Close()
|
||||
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
|
||||
return udpaddr.IP, udpaddr.Port
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
// LocalIPPort returns the local IP and port based on our destination IP, with caching unless NEXTTRACE_RANDOMPORT is set.
|
||||
func LocalIPPort(dstip net.IP) (net.IP, int) {
|
||||
// If NEXTTRACE_RANDOMPORT is set, bypass caching and return a new port every time.
|
||||
if GetenvDefault("NEXTTRACE_RANDOMPORT", "") != "" {
|
||||
return getLocalIPPort(dstip)
|
||||
}
|
||||
|
||||
// Otherwise, use the cached value (computed only once).
|
||||
localIPOnce.Do(func() {
|
||||
cachedLocalIP, cachedLocalPort = getLocalIPPort(dstip)
|
||||
})
|
||||
if cachedLocalIP != nil {
|
||||
return cachedLocalIP, cachedLocalPort
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func LocalIPPortv6(dstip net.IP) (net.IP, int) {
|
||||
serverAddr, err := net.ResolveUDPAddr("udp", "["+dstip.String()+"]:12345")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
// If NEXTTRACE_RANDOMPORT is set, bypass caching and return a new port every time.
|
||||
// 该ENV仅对TCP Mode有效,UDP Mode暂无办法固定Port
|
||||
if GetenvDefault("NEXTTRACE_RANDOMPORT", "") != "" {
|
||||
return getLocalIPPortv6(dstip)
|
||||
}
|
||||
|
||||
// We don't actually connect to anything, but we can determine
|
||||
// based on our destination ip what source ip we should use.
|
||||
if con, err := net.DialUDP("udp", nil, serverAddr); err == nil {
|
||||
defer con.Close()
|
||||
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
|
||||
return udpaddr.IP, udpaddr.Port
|
||||
}
|
||||
// Otherwise, use the cached value (computed only once).
|
||||
localIPv6Once.Do(func() {
|
||||
cachedLocalIPv6, cachedLocalPort6 = getLocalIPPortv6(dstip)
|
||||
})
|
||||
if cachedLocalIPv6 != nil {
|
||||
return cachedLocalIPv6, cachedLocalPort6
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
@@ -104,16 +104,16 @@ func (c *WsConn) messageSendHandler() {
|
||||
err := c.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
if err != nil {
|
||||
// log.Println("write close:", err)
|
||||
os.Exit(1)
|
||||
//os.Exit(1)
|
||||
panic(err)
|
||||
}
|
||||
select {
|
||||
// 等到了结果,直接退出
|
||||
case <-c.Done:
|
||||
// 如果等待 1s 还是拿不到结果,不再等待,超时退出
|
||||
case <-time.After(time.Second):
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
os.Exit(1)
|
||||
// return
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,8 +134,9 @@ func (c *WsConn) recreateWsConn() {
|
||||
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
//log.Println(err)
|
||||
//os.Exit(1)
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
// 使用 cacheToken
|
||||
@@ -206,8 +207,9 @@ func createWsConn() *WsConn {
|
||||
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
//log.Println(err)
|
||||
//os.Exit(1)
|
||||
panic(err)
|
||||
}
|
||||
ua = []string{util.UserAgent}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user