From 1e437aa32e89274372e4def12cf0c6dc0dc351d7 Mon Sep 17 00:00:00 2001 From: spiritlhl <103393591+spiritLHLS@users.noreply.github.com> Date: Thu, 27 Jun 2024 06:49:13 +0000 Subject: [PATCH] v0.0.1 --- .github/workflows/ci.yaml | 31 +++++ .github/workflows/main.yaml | 126 ++++++++++++++++++ README.md | 56 +++++++- cmd/main.go | 43 +++++++ cmd/main_test.go | 7 + go.mod | 43 +++++++ go.sum | 118 +++++++++++++++++ model/model.go | 87 +++++++++++++ nt/nt.go | 247 ++++++++++++++++++++++++++++++++++++ nt3_install.sh | 86 +++++++++++++ 10 files changed, 843 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/main.yaml create mode 100644 cmd/main.go create mode 100644 cmd/main_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 model/model.go create mode 100644 nt/nt.go create mode 100644 nt3_install.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..0c53bbb --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,31 @@ +name: CI + +on: + workflow_dispatch: + +jobs: + test: + strategy: + matrix: + go: [ '1.22.x' ] + os: [ ubuntu-latest ] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go }} + - name: Test + run: go test ./... -coverprofile=coverage.txt + - name: Create Tag + if: success() # 仅在测试成功时运行 + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@github.com' + TAG="v0.0.1-$(date +'%Y%m%d%H%M%S')" + git tag $TAG + git push origin $TAG + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..cbce97f --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,126 @@ +name: Build and Release + +on: + # push: + # branches: [ main ] + # pull_request: + # branches: [ main ] + # release: + # types: [published] + workflow_dispatch: + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 'stable' + + - name: Test on Default Platform + run: | + go test -v ./... + + - name: Delete Existing Release Assets + run: | + release_id=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/oneclickvirt/nt3/releases/tags/output" | jq -r '.id') + echo "Deleting existing release assets..." + assets=$(curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/oneclickvirt/nt3/releases/$release_id/assets" | jq -r '.[] | .id') + for asset in $assets; do + echo "Deleting asset with ID: $asset" + curl -X DELETE -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/oneclickvirt/nt3/releases/assets/$asset" + done + sleep 60 + + release-binary: + name: Release Go Binary + runs-on: ubuntu-latest + needs: build + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 'stable' + + - name: Build and Release + run: | + mkdir -p bin + cd cmd + CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o ../bin/nt3-${{ matrix.goos }}-${{ matrix.goarch }} -v -ldflags="-extldflags=-static" . + + - name: Upload New Assets + run: | + release_id=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/oneclickvirt/nt3/releases/tags/output" | jq -r '.id') + echo "Uploading new assets to release..." + for file in ./bin/*; do + echo "Uploading $file to release..." + curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @"$file" \ + "https://uploads.github.com/repos/oneclickvirt/nt3/releases/$release_id/assets?name=$(basename "$file")" + rm -rf $file + done + + strategy: + matrix: + goos: [windows, freebsd, linux, darwin] + goarch: [amd64, 386] + exclude: + - goarch: 386 + goos: darwin + include: + - goos: windows + goarch: 386 + - goos: windows + goarch: amd64 + - goos: windows + goarch: arm64 + - goos: darwin + goarch: arm64 + - goos: linux + goarch: arm + goarm: 7 + - goos: linux + goarch: arm64 + - goos: linux + goarch: riscv64 + - goos: linux + goarch: mips64 + - goos: linux + goarch: mips64le + - goos: linux + goarch: mipsle + - goos: linux + goarch: mips + - goos: freebsd + goarch: arm64 + - goos: freebsd + goarch: arm + goarm: 7 + # - goos: linux + # goarch: mipsle + # gomips: softfloat + # - goos: linux + # goarch: mips + # gomips: softfloat + # - goos: linux + # goarch: arm + # goarm: 6 + # - goos: linux + # goarch: arm + # goarm: 5 + # - goos: linux + # goarch: ppc64 + # - goos: linux + # goarch: ppc64le + # - goos: windows + # goarch: arm + # goarm: 7 diff --git a/README.md b/README.md index cd05219..b277f0f 100644 --- a/README.md +++ b/README.md @@ -1 +1,55 @@ -# nt3 \ No newline at end of file +# nt3 + +[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Foneclickvirt%2Fnt3&count_bg=%232EFFF8&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) [![Build and Release](https://github.com/oneclickvirt/nt3/actions/workflows/main.yaml/badge.svg)](https://github.com/oneclickvirt/nt3/actions/workflows/main.yaml) + +三网路由查询模块 + +## 说明 + +- [x] 使用[nexttrace](https://github.com/nxtrace/NTrace-core)进行ICMP测试,预先加载定义好的广州、上海、北京、成都的三网地址 +- [x] 支持双语输出,以```-l```指定```zh```或```en```可指定输出的语言,未指定时默认使用中文输出 +- [x] 全平台编译支持(除了WIN) + +## 使用 + +下载及安装 + +``` +curl https://raw.githubusercontent.com/oneclickvirt/nt3/main/nt3_install.sh -sSf | bash +``` + +或 + +``` +curl https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/nt3/main/nt3_install.sh -sSf | bash +``` + +使用 + +``` +nt3 +``` + +或 + +``` +./nt3 +``` + +进行测试 + +无环境依赖,理论上适配所有系统和主流架构,更多架构请查看 https://github.com/oneclickvirt/nt3/releases/tag/output + +``` + +``` + +## 在Golang中使用 + +``` +go get github.com/oneclickvirt/nt3@latest +``` + +## Thanks + +https://github.com/nxtrace/NTrace-core diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..32b69cc --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "flag" + "fmt" + "net/http" + "strings" + + "github.com/oneclickvirt/nt3/model" + "github.com/oneclickvirt/nt3/nt" +) + +func main() { + go func() { + http.Get("https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Foneclickvirt%2Fnt3&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false") + }() + fmt.Println("项目地址:", "https://github.com/oneclickvirt/nt3") + var showVersion bool + var language, checkType, location string + flag.BoolVar(&showVersion, "v", false, "Show version information") + flag.StringVar(&language, "l", "", "Specify language parameter (en or zh, default is zh)") + flag.StringVar(&checkType, "c", "", "Specify check type (both, ipv4, or ipv6, default is ipv4)") + flag.StringVar(&location, "loc", "", "Specify location (supports GZ, BJ, SH, CD; corresponding to Guangzhou, Beijing, Shanghai, Chengdu)") + // flag.BoolVar(&model.EnableLoger, "log", false, "Enable logging") + flag.Parse() + if showVersion { + fmt.Println(model.NextTraceVersion) + return + } + if language == "" { + language = "zh" + } else { + language = strings.ToLower(language) + } + if checkType == "" || checkType == "ipv4" { + checkType = "ipv4" + } else if strings.ToLower(checkType) == "both" { + checkType = "both" + } else if strings.ToLower(checkType) == "ipv6" { + checkType = "ipv6" + } + nt.TraceRoute(language, location, checkType) +} diff --git a/cmd/main_test.go b/cmd/main_test.go new file mode 100644 index 0000000..45fcc77 --- /dev/null +++ b/cmd/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..09b3d35 --- /dev/null +++ b/go.mod @@ -0,0 +1,43 @@ +module github.com/oneclickvirt/nt3 + +go 1.22.4 + +require ( + github.com/fatih/color v1.17.0 + github.com/nxtrace/NTrace-core v1.3.1 +) + +require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/google/gopacket v1.1.19 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/lionsoul2014/ip2region v2.11.2+incompatible // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/oschwald/maxminddb-golang v1.12.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/rodaine/table v1.2.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tsosunchia/powclient v0.1.5 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0b86867 --- /dev/null +++ b/go.sum @@ -0,0 +1,118 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lionsoul2014/ip2region v2.11.2+incompatible h1:+VRsGcrHz8ewXI/2UzTptJlACsxD/p4xCxuql4u2nKU= +github.com/lionsoul2014/ip2region v2.11.2+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/nxtrace/NTrace-core v1.3.1 h1:f4z5UaZEuhUP/g6xElpZ2bo+guWITJVrMKrJTqd27oc= +github.com/nxtrace/NTrace-core v1.3.1/go.mod h1:0Px/Zc60qk6cssmP+yv4kstFxvX9sXqDduoVqBO+qf8= +github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= +github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rodaine/table v1.2.0 h1:38HEnwK4mKSHQJIkavVj+bst1TEY7j9zhLMWu4QJrMA= +github.com/rodaine/table v1.2.0/go.mod h1:wejb/q/Yd4T/SVmBSRMr7GCq3KlcZp3gyNYdLSBhkaE= +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.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.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/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tsosunchia/powclient v0.1.5 h1:hpixFWoPbWSEC0zc9osSltyjtr1+SnhCueZVLkEpyyU= +github.com/tsosunchia/powclient v0.1.5/go.mod h1:yNlzyq+w9llYZV+0q7nrX83ULy4ghq2mCjpTLJFJ2pg= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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= diff --git a/model/model.go b/model/model.go new file mode 100644 index 0000000..9777d0a --- /dev/null +++ b/model/model.go @@ -0,0 +1,87 @@ +package model + +import ( + "time" + + fastTrace "github.com/nxtrace/NTrace-core/fast_trace" +) + +type ParamsFastTrace struct { + SrcDev string + SrcAddr string + BeginHop int + MaxHops int + RDns bool + AlwaysWaitRDNS bool + Lang string + PktSize int + Timeout time.Duration + File string + DontFragment bool +} + +// var EnableLoger bool +var NextTraceVersion = "v0.0.1" + +var ( + GuangZhouCT = fastTrace.ISPCollection{ + ISPName: "广州电信", + IP: "58.60.188.222", + IPv6: "240e:97c:2f:3000::44", + } + GuangZhouCU = fastTrace.ISPCollection{ + ISPName: "广州联通", + IP: "210.21.196.6", + IPv6: "2408:8756:f50:1001::c", + } + GuangZhouCMCC = fastTrace.ISPCollection{ + ISPName: "广州移动", + IP: "120.196.165.24", + IPv6: "2409:8c54:871:1001::12", + } + ShangHaiCT = fastTrace.ISPCollection{ + ISPName: "上海电信", + IP: "202.96.209.133", + IPv6: "240e:e1:aa00:4000::24", + } + ShangHaiCU = fastTrace.ISPCollection{ + ISPName: "上海联通", + IP: "210.22.97.1", + IPv6: "2408:80f1:21:5003::a", + } + ShangHaiCMCC = fastTrace.ISPCollection{ + ISPName: "上海移动", + IP: "211.136.112.200", + IPv6: "2409:8c1e:75b0:3003::26", + } + BeiJingCT = fastTrace.ISPCollection{ + ISPName: "北京电信", + IP: "219.141.140.10", + IPv6: "2400:89c0:1053:3::69", + } + BeiJingCU = fastTrace.ISPCollection{ + ISPName: "北京联通", + IP: "202.106.195.68", + IPv6: "2400:89c0:1013:3::54", + } + BeiJingCMCC = fastTrace.ISPCollection{ + ISPName: "北京移动", + IP: "221.179.155.161", + IPv6: "2409:8c00:8421:1303::55", + } + ChengDuCT = fastTrace.ISPCollection{ + ISPName: "成都电信", + IP: "61.139.2.69", + IPv6: "", + } + ChengDuCU = fastTrace.ISPCollection{ + ISPName: "成都联通", + IP: "119.6.6.6", + IPv6: "", + } + ChengDuCMCC = fastTrace.ISPCollection{ + ISPName: "成都移动", + IP: "211.137.96.205", + IPv6: "", + } +) diff --git a/nt/nt.go b/nt/nt.go new file mode 100644 index 0000000..3f0e546 --- /dev/null +++ b/nt/nt.go @@ -0,0 +1,247 @@ +package nt + +import ( + "fmt" + "log" + "net" + "os" + "os/signal" + "strconv" + "strings" + "time" + + "github.com/fatih/color" + fastTrace "github.com/nxtrace/NTrace-core/fast_trace" + "github.com/nxtrace/NTrace-core/ipgeo" + "github.com/nxtrace/NTrace-core/trace" + "github.com/nxtrace/NTrace-core/util" + "github.com/nxtrace/NTrace-core/wshandle" + "github.com/oneclickvirt/nt3/model" +) + +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) + for i, v := range res.Hops[ttl] { + if v.Address == nil && latestIP != "" { + tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%-10s", fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))) + 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("%-10s", fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))) + } + } + latestIP = v.Address.String() + } + tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%-10s", fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))) + } + if latestIP == "" { + fmt.Fprintf(color.Output, "%s\n", + color.New(color.FgWhite, color.Bold).Sprintf("*"), + ) + return + } + for ip, v := range tmpMap { + i, _ := strconv.Atoi(v[0]) + rtt := v[1] + // 打印RTT + fmt.Fprintf(color.Output, fmt.Sprintf("%-24s ", color.New(color.FgHiCyan, color.Bold).Sprintf("%s", rtt))) + // 打印AS号 + if res.Hops[ttl][i].Geo.Asnumber != "" { + fmt.Fprintf(color.Output, fmt.Sprintf("%-24s ", color.New(color.FgHiYellow, color.Bold).Sprintf("AS%s", res.Hops[ttl][i].Geo.Asnumber))) + } else { + fmt.Fprintf(color.Output, fmt.Sprintf("%-24s ", color.New(color.FgWhite, color.Bold).Sprintf("*"))) + } + // 打印地理信息 + 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("%-18s", whoisFormat[0])) + default: + fmt.Fprintf(color.Output, "%s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-18s", whoisFormat[0])) + } + var parts []string + country := res.Hops[ttl][i].Geo.Country + prov := res.Hops[ttl][i].Geo.Prov + city := res.Hops[ttl][i].Geo.City + owner := res.Hops[ttl][i].Geo.Owner + if country != "" { + parts = append(parts, color.New(color.FgWhite, color.Bold).Sprintf("%s", country)) + } + if prov != "" { + parts = append(parts, color.New(color.FgWhite, color.Bold).Sprintf("%s", prov)) + } + if city != "" { + parts = append(parts, color.New(color.FgWhite, color.Bold).Sprintf("%s", city)) + } + if owner != "" { + parts = append(parts, color.New(color.FgWhite, color.Bold).Sprintf("%s", owner)) + } + if len(parts) > 0 { + fmt.Fprintf(color.Output, strings.Join(parts, ", ")) + } + } + fmt.Println() + } +} + +func tracert(f fastTrace.FastTracer, ispCollection fastTrace.ISPCollection) { + fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize) + ip, err := util.DomainLookUp(ispCollection.IP, "4", "", true) + if err != nil { + log.Fatal(err) + } + var conf = trace.Config{ + BeginHop: 1, + DestIP: ip, + DestPort: 80, + MaxHops: 30, + NumMeasurements: 3, + ParallelRequests: 18, + RDns: f.ParamsFastTrace.RDns, + AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS, + PacketInterval: 50, + TTLInterval: 50, + IPGeoSource: ipgeo.GetSource("LeoMoeAPI"), + Timeout: time.Duration(1000) * time.Millisecond, + SrcAddr: f.ParamsFastTrace.SrcAddr, + PktSize: 52, + Lang: f.ParamsFastTrace.Lang, + DontFragment: f.ParamsFastTrace.DontFragment, + } + conf.RealtimePrinter = realtimePrinter + //conf.RealtimePrinter = printer.RealtimePrinter + //conf.RealtimePrinter = tracelog.RealtimePrinter + _, err = trace.Traceroute(f.TracerouteMethod, conf) + if err != nil { + log.Fatal(err) + } +} + +func tracert_v6(f fastTrace.FastTracer, ispCollection fastTrace.ISPCollection) { + fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize) + ip, err := util.DomainLookUp(ispCollection.IPv6, "6", "", true) + if err != nil { + log.Fatal(err) + } + var conf = trace.Config{ + BeginHop: 1, + DestIP: ip, + DestPort: 80, + MaxHops: 30, + NumMeasurements: 3, + ParallelRequests: 18, + RDns: f.ParamsFastTrace.RDns, + AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS, + PacketInterval: 50, + TTLInterval: 50, + IPGeoSource: ipgeo.GetSource("LeoMoeAPI"), + Timeout: time.Duration(1000) * time.Millisecond, + SrcAddr: f.ParamsFastTrace.SrcAddr, + PktSize: 52, + Lang: f.ParamsFastTrace.Lang, + DontFragment: f.ParamsFastTrace.DontFragment, + } + conf.RealtimePrinter = realtimePrinter + //conf.RealtimePrinter = printer.RealtimePrinter + //conf.RealtimePrinter = tracelog.RealtimePrinter + _, err = trace.Traceroute(f.TracerouteMethod, conf) + if err != nil { + log.Fatal(err) + } +} + +func TraceRoute(language, location, testType string) { + if language == "zh" || language == "" { + language = "cn" + } else if language != "en" { + fmt.Println("Invalid language.") + return + } + var TL []fastTrace.ISPCollection + if location == "GZ" { + TL = []fastTrace.ISPCollection{model.GuangZhouCT, model.GuangZhouCU, model.GuangZhouCMCC} + } else if location == "BJ" { + TL = []fastTrace.ISPCollection{model.BeiJingCT, model.BeiJingCU, model.BeiJingCMCC} + } else if location == "SH" { + TL = []fastTrace.ISPCollection{model.ShangHaiCT, model.ShangHaiCU, model.ShangHaiCMCC} + } else if location == "CD" { + TL = []fastTrace.ISPCollection{model.ChengDuCT, model.ChengDuCU, model.ChengDuCMCC} + } else { + fmt.Println("Invalid location.") + return + } + pFastTrace := fastTrace.ParamsFastTrace{ + SrcDev: "", + SrcAddr: "", + BeginHop: 1, + MaxHops: 30, + RDns: false, + AlwaysWaitRDNS: false, + Lang: language, + PktSize: 52, + } + ft := fastTrace.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() + }() + ft.TracerouteMethod = trace.ICMPTrace + if TL != nil { + for _, T := range TL { + if testType == "both" { + fmt.Fprintf(color.Output, "%s - ", color.New(color.FgHiBlue, color.Bold).Sprintf("%s - ICMP v4", T.ISPName)) + tracert(ft, T) + fmt.Fprintf(color.Output, "%s - ", color.New(color.FgHiBlue, color.Bold).Sprintf("%s - ICMP v6", T.ISPName)) + tracert_v6(ft, T) + } else if testType == "ipv4" { + fmt.Fprintf(color.Output, "%s - ", color.New(color.FgHiBlue, color.Bold).Sprintf("%s - ICMP v4", T.ISPName)) + tracert(ft, T) + } else if testType == "ipv6" { + fmt.Fprintf(color.Output, "%s - ", color.New(color.FgHiBlue, color.Bold).Sprintf("%s - ICMP v6", T.ISPName)) + tracert_v6(ft, T) + } + } + } +} diff --git a/nt3_install.sh b/nt3_install.sh new file mode 100644 index 0000000..5c8763f --- /dev/null +++ b/nt3_install.sh @@ -0,0 +1,86 @@ +#!/bin/bash +#From https://github.com/oneclickvirt/nt3 +#2024.06.27 + +rm -rf /usr/bin/nt3 +rm -rf nt3 +os=$(uname -s) +arch=$(uname -m) + +case $os in + Linux) + case $arch in + "x86_64" | "x86" | "amd64" | "x64") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-linux-amd64 + ;; + "i386" | "i686") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-linux-386 + ;; + "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-linux-arm64 + ;; + *) + echo "Unsupported architecture: $arch" + exit 1 + ;; + esac + ;; + Darwin) + case $arch in + "x86_64" | "x86" | "amd64" | "x64") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-darwin-amd64 + ;; + "i386" | "i686") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-darwin-386 + ;; + "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-darwin-arm64 + ;; + *) + echo "Unsupported architecture: $arch" + exit 1 + ;; + esac + ;; + FreeBSD) + case $arch in + amd64) + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-freebsd-amd64 + ;; + "i386" | "i686") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-freebsd-386 + ;; + "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-freebsd-arm64 + ;; + *) + echo "Unsupported architecture: $arch" + exit 1 + ;; + esac + ;; + OpenBSD) + case $arch in + amd64) + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-openbsd-amd64 + ;; + "i386" | "i686") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-openbsd-386 + ;; + "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64") + wget -O nt3 https://github.com/oneclickvirt/nt3/releases/download/output/nt3-openbsd-arm64 + ;; + *) + echo "Unsupported architecture: $arch" + exit 1 + ;; + esac + ;; + *) + echo "Unsupported operating system: $os" + exit 1 + ;; +esac + +chmod 777 nt3 +cp nt3 /usr/bin/nt3 \ No newline at end of file