mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
Compare commits
304 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cefed2a481 | ||
|
|
bc5a370f3b | ||
|
|
96e06e306c | ||
|
|
ac14ef4a7a | ||
|
|
bb192699ef | ||
|
|
b0b05f476b | ||
|
|
53de171bdb | ||
|
|
547906525a | ||
|
|
b4d2038895 | ||
|
|
adf8bb7076 | ||
|
|
3defedd27c | ||
|
|
4eeef8ddb8 | ||
|
|
35b67d9502 | ||
|
|
a5f888d1bc | ||
|
|
4d38c38cf7 | ||
|
|
d0e8e67d0a | ||
|
|
26b176ccb0 | ||
|
|
e2b23bb8e0 | ||
|
|
8c3a682e15 | ||
|
|
21e1c03596 | ||
|
|
ef57db1c09 | ||
|
|
93c0f8558b | ||
|
|
7bfccf9da4 | ||
|
|
b5673b65a1 | ||
|
|
946095458b | ||
|
|
28b329e365 | ||
|
|
c27dd3703a | ||
|
|
b20c4b74cc | ||
|
|
deca060a9d | ||
|
|
fedf2946e0 | ||
|
|
d0f5862258 | ||
|
|
0f8a646d71 | ||
|
|
dccc41b995 | ||
|
|
9af629b6f9 | ||
|
|
d6de649e60 | ||
|
|
bcd430c231 | ||
|
|
314a4b3015 | ||
|
|
4d8e7e322b | ||
|
|
8fb1220f1b | ||
|
|
90b1a3c1ad | ||
|
|
608a2904d4 | ||
|
|
62ab23bdeb | ||
|
|
c095599400 | ||
|
|
76d841f670 | ||
|
|
9dd36e9625 | ||
|
|
e1f4052518 | ||
|
|
b5df3efd1b | ||
|
|
3f9803680e | ||
|
|
db2b02d5f8 | ||
|
|
ec634fffb3 | ||
|
|
a3404cebac | ||
|
|
667285a8c3 | ||
|
|
c52d5a5414 | ||
|
|
6c009602b5 | ||
|
|
a179608da0 | ||
|
|
5cd2962a2b | ||
|
|
f774c0d29f | ||
|
|
69588b0d14 | ||
|
|
6c49957be8 | ||
|
|
5cc08151f4 | ||
|
|
d233e0e38d | ||
|
|
183516b14c | ||
|
|
00695f32b4 | ||
|
|
77ae2d1ef0 | ||
|
|
6c97ae8ea6 | ||
|
|
2e02a2b53f | ||
|
|
670654864f | ||
|
|
7f9c0fcb32 | ||
|
|
a0e4d68d8d | ||
|
|
13360aefff | ||
|
|
6096047c08 | ||
|
|
4ae9d8ece1 | ||
|
|
970893fe52 | ||
|
|
fa35005bf2 | ||
|
|
b0c0f8d3ce | ||
|
|
8774e8cd67 | ||
|
|
6a3ea6acb3 | ||
|
|
42e4a23233 | ||
|
|
2f1e086c7d | ||
|
|
b507dea8b1 | ||
|
|
fa8f6687de | ||
|
|
f47e742d81 | ||
|
|
e81400bd1d | ||
|
|
e072b0dda8 | ||
|
|
868bf3d691 | ||
|
|
79ac0bc456 | ||
|
|
f13889f48f | ||
|
|
a5641b5530 | ||
|
|
eea77b1f0d | ||
|
|
3e0a086961 | ||
|
|
c0332ff4d5 | ||
|
|
7fa5181062 | ||
|
|
f26e818764 | ||
|
|
31e3375462 | ||
|
|
d12c4fcd57 | ||
|
|
b70db0883c | ||
|
|
913a4f99b3 | ||
|
|
6369745859 | ||
|
|
756b51e6da | ||
|
|
ea2a6b596a | ||
|
|
2e9428ce76 | ||
|
|
052fe2dc06 | ||
|
|
24b7dae2d1 | ||
|
|
849cf488aa | ||
|
|
a539a4e079 | ||
|
|
efc19c4b7d | ||
|
|
f0a6c826fc | ||
|
|
9e05065a79 | ||
|
|
ade34e964a | ||
|
|
486cdfdc03 | ||
|
|
3260753a66 | ||
|
|
0c44e39a20 | ||
|
|
47870b1d9a | ||
|
|
57346f9db8 | ||
|
|
026460545f | ||
|
|
29b81d00ae | ||
|
|
c1cd80232a | ||
|
|
9c4505f9c7 | ||
|
|
e27c8bea77 | ||
|
|
f303397ce1 | ||
|
|
33239a78d1 | ||
|
|
e4fa907ed4 | ||
|
|
d0fb43e947 | ||
|
|
9dda330543 | ||
|
|
94372d9605 | ||
|
|
f50ca1f7f8 | ||
|
|
f06dba7458 | ||
|
|
d1e87c8a77 | ||
|
|
57d31a38f7 | ||
|
|
84b709de44 | ||
|
|
dad9282078 | ||
|
|
85b01ebfc6 | ||
|
|
a2b5cde829 | ||
|
|
f2536980b7 | ||
|
|
fae4b7b71a | ||
|
|
791dd45086 | ||
|
|
3751d9ce12 | ||
|
|
39169291e8 | ||
|
|
6937b54cdf | ||
|
|
742035cc0c | ||
|
|
7350d13850 | ||
|
|
970cff3b72 | ||
|
|
b053ee646b | ||
|
|
f6f90f3a5b | ||
|
|
e128fe1893 | ||
|
|
2cb13be378 | ||
|
|
7ae4eb13c9 | ||
|
|
d1a72458a6 | ||
|
|
5e3474d029 | ||
|
|
a93b7658b7 | ||
|
|
bbb905f113 | ||
|
|
5f44fc704d | ||
|
|
1257fe87b5 | ||
|
|
ea60a671f2 | ||
|
|
0d6e894a4b | ||
|
|
20069ab9f3 | ||
|
|
4f05b57fc2 | ||
|
|
190111f6da | ||
|
|
c6eb9bbd2e | ||
|
|
cbc511f097 | ||
|
|
66422c4661 | ||
|
|
b6828b4db1 | ||
|
|
0a750bc6d0 | ||
|
|
47612c022d | ||
|
|
42bf45c2c8 | ||
|
|
8ac1a6bacd | ||
|
|
f96c3e5139 | ||
|
|
c46907b881 | ||
|
|
7afc4c5104 | ||
|
|
27e560fbde | ||
|
|
1dc18cfff8 | ||
|
|
b16dabac7c | ||
|
|
760e751076 | ||
|
|
a82be1b5cf | ||
|
|
4ba76c97fe | ||
|
|
9900bff41a | ||
|
|
e81a8f9fdc | ||
|
|
e3346ff901 | ||
|
|
70034a81f8 | ||
|
|
eb0ac2565d | ||
|
|
757ff4232b | ||
|
|
4dda237a57 | ||
|
|
cfec8df6a4 | ||
|
|
f76c9401f9 | ||
|
|
9536cf345c | ||
|
|
f282cf8b8c | ||
|
|
01fb27b7d7 | ||
|
|
3133978195 | ||
|
|
c9dc71cd24 | ||
|
|
c67e46bf4f | ||
|
|
5d796ce5cd | ||
|
|
96e0558fef | ||
|
|
662e482593 | ||
|
|
e60a5aedd9 | ||
|
|
559cb478ab | ||
|
|
2598fb4d56 | ||
|
|
21a92e1f89 | ||
|
|
ef0631fdab | ||
|
|
5c4aa0c2de | ||
|
|
d0c1459752 | ||
|
|
156f8914d8 | ||
|
|
de15063be1 | ||
|
|
af5259e484 | ||
|
|
5e95aac500 | ||
|
|
bc267f5b74 | ||
|
|
dede4ed530 | ||
|
|
ba2354c429 | ||
|
|
03394df927 | ||
|
|
1fd3288fb9 | ||
|
|
44eee91c31 | ||
|
|
e593ad8fce | ||
|
|
533dacc9ce | ||
|
|
fa30750190 | ||
|
|
ffa7bb1d28 | ||
|
|
c0455ca8e7 | ||
|
|
faa3daa0c4 | ||
|
|
fc88c9be5c | ||
|
|
f5d1a606b3 | ||
|
|
b32947a824 | ||
|
|
36507ad3b5 | ||
|
|
9a3f250b9b | ||
|
|
8adceb8d77 | ||
|
|
7912af8481 | ||
|
|
4bef8b918a | ||
|
|
690f7346d0 | ||
|
|
d82a521954 | ||
|
|
0b7d01fb21 | ||
|
|
2fed1d152f | ||
|
|
98c0fa38ca | ||
|
|
cb63e9b61c | ||
|
|
e4626c4f82 | ||
|
|
47698be2ed | ||
|
|
ee0ebb126c | ||
|
|
1b59210eb1 | ||
|
|
ed518d175b | ||
|
|
9f67ea5e9b | ||
|
|
d98d2f7ee5 | ||
|
|
e0b874a687 | ||
|
|
b39657ca5b | ||
|
|
c79387ff75 | ||
|
|
e6b2d975b4 | ||
|
|
33c1af9f50 | ||
|
|
da825c18cc | ||
|
|
4adba2b59d | ||
|
|
65d8ac5c14 | ||
|
|
7472398c26 | ||
|
|
b2870a823f | ||
|
|
f82af5f9c5 | ||
|
|
9eda2d2a30 | ||
|
|
1a4a15eb74 | ||
|
|
f5556fea5d | ||
|
|
d760e75810 | ||
|
|
99409089b2 | ||
|
|
2040497d89 | ||
|
|
29ce61b24b | ||
|
|
701abc3447 | ||
|
|
f08778c862 | ||
|
|
8697325193 | ||
|
|
32b0f15e78 | ||
|
|
90e349eeed | ||
|
|
f275edba3a | ||
|
|
2974002c02 | ||
|
|
9b2fc9b570 | ||
|
|
960ab9687c | ||
|
|
07623ce4fd | ||
|
|
3164acfccf | ||
|
|
8b30ef39dd | ||
|
|
239b1c2f8d | ||
|
|
ed3c158e87 | ||
|
|
02348f08c0 | ||
|
|
9c7402accb | ||
|
|
a6848f8f23 | ||
|
|
29b7c668a4 | ||
|
|
86eacc006e | ||
|
|
81d6df8b82 | ||
|
|
dbe42d669c | ||
|
|
a7089e8d54 | ||
|
|
bcda750a66 | ||
|
|
1580c6111f | ||
|
|
ae16731bbe | ||
|
|
c532dfd05c | ||
|
|
bb522ed859 | ||
|
|
9e5cd736c8 | ||
|
|
b11d2334c2 | ||
|
|
84a03e8a10 | ||
|
|
761d506613 | ||
|
|
e33548d2ce | ||
|
|
1266f62aba | ||
|
|
8e837072c6 | ||
|
|
cf0639111d | ||
|
|
56cd0022f7 | ||
|
|
1407c499b9 | ||
|
|
f25662d481 | ||
|
|
4f94f01e87 | ||
|
|
1460ad67c0 | ||
|
|
d6dcfc8dc5 | ||
|
|
190b3ab94e | ||
|
|
87fa850d2d | ||
|
|
c71a26d018 | ||
|
|
79c39b655a | ||
|
|
580612ce08 | ||
|
|
ebe2a0f8e9 | ||
|
|
ca2a7a8dc8 | ||
|
|
b68b768a65 |
@@ -29,13 +29,13 @@ for pl in ${PLATFORMS}; do
|
||||
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
|
||||
-w -s"
|
||||
-w -s -checklinkname=0"
|
||||
else
|
||||
go build -trimpath -o ${TARGET} \
|
||||
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
|
||||
-w -s"
|
||||
-w -s -checklinkname=0"
|
||||
fi
|
||||
done
|
||||
export CGO_ENABLED=0
|
||||
@@ -49,13 +49,13 @@ done
|
||||
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
|
||||
-w -s"
|
||||
-w -s -checklinkname=0"
|
||||
else
|
||||
go build -trimpath -o ${TARGET} \
|
||||
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
|
||||
-w -s"
|
||||
-w -s -checklinkname=0"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -17,6 +17,11 @@ copyright: [v2fly](https://github.com/v2fly)
|
||||
除非特殊情况,请完整填写所有问题。不按模板发的 issue 将直接被关闭。
|
||||
如果你遇到的问题不是 nexttrace 的 bug,比如你不清楚如何配置,请在 https://github.com/nxtrace/NTrace-core/discussions 进行讨论。
|
||||
-->
|
||||
## 本项目是基于Linux/macOS的,请确认您遇到的问题是否在Linux或macOS上存在。
|
||||
|
||||
<!-- 是/否 -->
|
||||
<!-- 对于只出现在Windows上的问题,本项目有时无法解决,请知悉 -->
|
||||
|
||||
|
||||
## 你正在使用哪个版本的 nexttrace?
|
||||
|
||||
@@ -39,6 +44,8 @@ copyright: [v2fly](https://github.com/v2fly)
|
||||
|
||||
## 请附上出错时软件输出的错误信息
|
||||
|
||||
<!-- 是/否 -->
|
||||
|
||||
## 是否查询过本仓库wiki有没有类似错误
|
||||
|
||||
<!-- wiki: https://github.com/sjlleo/nexttrace/wiki -->
|
||||
<!-- wiki: https://github.com/nxtrace/NTrace-core/wiki -->
|
||||
|
||||
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
@@ -75,6 +75,12 @@ jobs:
|
||||
goarch: mipsle
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
- goos: linux
|
||||
goarch: mipsle
|
||||
gomips: softfloat
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
gomips: softfloat
|
||||
# END MIPS
|
||||
# BEGIN PPC
|
||||
- goos: linux
|
||||
@@ -104,6 +110,7 @@ jobs:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GOMIPS: ${{ matrix.gomips }}
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
@@ -117,15 +124,18 @@ jobs:
|
||||
if [ "$GOOS" == "windows" ]; then
|
||||
export _NAME="$_NAME.exe"
|
||||
fi
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, RELEASE_NAME: $_NAME"
|
||||
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@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.23'
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
- name: Build
|
||||
@@ -134,16 +144,16 @@ jobs:
|
||||
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
|
||||
-w -s"
|
||||
-checklinkname=0 -w -s"
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.ASSET_NAME }}
|
||||
path: |
|
||||
dist/${{ env.ASSET_NAME }}
|
||||
- name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with: # 将下述可执行文件 release 上去
|
||||
draft: true # Release草稿
|
||||
files: |
|
||||
@@ -166,7 +176,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
|
||||
@@ -183,7 +193,7 @@ jobs:
|
||||
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/xgadget-lab/homebrew-nexttrace.git
|
||||
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
|
||||
|
||||
4
.github/workflows/publishNewFormula.yml
vendored
4
.github/workflows/publishNewFormula.yml
vendored
@@ -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
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
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/xgadget-lab/homebrew-nexttrace.git
|
||||
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
|
||||
|
||||
9
.github/workflows/test.yml
vendored
9
.github/workflows/test.yml
vendored
@@ -29,15 +29,14 @@ jobs:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
check-latest: true
|
||||
go-version: '1.23'
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v4
|
||||
- name: Test with unix
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: sudo go test -v -coverprofile='coverage.out' -covermode=count ./...
|
||||
run: sudo go test -v -ldflags=-checklinkname=0 -coverprofile='coverage.out' -covermode=count ./...
|
||||
- name: Test with windows
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: go test -v -coverprofile='coverage.out' -covermode=count ./...
|
||||
run: go test -v -ldflags=-checklinkname=0 -coverprofile='coverage.out' -covermode=count ./...
|
||||
|
||||
15
.github/workflows/triggerDebRepo.yml
vendored
Normal file
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 }}"}}'
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -163,3 +163,5 @@ Temporary Items
|
||||
|
||||
# compile target directory
|
||||
dist/
|
||||
|
||||
NTrace-core
|
||||
|
||||
261
README.md
261
README.md
@@ -10,6 +10,10 @@
|
||||
|
||||
<h4 align="center">An open source visual routing tool that pursues light weight, developed using Golang.</h4>
|
||||
|
||||
---------------------------------------
|
||||
|
||||
<h6 align="center">HomePage: 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">
|
||||
@@ -17,106 +21,124 @@
|
||||
<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://github.com/nxtrace/Ntrace-V1/releases">
|
||||
<a href="https://www.nxtrace.org/downloads">
|
||||
<img src="https://img.shields.io/github/release/nxtrace/Ntrace-V1/all.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://telegram.dog/sjprojects">
|
||||
<img src="https://img.shields.io/endpoint?color=neon&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fnexttrace">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## IAAS Sponsor
|
||||
|
||||
<div style="text-align: center;">
|
||||
<a href="https://dmit.io">
|
||||
<img src="https://www.dmit.io/templates/dmit_theme_2020/dmit/assets/images/dmit_logo_with_text_blue.svg" width="170.7" height="62.9">
|
||||
<img src="https://assets.nxtrace.org/dmit.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
|
||||
<a href="https://misaka.io" >
|
||||
<img src="https://www.jsdelivr.com/assets/8997e39e1f9d776502ab4d7cdff9d1608aa67aaf/img/globalping/sponsors/misaka.svg" width="170.7" height="62.9">
|
||||
<img src="https://assets.nxtrace.org/misaka.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
|
||||
<a href="https://skywolf.cloud" >
|
||||
<img src="https://github.com/nxtrace/Ntrace-core/assets/59512455/19b659f4-31f5-4816-9821-bf2a73c60336" width="170.7" height="62.9">
|
||||
<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) and [Misaka](https://misaka.io) and [Skywolf](https://skywolf.cloud) for providing the network infrastructure that powers this project.
|
||||
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
|
||||
|
||||
Document Language: English | [简体中文](README_zh_CN.md)
|
||||
|
||||
⚠️ This is the README for the V1 Enhanced version of NextTrace.
|
||||
⚠️ 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.
|
||||
|
||||
### Automated Install
|
||||
|
||||
* Linux
|
||||
* One-click installation script
|
||||
|
||||
```shell
|
||||
bash -c "$(curl http://nexttrace-io-leomoe-api-a0.shop/nt_install_v1.sh)"
|
||||
curl -sL 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
|
||||
* Build from source
|
||||
|
||||
```shell
|
||||
yay -S nexttrace
|
||||
```
|
||||
* Directly download bin package (only supports amd64)
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
* Build from source (only supports amd64)
|
||||
```shell
|
||||
yay -S nexttrace
|
||||
```
|
||||
* The AUR builds are maintained by ouuan, huyz
|
||||
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
* The two types of AUR builds are maintained by huyz and ouuan, respectively
|
||||
* Linuxbrew's installation command
|
||||
* Linuxbrew's installation command
|
||||
|
||||
Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64)
|
||||
Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64)
|
||||
|
||||
* Deepin installation command
|
||||
```shell
|
||||
apt install nexttrace
|
||||
```
|
||||
|
||||
* [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
|
||||
```
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
* This repository's ACTIONS automatically built version (updates faster)
|
||||
|
||||
```shell
|
||||
brew tap xgadget-lab/nexttrace && brew install xgadget-lab/nexttrace/nexttrace
|
||||
```
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* The homebrew-core build is maintained by chenrui333, please note that this version's updates may lag behind the repository Action automatically version
|
||||
|
||||
* Windows
|
||||
* Windows WinGet installation command
|
||||
* WinGet version
|
||||
```powershell
|
||||
winget install nexttrace
|
||||
```
|
||||
* WinGet build maintained by Dragon1573
|
||||
|
||||
* Windows Scoop installation command
|
||||
* Scoop-extras version
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
* Scoop-extra is maintained by soenggam
|
||||
|
||||
Please note, the repositories for all of the above installation methods are maintained by open source enthusiasts. Availability and timely updates are not guaranteed. If you encounter problems, please contact the repository maintainer to solve them, or use the binary packages provided by the official build of this project.
|
||||
Please note, the repositories for all of the above installation methods are maintained by open source enthusiasts. Availability and timely updates are not guaranteed. If you encounter problems, please contact the repository maintainer to solve them, or use the binary packages provided by the official build of this project.
|
||||
|
||||
### Manual Install
|
||||
* Download the precompiled executable
|
||||
|
||||
For users not covered by the above methods, please go directly to [Release](https://github.com/nxtrace/Ntrace-V1/releases/latest) to download the compiled binary 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.
|
||||
|
||||
* Install from source
|
||||
|
||||
After installing Go >= 1.20 yourself, you can use the following command to install
|
||||
|
||||
```shell
|
||||
go install github.com/nxtrace/Ntrace-V1@latest
|
||||
```
|
||||
After installation, the executable is in the `$GOPATH/bin` directory. If you have not set `GOPATH`, it is in the `$HOME/go/bin` directory.
|
||||
|
||||
|
||||
### Get Started
|
||||
|
||||
`NextTrace` uses the `ICMP` protocol to perform TraceRoute requests by default, which supports both `IPv4` and `IPv6`
|
||||
@@ -134,7 +156,7 @@ nexttrace --table 1.0.0.1
|
||||
nexttrace --raw 1.0.0.1
|
||||
nexttrace --json 1.0.0.1
|
||||
|
||||
# IPv4/IPv6 Resolve Only
|
||||
# IPv4/IPv6 Resolve Only, and automatically select the first IP when there are multiple IPs
|
||||
nexttrace --ipv4 g.co
|
||||
nexttrace --ipv6 g.co
|
||||
|
||||
@@ -143,7 +165,7 @@ nexttrace 2606:4700:4700::1111
|
||||
|
||||
# Disable Path Visualization With the -M parameter
|
||||
nexttrace koreacentral.blob.core.windows.net
|
||||
# MapTrace URL: https://api.leo.moe/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
|
||||
# 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
|
||||
@@ -164,6 +186,17 @@ nexttrace --fast-trace
|
||||
|
||||
# You can also use TCP SYN for testing
|
||||
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` already supports route tracing for specified Network Devices
|
||||
@@ -177,7 +210,7 @@ nexttrace --dev eth0 2606:4700:4700::1111
|
||||
nexttrace --source 204.98.134.56 9.9.9.9
|
||||
```
|
||||
|
||||
`NextTrace` can also use `TCP` and `UDP` protocols to perform `Traceroute` requests, but `UDP` protocols only supports `IPv4` now
|
||||
`NextTrace` can also use `TCP` and `UDP` protocols to perform `Traceroute` requests
|
||||
|
||||
```bash
|
||||
# TCP SYN Trace
|
||||
@@ -189,7 +222,12 @@ nexttrace --tcp --port 443 2001:4860:4860::8888
|
||||
# UDP Trace
|
||||
nexttrace --udp 1.0.0.1
|
||||
|
||||
nexttrace --udp --port 53 1.0.0.1
|
||||
# You can specify the target port yourself [here it is 5353], the default is port 33494
|
||||
nexttrace --udp --port 5353 1.0.0.1
|
||||
|
||||
# For TCP/UDP Trace, you can specify the source port; by default, a fixed random port is used
|
||||
# (if you need to use a different random source port for each packet, please set the ENV variable NEXTTRACE_RANDOMPORT)
|
||||
nexttrace --tcp --source-port 14514 www.bing.com
|
||||
```
|
||||
|
||||
`NextTrace` also supports some advanced functions, such as ttl control, concurrent probe packet count control, mode switching, etc.
|
||||
@@ -203,6 +241,8 @@ nexttrace --parallel-requests 1 www.hkix.net
|
||||
|
||||
# Start Trace with TTL of 5, end at TTL of 10
|
||||
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 --no-rdns www.bbix.net
|
||||
@@ -210,6 +250,9 @@ nexttrace --no-rdns www.bbix.net
|
||||
# Set the payload size to 1024 bytes
|
||||
nexttrace --psize 1024 example.com
|
||||
|
||||
# Set the payload size and DF flag for TCP Trace
|
||||
nexttrace --psize 1024 --dont-fragment --tcp example.com
|
||||
|
||||
# Feature: print Route-Path diagram
|
||||
# Route-Path diagram example:
|
||||
# AS6453 Tata Communication「Singapore『Singapore』」
|
||||
@@ -220,6 +263,11 @@ nexttrace --psize 1024 example.com
|
||||
# ╭╯
|
||||
# ╰AS37963 Aliyun「ALIDNS.COM『ALIDNS.COM』」
|
||||
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`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`)
|
||||
@@ -229,9 +277,13 @@ nexttrace --route-path www.time.com.my
|
||||
nexttrace --data-provider ip-api.com
|
||||
## Note There are frequency limits for free queries of the ipinfo and IPInsight APIs. You can purchase services from these providers to remove the limits
|
||||
## If necessary, you can clone this project, add the token provided by ipinfo or IPInsight and compile it yourself
|
||||
## Fill the token to: ipgeo/tokens.go
|
||||
|
||||
## Note For the offline database IPInfoLocal, please download it manually and rename it to ipinfoLocal.mmdb. (You can download it from here: https://ipinfo.io/signup?ref=free-database-downloads)
|
||||
## Current directory, nexttrace binary directory and FHS directories (Unix-like) will be searched.
|
||||
## To customize it, please use environment variables,
|
||||
export NEXTTRACE_IPINFOLOCALPATH=/xxx/yyy.mmdb
|
||||
## For the offline database Ip2region, you can download it manually and rename it to ip2region.db, or let NextTrace download it automatically
|
||||
## Fill the token to: ipgeo/tokens.go
|
||||
## Please be aware: Due to the serious abuse of IP.SB, you will often be not able to query IP data from this source
|
||||
## IP-API.com has a stricter restiction on API calls, if you can't query IP data from this source, please try again in a few minutes
|
||||
|
||||
@@ -251,13 +303,13 @@ nexttrace --data-provider IPAPI.com --max-hops 20 --tcp --port 443 --queries 5 -
|
||||
nexttrace -tcp --queries 2 --parallel-requests 1 --table --route-path 2001:4860:4860::8888
|
||||
|
||||
Equivalent to:
|
||||
nexttrace -d IPAPI.com -m 20 -T -p 443 -q 5 -n 1.1.1.1
|
||||
nexttrace -T -q 2 --parallel-requests 1 -t -R 2001:4860:4860::8888
|
||||
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.
|
||||
We use [bgp.tools](https://bgp.tools) as a data provider for routing tables.
|
||||
|
||||
NextTrace BackEnd is now open-source.
|
||||
|
||||
@@ -278,15 +330,16 @@ Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
|
||||
<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)]
|
||||
[-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]
|
||||
[--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>"]
|
||||
[-R|--route] [-z|--send-time <integer>] [-i|--ttl-time
|
||||
<integer>] [--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_31 "<value>"] [--dot-server
|
||||
[-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)]
|
||||
(en|cn)] [--file "<value>"] [-C|--nocolor]
|
||||
|
||||
Arguments:
|
||||
|
||||
@@ -296,16 +349,10 @@ Arguments:
|
||||
-T --tcp Use TCP SYN for tracerouting (default port
|
||||
is 80)
|
||||
-U --udp Use UDP SYN for tracerouting (default port
|
||||
is 53)
|
||||
is 33494)
|
||||
-F --fast-trace One-Key Fast Trace to China ISPs
|
||||
-p --port Set the destination port to use. It is
|
||||
either initial udp port value for
|
||||
"default"method (incremented by each
|
||||
probe, default is 33434), or initial seq
|
||||
for "icmp" (incremented as well, default
|
||||
from 1), or some constantdestination port
|
||||
for other methods (with default of 80 for
|
||||
"tcp", 53 for "udp", etc.)
|
||||
-p --port Set the destination port to use. With
|
||||
default of 80 for "tcp", 33494 for "udp"
|
||||
-q --queries Set the number of probes per each hop.
|
||||
Default: 3
|
||||
--parallel-requests Set ParallelRequests number. It should be
|
||||
@@ -317,9 +364,9 @@ Arguments:
|
||||
IPInfo, IPInsight, IP-API.com, Ip2region,
|
||||
IPInfoLocal, CHUNZHEN, disable-geoip].
|
||||
Default: LeoMoeAPI
|
||||
--pow-provider Choose PoW Provider [api.leo.moe, sakura]
|
||||
For China mainland users, please use
|
||||
sakura. Default: api.leo.moe
|
||||
--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
|
||||
@@ -341,27 +388,31 @@ Arguments:
|
||||
-e --disable-mpls Disable MPLS
|
||||
-v --version Print version info and exit
|
||||
-s --source Use source src_addr for outgoing packets
|
||||
--source-port Use source port src_port for outgoing
|
||||
packets
|
||||
-D --dev Use the following Network Devices as the
|
||||
source address in outgoing packets
|
||||
-R --route Show Routing Table [Provided By BGP.Tools]
|
||||
-z --send-time Set how many [milliseconds] between
|
||||
sending each packet.. Useful when some
|
||||
routers use rate-limit for ICMP messages.
|
||||
Default: 100
|
||||
Default: 50
|
||||
-i --ttl-time Set how many [milliseconds] between
|
||||
sending packets groups by TTL. Useful when
|
||||
some routers use rate-limit for ICMP
|
||||
messages. Default: 500
|
||||
messages. Default: 50
|
||||
--timeout The number of [milliseconds] to keep probe
|
||||
sockets open before giving up on the
|
||||
connection.. Default: 1000
|
||||
--psize Set the packet size (payload size).
|
||||
Default: 52
|
||||
--_positionalArg_nexttrace_31 IP Address or domain name
|
||||
--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
|
||||
@@ -384,7 +435,15 @@ This software is still in the early stages of development and may have many flaw
|
||||
|
||||
[https://github.com/nxtrace/nexttracewebapi](https://github.com/nxtrace/nexttracewebapi)
|
||||
|
||||
## LeoMoeAPI Credit
|
||||
## 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.
|
||||
|
||||
@@ -396,44 +455,62 @@ The LeoMoeAPI data is subject to copyright restrictions from multiple data sourc
|
||||
|
||||
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!
|
||||
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.
|
||||
|
||||
|
||||
## AIWEN TECH Support
|
||||
|
||||
This project is sponsored by [AIWEN TECH](https://www.ipplus360.com). We’re pleased to enhance the accuracy and completeness of this project’s GEOIP lookups using `AIWEN TECH City-Level IP Database`, and to make it freely available to the public.
|
||||
|
||||
<img src="https://www.ipplus360.com/img/LOGO.c86cd0e1.svg" title="" alt="AIWEN TECH IP Geolocation Data" width="331">
|
||||
|
||||
## JetBrain Support
|
||||
|
||||
#### This Project uses [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport). We Proudly Develop By Goland.
|
||||
This Project uses [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport). We Proudly Develop By `Goland`.
|
||||
|
||||
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" title="" alt="GoLand logo" width="331">
|
||||
|
||||
## Credits
|
||||
|
||||
[sjlleo](https://github.com/sjlleo) The perpetual leader, founder, and core contributors of the NextTrace Project
|
||||
[Gubo](https://www.gubo.org) Reliable Host Recommendation Website
|
||||
|
||||
[BGP.TOOLS](https://bgp.tools) provided some data support for this project. And we would like to express our sincere gratitude.
|
||||
[IPInfo](https://ipinfo.io) Provided most of the data support for this project free of charge
|
||||
|
||||
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
|
||||
[BGP.TOOLS](https://bgp.tools) Provided some data support for this project free of charge
|
||||
|
||||
[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
|
||||
[PeeringDB](https://www.peeringdb.com) Provided some data support for this project free of charge
|
||||
|
||||
[tsosunchia](https://github.com/tsosunchia)
|
||||
[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)
|
||||
|
||||
[bobo liu](https://github.com/fakeboboliu)
|
||||
|
||||
[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/sjlleo/nexttrace/issues/41) in the GITHUB ISSUES section of this project (Recommended)
|
||||
>- This project's dedicated correction email: `correction@moeqing.com` (Please note that this email is only for correcting IP-related information. For other feedback, please submit an ISSUE)
|
||||
>- [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
|
||||
|
||||
[](https://star-history.com/#sjlleo/nexttrace&Date)
|
||||
[](https://star-history.com/#nxtrace/NTrace-core&Date)
|
||||
|
||||
281
README_zh_CN.md
281
README_zh_CN.md
@@ -11,6 +11,10 @@
|
||||
|
||||
<h4 align="center">一款追求轻量化的开源可视化路由跟踪工具。</h4>
|
||||
|
||||
---------------------------------------
|
||||
|
||||
<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">
|
||||
@@ -18,115 +22,131 @@
|
||||
<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://github.com/nxtrace/Ntrace-V1/releases">
|
||||
<a href="https://www.nxtrace.org/downloads">
|
||||
<img src="https://img.shields.io/github/release/nxtrace/Ntrace-V1/all.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://telegram.dog/sjprojects">
|
||||
<img src="https://img.shields.io/endpoint?color=neon&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fnexttrace">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## IAAS Sponsor
|
||||
|
||||
<div style="text-align: center;">
|
||||
<a href="https://dmit.io">
|
||||
<img src="https://www.dmit.io/templates/dmit_theme_2020/dmit/assets/images/dmit_logo_with_text_blue.svg" width="170.7" height="62.9">
|
||||
<img src="https://assets.nxtrace.org/dmit.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
|
||||
<a href="https://misaka.io" >
|
||||
<img src="https://www.jsdelivr.com/assets/8997e39e1f9d776502ab4d7cdff9d1608aa67aaf/img/globalping/sponsors/misaka.svg" width="170.7" height="62.9">
|
||||
<img src="https://assets.nxtrace.org/misaka.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
|
||||
<a href="https://skywolf.cloud" >
|
||||
<img src="https://github.com/nxtrace/Ntrace-core/assets/59512455/19b659f4-31f5-4816-9821-bf2a73c60336" width="170.7" height="62.9">
|
||||
<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) 和 [Skywolf](https://skywolf.cloud) 提供了支持本项目所需的网络基础设施。
|
||||
我们非常感谢 [DMIT](https://dmit.io)、 [Misaka](https://misaka.io) 和 [SnapStack](https://portal.saltyfish.io) 提供了支持本项目所需的网络基础设施。
|
||||
|
||||
## How To Use
|
||||
|
||||
Document Language: [English](README.md) | 简体中文
|
||||
|
||||
⚠️ 这是NextTrace V1 增强版的README文件。
|
||||
⚠️ 请注意:我们欢迎来自社区的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的某个版本出现了严重的bug,NTrace-core会跳过这一有缺陷的版本,直接同步到下一个修复了该问题的版本。
|
||||
|
||||
### Before Using
|
||||
|
||||
使用 NextTrace 之前,我们建议您先阅读 [#IP 数据以及精准度说明](https://github.com/sjlleo/nexttrace/blob/main/README_zh_CN.md#ip-%E6%95%B0%E6%8D%AE%E4%BB%A5%E5%8F%8A%E7%B2%BE%E5%87%86%E5%BA%A6%E8%AF%B4%E6%98%8E),在了解您自己的对数据精准度需求以后再进行抉择。
|
||||
|
||||
[NextTrace 的Telegram频道](https://t.me/nexttrace)由项目成员负责,会传递一部分通知,也会发布一些成员自己分享的小工具。项目成员的意见可作为未来项目发展的可能方向,随着开发进度变化可能会有所改动,不代表未来一定会实装,正式定稿公告会发布于 Issue 中。
|
||||
使用 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
|
||||
|
||||
* Linux
|
||||
* 一键安装脚本
|
||||
|
||||
```shell
|
||||
bash -c "$(curl http://nexttrace-io-leomoe-api-a0.shop/nt_install_v1.sh)"
|
||||
curl -sL 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 维护
|
||||
|
||||
* 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-bin`
|
||||
```
|
||||
* AUR 的2种构建分别由 huyz 和 ouuan 维护
|
||||
* Linuxbrew 安装命令
|
||||
|
||||
同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 xgadget-lab/nexttrace && brew install xgadget-lab/nexttrace/nexttrace
|
||||
```
|
||||
* homebrew-core 构建由 chenrui333 维护,请注意该版本更新可能会落后仓库Action自动构建版本
|
||||
* homebrew-core版
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
* 本仓库ACTIONS自动构建版(更新更快)
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* homebrew-core 构建由 chenrui333 维护,请注意该版本更新可能会落后仓库Action自动构建版本
|
||||
|
||||
* Windows
|
||||
* Windows WinGet 安装命令
|
||||
* WinGet 版
|
||||
```powershell
|
||||
winget install nexttrace
|
||||
```
|
||||
* WinGet 构建由 Dragon1573 维护
|
||||
|
||||
* Windows Scoop 安装命令
|
||||
* scoop-extras版
|
||||
* scoop-extras 版
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
* scoop-extra 由 soenggam 维护
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
|
||||
* scoop-extra 由 soenggam 维护
|
||||
|
||||
请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。
|
||||
请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。
|
||||
|
||||
### Manual Install
|
||||
* 下载预编译的可执行程序
|
||||
|
||||
对于以上方法没有涵盖的用户,请直接前往 [Release](https://github.com/nxtrace/Ntrace-V1/releases/latest) 下载编译后的二进制可执行文件。
|
||||
对于以上方法没有涵盖的用户,请直接前往 [Release](https://www.nxtrace.org/downloads) 下载编译后的二进制可执行文件。
|
||||
|
||||
* `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
|
||||
* 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。
|
||||
|
||||
* 从源码安装
|
||||
|
||||
您可在自行安装Go >= 1.20后,使用以下命令安装
|
||||
|
||||
```shell
|
||||
go install github.com/nxtrace/Ntrace-V1@latest
|
||||
```
|
||||
安装后可执行文件在`$GOPATH/bin`目录下,如果您没有设置`GOPATH`,则在`$HOME/go/bin`目录下。
|
||||
|
||||
|
||||
### Get Started
|
||||
|
||||
`NextTrace` 默认使用`ICMP`协议发起`TraceRoute`请求,该协议同时支持`IPv4`和`IPv6`
|
||||
@@ -144,7 +164,7 @@ nexttrace --table 1.0.0.1
|
||||
nexttrace --raw 1.0.0.1
|
||||
nexttrace --json 1.0.0.1
|
||||
|
||||
# 只进行IPv4/IPv6解析
|
||||
# 只进行IPv4/IPv6解析,且当多个IP时自动选择第一个IP
|
||||
nexttrace --ipv4 g.co
|
||||
nexttrace --ipv6 g.co
|
||||
|
||||
@@ -153,7 +173,7 @@ nexttrace 2606:4700:4700::1111
|
||||
|
||||
# 禁用路径可视化 使用 --map / -M 参数
|
||||
nexttrace koreacentral.blob.core.windows.net
|
||||
# MapTrace URL: https://api.leo.moe/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
|
||||
# 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
|
||||
@@ -174,6 +194,17 @@ nexttrace --fast-trace
|
||||
|
||||
# 也可以使用 TCP SYN 而非 ICMP 进行测试
|
||||
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` 已支持指定网卡进行路由跟踪
|
||||
@@ -189,20 +220,23 @@ nexttrace --dev eth0 2606:4700:4700::1111
|
||||
nexttrace --source 204.98.134.56 9.9.9.9
|
||||
```
|
||||
|
||||
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求,不过目前`UDP`只支持`IPv4`
|
||||
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求
|
||||
|
||||
```bash
|
||||
# TCP SYN Trace
|
||||
nexttrace --tcp www.bing.com
|
||||
|
||||
# 可以自行指定端口[此处为443],默认80端口
|
||||
# 可以自行指定目标端口[此处为443],默认80端口
|
||||
nexttrace --tcp --port 443 2001:4860:4860::8888
|
||||
|
||||
# UDP Trace
|
||||
nexttrace --udp 1.0.0.1
|
||||
|
||||
# 可以自行指定端口[此处为5353],默认53端口
|
||||
# 可以自行指定目标端口[此处为5353],默认33494端口
|
||||
nexttrace --udp --port 5353 1.0.0.1
|
||||
|
||||
# TCP/UDP Trace 可以自行指定源端口,默认使用随机一个固定的端口(如需每次发包随机使用不同的源端口,请设置`ENV` `NEXTTRACE_RANDOMPORT`)
|
||||
nexttrace --tcp --source-port 14514 www.bing.com
|
||||
```
|
||||
|
||||
`NextTrace`也同样支持一些进阶功能,如 TTL 控制、并发数控制、模式切换等
|
||||
@@ -216,6 +250,8 @@ nexttrace --parallel-requests 1 www.hkix.net
|
||||
|
||||
# 从TTL为5开始发送探测包,直到TTL为10结束
|
||||
nexttrace --first 5 --max-hops 10 www.decix.net
|
||||
# 此外还提供了一个ENV,可以设置是否隐匿目的IP
|
||||
export NEXTTRACE_ENABLEHIDDENDSTIP=1
|
||||
|
||||
# 关闭IP反向解析功能
|
||||
nexttrace --no-rdns www.bbix.net
|
||||
@@ -223,6 +259,9 @@ nexttrace --no-rdns www.bbix.net
|
||||
# 设置载荷大小为1024字节
|
||||
nexttrace --psize 1024 example.com
|
||||
|
||||
# 设置载荷大小以及DF标志进行TCP Trace
|
||||
nexttrace --psize 1024 --dont-fragment --tcp example.com
|
||||
|
||||
# 特色功能:打印Route-Path图
|
||||
# Route-Path图示例:
|
||||
# AS6453 塔塔通信「Singapore『Singapore』」
|
||||
@@ -233,6 +272,10 @@ nexttrace --psize 1024 example.com
|
||||
# ╭╯
|
||||
# ╰AS37963 阿里云「ALIDNS.COM『ALIDNS.COM』」
|
||||
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`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`)
|
||||
@@ -242,11 +285,15 @@ nexttrace --route-path www.time.com.my
|
||||
nexttrace --data-provider ip-api.com
|
||||
## 特别的: 其中 ipinfo 和 IPInsight API 对于免费版查询有频率限制,可从这些服务商自行购买服务以解除限制,如有需要可以 clone 本项目添加其提供的 token 自行编译
|
||||
## TOKEN填写路径:ipgeo/tokens.go
|
||||
## 特别的: 对于离线库 IPInfoLocal,请自行下载并命名为 ipinfoLocal.mmdb (可以从这里下载:https://ipinfo.io/signup?ref=free-database-downloads)
|
||||
|
||||
## 特别的: 对于离线库 IPInfoLocal,请自行下载并命名为 ipinfoLocal.mmdb
|
||||
## (可以从这里下载:https://ipinfo.io/signup?ref=free-database-downloads),
|
||||
## 默认搜索用户当前路径、程序所在路径、和 FHS 路径(Unix-like)
|
||||
## 如果需要自定义路径,请设置环境变量
|
||||
export NEXTTRACE_IPINFOLOCALPATH=/xxx/yyy.mmdb
|
||||
## 对于离线库 Ip2region 可NextTrace自动下载,也可自行下载并命名为 ip2region.db
|
||||
## 另外:由于IP.SB被滥用比较严重,会经常出现无法查询的问题,请知悉。
|
||||
## IP-API.com限制调用较为严格,如有查询不到的情况,请几分钟后再试。
|
||||
|
||||
# 纯真IP数据库默认使用 http://127.0.0.1:2060 作为查询接口,如需自定义请使用环境变量
|
||||
export NEXTTRACE_CHUNZHENURL=http://127.0.0.1:2060
|
||||
## 可使用 https://github.com/freshcn/qqwry 自行搭建纯真IP数据库服务
|
||||
@@ -264,7 +311,7 @@ nexttrace -tcp --queries 2 --parallel-requests 1 --table --route-path 2001:4860:
|
||||
|
||||
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 -R 2001:4860:4860::8888
|
||||
nexttrace -T -q 2 --parallel-requests 1 -t -P 2001:4860:4860::8888
|
||||
```
|
||||
|
||||
### 全部用法详见 Usage 菜单
|
||||
@@ -275,15 +322,16 @@ Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
|
||||
<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)]
|
||||
[-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]
|
||||
[--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>"]
|
||||
[-R|--route] [-z|--send-time <integer>] [-i|--ttl-time
|
||||
<integer>] [--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_31 "<value>"] [--dot-server
|
||||
[-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)]
|
||||
(en|cn)] [--file "<value>"] [-C|--nocolor]
|
||||
|
||||
Arguments:
|
||||
|
||||
@@ -293,16 +341,10 @@ Arguments:
|
||||
-T --tcp Use TCP SYN for tracerouting (default port
|
||||
is 80)
|
||||
-U --udp Use UDP SYN for tracerouting (default port
|
||||
is 53)
|
||||
is 33494)
|
||||
-F --fast-trace One-Key Fast Trace to China ISPs
|
||||
-p --port Set the destination port to use. It is
|
||||
either initial udp port value for
|
||||
"default"method (incremented by each
|
||||
probe, default is 33434), or initial seq
|
||||
for "icmp" (incremented as well, default
|
||||
from 1), or some constantdestination port
|
||||
for other methods (with default of 80 for
|
||||
"tcp", 53 for "udp", etc.)
|
||||
-p --port Set the destination port to use. With
|
||||
default of 80 for "tcp", 33494 for "udp"
|
||||
-q --queries Set the number of probes per each hop.
|
||||
Default: 3
|
||||
--parallel-requests Set ParallelRequests number. It should be
|
||||
@@ -314,9 +356,9 @@ Arguments:
|
||||
IPInfo, IPInsight, IP-API.com, Ip2region,
|
||||
IPInfoLocal, CHUNZHEN, disable-geoip].
|
||||
Default: LeoMoeAPI
|
||||
--pow-provider Choose PoW Provider [api.leo.moe, sakura]
|
||||
For China mainland users, please use
|
||||
sakura. Default: api.leo.moe
|
||||
--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
|
||||
@@ -338,27 +380,31 @@ Arguments:
|
||||
-e --disable-mpls Disable MPLS
|
||||
-v --version Print version info and exit
|
||||
-s --source Use source src_addr for outgoing packets
|
||||
--source-port Use source port src_port for outgoing
|
||||
packets
|
||||
-D --dev Use the following Network Devices as the
|
||||
source address in outgoing packets
|
||||
-R --route Show Routing Table [Provided By BGP.Tools]
|
||||
-z --send-time Set how many [milliseconds] between
|
||||
sending each packet.. Useful when some
|
||||
routers use rate-limit for ICMP messages.
|
||||
Default: 100
|
||||
Default: 50
|
||||
-i --ttl-time Set how many [milliseconds] between
|
||||
sending packets groups by TTL. Useful when
|
||||
some routers use rate-limit for ICMP
|
||||
messages. Default: 500
|
||||
messages. Default: 50
|
||||
--timeout The number of [milliseconds] to keep probe
|
||||
sockets open before giving up on the
|
||||
connection.. Default: 1000
|
||||
--psize Set the packet size (payload size).
|
||||
Default: 52
|
||||
--_positionalArg_nexttrace_31 IP Address or domain name
|
||||
--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
|
||||
```
|
||||
|
||||
## 项目截图
|
||||
@@ -369,7 +415,7 @@ Arguments:
|
||||
|
||||
## 第三方 IP 数据库 API 开发接口
|
||||
|
||||
NextTrace 所有的的 IP 地理位置 `API DEMO` 可以参考[这里](https://github.com/sjlleo/nexttrace/blob/main/ipgeo/)
|
||||
NextTrace 所有的的 IP 地理位置 `API DEMO` 可以参考[这里](https://github.com/nxtrace/NTrace-core/blob/main/ipgeo/)
|
||||
|
||||
你可以在这里添加你自己的 API 接口,为了 NextTrace 能够正确显示你接口中的内容,请参考 `leo.go` 中所需要的信息
|
||||
|
||||
@@ -392,8 +438,7 @@ nexttrace --pow-provider sakura
|
||||
|
||||
## OpenTrace
|
||||
|
||||
`OpenTrace`是 @Archeb 开发的`NextTrace`的跨平台`GUI`版本,带来您熟悉但更强大的用户体验。
|
||||
|
||||
`OpenTrace`是 @Archeb 开发的`NextTrace`的跨平台`GUI`版本,带来您熟悉但更强大的用户体验。
|
||||
该软件仍然处于早期开发阶段,可能存在许多缺陷和错误,需要您宝贵的使用反馈。
|
||||
|
||||
[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace)
|
||||
@@ -404,22 +449,54 @@ nexttrace --pow-provider sakura
|
||||
|
||||
[https://github.com/nxtrace/nexttracewebapi](https://github.com/nxtrace/nexttracewebapi)
|
||||
|
||||
## NextTraceroute
|
||||
|
||||
`NextTraceroute`,一款默认使用`NextTrace API`的免`root`安卓版路由跟踪应用,由 @surfaceocean 开发。
|
||||
感谢所有测试用户的热情支持,本应用已经通过封闭测试,正式进入 Google Play 商店。
|
||||
|
||||
[https://github.com/nxtrace/NextTraceroute](https://github.com/nxtrace/NextTraceroute)
|
||||
<a href='https://play.google.com/store/apps/details?id=com.surfaceocean.nexttraceroute&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' width="128" height="48" src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/></a>
|
||||
|
||||
## AIWEN TECH Support
|
||||
|
||||
本项目受 [埃文科技](https://www.ipplus360.com) 赞助。 很高兴使用`埃文科技城市级IP库`增强本项目 GEOIP 查询的准确性与完整性,并免费提供给公众。
|
||||
|
||||
<img src="https://www.ipplus360.com/img/LOGO.c86cd0e1.svg" title="" alt="埃文科技 IP 定位数据" width="331">
|
||||
|
||||
## JetBrain Support
|
||||
|
||||
本项目受 [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport) 支持。 很高兴使用`Goland`作为我们的开发工具。
|
||||
|
||||
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" title="" alt="GoLand logo" width="331">
|
||||
|
||||
## Credits
|
||||
|
||||
[sjlleo](https://github.com/sjlleo) NextTrace 项目永远的领导者、创始人及核心贡献者
|
||||
[Gubo](https://www.gubo.org) 靠谱主机推荐
|
||||
|
||||
[BGP.TOOLS](https://bgp.tools) 提供了本项目的一些数据支持,在此表示由衷地感谢。
|
||||
[IPInfo](https://ipinfo.io) 无偿提供了本项目大部分数据支持
|
||||
|
||||
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
|
||||
[BGP.TOOLS](https://bgp.tools) 无偿提供了本项目的一些数据支持
|
||||
|
||||
[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
|
||||
[PeeringDB](https://www.peeringdb.com) 无偿提供了本项目的一些数据支持
|
||||
|
||||
[tsosunchia](https://github.com/tsosunchia)
|
||||
[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)
|
||||
|
||||
[bobo liu](https://github.com/fakeboboliu)
|
||||
|
||||
[YekongTAT](https://github.com/isyekong)
|
||||
|
||||
## Others
|
||||
|
||||
其他第三方 API 尽管集成在本项目内,但是具体的 TOS 以及 AUP,请详见第三方 API 官网。如遇到 IP 数据错误,也请直接联系他们纠错。
|
||||
@@ -430,12 +507,12 @@ nexttrace --pow-provider sakura
|
||||
## IP 数据以及精准度说明
|
||||
|
||||
对于IP相关信息的纠错反馈,我们目前开放了两个渠道:
|
||||
>- 本项目的GITHUB ISSUES区中的[IP 错误报告汇总帖](https://github.com/sjlleo/nexttrace/issues/41)
|
||||
>- 本项目的纠错专用邮箱: `correction@moeqing.com` (请注意此邮箱仅供IP相关信息纠错专用,其他反馈请发送ISSUE)
|
||||
>- 本项目的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) 以及 MoeQing Network 完成后续的二开和维护工作。
|
||||
该项目由 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 编写,该接口未经允许不可用于任何第三方用途。
|
||||
|
||||
@@ -488,3 +565,7 @@ LAX,US,California,Los Anegles
|
||||
```
|
||||
|
||||
需要注意的是,NextTrace 支持自动匹配 CSV 中的城市名,如果您的 PTR 记录中有 `losangeles`,您可以只添加上面一条记录就可以正常识别并读取。
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#nxtrace/NTrace-core&Date)
|
||||
|
||||
131
cmd/cmd.go
131
cmd/cmd.go
@@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
"github.com/nxtrace/NTrace-core/config"
|
||||
fastTrace "github.com/nxtrace/NTrace-core/fast_trace"
|
||||
@@ -31,18 +33,16 @@ func Excute() {
|
||||
ipv4Only := parser.Flag("4", "ipv4", &argparse.Options{Help: "Use IPv4 only"})
|
||||
ipv6Only := parser.Flag("6", "ipv6", &argparse.Options{Help: "Use IPv6 only"})
|
||||
tcp := parser.Flag("T", "tcp", &argparse.Options{Help: "Use TCP SYN for tracerouting (default port is 80)"})
|
||||
udp := parser.Flag("U", "udp", &argparse.Options{Help: "Use UDP SYN for tracerouting (default port is 53)"})
|
||||
udp := parser.Flag("U", "udp", &argparse.Options{Help: "Use UDP SYN for tracerouting (default port is 33494)"})
|
||||
fast_trace := parser.Flag("F", "fast-trace", &argparse.Options{Help: "One-Key Fast Trace to China ISPs"})
|
||||
port := parser.Int("p", "port", &argparse.Options{Help: "Set the destination port to use. It is either initial udp port value for \"default\"" +
|
||||
"method (incremented by each probe, default is 33434), or initial seq for \"icmp\" (incremented as well, default from 1), or some constant" +
|
||||
"destination port for other methods (with default of 80 for \"tcp\", 53 for \"udp\", etc.)"})
|
||||
port := parser.Int("p", "port", &argparse.Options{Help: "Set the destination port to use. With default of 80 for \"tcp\", 33494 for \"udp\"", Default: 80})
|
||||
numMeasurements := parser.Int("q", "queries", &argparse.Options{Default: 3, Help: "Set the number of probes per each hop"})
|
||||
parallelRequests := parser.Int("", "parallel-requests", &argparse.Options{Default: 18, Help: "Set ParallelRequests number. It should be 1 when there is a multi-routing"})
|
||||
maxHops := parser.Int("m", "max-hops", &argparse.Options{Default: 30, Help: "Set the max number of hops (max TTL to be reached)"})
|
||||
dataOrigin := parser.Selector("d", "data-provider", []string{"Ip2region", "ip2region", "IP.SB", "ip.sb", "IPInfo", "ipinfo", "IPInsight", "ipinsight", "IPAPI.com", "ip-api.com", "IPInfoLocal", "ipinfolocal", "chunzhen", "LeoMoeAPI", "leomoeapi", "disable-geoip"}, &argparse.Options{Default: "LeoMoeAPI",
|
||||
dataOrigin := parser.Selector("d", "data-provider", []string{"Ip2region", "ip2region", "IP.SB", "ip.sb", "IPInfo", "ipinfo", "IPInsight", "ipinsight", "IPAPI.com", "ip-api.com", "IPInfoLocal", "ipinfolocal", "chunzhen", "LeoMoeAPI", "leomoeapi", "ipdb.one", "disable-geoip"}, &argparse.Options{Default: "LeoMoeAPI",
|
||||
Help: "Choose IP Geograph Data Provider [IP.SB, IPInfo, IPInsight, IP-API.com, Ip2region, IPInfoLocal, CHUNZHEN, disable-geoip]"})
|
||||
powProvider := parser.Selector("", "pow-provider", []string{"api.leo.moe", "sakura"}, &argparse.Options{Default: "api.leo.moe",
|
||||
Help: "Choose PoW Provider [api.leo.moe, sakura] For China mainland users, please use sakura"})
|
||||
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"})
|
||||
@@ -57,18 +57,22 @@ func Excute() {
|
||||
disableMaptrace := parser.Flag("M", "map", &argparse.Options{Help: "Disable Print Trace Map"})
|
||||
disableMPLS := parser.Flag("e", "disable-mpls", &argparse.Options{Help: "Disable MPLS"})
|
||||
ver := parser.Flag("v", "version", &argparse.Options{Help: "Print version info and exit"})
|
||||
srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr for outgoing packets"})
|
||||
srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source address src_addr for outgoing packets"})
|
||||
srcPort := parser.Int("", "source-port", &argparse.Options{Help: "Use source port src_port for outgoing packets"})
|
||||
srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the following Network Devices as the source address in outgoing packets"})
|
||||
router := parser.Flag("R", "route", &argparse.Options{Help: "Show Routing Table [Provided By BGP.Tools]"})
|
||||
packetInterval := parser.Int("z", "send-time", &argparse.Options{Default: 100, Help: "Set how many [milliseconds] between sending each packet.. Useful when some routers use rate-limit for ICMP messages"})
|
||||
ttlInterval := parser.Int("i", "ttl-time", &argparse.Options{Default: 500, Help: "Set how many [milliseconds] between sending packets groups by TTL. Useful when some routers use rate-limit for ICMP messages"})
|
||||
timeout := parser.Int("", "timeout", &argparse.Options{Default: 1000, Help: "The number of [milliseconds] to keep probe sockets open before giving up on the connection."})
|
||||
packetSize := parser.Int("", "psize", &argparse.Options{Default: 52, Help: "Set the packet size (payload size)"})
|
||||
//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 {
|
||||
@@ -77,24 +81,44 @@ func Excute() {
|
||||
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)
|
||||
}
|
||||
|
||||
domain := *str
|
||||
|
||||
if *port == 0 {
|
||||
*port = 80
|
||||
if !*tcp && *port == 80 {
|
||||
*port = 33494
|
||||
}
|
||||
|
||||
if *fast_trace {
|
||||
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,
|
||||
@@ -102,9 +126,12 @@ func Excute() {
|
||||
Lang: *lang,
|
||||
PktSize: *packetSize,
|
||||
Timeout: time.Duration(*timeout) * time.Millisecond,
|
||||
File: *file,
|
||||
DontFragment: *dontFragment,
|
||||
Dot: *dot,
|
||||
}
|
||||
|
||||
fastTrace.FastTest(*tcp, *output, paramsFastTrace)
|
||||
fastTrace.FastTest(m, *output, paramsFastTrace)
|
||||
if *output {
|
||||
fmt.Println("您的追踪日志已经存放在 /tmp/trace.log 中")
|
||||
}
|
||||
@@ -119,7 +146,13 @@ func Excute() {
|
||||
}
|
||||
|
||||
if strings.Contains(domain, "/") {
|
||||
domain = strings.Split(domain, "/")[2]
|
||||
domain = "n" + domain
|
||||
parts := strings.Split(domain, "/")
|
||||
if len(parts) < 3 {
|
||||
fmt.Println("Invalid input")
|
||||
return
|
||||
}
|
||||
domain = parts[2]
|
||||
}
|
||||
|
||||
if strings.Contains(domain, "]") {
|
||||
@@ -131,7 +164,7 @@ func Excute() {
|
||||
}
|
||||
// DOMAIN处理结束
|
||||
|
||||
capabilities_check()
|
||||
capabilitiesCheck()
|
||||
// return
|
||||
|
||||
var ip net.IP
|
||||
@@ -158,7 +191,7 @@ func Excute() {
|
||||
// defer wg.Done()
|
||||
if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
|
||||
val, ok := os.LookupEnv("NEXTTRACE_DATAPROVIDER")
|
||||
if strings.ToUpper(*powProvider) != "API.LEO.MOE" {
|
||||
if strings.ToUpper(*powProvider) != "API.NXTRACE.ORG" {
|
||||
util.PowProviderParam = *powProvider
|
||||
}
|
||||
if ok {
|
||||
@@ -168,7 +201,9 @@ func Excute() {
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(w.Interrupt, os.Interrupt)
|
||||
defer func() {
|
||||
w.Conn.Close()
|
||||
if w.Conn != nil {
|
||||
w.Conn.Close()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -176,20 +211,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)
|
||||
}
|
||||
ip = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
|
||||
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 = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
|
||||
} else if *ipv4Only {
|
||||
ip = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
} else {
|
||||
ip = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
|
||||
}
|
||||
ip, err = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
//fmt.Println(err)
|
||||
//os.Exit(1)
|
||||
panic(err)
|
||||
}
|
||||
//}()
|
||||
//
|
||||
@@ -215,27 +249,14 @@ func Excute() {
|
||||
}
|
||||
|
||||
if !*jsonPrint {
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize)
|
||||
}
|
||||
|
||||
var m trace.Method = ""
|
||||
|
||||
switch {
|
||||
case *tcp:
|
||||
m = trace.TCPTrace
|
||||
case *udp:
|
||||
m = trace.UDPTrace
|
||||
default:
|
||||
m = trace.ICMPTrace
|
||||
}
|
||||
|
||||
if !*tcp && *port == 80 {
|
||||
*port = 53
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize, *srcAddr, string(m))
|
||||
}
|
||||
|
||||
util.DestIP = ip.String()
|
||||
var conf = trace.Config{
|
||||
DN42: *dn42,
|
||||
SrcAddr: *srcAddr,
|
||||
SrcPort: *srcPort,
|
||||
BeginHop: *beginHop,
|
||||
DestIP: ip,
|
||||
DestPort: *port,
|
||||
@@ -250,8 +271,12 @@ func Excute() {
|
||||
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
|
||||
@@ -332,7 +357,7 @@ func Excute() {
|
||||
}
|
||||
}
|
||||
|
||||
func capabilities_check() {
|
||||
func capabilitiesCheck() {
|
||||
|
||||
// Windows 判断放在前面,防止遇到一些奇奇怪怪的问题
|
||||
if runtime.GOOS == "windows" {
|
||||
|
||||
@@ -31,10 +31,11 @@ const (
|
||||
CT163 string = "电信 163 AS4134"
|
||||
CTCN2 string = "电信 CN2 AS4809"
|
||||
CU169 string = "联通 169 AS4837"
|
||||
CU9929 string = "联通 A网 AS9929"
|
||||
CM string = "移动 骨干网 AS9808"
|
||||
CU9929 string = "联通 A网(CNC) AS9929"
|
||||
CM string = "移动 CMNET AS9808"
|
||||
CMIN2 string = "移动 CMIN2 AS58807"
|
||||
EDU string = "教育网 CERNET AS4538"
|
||||
CST string = "科技网 CSTNET AS7497"
|
||||
)
|
||||
|
||||
var TestIPsCollection = AllLocationCollection{
|
||||
@@ -49,36 +50,48 @@ var Beijing = BackBoneCollection{
|
||||
Location: "北京",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "ipv4.pek-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.pek-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
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: "ipv4.pek-4837.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.pek-4837.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.pek-4837.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.pek-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU9929: ISPCollection{
|
||||
ISPName: CU9929,
|
||||
IP: "ipv4.pek-9929.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.pek-9929.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "ipv4.pek-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.pek-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.pek-9808.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.pek-9808.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CMIN2: ISPCollection{
|
||||
ISPName: CMIN2,
|
||||
IP: "ipv4.pek-58807.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.pek-58807.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "ipv4.pek-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.pek-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
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.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -86,42 +99,42 @@ var Shanghai = BackBoneCollection{
|
||||
Location: "上海",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "ipv4.sha-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.sha-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.sha-4134.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-4134.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CTCN2: ISPCollection{
|
||||
ISPName: CTCN2,
|
||||
IP: "ipv4.sha-4809.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.sha-4809.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "ipv4.sha-4837.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.sha-4837.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.sha-4837.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU9929: ISPCollection{
|
||||
ISPName: CU9929,
|
||||
IP: "ipv4.sha-9929.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.sha-9929.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.sha-9929.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-9929.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "ipv4.sha-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.sha-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.sha-9808.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-9808.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CMIN2: ISPCollection{
|
||||
ISPName: CMIN2,
|
||||
IP: "ipv4.sha-58807.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.sha-58807.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "ipv4.sha-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.sha-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.sha-4538.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-4538.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -129,20 +142,42 @@ var Guangzhou = BackBoneCollection{
|
||||
Location: "广州",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "ipv4.can-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.can-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
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: "ipv4.can-4837.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.can-4837.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.can-4837.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.can-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU9929: ISPCollection{
|
||||
ISPName: CU9929,
|
||||
IP: "ipv4.can-9929.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "ipv4.can-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.can-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
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.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -150,23 +185,24 @@ var Hangzhou = BackBoneCollection{
|
||||
Location: "杭州",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "ipv4.hgh-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.hgh-4134.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.hgh-4134.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hgh-4134.endpoint.nxtrace.org.",
|
||||
},
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "ipv4.hgh-4837.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.hgh-4837.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hgh-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "ipv4.hgh-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.hgh-9808.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.hgh-9808.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hgh-9808.endpoint.nxtrace.org.",
|
||||
},
|
||||
// 浙江大学 教育网
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "ipv4.hgh-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.hgh-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.hgh-4538.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hgh-4538.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -175,12 +211,12 @@ var Hefei = BackBoneCollection{
|
||||
// 中国科学技术大学 教育网
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "ipv4.hfe-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IPv6: "ipv6.hfe-4538.nexttrace-io-fasttrace-endpoint.win.",
|
||||
IP: "ipv4.hfe-4538.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hfe-4538.endpoint.nxtrace.org.",
|
||||
},
|
||||
// 中国科学技术大学 科技网
|
||||
CST: ISPCollection{
|
||||
ISPName: "中国科学技术大学 科技网 AS7497",
|
||||
IP: "ipv4.hfe-7497.nexttrace-io-fasttrace-endpoint.win.",
|
||||
ISPName: CST,
|
||||
IP: "ipv4.hfe-7497.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package fastTrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/printer"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
@@ -11,47 +12,54 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var pFastTracer ParamsFastTrace
|
||||
//var pFastTracer ParamsFastTrace
|
||||
|
||||
func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
|
||||
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)
|
||||
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)))
|
||||
|
||||
log.SetOutput(fp)
|
||||
log.SetFlags(0)
|
||||
fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX)
|
||||
log.Printf("『%s %s 』\n", location, ispCollection.ISPName)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, pFastTracer.MaxHops, pFastTracer.PktSize)
|
||||
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, pFastTracer.MaxHops, pFastTracer.PktSize)
|
||||
ip := util.DomainLookUp(ispCollection.IPv6, "6", "", true)
|
||||
// ip, err := util.DomainLookUp(ispCollection.IPv6, "6", "", true)
|
||||
ip, err := util.DomainLookUp(ispCollection.IPv6, "6", f.ParamsFastTrace.Dot, true)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var conf = trace.Config{
|
||||
BeginHop: pFastTracer.BeginHop,
|
||||
BeginHop: f.ParamsFastTrace.BeginHop,
|
||||
DestIP: ip,
|
||||
DestPort: 80,
|
||||
MaxHops: pFastTracer.MaxHops,
|
||||
DestPort: f.ParamsFastTrace.DestPort,
|
||||
MaxHops: f.ParamsFastTrace.MaxHops,
|
||||
NumMeasurements: 3,
|
||||
ParallelRequests: 18,
|
||||
RDns: pFastTracer.RDns,
|
||||
AlwaysWaitRDNS: pFastTracer.AlwaysWaitRDNS,
|
||||
RDns: f.ParamsFastTrace.RDns,
|
||||
AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS,
|
||||
PacketInterval: 100,
|
||||
TTLInterval: 500,
|
||||
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
|
||||
Timeout: pFastTracer.Timeout,
|
||||
PktSize: pFastTracer.PktSize,
|
||||
Lang: pFastTracer.Lang,
|
||||
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
|
||||
@@ -63,7 +71,7 @@ func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
println()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func (f *FastTracer) testAll_v6() {
|
||||
@@ -87,6 +95,7 @@ func (f *FastTracer) testCU_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929)
|
||||
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
}
|
||||
|
||||
@@ -101,29 +110,47 @@ 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() {
|
||||
func (f *FastTracer) testFastBJ_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
|
||||
}
|
||||
|
||||
func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
func (f *FastTracer) testFastSH_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFastGZ_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
}
|
||||
|
||||
func FastTestv6(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
var c string
|
||||
|
||||
oe = outEnable
|
||||
pFastTracer = paramsFastTrace
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网\n6. 全部")
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 北京三网快速测试\n2. 上海三网快速测试\n3. 广州三网快速测试\n4. 全国电信\n5. 全国联通\n6. 全国移动\n7. 全国教育网\n8. 全国五网")
|
||||
fmt.Print("请选择选项:")
|
||||
_, err := fmt.Scanln(&c)
|
||||
if err != nil {
|
||||
c = "1"
|
||||
}
|
||||
|
||||
ft := FastTracer{}
|
||||
ft := FastTracer{
|
||||
ParamsFastTrace: paramsFastTrace,
|
||||
}
|
||||
|
||||
// 建立 WebSocket 连接
|
||||
w := wshandle.New()
|
||||
@@ -133,27 +160,33 @@ func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
|
||||
if !tm {
|
||||
switch traceMode {
|
||||
case trace.ICMPTrace:
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数")
|
||||
} else {
|
||||
case trace.TCPTrace:
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
case trace.UDPTrace:
|
||||
ft.TracerouteMethod = trace.UDPTrace
|
||||
}
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testFast_v6()
|
||||
ft.testFastBJ_v6()
|
||||
case "2":
|
||||
ft.testCT_v6()
|
||||
ft.testFastSH_v6()
|
||||
case "3":
|
||||
ft.testCU_v6()
|
||||
ft.testFastGZ_v6()
|
||||
case "4":
|
||||
ft.testCM_v6()
|
||||
ft.testCT_v6()
|
||||
case "5":
|
||||
ft.testEDU_v6()
|
||||
ft.testCU_v6()
|
||||
case "6":
|
||||
ft.testCM_v6()
|
||||
case "7":
|
||||
ft.testEDU_v6()
|
||||
case "8":
|
||||
ft.testAll_v6()
|
||||
default:
|
||||
ft.testFast_v6()
|
||||
ft.testFastBJ_v6()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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"
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -23,6 +26,7 @@ type FastTracer struct {
|
||||
type ParamsFastTrace struct {
|
||||
SrcDev string
|
||||
SrcAddr string
|
||||
DestPort int
|
||||
BeginHop int
|
||||
MaxHops int
|
||||
RDns bool
|
||||
@@ -30,33 +34,32 @@ type ParamsFastTrace struct {
|
||||
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) {
|
||||
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)
|
||||
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)))
|
||||
|
||||
log.SetOutput(fp)
|
||||
log.SetFlags(0)
|
||||
fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX)
|
||||
log.Printf("『%s %s 』\n", location, ispCollection.ISPName)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
ip := util.DomainLookUp(ispCollection.IP, "4", "", true)
|
||||
// ip, err := util.DomainLookUp(ispCollection.IP, "4", "", true)
|
||||
ip, err := util.DomainLookUp(ispCollection.IP, "4", f.ParamsFastTrace.Dot, true)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var conf = trace.Config{
|
||||
BeginHop: f.ParamsFastTrace.BeginHop,
|
||||
DestIP: ip,
|
||||
DestPort: 80,
|
||||
DestPort: f.ParamsFastTrace.DestPort,
|
||||
MaxHops: f.ParamsFastTrace.MaxHops,
|
||||
NumMeasurements: 3,
|
||||
ParallelRequests: 18,
|
||||
@@ -69,9 +72,25 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
|
||||
SrcAddr: f.ParamsFastTrace.SrcAddr,
|
||||
PktSize: f.ParamsFastTrace.PktSize,
|
||||
Lang: f.ParamsFastTrace.Lang,
|
||||
DontFragment: f.ParamsFastTrace.DontFragment,
|
||||
}
|
||||
|
||||
if oe {
|
||||
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
|
||||
@@ -82,13 +101,19 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
println()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
func FastTest(traceMode trace.Method, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
// tm means tcp mode
|
||||
var c string
|
||||
pFastTrace := paramsFastTrace
|
||||
oe = outEnable
|
||||
|
||||
if paramsFastTrace.File != "" {
|
||||
testFile(paramsFastTrace, traceMode)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Hi,欢迎使用 Fast Trace 功能,请注意 Fast Trace 功能只适合新手使用\n因为国内网络复杂,我们设置的测试目标有限,建议普通用户自测以获得更加精准的路由情况")
|
||||
fmt.Println("请您选择要测试的 IP 类型\n1. IPv4\n2. IPv6")
|
||||
fmt.Print("请选择选项:")
|
||||
@@ -97,22 +122,47 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
c = "1"
|
||||
}
|
||||
if c == "2" {
|
||||
FastTestv6(tm, outEnable, paramsFastTrace)
|
||||
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 pFastTrace.SrcDev != "" {
|
||||
dev, _ := net.InterfaceByName(pFastTrace.SrcDev)
|
||||
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 {
|
||||
pFastTrace.SrcAddr = addr.(*net.IPNet).IP.String()
|
||||
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.Println("您想测试哪些ISP的路由?\n1. 北京三网快速测试\n2. 上海三网快速测试\n3. 广州三网快速测试\n4. 全国电信\n5. 全国联通\n6. 全国移动\n7. 全国教育网\n8. 全国五网")
|
||||
fmt.Print("请选择选项:")
|
||||
_, err = fmt.Scanln(&c)
|
||||
if err != nil {
|
||||
@@ -120,7 +170,7 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
}
|
||||
|
||||
ft := FastTracer{
|
||||
ParamsFastTrace: pFastTrace,
|
||||
ParamsFastTrace: paramsFastTrace,
|
||||
}
|
||||
|
||||
// 建立 WebSocket 连接
|
||||
@@ -131,31 +181,207 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
|
||||
if !tm {
|
||||
switch traceMode {
|
||||
case trace.ICMPTrace:
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数")
|
||||
} else {
|
||||
case trace.TCPTrace:
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
case trace.UDPTrace:
|
||||
ft.TracerouteMethod = trace.UDPTrace
|
||||
}
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testFast()
|
||||
ft.testFastBJ()
|
||||
case "2":
|
||||
ft.testCT()
|
||||
ft.testFastSH()
|
||||
case "3":
|
||||
ft.testCU()
|
||||
ft.testFastGZ()
|
||||
case "4":
|
||||
ft.testCM()
|
||||
ft.testCT()
|
||||
case "5":
|
||||
ft.testEDU()
|
||||
ft.testCU()
|
||||
case "6":
|
||||
ft.testCM()
|
||||
case "7":
|
||||
ft.testEDU()
|
||||
case "8":
|
||||
ft.testAll()
|
||||
default:
|
||||
ft.testFast()
|
||||
ft.testFastBJ()
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
f.testCT()
|
||||
println()
|
||||
@@ -168,10 +394,12 @@ func (f *FastTracer) testAll() {
|
||||
|
||||
func (f *FastTracer) testCT() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CTCN2)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CTCN2)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CTCN2)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCU() {
|
||||
@@ -179,8 +407,10 @@ func (f *FastTracer) testCU() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU9929)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU9929)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169)
|
||||
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCM() {
|
||||
@@ -188,21 +418,38 @@ func (f *FastTracer) testCM() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CMIN2)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CMIN2)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CMIN2)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testEDU() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.EDU)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.EDU)
|
||||
f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.EDU)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.EDU)
|
||||
// 科技网暂时算在EDU里面,等拿到了足够多的数据再分离出去,单独用于测试
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
|
||||
f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.CST)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFast() {
|
||||
func (f *FastTracer) testFastBJ() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CST)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFastSH() {
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testFastGZ() {
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
}
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
package fastTrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"testing"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
"github.com/nxtrace/NTrace-core/wshandle"
|
||||
)
|
||||
|
||||
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
|
||||
//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)
|
||||
|
||||
53
go.mod
53
go.mod
@@ -1,46 +1,45 @@
|
||||
module github.com/nxtrace/NTrace-core
|
||||
|
||||
go 1.21
|
||||
go 1.24.5
|
||||
|
||||
require (
|
||||
github.com/akamensky/argparse v1.4.0
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/spf13/viper v1.20.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
github.com/tsosunchia/powclient v0.1.3
|
||||
golang.org/x/net v0.15.0
|
||||
golang.org/x/sync v0.3.0
|
||||
github.com/tsosunchia/powclient v0.1.5
|
||||
golang.org/x/net v0.42.0
|
||||
golang.org/x/sync v0.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // 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.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
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.9.2 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/lionsoul2014/ip2region v2.11.2+incompatible
|
||||
github.com/rodaine/table v1.1.0
|
||||
github.com/tidwall/gjson v1.17.0
|
||||
github.com/rodaine/table v1.3.0
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
546
go.sum
546
go.sum
@@ -1,527 +1,109 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
|
||||
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
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/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
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.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4=
|
||||
github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
|
||||
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
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.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
||||
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
||||
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.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
||||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tsosunchia/powclient v0.1.3 h1:L29HQdyZZ0Vcrn0ZzNIyrTpLOnycm3oHF46sYJ+vkhQ=
|
||||
github.com/tsosunchia/powclient v0.1.3/go.mod h1:Pm4MP3QqN74SfNskPpFIEyT+NQrcABGoXkkeRwjlMEE=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
const (
|
||||
ipDataBasePath = "./ip2region.db"
|
||||
defaultDownURL = "1"
|
||||
originURL = "https://ghproxy.com/?q=https://github.com/bqf9979/ip2region/blob/master/data/ip2region.db?raw=true"
|
||||
originURL = "https://mirror.ghproxy.com/?q=https://github.com/bqf9979/ip2region/blob/master/data/ip2region.db?raw=true"
|
||||
)
|
||||
|
||||
func downloadDataBase() error {
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func IPApiCom(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
url := "http://ip-api.com/json/" + ip + "?fields=status,message,country,regionName,city,isp,district,as,lat,lon"
|
||||
url := token.BaseOrDefault("http://ip-api.com/json/") + ip + "?fields=status,message,country,regionName,city,isp,district,as,lat,lon"
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: timeout,
|
||||
|
||||
265
ipgeo/ipdbone.go
Normal file
265
ipgeo/ipdbone.go
Normal file
@@ -0,0 +1,265 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/config"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// Language mapping for IPDB.One API
|
||||
var LangMap = map[string]string{
|
||||
"en": "en",
|
||||
"cn": "zh",
|
||||
}
|
||||
|
||||
// IPDBOneConfig holds the configuration for IPDB.One service
|
||||
type IPDBOneConfig struct {
|
||||
BaseURL string
|
||||
ApiID string
|
||||
ApiKey string
|
||||
}
|
||||
|
||||
// GetDefaultConfig returns the default configuration with fallback values
|
||||
func GetDefaultConfig() *IPDBOneConfig {
|
||||
return &IPDBOneConfig{
|
||||
BaseURL: util.GetenvDefault("IPDBONE_BASE_URL", "https://api.ipdb.one"),
|
||||
ApiID: util.GetenvDefault("IPDBONE_API_ID", ""),
|
||||
ApiKey: util.GetenvDefault("IPDBONE_API_KEY", ""),
|
||||
}
|
||||
}
|
||||
|
||||
// IPDBOneTokenCache manages the caching of auth tokens
|
||||
type IPDBOneTokenCache struct {
|
||||
token string
|
||||
expiresAt time.Time
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// GetToken retrieves cached token if valid, otherwise returns empty string
|
||||
func (c *IPDBOneTokenCache) GetToken() string {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
if c.token == "" || time.Now().After(c.expiresAt) {
|
||||
return ""
|
||||
}
|
||||
return c.token
|
||||
}
|
||||
|
||||
// SetToken updates the token with its expiration time
|
||||
func (c *IPDBOneTokenCache) SetToken(token string, expiresIn time.Duration) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.token = token
|
||||
c.expiresAt = time.Now().Add(expiresIn)
|
||||
}
|
||||
|
||||
// IPDBOneClient handles communication with IPDB.One API
|
||||
type IPDBOneClient struct {
|
||||
config *IPDBOneConfig
|
||||
tokenCache *IPDBOneTokenCache
|
||||
tokenInit sync.Once
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewIPDBOneClient creates a new client for IPDB.One with default configuration
|
||||
func NewIPDBOneClient() *IPDBOneClient {
|
||||
return &IPDBOneClient{
|
||||
config: GetDefaultConfig(),
|
||||
tokenCache: &IPDBOneTokenCache{},
|
||||
httpClient: &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// fetchToken requests a new authentication token from the API
|
||||
func (c *IPDBOneClient) fetchToken() error {
|
||||
authURL := c.config.BaseURL + "/auth/requestToken/query"
|
||||
|
||||
req, err := http.NewRequest("GET", authURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "NextTrace/"+config.Version)
|
||||
req.Header.Set("x-api-id", c.config.ApiID)
|
||||
req.Header.Set("x-api-key", c.config.ApiKey)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
statusCode := gjson.Get(string(body), "code").Int()
|
||||
statusMessage := gjson.Get(string(body), "message").String()
|
||||
|
||||
if statusCode != 200 {
|
||||
return errors.New("failed to authenticate: " + statusMessage)
|
||||
}
|
||||
|
||||
token := gjson.Get(string(body), "data.token").String()
|
||||
if token == "" {
|
||||
return errors.New("authentication failed: empty token received")
|
||||
}
|
||||
|
||||
// Cache token with a 30-second expiration
|
||||
c.tokenCache.SetToken(token, 30*time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureToken makes sure a valid token is available, fetching a new one if needed
|
||||
func (c *IPDBOneClient) ensureToken() error {
|
||||
var initErr error
|
||||
|
||||
// Ensure API credentials are set
|
||||
if c.config.ApiID == "" || c.config.ApiKey == "" {
|
||||
return errors.New("api id or api key is not set")
|
||||
}
|
||||
|
||||
// Initialize token the first time this is called
|
||||
c.tokenInit.Do(func() {
|
||||
initErr = c.fetchToken()
|
||||
})
|
||||
|
||||
if initErr != nil {
|
||||
return initErr
|
||||
}
|
||||
|
||||
// If token expired or not available, get a new one
|
||||
if c.tokenCache.GetToken() == "" {
|
||||
return c.fetchToken()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupIP queries the IP information from IPDB.One
|
||||
func (c *IPDBOneClient) LookupIP(ip string, lang string) (*IPGeoData, error) {
|
||||
|
||||
// Ensure we have a valid token
|
||||
if err := c.ensureToken(); err != nil {
|
||||
return &IPGeoData{}, nil
|
||||
}
|
||||
|
||||
// Map language code if needed
|
||||
langCode, ok := LangMap[lang]
|
||||
if !ok {
|
||||
langCode = "en" // Default to English
|
||||
}
|
||||
|
||||
// Query the IP information
|
||||
queryURL := c.config.BaseURL + "/query/" + ip + "?lang=" + langCode
|
||||
|
||||
req, err := http.NewRequest("GET", queryURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "NextTrace/"+config.Version)
|
||||
req.Header.Set("Authorization", "Bearer "+c.tokenCache.GetToken())
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statusCode := gjson.Get(string(body), "code").Int()
|
||||
if statusCode != 200 {
|
||||
return nil, errors.New("failed to get IP info: " + gjson.Get(string(body), "message").String())
|
||||
}
|
||||
|
||||
return parseIPDBOneResponse(ip, body)
|
||||
}
|
||||
|
||||
// parseIPDBOneResponse converts the API response to an IPGeoData struct
|
||||
func parseIPDBOneResponse(ip string, responseBody []byte) (*IPGeoData, error) {
|
||||
data := gjson.Get(string(responseBody), "data")
|
||||
geoData := data.Get("geo")
|
||||
routingData := data.Get("routing")
|
||||
|
||||
result := &IPGeoData{
|
||||
IP: ip,
|
||||
}
|
||||
|
||||
// Parse geo information if available
|
||||
if geoData.Exists() {
|
||||
coordinate := geoData.Get("coordinate")
|
||||
if coordinate.Exists() && coordinate.Type != gjson.Null && coordinate.IsArray() && len(coordinate.Array()) >= 2 {
|
||||
result.Lat = coordinate.Array()[0].Float()
|
||||
result.Lng = coordinate.Array()[1].Float()
|
||||
}
|
||||
|
||||
if geoData.Get("country").Exists() && geoData.Get("country").Type != gjson.Null {
|
||||
result.Country = geoData.Get("country").String()
|
||||
}
|
||||
|
||||
if geoData.Get("region").Exists() && geoData.Get("region").Type != gjson.Null {
|
||||
result.Prov = geoData.Get("region").String()
|
||||
}
|
||||
|
||||
if geoData.Get("city").Exists() && geoData.Get("city").Type != gjson.Null {
|
||||
result.City = geoData.Get("city").String()
|
||||
}
|
||||
}
|
||||
|
||||
// Parse routing information if available
|
||||
if routingData.Exists() {
|
||||
asnData := routingData.Get("asn")
|
||||
if asnData.Get("number").Exists() && asnData.Get("number").Type != gjson.Null {
|
||||
result.Asnumber = strconv.FormatInt(asnData.Get("number").Int(), 10)
|
||||
}
|
||||
|
||||
if routingData.Get("asn.name").Exists() && routingData.Get("asn.name").Type != gjson.Null {
|
||||
result.Owner = routingData.Get("asn.name").String()
|
||||
}
|
||||
|
||||
// Get domain, override owner
|
||||
if routingData.Get("asn.domain").Exists() && routingData.Get("asn.domain").Type != gjson.Null {
|
||||
result.Owner = routingData.Get("asn.domain").String()
|
||||
}
|
||||
|
||||
// Get asname as Whois
|
||||
if routingData.Get("asn.asname").Exists() && routingData.Get("asn.asname").Type != gjson.Null {
|
||||
result.Whois = routingData.Get("asn.asname").String()
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Global client instance for backward compatibility
|
||||
var defaultClient = NewIPDBOneClient()
|
||||
|
||||
// IPDBOne looks up IP information from IPDB.One (maintains backward compatibility)
|
||||
func IPDBOne(ip string, timeout time.Duration, lang string, _ bool) (*IPGeoData, error) {
|
||||
// Override timeout if specified
|
||||
if timeout > 0 {
|
||||
defaultClient.httpClient.Timeout = timeout
|
||||
}
|
||||
|
||||
return defaultClient.LookupIP(ip, lang)
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func Filter(ip string) (*IPGeoData, bool) {
|
||||
default:
|
||||
}
|
||||
// 判断是否为v6 且不在2000::/3
|
||||
if net.ParseIP(ip).To4() == nil && !cidrRangeContains("2000::/3", ip) {
|
||||
if net.ParseIP(ip).To4() == nil && !cidrRangeContains("2000::/3", ip) && !isFiltered {
|
||||
asn = ""
|
||||
whois = "INVALID"
|
||||
isFiltered = true
|
||||
|
||||
@@ -52,6 +52,8 @@ func GetSource(s string) Source {
|
||||
return Chunzhen
|
||||
case "DISABLE-GEOIP":
|
||||
return disableGeoIP
|
||||
case "IPDB.ONE":
|
||||
return IPDBOne
|
||||
default:
|
||||
return LeoIP
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ import (
|
||||
// )
|
||||
|
||||
func TestXxx(t *testing.T) {
|
||||
const ID_FIXED_HEADER = "10"
|
||||
const IdFixedHeader = "10"
|
||||
var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位
|
||||
var ttl = fmt.Sprintf("%06b", 95) //取TTL的后6位
|
||||
fmt.Println(os.Getpid()&0x7f, 95)
|
||||
|
||||
var parity int
|
||||
id := ID_FIXED_HEADER + processID + ttl
|
||||
id := IdFixedHeader + processID + ttl
|
||||
for _, c := range id {
|
||||
if c == '1' {
|
||||
parity++
|
||||
@@ -33,12 +33,12 @@ func TestXxx(t *testing.T) {
|
||||
} else {
|
||||
id += "0"
|
||||
}
|
||||
process_id, ttl_r, _ := reverseID(id)
|
||||
log.Println(process_id, ttl_r)
|
||||
processId, ttlR, _ := reverseID(id)
|
||||
log.Println(processId, ttlR)
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
res, err := Filter("::1")
|
||||
res, err := Filter("fd11::1")
|
||||
//打印whois信息
|
||||
fmt.Println(res.Whois)
|
||||
print(err)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func IPInfo(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
url := "http://ipinfo.io/" + ip + "?token=" + token.ipinfo
|
||||
url := token.BaseOrDefault("http://ipinfo.io/") + ip + "?token=" + token.ipinfo
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: timeout,
|
||||
|
||||
@@ -2,24 +2,71 @@ package ipgeo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
)
|
||||
|
||||
const (
|
||||
ipinfoDataBasePath = "./ipinfoLocal.mmdb"
|
||||
ipinfoDataBaseFilename = "ipinfoLocal.mmdb"
|
||||
)
|
||||
|
||||
// Cache the path of the ipinfoLocal.mmdb file
|
||||
var ipinfoDataBasePath = ""
|
||||
|
||||
// We will try to get the path of the ipinfoLocal.mmdb file in the following order:
|
||||
// 1. Use the value of the environment variable NEXTTRACE_IPINFOLOCALPATH
|
||||
// 2. Search in the current folder and the executable folder
|
||||
// 3. Search in /usr/local/share/nexttrace/ and /usr/share/nexttrace/ (for Unix/Linux)
|
||||
// If the file is found, the path will be stored in the ipinfoDataBasePath variable
|
||||
func getIPInfoLocalPath() (error) {
|
||||
if ipinfoDataBasePath != "" {
|
||||
return nil
|
||||
}
|
||||
// NEXTTRACE_IPINFOLOCALPATH
|
||||
if util.EnvIPInfoLocalPath != "" {
|
||||
if _, err := os.Stat(util.EnvIPInfoLocalPath); err == nil {
|
||||
ipinfoDataBasePath = util.EnvIPInfoLocalPath
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("NEXTTRACE_IPINFOLOCALPATH is set but the file does not exist")
|
||||
}
|
||||
}
|
||||
folders := []string{}
|
||||
// current folder
|
||||
if cur, err := os.Getwd(); err == nil {
|
||||
folders = append(folders, cur + string(filepath.Separator))
|
||||
}
|
||||
// exeutable folder
|
||||
if exe, err := os.Executable(); err == nil {
|
||||
folders = append(folders, filepath.Dir(exe) + string(filepath.Separator))
|
||||
}
|
||||
if runtime.GOOS != "windows" {
|
||||
folders = append(folders, "/usr/local/share/nexttrace/")
|
||||
folders = append(folders, "/usr/share/nexttrace/")
|
||||
}
|
||||
for _, folder := range folders {
|
||||
if _, err := os.Stat(folder + ipinfoDataBaseFilename); err == nil {
|
||||
ipinfoDataBasePath = folder + ipinfoDataBaseFilename
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("no ipinfoLocal.mmdb found")
|
||||
}
|
||||
|
||||
func IPInfoLocal(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
if _, err := os.Stat(ipinfoDataBasePath); os.IsNotExist(err) {
|
||||
if err := getIPInfoLocalPath(); err != nil {
|
||||
panic("Cannot find ipinfoLocal.mmdb")
|
||||
}
|
||||
region, err := maxminddb.Open(ipinfoDataBasePath)
|
||||
if err != nil {
|
||||
panic("Cannot find ipinfoLocal.mmdb")
|
||||
panic("Cannot open ipinfoLocal.mmdb at " + ipinfoDataBasePath)
|
||||
}
|
||||
defer func(region *maxminddb.Reader) {
|
||||
err := region.Close()
|
||||
@@ -33,19 +80,19 @@ func IPInfoLocal(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, erro
|
||||
return &IPGeoData{}, errors.New("no results")
|
||||
}
|
||||
recordMap := record.(map[string]interface{})
|
||||
country_name := recordMap["country_name"].(string)
|
||||
countryName := recordMap["country_name"].(string)
|
||||
prov := ""
|
||||
if recordMap["country"].(string) == "HK" {
|
||||
country_name = "China"
|
||||
countryName = "China"
|
||||
prov = "Hong Kong"
|
||||
}
|
||||
if recordMap["country"].(string) == "TW" {
|
||||
country_name = "China"
|
||||
countryName = "China"
|
||||
prov = "Taiwan"
|
||||
}
|
||||
return &IPGeoData{
|
||||
Asnumber: strings.TrimPrefix(recordMap["asn"].(string), "AS"),
|
||||
Country: country_name,
|
||||
Country: countryName,
|
||||
City: "",
|
||||
Prov: prov,
|
||||
Owner: recordMap["as_name"].(string),
|
||||
|
||||
@@ -13,7 +13,7 @@ func IPInSight(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData,
|
||||
// 2 秒超时
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Get("https://api.ipinsight.io/ip/" + ip + "?token=" + token.ipinsight)
|
||||
resp, err := client.Get(token.BaseOrDefault("https://api.ipinsight.io/ip/") + ip + "?token=" + token.ipinsight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func IPSB(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
url := "https://api.ip.sb/geoip/" + ip
|
||||
url := token.BaseOrDefault("https://api.ip.sb/geoip/") + ip
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: timeout,
|
||||
|
||||
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 {
|
||||
|
||||
@@ -6,10 +6,19 @@ type tokenData struct {
|
||||
ipinsight string
|
||||
ipinfo string
|
||||
ipleo string
|
||||
baseUrl string
|
||||
}
|
||||
|
||||
func (t *tokenData) BaseOrDefault(def string) string {
|
||||
if t.baseUrl == "" {
|
||||
return def
|
||||
}
|
||||
return t.baseUrl
|
||||
}
|
||||
|
||||
var token = tokenData{
|
||||
ipinsight: util.GetenvDefault("NEXTTRACE_IPINSIGHT_TOKEN", ""),
|
||||
ipinfo: util.GetenvDefault("NEXTTRACE_IPINFO_TOKEN", ""),
|
||||
baseUrl: util.GetenvDefault("NEXTTRACE_IPAPI_BASE", ""),
|
||||
ipleo: "NextTraceDemo",
|
||||
}
|
||||
|
||||
124
nt_install.sh
Normal file
124
nt_install.sh
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$1" = "http" ]; then
|
||||
protocol="http"
|
||||
else
|
||||
protocol="https"
|
||||
fi
|
||||
|
||||
|
||||
Green_font="\033[32m"
|
||||
Yellow_font="\033[33m"
|
||||
Red_font="\033[31m"
|
||||
Font_suffix="\033[0m"
|
||||
Info="${Green_font}[Info]${Font_suffix}"
|
||||
Error="${Red_font}[Error]${Font_suffix}"
|
||||
Tips="${Green_font}[Tips]${Font_suffix}"
|
||||
Temp_path="/var/tmp/nexttrace"
|
||||
|
||||
checkRootPermit() {
|
||||
[[ $EUID -ne 0 ]] && echo -e "${Error} 请使用sudo/root权限运行本脚本" && exit 1
|
||||
}
|
||||
|
||||
checkSystemArch() {
|
||||
arch=$(uname -m)
|
||||
if [[ $arch == "x86_64" ]]; then
|
||||
archParam="amd64"
|
||||
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*)
|
||||
osDistribution="linux"
|
||||
|
||||
if [ ! -d "/usr/local" ];
|
||||
then
|
||||
downPath="/usr/bin/nexttrace"
|
||||
else
|
||||
downPath="/usr/local/bin/nexttrace"
|
||||
fi
|
||||
|
||||
;;
|
||||
*)
|
||||
echo "unknown: $OSTYPE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
downloadBinrayFile() {
|
||||
echo -e "${Info} 获取最新版的 NextTrace 发行版文件信息"
|
||||
for i in {1..3}; do
|
||||
downloadUrls=$(curl -sLf ${protocol}://www.nxtrace.org/api/dist/core/nexttrace_${osDistribution}_${archParam} --connect-timeout 2)
|
||||
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 2
|
||||
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 2
|
||||
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} 获取下载地址失败,请检查您的网络是否正常"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
changeMode() {
|
||||
chmod +x ${Temp_path} &> /dev/null
|
||||
}
|
||||
|
||||
runBinrayFileHelp() {
|
||||
if [ -e ${downPath} ]; then
|
||||
${downPath} --version
|
||||
echo -e "${Tips} 一切准备就绪!使用命令 nexttrace 1.1.1.1 开始您的第一次路由测试吧~ 更多进阶命令玩法可以用 nexttrace -h 查看哦\n 关于软件卸载,因为nexttrace是绿色版单文件,卸载只需输入命令 rm ${downPath} 即可"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check Procedure
|
||||
checkRootPermit
|
||||
checkSystemDistribution
|
||||
checkSystemArch
|
||||
|
||||
# Download Procedure
|
||||
downloadBinrayFile
|
||||
|
||||
# Run Procedure
|
||||
runBinrayFileHelp
|
||||
|
||||
131
nt_install_v1.sh
131
nt_install_v1.sh
@@ -1,131 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
Green_font="\033[32m"
|
||||
Yellow_font="\033[33m"
|
||||
Red_font="\033[31m"
|
||||
Font_suffix="\033[0m"
|
||||
Info="${Green_font}[Info]${Font_suffix}"
|
||||
Error="${Red_font}[Error]${Font_suffix}"
|
||||
Tips="${Green_font}[Tips]${Font_suffix}"
|
||||
Temp_path="/var/tmp/nexttrace"
|
||||
|
||||
checkRootPermit() {
|
||||
[[ $EUID -ne 0 ]] && echo -e "${Error} 请使用sudo/root权限运行本脚本" && exit 1
|
||||
}
|
||||
|
||||
checkSystemArch() {
|
||||
arch=$(uname -m)
|
||||
if [[ $arch == "x86_64" ]]; then
|
||||
archParam="amd64"
|
||||
elif [[ $arch == "i386" ]]; 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*)
|
||||
osDistribution="linux"
|
||||
|
||||
if [ ! -d "/usr/local" ];
|
||||
then
|
||||
downPath="/usr/bin/nexttrace"
|
||||
else
|
||||
downPath="/usr/local/bin/nexttrace"
|
||||
fi
|
||||
|
||||
;;
|
||||
*)
|
||||
echo "unknown: $OSTYPE"
|
||||
exit 1
|
||||
;;
|
||||
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 -sL http://nexttrace-io-leomoe-api-a0.shop/latest_v1.json | grep -i "browser_download_url.*${osDistribution}.*${archParam}" | awk -F '"' '{print $4}')
|
||||
|
||||
echo -e "${Info} 正在下载 NextTrace 二进制文件..."
|
||||
wget -O ${Temp_path} ${latestURL} &> /dev/null
|
||||
if [ $? -eq 0 ];
|
||||
then
|
||||
changeMode
|
||||
mv ${Temp_path} ${downPath}
|
||||
echo -e "${Info} NextTrace 现在已经在您的系统中可用"
|
||||
else
|
||||
echo -e "${Error} NextTrace 下载失败,请检查您的网络是否正常"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
changeMode() {
|
||||
chmod +x ${Temp_path} &> /dev/null
|
||||
}
|
||||
|
||||
runBinrayFileHelp() {
|
||||
if [ -e ${downPath} ]; then
|
||||
${downPath} --version
|
||||
echo -e "${Tips} 一切准备就绪!使用命令 nexttrace 1.1.1.1 开始您的第一次路由测试吧~ 更多进阶命令玩法可以用 nexttrace -h 查看哦\n 关于软件卸载,因为nexttrace是绿色版单文件,卸载只需输入命令 rm ${downPath} 即可"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check Procedure
|
||||
checkRootPermit
|
||||
checkSystemDistribution
|
||||
checkSystemArch
|
||||
checkWgetPackage
|
||||
|
||||
downloadBinrayFile
|
||||
|
||||
# Run Procedure
|
||||
runBinrayFileHelp
|
||||
47
pow/pow.go
47
pow/pow.go
@@ -6,6 +6,9 @@ import (
|
||||
"github.com/tsosunchia/powclient"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,6 +16,9 @@ const (
|
||||
)
|
||||
|
||||
func GetToken(fastIp string, host string, port string) (string, error) {
|
||||
// 捕获中断信号
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
getTokenParams := powclient.NewGetTokenParams()
|
||||
u := url.URL{Scheme: "https", Host: fastIp + ":" + port, Path: baseURL}
|
||||
getTokenParams.BaseUrl = u.String()
|
||||
@@ -23,20 +29,33 @@ func GetToken(fastIp string, host string, port string) (string, error) {
|
||||
if proxyUrl != nil {
|
||||
getTokenParams.Proxy = proxyUrl
|
||||
}
|
||||
var err error
|
||||
// 尝试三次RetToken,如果都失败了,异常退出
|
||||
for i := 0; i < 3; i++ {
|
||||
token, err := powclient.RetToken(getTokenParams)
|
||||
if err != nil {
|
||||
continue
|
||||
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
|
||||
}
|
||||
//fmt.Println("GetToken success", token, getTokenParams.UserAgent)
|
||||
return token, nil
|
||||
done <- false // 失败后通知主线程
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-sigChan: // 监听中断信号
|
||||
return "", fmt.Errorf("Program interrupted by user ") // 添加返回值
|
||||
case success := <-done: // 等待 goroutine 完成
|
||||
if success {
|
||||
return token, nil
|
||||
}
|
||||
return "", fmt.Errorf("RetToken failed 3 times, please try again later")
|
||||
case <-time.After(10 * time.Second): // 超时处理
|
||||
return "", fmt.Errorf("RetToken timed out(10s), please check your network") // 添加返回值
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println("RetToken failed 3 times, please try again after a while, exit")
|
||||
os.Exit(1)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func TestGetToken(t *testing.T) {
|
||||
// 计时开始
|
||||
start := time.Now()
|
||||
token, err := GetToken("api.leo.moe", "api.leo.moe", "443")
|
||||
token, err := GetToken("origin-fallback.nxtrace.org", "origin-fallback.nxtrace.org", "443")
|
||||
// 计时结束
|
||||
end := time.Now()
|
||||
fmt.Println("耗时:", end.Sub(start))
|
||||
|
||||
@@ -4,7 +4,9 @@ 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"
|
||||
)
|
||||
@@ -23,53 +25,73 @@ func Version() {
|
||||
}
|
||||
|
||||
func CopyRight() {
|
||||
fmt.Fprintf(color.Output, "\n%s\n%s\n%s %s\n\n%s\n%s %s\n%s %s\n%s %s\n\n%s\n%s\n%s %s\n\n",
|
||||
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", "NextTrace Project Creator"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Leo"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@leo.moe"),
|
||||
color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Project Maintainer"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Tso"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Vincent"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@vincent.moe"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Leo"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "i@leo.moe"),
|
||||
color.New(color.FgCyan, color.Bold).Sprintf("%s", "Special Acknowledgement List"),
|
||||
color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Major Contributor"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "zhshch"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "zhshch@athorx.com"),
|
||||
//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"),
|
||||
)
|
||||
|
||||
MoeQingOrgCopyRight()
|
||||
PluginCopyRight()
|
||||
//PluginCopyRight()
|
||||
}
|
||||
|
||||
func MoeQingOrgCopyRight() {
|
||||
fmt.Fprintf(color.Output, "%s\n%s %s\n%s %s\n\n",
|
||||
color.New(color.FgHiYellow, color.Bold).Sprintf("%s", "MoeQing Network"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "YekongTAT"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "yekongtat@gmail.com"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Haima"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "haima@peers.cloud"),
|
||||
func sponsor() {
|
||||
italic := "\x1b[3m%s\x1b[0m"
|
||||
formatted := fmt.Sprintf(italic, "(Listed in no particular order)")
|
||||
|
||||
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 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) {
|
||||
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 ip.String() == domain {
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", ip.String(), maxHops, packetSize)
|
||||
if srcAddr == "" {
|
||||
srcAddr = "traceroute to"
|
||||
} else {
|
||||
fmt.Printf("traceroute to %s (%s), %d hops max, %d bytes packets\n", ip.String(), domain, maxHops, packetSize)
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
|
||||
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)
|
||||
@@ -51,13 +51,25 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
fmt.Printf("%4s", "")
|
||||
}
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip),
|
||||
)
|
||||
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 {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
|
||||
)
|
||||
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])
|
||||
@@ -79,6 +91,12 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
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."):
|
||||
@@ -119,6 +137,8 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
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]":
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"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 {
|
||||
@@ -39,6 +41,7 @@ func (t *ICMPTracer) PrintFunc() {
|
||||
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 {
|
||||
@@ -66,7 +69,7 @@ func (t *ICMPTracer) Execute() (*Result, error) {
|
||||
|
||||
var err error
|
||||
|
||||
t.icmpListen, err = net.ListenPacket("ip4:1", t.SrcAddr)
|
||||
t.icmpListen, err = internal.ListenICMP("ip4:icmp", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -147,9 +150,10 @@ func (t *ICMPTracer) listenICMP() {
|
||||
}
|
||||
continue
|
||||
}
|
||||
packet_id := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[32:34])), 2)
|
||||
if process_id, ttl, err := reverseID(packet_id); err == nil {
|
||||
if process_id == int64(os.Getpid()&0x7f) {
|
||||
ttl := int64(binary.BigEndian.Uint16(msg.Msg[34:36]))
|
||||
packetId := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[32:34])), 2)
|
||||
if processId, _, err := reverseID(packetId); err == nil {
|
||||
if processId == int64(os.Getpid()&0x7f) {
|
||||
dstip := net.IP(msg.Msg[24:28])
|
||||
if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv4zero) {
|
||||
// 匹配再继续解析包,否则直接丢弃
|
||||
@@ -164,6 +168,9 @@ func (t *ICMPTracer) listenICMP() {
|
||||
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)
|
||||
}
|
||||
@@ -176,6 +183,12 @@ func (t *ICMPTracer) listenICMP() {
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -189,13 +202,13 @@ func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data
|
||||
}
|
||||
}
|
||||
|
||||
func gernerateID(ttl_int int) int {
|
||||
const ID_FIXED_HEADER = "10"
|
||||
func gernerateID(ttlInt int) int {
|
||||
const IdFixedHeader = "10"
|
||||
var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位
|
||||
var ttl = fmt.Sprintf("%06b", ttl_int) //取TTL的后6位
|
||||
var ttl = fmt.Sprintf("%06b", ttlInt) //取TTL的后6位
|
||||
|
||||
var parity int
|
||||
id := ID_FIXED_HEADER + processID + ttl
|
||||
id := IdFixedHeader + processID + ttl
|
||||
for _, c := range id {
|
||||
if c == '1' {
|
||||
parity++
|
||||
@@ -254,20 +267,29 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
id := gernerateID(ttl)
|
||||
//id := gernerateID(ttl)
|
||||
id := gernerateID(0)
|
||||
// log.Println("发送的", id)
|
||||
|
||||
//data := []byte{byte(ttl)}
|
||||
data := []byte{byte(0)}
|
||||
data = append(data, bytes.Repeat([]byte{1}, t.Config.PktSize-5)...)
|
||||
data = append(data, 0x00, 0x00, 0x4f, 0xff)
|
||||
|
||||
icmpHeader := icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
//Data: []byte("HELLO-R-U-THERE"),
|
||||
Data: append(bytes.Repeat([]byte{1}, t.Config.PktSize-4), 0x00, 0x00, 0x4f, 0xff),
|
||||
Data: data,
|
||||
Seq: ttl,
|
||||
},
|
||||
}
|
||||
|
||||
ipv4.NewPacketConn(t.icmpListen).SetTTL(ttl)
|
||||
err := ipv4.NewPacketConn(t.icmpListen).SetTTL(ttl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wb, err := icmpHeader.Marshal(nil)
|
||||
if err != nil {
|
||||
@@ -309,7 +331,10 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
err := h.fetchIPData(t.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.res.add(h)
|
||||
case <-time.After(t.Timeout):
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"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 {
|
||||
@@ -30,7 +32,7 @@ type ICMPTracerv6 struct {
|
||||
}
|
||||
|
||||
func (t *ICMPTracerv6) PrintFunc() {
|
||||
// defer t.wg.Done()
|
||||
defer t.wg.Done()
|
||||
var ttl = t.Config.BeginHop - 1
|
||||
for {
|
||||
if t.AsyncPrinter != nil {
|
||||
@@ -44,12 +46,12 @@ func (t *ICMPTracerv6) PrintFunc() {
|
||||
t.RealtimePrinter(&t.res, ttl)
|
||||
}
|
||||
ttl++
|
||||
if ttl == t.final {
|
||||
|
||||
if ttl == t.final-1 || ttl >= t.MaxHops-1 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<-time.After(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -65,7 +67,7 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
|
||||
|
||||
var err error
|
||||
|
||||
t.icmpListen, err = net.ListenPacket("ip6:58", t.SrcAddr)
|
||||
t.icmpListen, err = internal.ListenICMP("ip6:58", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -78,6 +80,7 @@ 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()
|
||||
@@ -168,9 +171,10 @@ func (t *ICMPTracerv6) listenICMP() {
|
||||
}
|
||||
|
||||
}
|
||||
packet_id := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[52:54])), 2)
|
||||
if process_id, ttl, err := reverseID(packet_id); err == nil {
|
||||
if process_id == int64(os.Getpid()&0x7f) {
|
||||
ttl := int64(binary.BigEndian.Uint16(msg.Msg[54:56]))
|
||||
packetId := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[52:54])), 2)
|
||||
if processId, _, err := reverseID(packetId); err == nil {
|
||||
if processId == int64(os.Getpid()&0x7f) {
|
||||
dstip := net.IP(msg.Msg[32:48])
|
||||
// 无效包本地环回包
|
||||
if dstip.String() == "::" {
|
||||
@@ -189,6 +193,8 @@ func (t *ICMPTracerv6) listenICMP() {
|
||||
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)
|
||||
}
|
||||
@@ -232,6 +238,11 @@ func (t *ICMPTracerv6) listenICMP() {
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -250,14 +261,20 @@ func (t *ICMPTracerv6) send(ttl int) error {
|
||||
if t.final != -1 && ttl > t.final {
|
||||
return nil
|
||||
}
|
||||
id := gernerateID(ttl)
|
||||
//id := gernerateID(ttl)
|
||||
id := gernerateID(0)
|
||||
|
||||
//data := []byte{byte(ttl)}
|
||||
data := []byte{byte(0)}
|
||||
data = append(data, bytes.Repeat([]byte{1}, t.Config.PktSize-5)...)
|
||||
data = append(data, 0x00, 0x00, 0x4f, 0xff)
|
||||
|
||||
icmpHeader := icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
//Data: []byte("HELLO-R-U-THERE"),
|
||||
Data: append(bytes.Repeat([]byte{1}, t.Config.PktSize-4), 0x00, 0x00, 0x4f, 0xff),
|
||||
Data: data,
|
||||
Seq: ttl,
|
||||
},
|
||||
}
|
||||
@@ -265,7 +282,10 @@ func (t *ICMPTracerv6) send(ttl int) error {
|
||||
p := ipv6.NewPacketConn(t.icmpListen)
|
||||
|
||||
icmpHeader.Body.(*icmp.Echo).Seq = ttl
|
||||
p.SetHopLimit(ttl)
|
||||
err := p.SetHopLimit(ttl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wb, err := icmpHeader.Marshal(nil)
|
||||
if err != nil {
|
||||
@@ -307,7 +327,10 @@ func (t *ICMPTracerv6) send(ttl int) error {
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
err := h.fetchIPData(t.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
|
||||
@@ -6,19 +6,19 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:linkname internetSocket net.internetSocket
|
||||
func internetSocket(ctx context.Context, net string, laddr, raddr interface{}, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
|
||||
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",
|
||||
@@ -28,23 +28,70 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// ListenICMP 会造成指定出口IP功能不可使用
|
||||
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
|
||||
if os.Getuid() == 0 { // root
|
||||
return net.ListenPacket(network, laddr)
|
||||
} else {
|
||||
if nw, ok := networkMap[network]; ok {
|
||||
proto := syscall.IPPROTO_ICMP
|
||||
if nw == "udp6" {
|
||||
proto = syscall.IPPROTO_ICMPV6
|
||||
}
|
||||
isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return newIPConn(isock), nil
|
||||
} else {
|
||||
return nil, errUnknownNetwork
|
||||
// 为兼容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
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -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
@@ -211,12 +211,21 @@ func (t *TCPTracer) send(ttl int) error {
|
||||
}
|
||||
// 随机种子
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
_, srcPort := util.LocalIPPort(t.DestIP)
|
||||
_, srcPort := func() (net.IP, int) {
|
||||
if util.EnvRandomPort == "" && t.SrcPort != 0 {
|
||||
return nil, t.SrcPort
|
||||
}
|
||||
return util.LocalIPPort(t.DestIP)
|
||||
}()
|
||||
ipHeader := &layers.IPv4{
|
||||
SrcIP: t.SrcIP,
|
||||
DstIP: t.DestIP,
|
||||
Protocol: layers.IPProtocolTCP,
|
||||
TTL: uint8(ttl),
|
||||
//Flags: layers.IPv4DontFragment, // 我感觉没必要
|
||||
}
|
||||
if t.DontFragment {
|
||||
ipHeader.Flags = layers.IPv4DontFragment
|
||||
}
|
||||
// 使用Uint16兼容32位系统,防止在rand的时候因使用int32而溢出
|
||||
sequenceNumber := uint32(r.Intn(math.MaxUint16))
|
||||
@@ -237,9 +246,16 @@ func (t *TCPTracer) send(ttl int) error {
|
||||
|
||||
desiredPayloadSize := t.Config.PktSize
|
||||
payload := make([]byte, desiredPayloadSize)
|
||||
copy(buf.Bytes(), payload)
|
||||
// 设置随机种子
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
|
||||
// 填充随机数
|
||||
for i := range payload {
|
||||
payload[i] = byte(rand.Intn(256))
|
||||
}
|
||||
//copy(buf.Bytes(), payload)
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, tcpHeader, gopacket.Payload(payload)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -253,11 +269,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个Sender,N个Reciever的情况,在哪里关闭Channel都容易导致Panic
|
||||
// 这里属于 2个Sender,N个Receiver的情况,在哪里关闭Channel都容易导致Panic
|
||||
defer func() {
|
||||
t.inflightRequestLock.Lock()
|
||||
close(hopCh)
|
||||
@@ -293,7 +309,10 @@ func (t *TCPTracer) send(ttl int) error {
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
err := h.fetchIPData(t.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
type TCPTracerv6 struct {
|
||||
type TCPTracerIPv6 struct {
|
||||
Config
|
||||
wg sync.WaitGroup
|
||||
res Result
|
||||
@@ -35,7 +35,7 @@ type TCPTracerv6 struct {
|
||||
fetchLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
func (t *TCPTracerIPv6) Execute() (*Result, error) {
|
||||
if len(t.res.Hops) > 0 {
|
||||
return &t.res, ErrTracerouteExecuted
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.icmp, err = icmp.ListenPacket("ip6:58", "::")
|
||||
t.icmp, err = icmp.ListenPacket("ip6:ipv6-icmp", "::")
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
return &t.res, nil
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) listenICMP() {
|
||||
func (t *TCPTracerIPv6) listenICMP() {
|
||||
lc := NewPacketListener(t.icmp, t.ctx)
|
||||
go lc.Start()
|
||||
for {
|
||||
@@ -133,7 +133,7 @@ func (t *TCPTracerv6) listenICMP() {
|
||||
|
||||
// @title listenTCP
|
||||
// @description 监听TCP的响应数据包
|
||||
func (t *TCPTracerv6) listenTCP() {
|
||||
func (t *TCPTracerIPv6) listenTCP() {
|
||||
lc := NewPacketListener(t.tcp, t.ctx)
|
||||
go lc.Start()
|
||||
|
||||
@@ -170,7 +170,7 @@ func (t *TCPTracerv6) listenTCP() {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
func (t *TCPTracerIPv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
var sequenceNumber = binary.BigEndian.Uint32(msg.Msg[52:56])
|
||||
|
||||
t.inflightRequestLock.Lock()
|
||||
@@ -187,7 +187,7 @@ func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
// log.Println("发送成功")
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) send(ttl int) error {
|
||||
func (t *TCPTracerIPv6) send(ttl int) error {
|
||||
err := t.sem.Acquire(context.Background(), 1)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -200,7 +200,12 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
}
|
||||
// 随机种子
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
_, srcPort := util.LocalIPPortv6(t.DestIP)
|
||||
_, srcPort := func() (net.IP, int) {
|
||||
if util.EnvRandomPort == "" && t.SrcPort != 0 {
|
||||
return nil, t.SrcPort
|
||||
}
|
||||
return util.LocalIPPortv6(t.DestIP)
|
||||
}()
|
||||
ipHeader := &layers.IPv6{
|
||||
SrcIP: t.SrcIP,
|
||||
DstIP: t.DestIP,
|
||||
@@ -227,13 +232,20 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
|
||||
desiredPayloadSize := t.Config.PktSize
|
||||
payload := make([]byte, desiredPayloadSize)
|
||||
copy(buf.Bytes(), payload)
|
||||
// 设置随机种子
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
|
||||
// 填充随机数
|
||||
for i := range payload {
|
||||
payload[i] = byte(rand.Intn(256))
|
||||
}
|
||||
//copy(buf.Bytes(), payload)
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, tcpHeader, gopacket.Payload(payload)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ipv6.NewPacketConn(t.tcp).SetHopLimit(ttl)
|
||||
err = ipv6.NewPacketConn(t.tcp).SetHopLimit(ttl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -244,7 +256,7 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
}
|
||||
// log.Println(ttl, sequenceNumber)
|
||||
t.inflightRequestLock.Lock()
|
||||
hopCh := make(chan Hop)
|
||||
hopCh := make(chan Hop, 1)
|
||||
t.inflightRequest[int(sequenceNumber)] = hopCh
|
||||
t.inflightRequestLock.Unlock()
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
@@ -22,6 +23,7 @@ var (
|
||||
|
||||
type Config struct {
|
||||
SrcAddr string
|
||||
SrcPort int
|
||||
BeginHop int
|
||||
MaxHops int
|
||||
NumMeasurements int
|
||||
@@ -41,6 +43,7 @@ type Config struct {
|
||||
AsyncPrinter func(res *Result)
|
||||
PktSize int
|
||||
Maptrace bool
|
||||
DontFragment bool
|
||||
}
|
||||
|
||||
type Method string
|
||||
@@ -80,19 +83,22 @@ 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 {
|
||||
@@ -141,7 +147,7 @@ func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
h.Hostname = r[0][:len(r[0])-1]
|
||||
ip = h.Address.String() + "," + h.Hostname
|
||||
}
|
||||
h.Geo, err = c.IPGeoSource(ip, c.Timeout, c.Lang, c.Maptrace)
|
||||
h.Geo, _ = c.IPGeoSource(ip, c.Timeout, c.Lang, c.Maptrace)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -200,7 +206,7 @@ func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
if !c.AlwaysWaitRDNS {
|
||||
select {
|
||||
case <-fetchDoneChan:
|
||||
// When fetch done signal recieved, stop waiting PTR record
|
||||
// When fetch done signal received, stop waiting PTR record
|
||||
case ptr := <-rDNSChan:
|
||||
// process result
|
||||
if err == nil && len(ptr) > 0 {
|
||||
@@ -221,9 +227,9 @@ func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
selectClose = true
|
||||
}
|
||||
|
||||
// When Select Close, fetchDoneChan Reciever will also be closed
|
||||
// When Select Close, fetchDoneChan Received will also be closed
|
||||
if selectClose {
|
||||
// New a reciever to prevent channel congestion
|
||||
// New a receiver to prevent channel congestion
|
||||
<-fetchDoneChan
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ package trace
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -30,6 +32,7 @@ type UDPTracer struct {
|
||||
|
||||
sem *semaphore.Weighted
|
||||
fetchLock sync.Mutex
|
||||
udpMutex sync.Mutex
|
||||
}
|
||||
|
||||
func (t *UDPTracer) Execute() (*Result, error) {
|
||||
@@ -53,7 +56,7 @@ func (t *UDPTracer) Execute() (*Result, error) {
|
||||
go t.listenICMP()
|
||||
|
||||
t.sem = semaphore.NewWeighted(int64(t.ParallelRequests))
|
||||
for ttl := 1; ttl <= t.MaxHops; ttl++ {
|
||||
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
|
||||
// 如果到达最终跳,则退出
|
||||
if t.final != -1 && ttl > t.final {
|
||||
break
|
||||
@@ -134,9 +137,10 @@ func (t *UDPTracer) handleICMPMessage(msg ReceivedMessage, data []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *UDPTracer) getUDPConn(try int) (net.IP, int, net.PacketConn) {
|
||||
srcIP, _ := util.LocalIPPort(t.DestIP)
|
||||
var cachedLocalPort int
|
||||
|
||||
func (t *UDPTracer) getUDPConn(try int) (net.IP, int, net.PacketConn, error) {
|
||||
srcIP, _ := util.LocalIPPort(t.DestIP)
|
||||
var ipString string
|
||||
if srcIP == nil {
|
||||
ipString = ""
|
||||
@@ -144,14 +148,40 @@ 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.EnvRandomPort == "" {
|
||||
if t.SrcPort != 0 {
|
||||
cachedLocalPort = t.SrcPort
|
||||
}
|
||||
return t.getUDPConn(try + 1)
|
||||
// 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()
|
||||
}
|
||||
// Use the cached local port to establish a new connection
|
||||
udpConn, err := net.ListenPacket("udp", ipString+":"+strconv.Itoa(cachedLocalPort))
|
||||
if err != nil {
|
||||
return srcIP, cachedLocalPort, nil, err
|
||||
}
|
||||
return srcIP, cachedLocalPort, udpConn, nil
|
||||
} else {
|
||||
// Without caching: create a new connection each time using a new random port
|
||||
udpConn, err := net.ListenPacket("udp", ipString+":0")
|
||||
if err != nil {
|
||||
return srcIP, 0, nil, err
|
||||
}
|
||||
localPort := udpConn.LocalAddr().(*net.UDPAddr).Port
|
||||
return srcIP, localPort, udpConn, nil
|
||||
}
|
||||
return srcIP, udpConn.LocalAddr().(*net.UDPAddr).Port, udpConn
|
||||
}
|
||||
|
||||
func (t *UDPTracer) send(ttl int) error {
|
||||
@@ -166,54 +196,73 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
srcIP, srcPort, udpConn := t.getUDPConn(0)
|
||||
|
||||
var payload []byte
|
||||
if t.Quic {
|
||||
payload = GenerateQuicPayloadWithRandomIds()
|
||||
} else {
|
||||
ipHeader := &layers.IPv4{
|
||||
SrcIP: srcIP,
|
||||
DstIP: t.DestIP,
|
||||
Protocol: layers.IPProtocolTCP,
|
||||
TTL: uint8(ttl),
|
||||
}
|
||||
|
||||
udpHeader := &layers.UDP{
|
||||
SrcPort: layers.UDPPort(srcPort),
|
||||
DstPort: layers.UDPPort(t.DestPort),
|
||||
}
|
||||
_ = udpHeader.SetNetworkLayerForChecksum(ipHeader)
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
}
|
||||
|
||||
desiredPayloadSize := t.Config.PktSize
|
||||
payload := make([]byte, desiredPayloadSize)
|
||||
copy(buf.Bytes(), payload)
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, udpHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payload = buf.Bytes()
|
||||
if util.EnvRandomPort == "" {
|
||||
t.udpMutex.Lock()
|
||||
defer t.udpMutex.Unlock()
|
||||
}
|
||||
|
||||
srcIP, srcPort, udpConn, err := t.getUDPConn(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer udpConn.Close()
|
||||
|
||||
//var payload []byte
|
||||
//if t.Quic {
|
||||
// payload = GenerateQuicPayloadWithRandomIds()
|
||||
//} else {
|
||||
ipHeader := &layers.IPv4{
|
||||
SrcIP: srcIP,
|
||||
DstIP: t.DestIP,
|
||||
Protocol: layers.IPProtocolUDP,
|
||||
TTL: uint8(ttl),
|
||||
}
|
||||
|
||||
udpHeader := &layers.UDP{
|
||||
SrcPort: layers.UDPPort(srcPort),
|
||||
DstPort: layers.UDPPort(t.DestPort),
|
||||
}
|
||||
_ = udpHeader.SetNetworkLayerForChecksum(ipHeader)
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
}
|
||||
|
||||
desiredPayloadSize := t.Config.PktSize
|
||||
if desiredPayloadSize-8 > 0 {
|
||||
desiredPayloadSize -= 8
|
||||
}
|
||||
payload := make([]byte, desiredPayloadSize)
|
||||
// 设置随机种子
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// 填充随机数
|
||||
for i := range payload {
|
||||
payload[i] = byte(rand.Intn(256))
|
||||
}
|
||||
//copy(buf.Bytes(), payload)
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, udpHeader, gopacket.Payload(payload)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//payload = buf.Bytes()
|
||||
//}
|
||||
|
||||
err = ipv4.NewPacketConn(udpConn).SetTTL(ttl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
if _, err := udpConn.WriteTo(payload, &net.UDPAddr{IP: t.DestIP, Port: t.DestPort}); err != nil {
|
||||
if _, err := udpConn.WriteTo(buf.Bytes(), &net.UDPAddr{IP: t.DestIP, Port: t.DestPort}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 在对inflightRequest进行写操作的时候应该加锁保护,以免多个goroutine协程试图同时写入造成panic
|
||||
t.inflightRequestLock.Lock()
|
||||
hopCh := make(chan Hop)
|
||||
hopCh := make(chan Hop, 1)
|
||||
t.inflightRequest[srcPort] = hopCh
|
||||
t.inflightRequestLock.Unlock()
|
||||
defer func() {
|
||||
@@ -232,7 +281,7 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
}
|
||||
hopCh <- Hop{
|
||||
Success: true,
|
||||
Address: peer,
|
||||
Address: &net.IPAddr{IP: peer.(*net.UDPAddr).IP},
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -244,7 +293,6 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
if t.final != -1 && ttl > t.final {
|
||||
return nil
|
||||
}
|
||||
|
||||
if addr, ok := h.Address.(*net.IPAddr); ok && addr.IP.Equal(t.DestIP) {
|
||||
t.finalLock.Lock()
|
||||
if t.final == -1 || ttl < t.final {
|
||||
@@ -264,7 +312,10 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
err := h.fetchIPData(t.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
394
trace/udp_ipv6.go
Normal file
394
trace/udp_ipv6.go
Normal file
@@ -0,0 +1,394 @@
|
||||
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.EnvRandomPort == "" {
|
||||
if t.SrcPort != 0 {
|
||||
cachedLocalPortv6 = t.SrcPort
|
||||
}
|
||||
// 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.EnvRandomPort == "" {
|
||||
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
|
||||
}
|
||||
@@ -27,8 +27,8 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
multiWriter := io.MultiWriter(os.Stdout, f)
|
||||
log.SetOutput(multiWriter)
|
||||
log.SetFlags(0)
|
||||
var res_str string
|
||||
res_str += fmt.Sprintf("%-2d ", ttl+1)
|
||||
var resStr string
|
||||
resStr += fmt.Sprintf("%-2d ", ttl+1)
|
||||
|
||||
// 去重
|
||||
var latestIP string
|
||||
@@ -56,28 +56,28 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
}
|
||||
|
||||
if latestIP == "" {
|
||||
res_str += fmt.Sprintf("%s\n", "*")
|
||||
log.Print(res_str)
|
||||
resStr += fmt.Sprintf("%s\n", "*")
|
||||
log.Print(resStr)
|
||||
return
|
||||
}
|
||||
|
||||
var blockDisplay = false
|
||||
for ip, v := range tmpMap {
|
||||
if blockDisplay {
|
||||
res_str += fmt.Sprintf("%4s", "")
|
||||
resStr += fmt.Sprintf("%4s", "")
|
||||
}
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
res_str += fmt.Sprintf("%-25s ", ip)
|
||||
resStr += fmt.Sprintf("%-25s ", ip)
|
||||
} else {
|
||||
res_str += fmt.Sprintf("%-15s ", ip)
|
||||
resStr += fmt.Sprintf("%-15s ", ip)
|
||||
}
|
||||
|
||||
i, _ := strconv.Atoi(v[0])
|
||||
|
||||
if res.Hops[ttl][i].Geo.Asnumber != "" {
|
||||
res_str += fmt.Sprintf("AS%-7s", res.Hops[ttl][i].Geo.Asnumber)
|
||||
resStr += fmt.Sprintf("AS%-7s", res.Hops[ttl][i].Geo.Asnumber)
|
||||
} else {
|
||||
res_str += fmt.Sprintf(" %-8s", "*")
|
||||
resStr += fmt.Sprintf(" %-8s", "*")
|
||||
}
|
||||
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
@@ -89,7 +89,7 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
if whoisFormat[0] != "" {
|
||||
whoisFormat[0] = "[" + whoisFormat[0] + "]"
|
||||
}
|
||||
res_str += fmt.Sprintf("%-16s", whoisFormat[0])
|
||||
resStr += fmt.Sprintf("%-16s", whoisFormat[0])
|
||||
}
|
||||
|
||||
if res.Hops[ttl][i].Geo.Country == "" {
|
||||
@@ -98,19 +98,19 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
|
||||
res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-39s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
|
||||
resStr += fmt.Sprintf(" %s %s %s %s %-6s\n %-39s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
|
||||
} else {
|
||||
res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-35s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
|
||||
resStr += fmt.Sprintf(" %s %s %s %s %-6s\n %-35s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
|
||||
}
|
||||
|
||||
for j := 1; j < len(v); j++ {
|
||||
if len(v) == 2 || j == 1 {
|
||||
res_str += v[j]
|
||||
resStr += v[j]
|
||||
} else {
|
||||
res_str += fmt.Sprintf("/ %s", v[j])
|
||||
resStr += fmt.Sprintf("/ %s", v[j])
|
||||
}
|
||||
}
|
||||
log.Print(res_str)
|
||||
log.Print(resStr)
|
||||
blockDisplay = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,14 @@ import (
|
||||
|
||||
func GetMapUrl(r string) (string, error) {
|
||||
host, port := util.GetHostAndPort()
|
||||
fastIp := "api.leo.moe"
|
||||
var fastIp string
|
||||
// 如果 host 是一个 IP 使用默认域名
|
||||
if valid := net.ParseIP(host); valid != nil {
|
||||
host = "api.leo.moe"
|
||||
fastIp = host
|
||||
if len(strings.Split(fastIp, ":")) > 1 {
|
||||
fastIp = "[" + fastIp + "]"
|
||||
}
|
||||
host = "api.nxtrace.org"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
fastIp = util.GetFastIP(host, port, false)
|
||||
|
||||
@@ -13,8 +13,8 @@ func TestDNS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDomainLookUp(t *testing.T) {
|
||||
ips := DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "all", "", false)
|
||||
ips, _ := DomainLookUp("pek-4134.endpoint.nxtrace.org.", "all", "", false)
|
||||
fmt.Println(ips)
|
||||
ips = DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "4", "", false)
|
||||
ips, _ = DomainLookUp("pek-4134.endpoint.nxtrace.org.", "4", "", false)
|
||||
fmt.Println(ips)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func newDoTResolver(serverName string, addrs string) *net.Resolver {
|
||||
Timeout: 1000 * time.Millisecond,
|
||||
}
|
||||
|
||||
tls_config := &tls.Config{
|
||||
tlsConfig := &tls.Config{
|
||||
// 设置 TLS Server Name 以确保证书能和域名对应
|
||||
ServerName: serverName,
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func newDoTResolver(serverName string, addrs string) *net.Resolver {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tls.Client(conn, tls_config), nil
|
||||
return tls.Client(conn, tlsConfig), nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
127
util/latency.go
127
util/latency.go
@@ -1,79 +1,142 @@
|
||||
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 (
|
||||
result string
|
||||
results = make(chan string)
|
||||
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 "api.leo.moe"
|
||||
return "api.nxtrace.org"
|
||||
}
|
||||
if FastIpCache != "" {
|
||||
return FastIpCache
|
||||
}
|
||||
|
||||
ips, err := net.LookupIP(domain)
|
||||
var ips []net.IP
|
||||
var err error
|
||||
if domain == "api.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")
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
go checkLatency(ip.String(), port)
|
||||
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(1 * time.Second):
|
||||
// 正常返回结果
|
||||
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 result == "" {
|
||||
log.Fatal("IP connection has been timeout, please check your network")
|
||||
}
|
||||
res := strings.Split(result, "-")
|
||||
|
||||
if len(ips) > 1 {
|
||||
if enableOutput {
|
||||
_, _ = fmt.Fprintf(color.Output, "%s prefered API IP - %s - %s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("[NextTrace API]"),
|
||||
color.New(color.FgGreen, color.Bold).Sprintf("%s", res[0]),
|
||||
color.New(color.FgCyan, color.Bold).Sprintf("%sms", res[1]),
|
||||
)
|
||||
}
|
||||
//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),
|
||||
)
|
||||
}
|
||||
FastIpCache = res[0]
|
||||
return res[0]
|
||||
//}
|
||||
|
||||
//有些时候真的啥都不通,还是挑一个顶上吧
|
||||
if result.IP == "" {
|
||||
result.IP = "45.88.195.154"
|
||||
}
|
||||
|
||||
FastIpCache = result.IP
|
||||
return result.IP
|
||||
}
|
||||
|
||||
func checkLatency(ip string, port string) {
|
||||
func checkLatency(domain string, ip string, port string) {
|
||||
start := time.Now()
|
||||
if !strings.Contains(ip, ".") {
|
||||
ip = "[" + ip + "]"
|
||||
}
|
||||
conn, err := net.DialTimeout("tcp", ip+":"+port, time.Second*1)
|
||||
|
||||
// 自定义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
|
||||
}
|
||||
defer func(conn net.Conn) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(conn)
|
||||
if result == "" {
|
||||
result = fmt.Sprintf("%s-%.2f", ip, float64(time.Since(start))/float64(time.Millisecond))
|
||||
results <- result
|
||||
req.Host = domain
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
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}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetFastIP(t *testing.T) {
|
||||
GetFastIP("api.leo.moe", "443", true)
|
||||
}
|
||||
//github action test 不支持v6 这里会报错
|
||||
//func TestGetFastIP(t *testing.T) {
|
||||
// GetFastIP("origin-fallback.nxtrace.org", "443", true)
|
||||
//}
|
||||
|
||||
130
util/util.go
130
util/util.go
@@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/config"
|
||||
"log"
|
||||
@@ -15,12 +16,22 @@ import (
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var Uninterrupted = GetenvDefault("NEXTTRACE_UNINTERRUPTED", "")
|
||||
var EnvToken = GetenvDefault("NEXTTRACE_TOKEN", "")
|
||||
var UserAgent = fmt.Sprintf("NextTrace %s/%s/%s", config.Version, runtime.GOOS, runtime.GOARCH)
|
||||
var RdnsCache sync.Map
|
||||
var PowProviderParam = ""
|
||||
var DisableMPLS = GetenvDefault("NEXTTRACE_DISABLEMPLS", "")
|
||||
var EnableHidDstIP = GetenvDefault("NEXTTRACE_ENABLEHIDDENDSTIP", "")
|
||||
var EnvIPInfoLocalPath = GetenvDefault("NEXTTRACE_IPINFOLOCALPATH", "")
|
||||
var EnvRandomPort = GetenvDefault("NEXTTRACE_RANDOMPORT", "")
|
||||
var EnvToken = GetenvDefault("NEXTTRACE_TOKEN", "")
|
||||
var Uninterrupted = GetenvDefault("NEXTTRACE_UNINTERRUPTED", "")
|
||||
var DestIP string
|
||||
var PowProviderParam = ""
|
||||
var RdnsCache sync.Map
|
||||
var UserAgent = fmt.Sprintf("NextTrace %s/%s/%s", config.Version, runtime.GOOS, runtime.GOARCH)
|
||||
var cachedLocalIP net.IP
|
||||
var cachedLocalPort int
|
||||
var localIPOnce sync.Once
|
||||
var cachedLocalIPv6 net.IP
|
||||
var cachedLocalPort6 int
|
||||
var localIPv6Once sync.Once
|
||||
|
||||
func LookupAddr(addr string) ([]string, error) {
|
||||
// 如果在缓存中找到,直接返回
|
||||
@@ -40,42 +51,74 @@ 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 EnvRandomPort is set.
|
||||
func LocalIPPort(dstip net.IP) (net.IP, int) {
|
||||
// If EnvRandomPort is set, bypass caching and return a new port every time.
|
||||
if EnvRandomPort != "" {
|
||||
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 EnvRandomPort is set, bypass caching and return a new port every time.
|
||||
if EnvRandomPort != "" {
|
||||
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
|
||||
}
|
||||
|
||||
func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput bool) net.IP {
|
||||
func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput bool) (net.IP, error) {
|
||||
// ipVersion: 4, 6, all
|
||||
var (
|
||||
r *net.Resolver
|
||||
@@ -101,8 +144,7 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput
|
||||
ips = append(ips, net.ParseIP(v))
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("Domain " + host + " Lookup Fail.")
|
||||
os.Exit(1)
|
||||
return nil, errors.New("DNS lookup failed")
|
||||
}
|
||||
|
||||
//var ipv6Flag = false
|
||||
@@ -119,16 +161,18 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput
|
||||
var filteredIPs []net.IP
|
||||
for _, ip := range ips {
|
||||
if ipVersion == "4" && ip.To4() != nil {
|
||||
filteredIPs = append(filteredIPs, ip)
|
||||
filteredIPs = []net.IP{ip}
|
||||
break
|
||||
} else if ipVersion == "6" && strings.Contains(ip.String(), ":") {
|
||||
filteredIPs = append(filteredIPs, ip)
|
||||
filteredIPs = []net.IP{ip}
|
||||
break
|
||||
}
|
||||
}
|
||||
ips = filteredIPs
|
||||
}
|
||||
|
||||
if (len(ips) == 1) || (disableOutput) {
|
||||
return ips[0]
|
||||
return ips[0], nil
|
||||
} else {
|
||||
fmt.Println("Please Choose the IP You Want To TraceRoute")
|
||||
for i, ip := range ips {
|
||||
@@ -147,20 +191,24 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput
|
||||
fmt.Println("Your Option is invalid")
|
||||
os.Exit(3)
|
||||
}
|
||||
return ips[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", "api.leo.moe")
|
||||
var hostP = GetenvDefault("NEXTTRACE_HOSTPORT", "api.nxtrace.org")
|
||||
// 解析域名
|
||||
hostArr := strings.Split(hostP, ":")
|
||||
// 判断是否有指定端口
|
||||
@@ -200,9 +248,9 @@ func GetProxy() *url.URL {
|
||||
}
|
||||
|
||||
func GetPowProvider() string {
|
||||
var powProvider = ""
|
||||
var powProvider string
|
||||
if PowProviderParam == "" {
|
||||
powProvider = GetenvDefault("NEXTTRACE_POWPROVIDER", "api.leo.moe")
|
||||
powProvider = GetenvDefault("NEXTTRACE_POWPROVIDER", "api.nxtrace.org")
|
||||
} else {
|
||||
powProvider = PowProviderParam
|
||||
}
|
||||
@@ -220,3 +268,17 @@ func StringInSlice(val string, list []string) bool {
|
||||
}
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -103,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,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
|
||||
@@ -187,7 +189,11 @@ func createWsConn() *WsConn {
|
||||
host, port = util.GetHostAndPort()
|
||||
// 如果 host 是一个 IP 使用默认域名
|
||||
if valid := net.ParseIP(host); valid != nil {
|
||||
host = "api.leo.moe"
|
||||
fastIp = host
|
||||
if len(strings.Split(fastIp, ":")) > 1 {
|
||||
fastIp = "[" + fastIp + "]"
|
||||
}
|
||||
host = "api.nxtrace.org"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
fastIp = util.GetFastIP(host, port, true)
|
||||
@@ -201,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