mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
Compare commits
269 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a3ef1b2574 | ||
|
|
ba27ff967b | ||
|
|
b326d7bed6 | ||
|
|
b733ef2d82 | ||
|
|
2113264336 | ||
|
|
ba7d6ea87b | ||
|
|
db1e9e2b0c | ||
|
|
13aaa54067 | ||
|
|
db9b3fed9a | ||
|
|
7b145f4e64 | ||
|
|
d444ff3c62 | ||
|
|
f1ceaf7f86 | ||
|
|
d6536f7c69 | ||
|
|
ae2e6b3631 | ||
|
|
f63ef9552e | ||
|
|
ce1bae2125 | ||
|
|
9d3878efbe | ||
|
|
c940b9f19f | ||
|
|
2d6ae4ff4c | ||
|
|
7977383fec | ||
|
|
ae16731bbe | ||
|
|
e0ea009b2c | ||
|
|
c532dfd05c | ||
|
|
b967ee411d | ||
|
|
46ce56f3a7 | ||
|
|
bb522ed859 | ||
|
|
cdfe926c37 | ||
|
|
aa7cea4cf8 | ||
|
|
549dc549dd | ||
|
|
76fa8f2019 | ||
|
|
9e17f4a24f | ||
|
|
b06487e293 | ||
|
|
a0ab83c8ed | ||
|
|
9e5cd736c8 | ||
|
|
b11d2334c2 | ||
|
|
84a03e8a10 | ||
|
|
761d506613 | ||
|
|
c3ab3e6c1c | ||
|
|
96671d263c | ||
|
|
9228cdf307 | ||
|
|
12565941ab | ||
|
|
e33548d2ce | ||
|
|
5d766de52f | ||
|
|
ce46abd232 | ||
|
|
6179f1b8f0 | ||
|
|
3c12d99168 | ||
|
|
f3f99ac0aa | ||
|
|
cb7bc7450f | ||
|
|
74f46f5d24 | ||
|
|
2bc386355c | ||
|
|
1266f62aba | ||
|
|
8e837072c6 | ||
|
|
cf0639111d | ||
|
|
56cd0022f7 | ||
|
|
1407c499b9 | ||
|
|
f25662d481 | ||
|
|
4f94f01e87 | ||
|
|
1460ad67c0 | ||
|
|
d6dcfc8dc5 | ||
|
|
190b3ab94e | ||
|
|
87fa850d2d | ||
|
|
c71a26d018 | ||
|
|
79c39b655a | ||
|
|
580612ce08 | ||
|
|
141653a3e9 | ||
|
|
f59d7ff2f5 | ||
|
|
87f153cdf1 | ||
|
|
ebe2a0f8e9 | ||
|
|
ca2a7a8dc8 | ||
|
|
b68b768a65 | ||
|
|
efbede8ca1 | ||
|
|
b1edef305f | ||
|
|
54242cfa8b | ||
|
|
2d4de9e4c1 | ||
|
|
d75a384d70 | ||
|
|
f6fbf3803f | ||
|
|
2c86f86204 | ||
|
|
dcce91614f | ||
|
|
85df0121fd | ||
|
|
4a749285be | ||
|
|
a92cfc7783 | ||
|
|
268971e85b | ||
|
|
2bac716bd7 | ||
|
|
eb64f68663 | ||
|
|
e9ca9cf388 | ||
|
|
364be22383 | ||
|
|
2f6a2573ae | ||
|
|
a25157867c | ||
|
|
7c37598804 | ||
|
|
8d30c59c17 | ||
|
|
e8e5c1438e | ||
|
|
cb0c988eda | ||
|
|
b1cabbc6d4 | ||
|
|
2493f471dc | ||
|
|
8578109243 | ||
|
|
089250fee1 | ||
|
|
0e87592537 | ||
|
|
14552cd853 | ||
|
|
44b70cf7e7 | ||
|
|
3f8b043821 | ||
|
|
fa8eb050af | ||
|
|
50582f0fd0 | ||
|
|
64371fb41a | ||
|
|
6476c3aff3 | ||
|
|
4d7831fd29 | ||
|
|
d602941722 | ||
|
|
2d548fcca0 | ||
|
|
8a79a5dd1c | ||
|
|
390efdd75d | ||
|
|
d0d1511c29 | ||
|
|
7013fb6a41 | ||
|
|
0513ab2a1b | ||
|
|
11489d7027 | ||
|
|
97880049f7 | ||
|
|
781c6ccaee | ||
|
|
9e84a3da65 | ||
|
|
4e5cd2d053 | ||
|
|
288aee254f | ||
|
|
2d235baa53 | ||
|
|
d23f90dd19 | ||
|
|
fdc6145087 | ||
|
|
1d6ce5de88 | ||
|
|
86b817291d | ||
|
|
b38fc18bb4 | ||
|
|
23d20de7c8 | ||
|
|
bf4ec99323 | ||
|
|
4bfe85df1b | ||
|
|
e48b30c47a | ||
|
|
f2fbce0358 | ||
|
|
98bd95d046 | ||
|
|
b622a40022 | ||
|
|
6bc74e14cb | ||
|
|
8233c7c303 | ||
|
|
3084effd78 | ||
|
|
ac1ee5e08c | ||
|
|
723f5c4c5e | ||
|
|
308ae73728 | ||
|
|
b88d5292da | ||
|
|
903b6f232d | ||
|
|
5211daf814 | ||
|
|
62f817af91 | ||
|
|
5907ebcf84 | ||
|
|
825eccf986 | ||
|
|
a33307d746 | ||
|
|
eee741d848 | ||
|
|
98344193e7 | ||
|
|
7a74bb83c2 | ||
|
|
3e010bd9da | ||
|
|
abc1ecd035 | ||
|
|
989dc29931 | ||
|
|
1f2b4cde1f | ||
|
|
939a4c7aab | ||
|
|
4890615a2b | ||
|
|
9b8fd51ef2 | ||
|
|
73c9d91637 | ||
|
|
06e85c11c6 | ||
|
|
d151416566 | ||
|
|
b9d9853458 | ||
|
|
3f177c87d2 | ||
|
|
d3a00335a1 | ||
|
|
aa38ed374f | ||
|
|
08f87cc229 | ||
|
|
ead7ebbc25 | ||
|
|
7a048fa976 | ||
|
|
dbb844eb44 | ||
|
|
916ac3cb69 | ||
|
|
bdfba172d1 | ||
|
|
a95a741ce2 | ||
|
|
362317e95e | ||
|
|
dc735b0f97 | ||
|
|
fe0fce07ec | ||
|
|
60a4454802 | ||
|
|
525cfd8c8f | ||
|
|
c2eebca25c | ||
|
|
110bdc27ff | ||
|
|
510b0f6c32 | ||
|
|
8a01bccbff | ||
|
|
64224c905e | ||
|
|
2403308c1b | ||
|
|
560a1693af | ||
|
|
45e9fe604d | ||
|
|
88e69f7dc2 | ||
|
|
b1a66546a0 | ||
|
|
5c46e4eddb | ||
|
|
30d550753c | ||
|
|
50b3e442da | ||
|
|
ba133e3c73 | ||
|
|
3a5184b06c | ||
|
|
e825b8d092 | ||
|
|
f64934bfde | ||
|
|
9871449b30 | ||
|
|
31a9c356e1 | ||
|
|
5c0c490ae8 | ||
|
|
22660143b0 | ||
|
|
f4b5de08ca | ||
|
|
b0e1329d66 | ||
|
|
805e827e45 | ||
|
|
ed2250f811 | ||
|
|
fe18c80f83 | ||
|
|
d1c9fd6c3a | ||
|
|
c67e2d6384 | ||
|
|
309ae9d74a | ||
|
|
cf65ff5e7e | ||
|
|
eae92ebfae |
@@ -5,7 +5,7 @@ set -e
|
||||
DIST_PREFIX="nexttrace"
|
||||
DEBUG_MODE=${2}
|
||||
TARGET_DIR="dist"
|
||||
PLATFORMS="darwin/amd64 darwin/arm64 linux/386 linux/amd64 linux/arm64 linux/mips windows/amd64 windows/arm64 openbsd/amd64 openbsd/arm64 freebsd/amd64 freebsd/arm64"
|
||||
PLATFORMS="darwin/amd64 darwin/arm64 linux/386 linux/amd64 linux/arm64 linux/mips linux/mips64 linux/mipsle linux/mips64le windows/amd64 windows/arm64 openbsd/amd64 openbsd/arm64 freebsd/amd64 freebsd/arm64"
|
||||
|
||||
BUILD_VERSION="$(git describe --tags --always)"
|
||||
BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
||||
@@ -26,15 +26,15 @@ for pl in ${PLATFORMS}; do
|
||||
echo "build => ${TARGET}"
|
||||
if [ "${DEBUG_MODE}" == "debug" ]; then
|
||||
go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \
|
||||
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
|
||||
-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"
|
||||
else
|
||||
go build -trimpath -o ${TARGET} \
|
||||
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
|
||||
-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"
|
||||
fi
|
||||
done
|
||||
@@ -46,15 +46,15 @@ done
|
||||
echo "build => ${TARGET}"
|
||||
if [ "${DEBUG_MODE}" == "debug" ]; then
|
||||
go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \
|
||||
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
|
||||
-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"
|
||||
else
|
||||
go build -trimpath -o ${TARGET} \
|
||||
-ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
|
||||
-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"
|
||||
fi
|
||||
|
||||
|
||||
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
13
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -15,8 +15,13 @@ copyright: [v2fly](https://github.com/v2fly)
|
||||
|
||||
<!--
|
||||
除非特殊情况,请完整填写所有问题。不按模板发的 issue 将直接被关闭。
|
||||
如果你遇到的问题不是 nexttrace 的 bug,比如你不清楚如何配置,请在 https://github.com/xgadget-lab/nexttrace/discussions 进行讨论。
|
||||
如果你遇到的问题不是 nexttrace 的 bug,比如你不清楚如何配置,请在 https://github.com/nxtrace/NTrace-core/discussions 进行讨论。
|
||||
-->
|
||||
## 本项目是基于Linux/macOS的,请确认您遇到的问题是否在Linux或macOS上存在。
|
||||
|
||||
<!-- 是/否 -->
|
||||
<!-- 对于只出现在Windows上的问题,本项目有时无法解决,请知悉 -->
|
||||
|
||||
|
||||
## 你正在使用哪个版本的 nexttrace?
|
||||
|
||||
@@ -38,3 +43,9 @@ copyright: [v2fly](https://github.com/v2fly)
|
||||
|
||||
|
||||
## 请附上出错时软件输出的错误信息
|
||||
|
||||
<!-- 是/否 -->
|
||||
|
||||
## 是否查询过本仓库wiki有没有类似错误
|
||||
|
||||
<!-- wiki: https://github.com/nxtrace/NTrace-core/wiki -->
|
||||
|
||||
15
.github/dependabot.yml
vendored
Normal file
15
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
192
.github/workflows/build.yml
vendored
192
.github/workflows/build.yml
vendored
@@ -1,52 +1,163 @@
|
||||
name: Build & Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "v*"
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
pull_request:
|
||||
|
||||
name: Test & Build Release
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
jobs:
|
||||
Test:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# Include amd64 on all platforms.
|
||||
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
|
||||
goarch: [amd64, 386]
|
||||
exclude:
|
||||
# Exclude i386 on darwin and dragonfly.
|
||||
- goarch: 386
|
||||
goos: dragonfly
|
||||
- goarch: 386
|
||||
goos: darwin
|
||||
include:
|
||||
# BEIGIN MacOS ARM64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
# END macOS ARM64
|
||||
# BEGIN Linux ARM 5 6 7
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 6
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 5
|
||||
# END Linux ARM 5 6 7
|
||||
# BEGIN Android ARM 8
|
||||
- goos: android
|
||||
goarch: arm64
|
||||
# END Android ARM 8
|
||||
# Windows ARM
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# BEGIN Other architectures
|
||||
# BEGIN riscv64 & ARM64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: linux
|
||||
goarch: riscv64
|
||||
# END riscv64 & ARM64
|
||||
# BEGIN MIPS
|
||||
- goos: linux
|
||||
goarch: mips64
|
||||
- goos: linux
|
||||
goarch: mips64le
|
||||
- goos: linux
|
||||
goarch: mipsle
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
- goos: linux
|
||||
goarch: mipsle
|
||||
gomips: softfloat
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
gomips: softfloat
|
||||
# END MIPS
|
||||
# BEGIN PPC
|
||||
- goos: linux
|
||||
goarch: ppc64
|
||||
- goos: linux
|
||||
goarch: ppc64le
|
||||
# END PPC
|
||||
# BEGIN FreeBSD ARM
|
||||
- goos: freebsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# END FreeBSD ARM
|
||||
# BEGIN S390X
|
||||
- goos: linux
|
||||
goarch: s390x
|
||||
# END S390X
|
||||
# END Other architectures
|
||||
# BEGIN OPENBSD ARM
|
||||
- goos: openbsd
|
||||
goarch: arm64
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GOMIPS: ${{ matrix.gomips }}
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v4
|
||||
- name: Show workflow information
|
||||
run: |
|
||||
if [ ! -z $GOARM ]; then
|
||||
export GOARM=v$GOARM
|
||||
fi
|
||||
export _NAME="nexttrace_${GOOS}_${GOARCH}${GOARM}"
|
||||
if [ "$GOOS" == "windows" ]; then
|
||||
export _NAME="$_NAME.exe"
|
||||
fi
|
||||
if [ "$GOMIPS" == "softfloat" ]; then
|
||||
export _NAME="${_NAME}_softfolat"
|
||||
fi
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||
echo "BUILD_VERSION=$(git describe --tags --always)" >> $GITHUB_ENV
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||
echo "COMMIT_SHA1=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.18"
|
||||
|
||||
- name: Test
|
||||
run: sudo go test -v -coverprofile='coverage.out' -covermode=count ./...
|
||||
|
||||
Build:
|
||||
needs: test
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
go-version: '1.21'
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
- name: Build
|
||||
run: |
|
||||
go build -trimpath -o dist/${ASSET_NAME} \
|
||||
-ldflags "-X 'github.com/nxtrace/NTrace-core/config.Version=${BUILD_VERSION}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.BuildDate=${BUILD_DATE}' \
|
||||
-X 'github.com/nxtrace/NTrace-core/config.CommitID=${COMMIT_SHA1}'\
|
||||
-w -s"
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
go-version: "1.18"
|
||||
|
||||
- run: bash .cross_compile.sh
|
||||
|
||||
name: ${{ env.ASSET_NAME }}
|
||||
path: |
|
||||
dist/${{ env.ASSET_NAME }}
|
||||
- name: Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@v1
|
||||
with: # 将下述可执行文件 release 上去
|
||||
draft: false # Release草稿
|
||||
draft: true # Release草稿
|
||||
files: |
|
||||
dist/nexttrace_darwin_amd64
|
||||
dist/nexttrace_darwin_arm64
|
||||
dist/nexttrace_linux_386
|
||||
dist/nexttrace_linux_amd64
|
||||
dist/nexttrace_linux_arm64
|
||||
dist/nexttrace_linux_armv7
|
||||
dist/nexttrace_linux_mips
|
||||
dist/nexttrace_windows_amd64.exe
|
||||
dist/nexttrace_windows_arm64.exe
|
||||
dist/nexttrace_openbsd_amd64
|
||||
dist/nexttrace_openbsd_arm64
|
||||
dist/nexttrace_freebsd_amd64
|
||||
dist/nexttrace_freebsd_arm64
|
||||
dist/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GT_Token }}
|
||||
|
||||
@@ -54,6 +165,7 @@ jobs:
|
||||
needs: build
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
@@ -64,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
|
||||
@@ -81,8 +193,8 @@ 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
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
|
||||
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/sjlleo/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/sjlleo/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
|
||||
|
||||
43
.github/workflows/test.yml
vendored
Normal file
43
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.21'
|
||||
check-latest: true
|
||||
- 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 ./...
|
||||
- name: Test with windows
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: go test -v -coverprofile='coverage.out' -covermode=count ./...
|
||||
354
README.md
354
README.md
@@ -4,31 +4,135 @@
|
||||
|
||||
</div>
|
||||
|
||||
## NextTrace
|
||||
<h1 align="center">
|
||||
<br>NextTrace<br>
|
||||
</h1>
|
||||
|
||||
An open source visual routing tool that pursues light weight, developed using Golang.
|
||||
<h4 align="center">An open source visual routing tool that pursues light weight, developed using Golang.</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/nxtrace/Ntrace-V1/actions">
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/nxtrace/Ntrace-V1/build.yml?branch=main&style=flat-square" alt="Github Actions">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/github.com/nxtrace/Ntrace-V1">
|
||||
<img src="https://goreportcard.com/badge/github.com/nxtrace/Ntrace-V1?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/nxtrace/Ntrace-V1/releases">
|
||||
<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">
|
||||
</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">
|
||||
</a>
|
||||
|
||||
<a href="https://skywolf.cloud" >
|
||||
<img src="https://hk.skywolf.cloud/assets/img/skywolf.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
|
||||
<a href="https://portal.saltyfish.io" >
|
||||
<img src="https://snapstack-static.vnc.xyz/static/logo.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
We are extremely grateful to [DMIT](https://dmit.io), [Misaka](https://misaka.io), [Skywolf](https://skywolf.cloud) 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)
|
||||
|
||||
### Automated Installation
|
||||
⚠️ 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.
|
||||
|
||||
```bash
|
||||
# Linux one-click install script
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)
|
||||
### Automated Install
|
||||
|
||||
# GHPROXY 镜像(国内使用)
|
||||
bash -c "$(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)"
|
||||
* Linux
|
||||
* One-click installation script
|
||||
|
||||
# macOS brew install command
|
||||
brew tap xgadget-lab/nexttrace && brew install nexttrace
|
||||
```
|
||||
```shell
|
||||
curl nxtrace.org/nt |bash
|
||||
```
|
||||
* Arch Linux AUR installation command
|
||||
* Directly download bin package (only supports amd64)
|
||||
|
||||
Windows users please go to [Release Page](https://github.com/sjlleo/nexttrace/releases/latest) directly and download exe file.
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
* The AUR builds are maintained by ouuan
|
||||
* Linuxbrew's installation command
|
||||
|
||||
- `Release` provides compiled executables for many systems and architectures, if not, you can compile it yourself.
|
||||
- Some of the necessary dependencies of this project are not fully implemented in `Golang` on `Windows`, so currently `NextTrace` is experimental on `Windows` platform.
|
||||
Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64)
|
||||
* Deepin installation command
|
||||
|
||||
```shell
|
||||
apt install nexttrace
|
||||
```
|
||||
* Termux installation command
|
||||
|
||||
```shell
|
||||
pkg install nexttrace-enhanced
|
||||
```
|
||||
|
||||
* macOS
|
||||
* macOS Homebrew's installation command
|
||||
* Homebrew-core version
|
||||
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
* This repository's ACTIONS automatically built version (updates faster)
|
||||
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* The homebrew-core build is maintained by chenrui333, please note that this version's updates may lag behind the repository Action automatically version
|
||||
|
||||
* Windows
|
||||
* Windows Scoop installation command
|
||||
* Scoop-extras version
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
|
||||
* Scoop-extra is maintained by soenggam
|
||||
|
||||
Please note, the repositories for all of the above installation methods are maintained by open source enthusiasts. Availability and timely updates are not guaranteed. If you encounter problems, please contact the repository maintainer to solve them, or use the binary packages provided by the official build of this project.
|
||||
|
||||
### Manual Install
|
||||
* Download the precompiled executable
|
||||
|
||||
For users not covered by the above methods, please go directly to [Release](https://github.com/nxtrace/Ntrace-V1/releases/latest) 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-core@latest
|
||||
```
|
||||
*because of the version constraints conflict, you can not install `NTrace-V1` by this*
|
||||
After installation, the executable is in the `$GOPATH/bin` directory. If you have not set `GOPATH`, it is in the `$HOME/go/bin` directory.
|
||||
The binary file name is consistent with the project name. You need to replace the `nexttrace` command below with `NTrace-core`.
|
||||
If you want to be consistent with the commands below, you can rename the binary after executing the `go install` command
|
||||
```shell
|
||||
mv $GOPATH/bin/NTrace-core $GOPATH/bin/nexttrace
|
||||
```
|
||||
|
||||
### Get Started
|
||||
|
||||
@@ -37,16 +141,30 @@ Windows users please go to [Release Page](https://github.com/sjlleo/nexttrace/re
|
||||
```bash
|
||||
# IPv4 ICMP Trace
|
||||
nexttrace 1.0.0.1
|
||||
# URL
|
||||
nexttrace http://example.com:8080/index.html?q=1
|
||||
|
||||
# Form printing (output all hops at one time, wait 20-40 seconds)
|
||||
# Form printing
|
||||
nexttrace --table 1.0.0.1
|
||||
|
||||
# An Output Easy to Parse
|
||||
nexttrace --raw 1.0.0.1
|
||||
nexttrace --json 1.0.0.1
|
||||
|
||||
# IPv4/IPv6 Resolve Only, and automatically select the first IP when there are multiple IPs
|
||||
nexttrace --ipv4 g.co
|
||||
nexttrace --ipv6 g.co
|
||||
|
||||
# IPv6 ICMP Trace
|
||||
nexttrace 2606:4700:4700::1111
|
||||
|
||||
# Path Visualization With the -M parameter, a map URL is returned
|
||||
nexttrace --map koreacentral.blob.core.windows.net
|
||||
# MapTrace URL: https://api.leo.moe/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
|
||||
# Disable Path Visualization With the -M parameter
|
||||
nexttrace koreacentral.blob.core.windows.net
|
||||
# MapTrace URL: https://api.nxtrace.org/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
|
||||
|
||||
# Disable MPLS display using the --disable-mpls / -e parameter or the NEXTTRACE_DISABLEMPLS environment variable
|
||||
nexttrace --disable-mpls example.com
|
||||
export NEXTTRACE_DISABLEMPLS=1
|
||||
```
|
||||
|
||||
PS: The routing visualization drawing module was written by [@tsosunchia](https://github.com/tsosunchia), and the specific code can be viewed at [tsosunchia/traceMap](https://github.com/tsosunchia/traceMap).
|
||||
@@ -63,6 +181,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
|
||||
@@ -76,14 +205,14 @@ 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 these protocols only supports `IPv4` now
|
||||
`NextTrace` can also use `TCP` and `UDP` protocols to perform `Traceroute` requests, but `UDP` protocols only supports `IPv4` now
|
||||
|
||||
```bash
|
||||
# TCP SYN Trace
|
||||
nexttrace --tcp www.bing.com
|
||||
|
||||
# You can specify the port by yourself [here is 443], the default port is 80
|
||||
nexttrace --tcp --port 443 1.0.0.1
|
||||
nexttrace --tcp --port 443 2001:4860:4860::8888
|
||||
|
||||
# UDP Trace
|
||||
nexttrace --udp 1.0.0.1
|
||||
@@ -102,10 +231,15 @@ 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
|
||||
|
||||
# Set the payload size to 1024 bytes
|
||||
nexttrace --psize 1024 example.com
|
||||
|
||||
# Feature: print Route-Path diagram
|
||||
# Route-Path diagram example:
|
||||
# AS6453 Tata Communication「Singapore『Singapore』」
|
||||
@@ -116,17 +250,32 @@ nexttrace --no-rdns www.bbix.net
|
||||
# ╭╯
|
||||
# ╰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`)
|
||||
`NextTrace` supports users to select their own IP API (currently supports: `LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`)
|
||||
|
||||
```bash
|
||||
# You can specify the IP database by yourself [IP.SB here], if not specified, LeoMoeAPI will be used
|
||||
nexttrace --data-provider IP.SB
|
||||
## Note that the ipinfo API needs users to purchase services from ipinfo. If necessary, you can clone this project, add the token provided by ipinfo and compile it yourself
|
||||
# You can specify the IP database by yourself [IP-API.com here], if not specified, LeoMoeAPI will be used
|
||||
nexttrace --data-provider ip-api.com
|
||||
## Note There are frequency limits for free queries of the ipinfo and IPInsight APIs. You can purchase services from these providers to remove the limits
|
||||
## If necessary, you can clone this project, add the token provided by ipinfo or IPInsight and compile it yourself
|
||||
## 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)
|
||||
## 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
|
||||
## IPAPI.com has a stricter restiction on API calls, if you can't query IP data from this source, please try again in a few minutes.
|
||||
## IP-API.com has a stricter restiction on API calls, if you can't query IP data from this source, please try again in a few minutes
|
||||
|
||||
# The Pure-FTPd IP database defaults to using http://127.0.0.1:2060 as the query interface. To customize it, please use environment variables
|
||||
export NEXTTRACE_CHUNZHENURL=http://127.0.0.1:2060
|
||||
## You can use https://github.com/freshcn/qqwry to build your own Pure-FTPd IP database service
|
||||
|
||||
# You can also specify the default IP database by setting an environment variable
|
||||
export NEXTTRACE_DATAPROVIDER=ipinfo
|
||||
```
|
||||
|
||||
`NextTrace` supports mixed parameters and shortened parameters
|
||||
@@ -137,8 +286,8 @@ 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
|
||||
@@ -149,23 +298,37 @@ NextTrace BackEnd is now open-source.
|
||||
|
||||
https://github.com/sjlleo/nexttrace-backend
|
||||
|
||||
All NextTrace IP geolocation `API DEMO` can refer to [here](https://github.com/xgadget-lab/nexttrace/blob/main/ipgeo/)
|
||||
NextTrace `LeoMoeAPI` now utilizes the Proof of Work (POW) mechanism to prevent abuse, where NextTrace introduces the powclient library as a client-side component. Both the POW CLIENT and SERVER are open source, and everyone is welcome to use them. (Please direct any POW module-related questions to the corresponding repositories)
|
||||
|
||||
- [GitHub - tsosunchia/powclient: Proof of Work CLIENT for NextTrace](https://github.com/tsosunchia/powclient)
|
||||
- [GitHub - tsosunchia/powserver: Proof of Work SERVER for NextTrace](https://github.com/tsosunchia/powserver)
|
||||
|
||||
All NextTrace IP geolocation `API DEMO` can refer to [here](https://github.com/nxtrace/NTrace-core/blob/main/ipgeo/)
|
||||
|
||||
### For full usage list, please refer to the usage menu
|
||||
|
||||
```shell
|
||||
Usage: nexttrace [-h|--help] [-T|--tcp] [-U|--udp] [-F|--fast-trace] [-p|--port
|
||||
<integer>] [-q|--queries <integer>] [--parallel-requests
|
||||
<integer>] [-m|--max-hops <integer>] [-d|--data-provider
|
||||
(IP.SB|IPInfo|IPInsight|IPAPI.com)] [-n|--no-rdns]
|
||||
[-r|--route-path] [-o|--output] [-t|--table] [-c|--classic]
|
||||
[-f|--first <integer>] [-M|--map] [-v|--version] [-s|--source
|
||||
"<value>"] [-D|--dev "<value>"] [-R|--route] [-z|--send-time
|
||||
<integer>] [-i|--ttl-time <integer>]
|
||||
[IP Address or Domain name]
|
||||
Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
|
||||
[-F|--fast-trace] [-p|--port <integer>] [-q|--queries
|
||||
<integer>] [--parallel-requests <integer>] [-m|--max-hops
|
||||
<integer>] [-d|--data-provider
|
||||
(Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)]
|
||||
[--pow-provider (api.nxtrace.org|sakura)] [-n|--no-rdns]
|
||||
[-a|--always-rdns] [-P|--route-path] [-r|--report] [--dn42]
|
||||
[-o|--output] [-t|--table] [--raw] [-j|--json] [-c|--classic]
|
||||
[-f|--first <integer>] [-M|--map] [-e|--disable-mpls]
|
||||
[-v|--version] [-s|--source "<value>"] [-D|--dev "<value>"]
|
||||
[-z|--send-time <integer>] [-i|--ttl-time <integer>]
|
||||
[--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_32 "<value>"] [--dot-server
|
||||
(dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language
|
||||
(en|cn)] [--file "<value>"] [-C|--nocolor]
|
||||
|
||||
Arguments:
|
||||
|
||||
-h --help Print help information
|
||||
-4 --ipv4 Use IPv4 only
|
||||
-6 --ipv6 Use IPv6 only
|
||||
-T --tcp Use TCP SYN for tracerouting (default port
|
||||
is 80)
|
||||
-U --udp Use UDP SYN for tracerouting (default port
|
||||
@@ -186,87 +349,87 @@ Arguments:
|
||||
18
|
||||
-m --max-hops Set the max number of hops (max TTL to be
|
||||
reached). Default: 30
|
||||
-d --data-provider Choose IP Geograph Data Provider
|
||||
[LeoMoeAPI,IP.SB, IPInfo, IPInsight,
|
||||
IPAPI.com]. Default: LeoMoeAPI
|
||||
-n --no-rdns Do not resolve IP addresses to their
|
||||
-d --data-provider Choose IP Geograph Data Provider [IP.SB,
|
||||
IPInfo, IPInsight, IP-API.com, Ip2region,
|
||||
IPInfoLocal, CHUNZHEN, disable-geoip].
|
||||
Default: LeoMoeAPI
|
||||
--pow-provider Choose PoW Provider [api.nxtrace.org, sakura]
|
||||
For China mainland users, please use
|
||||
sakura. Default: api.nxtrace.org
|
||||
-n --no-rdns Do not resolve IP addresses to their
|
||||
domain names
|
||||
-r --route-path Print traceroute hop path by ASN and
|
||||
-a --always-rdns Always resolve IP addresses to their
|
||||
domain names
|
||||
-P --route-path Print traceroute hop path by ASN and
|
||||
location
|
||||
-r --report output using report mode
|
||||
--dn42 DN42 Mode
|
||||
-o --output Write trace result to file
|
||||
(RealTimePrinter ONLY)
|
||||
-t --table Output trace results as table
|
||||
--raw An Output Easy to Parse
|
||||
-j --json Output trace results as JSON
|
||||
-c --classic Classic Output trace results like
|
||||
BestTrace
|
||||
-f --first Start from the first_ttl hop (instead from
|
||||
1). Default: 1
|
||||
-M --map Print Trace Map. This will return a Trace
|
||||
Map URL
|
||||
-M --map Disable Print Trace Map
|
||||
-e --disable-mpls Disable MPLS
|
||||
-v --version Print version info and exit
|
||||
-s --source Use source src_addr for outgoing packets
|
||||
-D --dev Use the following Network Devices as the
|
||||
source address in outgoing packets
|
||||
-R --route Show Routing Table [Provided By BGP.Tools]
|
||||
-z --send-time Set the time interval for sending every
|
||||
packet. Useful when some routers use
|
||||
rate-limit for ICMP messages.. Default: 0
|
||||
-i --ttl-time Set the time interval for sending packets
|
||||
groups by TTL. Useful when some routers
|
||||
use rate-limit for ICMP messages..
|
||||
Default: 500
|
||||
-z --send-time Set how many [milliseconds] between
|
||||
sending each packet.. Useful when some
|
||||
routers use rate-limit for ICMP messages.
|
||||
Default: 100
|
||||
-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
|
||||
--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_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
|
||||
```
|
||||
|
||||
## Project screenshot
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## OpenTrace
|
||||
|
||||
## NextTrace Enhanced
|
||||
`OpenTrace` is the cross-platform `GUI` version of `NextTrace` developed by @Archeb, bringing a familiar but more powerful user experience.
|
||||
|
||||
`NextTrace Enhanced` is an enhanced version for enthusiasts, `Enhanced` provides trace route calls in the form of Web API and a simple Looking Glass webpage with built-in visualization.
|
||||
This software is still in the early stages of development and may have many flaws and errors. We value your feedback.
|
||||
|
||||
The `Enhanced` version supports many functions that the `lite` version does not have, such as the ability to customize the timeout period, and the ability to specify TTL as the starting point for route tracking, etc. For ordinary users, the `lite` version is usually enough.
|
||||
[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace)
|
||||
|
||||
https://github.com/OwO-Network/nexttrace-enhanced
|
||||
## NEXTTRACE WEB API
|
||||
|
||||
## 公告
|
||||
`NextTraceWebApi` is a web-based server implementation of `NextTrace` in the `MTR` style, offering various deployment options including `Docker`.
|
||||
|
||||
我今天看到了一些非常难过的事情,一些用户在 BestTrace 和 WorstTrace 下面宣传 NextTrace 的完全可替代性。
|
||||
|
||||
这么做是不正确的,NextTrace 从来都不是一个从零开始的软件,NextTrace 之所以能够拥有某些功能特性,是因为吸取了 BestTrace 、WorstTrace 的一些想法。
|
||||
|
||||
我们希望您在使用的时候知晓这一点,**我们是站在巨人的肩膀上,而尊重其他软件作者,向他们或者是我们提交 Bug 或贡献代码,才是推动整个 traceroute 工具的软件多样化发展的最好方式**。
|
||||
|
||||
NextTrace 并不追求成为一个替代者,同类软件越多样化,才能满足更多人的需求,这才是我们希望看到的,而去诋毁其他软件,这违背了我们对于开发 NextTrace 的初衷。
|
||||
|
||||
我们希望看到这条公告的朋友应该主动删除自己过激的言论,如果您有任何问题或建议,请随时在我们的社区中发表。
|
||||
[https://github.com/nxtrace/nexttracewebapi](https://github.com/nxtrace/nexttracewebapi)
|
||||
|
||||
## LeoMoeAPI Credit
|
||||
|
||||
NextTrace 重点在于研究 Go 语言 Traceroute 的实现,其 LeoMoeAPI 的地理位置信息并没有原始数据的支撑,故也不可能有商用版本。
|
||||
|
||||
LeoMoeAPI 存在部分社区贡献者校准的数据,也包含了部分其他第三方数据库的数据,这些数据的所有权归校准者、第三方数据库所有,**仅供路由跟踪地理位置的展示参考使用**,我们不对数据提供准度做任何保证,请尊重他们的成果,如用于其他用途后果自负,特此告知。
|
||||
|
||||
1. 对于辛勤提供马来西亚地区节点的 samleong123、全球节点的 TOHUNET Looking Glass 以及来自 Misaka 的 Ping.sx 表示感谢,目前 80% 以上的可靠校准数据出自这些节点的 ping / mtr 报告。
|
||||
|
||||
2. 同时感谢 isyekong 在基于 rDNS 校准上思路以及数据上做出的贡献,LeoMoeAPI 正在加快对 rDNS 的解析功能研发,目前已经做到部分骨干网的地理位置自动化解析,但存在一定误判。
|
||||
我们希望 NextTrace 在未来能成为对 One-Man ISP 友好的 Traceroute 工具,我们也在尽可能完善对这些 ASN 的微型骨干网的校准。
|
||||
|
||||
3. 在开发上,我要由衷感谢 missuo 以及 zhshch 在 Go 交叉编译、设计理念以及 TCP/UDP Traceroute 重构上的帮助、tsosunchia 在 TraceMap 上的倾力支持。
|
||||
|
||||
4. 我还要感谢 FFEE_CO、TheresaQWQ、stydxm 和其他朋友的帮助。LeoMoeAPI自首次发布以来得到了很多各方面的支持,所以我想把他们都归功于此。
|
||||
|
||||
我们希望您能够在使用时尽可能多多反馈 IP 地理位置错误(详见 issue),这样它就能够在第一时间得到校准,他人也会因此而受益。
|
||||
|
||||
NextTrace focuses on Golang Traceroute implementations, and its LeoMoeAPI geolocation information is not supported by raw data, so a commercial version is not possible.
|
||||
|
||||
The LeoMoeAPI data is subject to copyright restrictions from multiple data sources, and is only used for the purpose of displaying the geolocation of route tracing.
|
||||
|
||||
1. We would like to credit samleong123 for providing nodes in Malaysia, TOHUNET Looking Glass for global nodes, and Ping.sx from Misaka, where more than 80% of reliable calibration data comes from ping/mtr reports.
|
||||
|
||||
2. At the same time, we would like to credit isyekong for their contribution on rDNS-based calibration ideas and data. LeoMoeAPI is accelerating the development of rDNS resolution function, and has already achieved automated geolocation resolution for some backbone networks, but there are some misjudgments. We hope that NextTrace will become a One-Man ISP-friendly traceroute tool in the future, and we are working on improving the calibration of these ASN micro-backbones as much as possible.
|
||||
2. At the same time, we would like to credit isyekong for their contribution to rDNS-based calibration ideas and data. LeoMoeAPI is accelerating the development of rDNS resolution function, and has already achieved automated geolocation resolution for some backbone networks, but there are some misjudgments. We hope that NextTrace will become a One-Man ISP-friendly traceroute tool in the future, and we are working on improving the calibration of these ASN micro-backbones as much as possible.
|
||||
|
||||
3. In terms of development, I would like to credit missuo and zhshch for their help with Go cross-compilation, design concepts and TCP/UDP Traceroute refactoring, and tsosunchia for their support on TraceMap.
|
||||
|
||||
@@ -274,14 +437,6 @@ The LeoMoeAPI data is subject to copyright restrictions from multiple data sourc
|
||||
|
||||
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.
|
||||
|
||||
## FAQ Frequently Asked Questions
|
||||
|
||||
If you encounter problems while installing or using it, we do not recommend you to choose creating an `issue` as a preference
|
||||
|
||||
Here is our recommended troubleshooting process:
|
||||
|
||||
1. Check if it is already in FAQ -> [Go to Github Wiki](https://github.com/xgadget-lab/nexttrace/wiki/FAQ---%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94)
|
||||
2. Suspected bug or feature suggestion -> [Go to Github Issues](https://github.com/xgadget-lab/nexttrace/issues)
|
||||
|
||||
## JetBrain Support
|
||||
|
||||
@@ -291,7 +446,9 @@ Here is our recommended troubleshooting process:
|
||||
|
||||
## Credits
|
||||
|
||||
BGP.TOOLS provided some data support for this project and we would like to express our sincere gratitude.
|
||||
[sjlleo](https://github.com/sjlleo) The perpetual leader, founder, and core contributors of the NextTrace Project
|
||||
|
||||
[BGP.TOOLS](https://bgp.tools) provided some data support for this project. And we would like to express our sincere gratitude.
|
||||
|
||||
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
|
||||
|
||||
@@ -301,12 +458,19 @@ BGP.TOOLS provided some data support for this project and we would like to expre
|
||||
|
||||
[waiting4new](https://github.com/waiting4new)
|
||||
|
||||
[FFEE_CO](https://github.com/fkx4-p)
|
||||
[FFEE_CO](https://github.com/fkx4-p)
|
||||
|
||||
### 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/nxtrace/NTrace-core/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)
|
||||
|
||||
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/#xgadget-lab/nexttrace&Date)
|
||||
[](https://star-history.com/#nxtrace/NTrace-core&Date)
|
||||
|
||||
380
README_zh_CN.md
380
README_zh_CN.md
@@ -12,41 +12,140 @@
|
||||
<h4 align="center">一款追求轻量化的开源可视化路由跟踪工具。</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/sjlleo/nexttrace/actions">
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/sjlleo/nexttrace/build.yml?branch=main&style=flat-square" alt="Github Actions">
|
||||
<a href="https://github.com/nxtrace/Ntrace-V1/actions">
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/nxtrace/Ntrace-V1/build.yml?branch=main&style=flat-square" alt="Github Actions">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/github.com/sjlleo/nexttrace">
|
||||
<img src="https://goreportcard.com/badge/github.com/sjlleo/nexttrace?style=flat-square">
|
||||
<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/sjlleo/nexttrace/releases">
|
||||
<img src="https://img.shields.io/github/release/sjlleo/nexttrace/all.svg?style=flat-square">
|
||||
<a href="https://github.com/nxtrace/Ntrace-V1/releases">
|
||||
<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">
|
||||
</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">
|
||||
</a>
|
||||
|
||||
<a href="https://skywolf.cloud" >
|
||||
<img src="https://hk.skywolf.cloud/assets/img/skywolf.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
|
||||
<a href="https://portal.saltyfish.io" >
|
||||
<img src="https://snapstack-static.vnc.xyz/static/logo.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
我们非常感谢 [DMIT](https://dmit.io)、 [Misaka](https://misaka.io)、 [Skywolf](https://skywolf.cloud) 和 [SnapStack](https://portal.saltyfish.io) 提供了支持本项目所需的网络基础设施。
|
||||
|
||||
## How To Use
|
||||
|
||||
Document Language: [English](README.md) | 简体中文
|
||||
|
||||
⚠️ 请注意:我们欢迎来自社区的PR提交,但是请将您的PR提交至 [NTrace-V1](https://github.com/nxtrace/NTrace-V1) 仓库,而不是 [NTrace-core](https://github.com/nxtrace/NTrace-core) 仓库。<br>
|
||||
关于NTrace-V1和NTrace-core两个仓库的说明:<br>
|
||||
二者将大体上保持一致。所有的开发工作均在NTrace-V1仓库中进行。NTrace-V1仓库首先发布新版本,在稳定运行一段时间后(时长不定),我们会把版本同步至NTrace-core。这意味着NTrace-V1仓库充当了一个“测试版”的角色。<br>
|
||||
请注意,版本同步也存在例外。如果NTrace-V1的某个版本出现了严重的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 之前,我们建议您先阅读 [#IP 数据以及精准度说明](https://github.com/nxtrace/NTrace-core/blob/main/README_zh_CN.md#ip-%E6%95%B0%E6%8D%AE%E4%BB%A5%E5%8F%8A%E7%B2%BE%E5%87%86%E5%BA%A6%E8%AF%B4%E6%98%8E),在了解您自己的对数据精准度需求以后再进行抉择。
|
||||
|
||||
[NextTrace 的Telegram频道](https://t.me/nexttrace)由项目成员负责,会传递一部分通知,也会发布一些成员自己分享的小工具。项目成员的意见可作为未来项目发展的可能方向,随着开发进度变化可能会有所改动,不代表未来一定会实装,正式定稿公告会发布于 Issue 中。
|
||||
|
||||
### Automated Install
|
||||
|
||||
```bash
|
||||
# Linux 一键安装脚本
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)
|
||||
* Linux
|
||||
* 一键安装脚本
|
||||
|
||||
# GHPROXY 镜像(国内使用)
|
||||
bash <(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)
|
||||
```shell
|
||||
curl nxtrace.org/nt | bash
|
||||
```
|
||||
|
||||
* Arch Linux AUR 安装命令
|
||||
* 直接下载bin包(仅支持amd64)
|
||||
|
||||
# macOS brew 安装命令
|
||||
brew tap xgadget-lab/nexttrace && brew install nexttrace
|
||||
```
|
||||
```shell
|
||||
yay -S nexttrace-bin`
|
||||
```
|
||||
* AUR 的构建分别由 ouuan 维护
|
||||
* Linuxbrew 安装命令
|
||||
|
||||
Windows 用户请直接前往 [Release](https://github.com/sjlleo/nexttrace/releases/latest) 下载编译后的二进制 exe 文件。
|
||||
同macOS Homebrew安装方法(homebrew-core版仅支持amd64)
|
||||
* Deepin 安装命令
|
||||
|
||||
```shell
|
||||
apt install nexttrace
|
||||
```
|
||||
* Termux 安装命令
|
||||
|
||||
```shell
|
||||
pkg install nexttrace-enhanced
|
||||
```
|
||||
|
||||
|
||||
* macOS
|
||||
* macOS Homebrew 安装命令
|
||||
* homebrew-core版
|
||||
|
||||
```shell
|
||||
brew install nexttrace
|
||||
```
|
||||
* 本仓库ACTIONS自动构建版(更新更快)
|
||||
|
||||
```shell
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* homebrew-core 构建由 chenrui333 维护,请注意该版本更新可能会落后仓库Action自动构建版本
|
||||
|
||||
* Windows
|
||||
* Windows Scoop 安装命令
|
||||
* scoop-extras版
|
||||
|
||||
```powershell
|
||||
scoop bucket add extras && scoop install extras/nexttrace
|
||||
```
|
||||
|
||||
* scoop-extra 由 soenggam 维护
|
||||
|
||||
请注意,以上多种安装方式的仓库均由开源爱好者自行维护,不保证可用性和及时更新,如遇到问题请联系仓库维护者解决,或使用本项目官方编译提供的二进制包。
|
||||
|
||||
### Manual Install
|
||||
* 下载预编译的可执行程序
|
||||
|
||||
对于以上方法没有涵盖的用户,请直接前往 [Release](https://github.com/nxtrace/Ntrace-V1/releases/latest) 下载编译后的二进制可执行文件。
|
||||
|
||||
* `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
|
||||
* 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。
|
||||
|
||||
* 从源码安装
|
||||
|
||||
您可在自行安装Go >= 1.20后,使用以下命令安装
|
||||
|
||||
```shell
|
||||
go install github.com/nxtrace/NTrace-core@latest
|
||||
```
|
||||
*由于go.mod文件声明和文件目录冲突的问题,你不能用go install命令安装 `NTrace-V1` 版本*
|
||||
安装后可执行文件在`$GOPATH/bin`目录下,如果您没有设置`GOPATH`,则在`$HOME/go/bin`目录下。
|
||||
安装后二进制文件名称与项目名称保持一致,你需要将下文中的 `nexttrace` 命令替换为 `NTrace-core` 使用
|
||||
如果你希望与下文命令保持一致,可以在执行 `go install` 命令后重命名二进制文件
|
||||
|
||||
```shell
|
||||
mv $GOPATH/bin/NTrace-core $GOPATH/bin/nexttrace
|
||||
```
|
||||
|
||||
- `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
|
||||
- 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。
|
||||
|
||||
### Get Started
|
||||
|
||||
@@ -55,16 +154,30 @@ Windows 用户请直接前往 [Release](https://github.com/sjlleo/nexttrace/rele
|
||||
```bash
|
||||
# IPv4 ICMP Trace
|
||||
nexttrace 1.0.0.1
|
||||
# URL
|
||||
nexttrace http://example.com:8080/index.html?q=1
|
||||
|
||||
# 表格打印,使用 --table / -t 参数,将实时显示结果
|
||||
nexttrace --table 1.0.0.1
|
||||
|
||||
# 一个方便供机器读取转化的模式
|
||||
nexttrace --raw 1.0.0.1
|
||||
nexttrace --json 1.0.0.1
|
||||
|
||||
# 只进行IPv4/IPv6解析,且当多个IP时自动选择第一个IP
|
||||
nexttrace --ipv4 g.co
|
||||
nexttrace --ipv6 g.co
|
||||
|
||||
# IPv6 ICMP Trace
|
||||
nexttrace 2606:4700:4700::1111
|
||||
|
||||
# 路径可视化 使用 --map / -M 参数,将返回一个地图 URL
|
||||
nexttrace --map koreacentral.blob.core.windows.net
|
||||
# MapTrace URL: https://api.leo.moe/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
|
||||
# 禁用路径可视化 使用 --map / -M 参数
|
||||
nexttrace koreacentral.blob.core.windows.net
|
||||
# MapTrace URL: https://api.nxtrace.org/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
|
||||
|
||||
# 禁用MPLS显示 使用 --disable-mpls / -e 参数 或 NEXTTRACE_DISABLEMPLS 环境变量
|
||||
nexttrace --disable-mpls example.com
|
||||
export NEXTTRACE_DISABLEMPLS=1
|
||||
```
|
||||
|
||||
PS: 路由可视化的绘制模块由 [@tsosunchia](https://github.com/tsosunchia) 同学编写,具体代码可在 [tsosunchia/traceMap](https://github.com/tsosunchia/traceMap) 查看
|
||||
@@ -79,8 +192,19 @@ PS: 路由可视化的绘制模块由 [@tsosunchia](https://github.com/tsosunchi
|
||||
# 北上广(电信+联通+移动+教育网)IPv4 / IPv6 ICMP 快速测试
|
||||
nexttrace --fast-trace
|
||||
|
||||
# 也可以使用 TCP SYN 而非 ICMP 进行测试(不支持 IPv6)
|
||||
# 也可以使用 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` 已支持指定网卡进行路由跟踪
|
||||
@@ -96,14 +220,14 @@ nexttrace --dev eth0 2606:4700:4700::1111
|
||||
nexttrace --source 204.98.134.56 9.9.9.9
|
||||
```
|
||||
|
||||
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求,不过目前只支持`IPv4`
|
||||
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求,不过目前`UDP`只支持`IPv4`
|
||||
|
||||
```bash
|
||||
# TCP SYN Trace
|
||||
nexttrace --tcp www.bing.com
|
||||
|
||||
# 可以自行指定端口[此处为443],默认80端口
|
||||
nexttrace --tcp --port 443 1.0.0.1
|
||||
nexttrace --tcp --port 443 2001:4860:4860::8888
|
||||
|
||||
# UDP Trace
|
||||
nexttrace --udp 1.0.0.1
|
||||
@@ -123,10 +247,15 @@ 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
|
||||
|
||||
# 设置载荷大小为1024字节
|
||||
nexttrace --psize 1024 example.com
|
||||
|
||||
# 特色功能:打印Route-Path图
|
||||
# Route-Path图示例:
|
||||
# AS6453 塔塔通信「Singapore『Singapore』」
|
||||
@@ -137,47 +266,68 @@ nexttrace --no-rdns www.bbix.net
|
||||
# ╭╯
|
||||
# ╰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`)
|
||||
`NextTrace`支持用户自主选择 IP 数据库(目前支持:`LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`, `Ip2region`, `IPInfoLocal`, `CHUNZHEN`)
|
||||
|
||||
```bash
|
||||
# 可以自行指定IP数据库[此处为IP.SB],不指定则默认为LeoMoeAPI
|
||||
nexttrace --data-provider IP.SB
|
||||
## 特别的:其中 ipinfo API 需要从 ipinfo 自行购买服务,如有需要可以 clone 本项目添加其提供的 token 自行编译
|
||||
# 可以自行指定IP数据库[此处为IP-API.com],不指定则默认为LeoMoeAPI
|
||||
nexttrace --data-provider ip-api.com
|
||||
## 特别的: 其中 ipinfo 和 IPInsight API 对于免费版查询有频率限制,可从这些服务商自行购买服务以解除限制,如有需要可以 clone 本项目添加其提供的 token 自行编译
|
||||
## TOKEN填写路径:ipgeo/tokens.go
|
||||
|
||||
## 特别的: 对于离线库 IPInfoLocal,请自行下载并命名为 ipinfoLocal.mmdb (可以从这里下载:https://ipinfo.io/signup?ref=free-database-downloads)
|
||||
## 对于离线库 Ip2region 可NextTrace自动下载,也可自行下载并命名为 ip2region.db
|
||||
## 另外:由于IP.SB被滥用比较严重,会经常出现无法查询的问题,请知悉。
|
||||
## IPAPI.com限制调用较为严格,如有查询不到的情况,请几分钟后再试。
|
||||
## IP-API.com限制调用较为严格,如有查询不到的情况,请几分钟后再试。
|
||||
|
||||
# 纯真IP数据库默认使用 http://127.0.0.1:2060 作为查询接口,如需自定义请使用环境变量
|
||||
export NEXTTRACE_CHUNZHENURL=http://127.0.0.1:2060
|
||||
## 可使用 https://github.com/freshcn/qqwry 自行搭建纯真IP数据库服务
|
||||
|
||||
# 也可以通过设置环境变量来指定默认IP数据库
|
||||
export NEXTTRACE_DATAPROVIDER=ipinfo
|
||||
```
|
||||
|
||||
`NextTrace`支持使用混合参数和简略参数
|
||||
|
||||
```bash
|
||||
Example:
|
||||
nexttrace --data-provider IPAPI.com --max-hops 20 --tcp --port 443 --queries 5 --no-rdns 1.1.1.1
|
||||
nexttrace --data-provider ip-api.com --max-hops 20 --tcp --port 443 --queries 5 --no-rdns 1.1.1.1
|
||||
nexttrace -tcp --queries 2 --parallel-requests 1 --table --route-path 2001:4860:4860::8888
|
||||
|
||||
Equivalent to:
|
||||
nexttrace -d 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
|
||||
```
|
||||
|
||||
### 全部用法详见 Usage 菜单
|
||||
|
||||
```shell
|
||||
Usage: nexttrace [-h|--help] [-T|--tcp] [-U|--udp] [-F|--fast-trace] [-p|--port
|
||||
<integer>] [-q|--queries <integer>] [--parallel-requests
|
||||
<integer>] [-m|--max-hops <integer>] [-d|--data-provider
|
||||
(IP.SB|IPInfo|IPInsight|IPAPI.com)] [-n|--no-rdns]
|
||||
[-r|--route-path] [-o|--output] [-t|--table] [-c|--classic]
|
||||
[-f|--first <integer>] [-M|--map] [-v|--version] [-s|--source
|
||||
"<value>"] [-D|--dev "<value>"] [-R|--route] [-z|--send-time
|
||||
<integer>] [-i|--ttl-time <integer>]
|
||||
[IP Address or Domain name]
|
||||
Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
|
||||
[-F|--fast-trace] [-p|--port <integer>] [-q|--queries
|
||||
<integer>] [--parallel-requests <integer>] [-m|--max-hops
|
||||
<integer>] [-d|--data-provider
|
||||
(Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)]
|
||||
[--pow-provider (api.nxtrace.org|sakura)] [-n|--no-rdns]
|
||||
[-a|--always-rdns] [-P|--route-path] [-r|--report] [--dn42]
|
||||
[-o|--output] [-t|--table] [--raw] [-j|--json] [-c|--classic]
|
||||
[-f|--first <integer>] [-M|--map] [-e|--disable-mpls]
|
||||
[-v|--version] [-s|--source "<value>"] [-D|--dev "<value>"]
|
||||
[-z|--send-time <integer>] [-i|--ttl-time <integer>]
|
||||
[--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_32 "<value>"] [--dot-server
|
||||
(dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language
|
||||
(en|cn)] [--file "<value>"] [-C|--nocolor]
|
||||
|
||||
Arguments:
|
||||
|
||||
-h --help Print help information
|
||||
-4 --ipv4 Use IPv4 only
|
||||
-6 --ipv6 Use IPv6 only
|
||||
-T --tcp Use TCP SYN for tracerouting (default port
|
||||
is 80)
|
||||
-U --udp Use UDP SYN for tracerouting (default port
|
||||
@@ -198,45 +348,67 @@ Arguments:
|
||||
18
|
||||
-m --max-hops Set the max number of hops (max TTL to be
|
||||
reached). Default: 30
|
||||
-d --data-provider Choose IP Geograph Data Provider
|
||||
[LeoMoeAPI,IP.SB, IPInfo, IPInsight,
|
||||
IPAPI.com]. Default: LeoMoeAPI
|
||||
-n --no-rdns Do not resolve IP addresses to their
|
||||
-d --data-provider Choose IP Geograph Data Provider [IP.SB,
|
||||
IPInfo, IPInsight, IP-API.com, Ip2region,
|
||||
IPInfoLocal, CHUNZHEN, disable-geoip].
|
||||
Default: LeoMoeAPI
|
||||
--pow-provider Choose PoW Provider [api.nxtrace.org, sakura]
|
||||
For China mainland users, please use
|
||||
sakura. Default: api.nxtrace.org
|
||||
-n --no-rdns Do not resolve IP addresses to their
|
||||
domain names
|
||||
-r --route-path Print traceroute hop path by ASN and
|
||||
-a --always-rdns Always resolve IP addresses to their
|
||||
domain names
|
||||
-P --route-path Print traceroute hop path by ASN and
|
||||
location
|
||||
-r --report output using report mode
|
||||
--dn42 DN42 Mode
|
||||
-o --output Write trace result to file
|
||||
(RealTimePrinter ONLY)
|
||||
-t --table Output trace results as table
|
||||
--raw An Output Easy to Parse
|
||||
-j --json Output trace results as JSON
|
||||
-c --classic Classic Output trace results like
|
||||
BestTrace
|
||||
-f --first Start from the first_ttl hop (instead from
|
||||
1). Default: 1
|
||||
-M --map Print Trace Map. This will return a Trace
|
||||
Map URL
|
||||
-M --map Disable Print Trace Map
|
||||
-e --disable-mpls Disable MPLS
|
||||
-v --version Print version info and exit
|
||||
-s --source Use source src_addr for outgoing packets
|
||||
-D --dev Use the following Network Devices as the
|
||||
source address in outgoing packets
|
||||
-R --route Show Routing Table [Provided By BGP.Tools]
|
||||
-z --send-time Set the time interval for sending every
|
||||
packet. Useful when some routers use
|
||||
rate-limit for ICMP messages.. Default: 0
|
||||
-i --ttl-time Set the time interval for sending packets
|
||||
groups by TTL. Useful when some routers
|
||||
use rate-limit for ICMP messages..
|
||||
Default: 500
|
||||
-z --send-time Set how many [milliseconds] between
|
||||
sending each packet.. Useful when some
|
||||
routers use rate-limit for ICMP messages.
|
||||
Default: 100
|
||||
-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
|
||||
--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_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
|
||||
```
|
||||
|
||||
## 项目截图
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## 第三方 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` 中所需要的信息
|
||||
|
||||
@@ -244,13 +416,38 @@ NextTrace 所有的的 IP 地理位置 `API DEMO` 可以参考[这里](https://g
|
||||
|
||||
[GitHub - sjlleo/nexttrace-backend: NextTrace BackEnd](https://github.com/sjlleo/nexttrace-backend)
|
||||
|
||||
## NextTrace Enhanced
|
||||
NextTrace `LeoMoeAPI`现已使用Proof of Work(POW)机制来防止滥用,其中NextTrace作为客户端引入了powclient库,POW CLIENT/SERVER均已开源,欢迎大家使用。(POW模块相关问题请发到对应的仓库)
|
||||
- [GitHub - tsosunchia/powclient: Proof of Work CLIENT for NextTrace](https://github.com/tsosunchia/powclient)
|
||||
- [GitHub - tsosunchia/powserver: Proof of Work SERVER for NextTrace](https://github.com/tsosunchia/powserver)
|
||||
|
||||
https://github.com/OwO-Network/nexttrace-enhanced
|
||||
对于中国大陆用户,可以使用 [Nya Labs](https://natfrp.com) 提供的位于大陆的POW服务器优化访问速度
|
||||
```shell
|
||||
#使用方法任选其一
|
||||
#1. 在环境变量中设置
|
||||
export NEXTTRACE_POWPROVIDER=sakura
|
||||
#2. 在命令行中设置
|
||||
nexttrace --pow-provider sakura
|
||||
```
|
||||
|
||||
## OpenTrace
|
||||
|
||||
`OpenTrace`是 @Archeb 开发的`NextTrace`的跨平台`GUI`版本,带来您熟悉但更强大的用户体验。
|
||||
|
||||
该软件仍然处于早期开发阶段,可能存在许多缺陷和错误,需要您宝贵的使用反馈。
|
||||
|
||||
[https://github.com/Archeb/opentrace](https://github.com/Archeb/opentrace)
|
||||
|
||||
## NEXTTRACE WEB API
|
||||
|
||||
`NextTraceWebApi`是一个`MTR`风格的`NextTrace`网页版服务端实现,提供了包括`Docker`在内多种部署方式。
|
||||
|
||||
[https://github.com/nxtrace/nexttracewebapi](https://github.com/nxtrace/nexttracewebapi)
|
||||
|
||||
## Credits
|
||||
|
||||
BGP.TOOLS 提供了本项目的一些数据支持,在此表示由衷地感谢。
|
||||
[sjlleo](https://github.com/sjlleo) NextTrace 项目永远的领导者、创始人及核心贡献者
|
||||
|
||||
[BGP.TOOLS](https://bgp.tools) 提供了本项目的一些数据支持,在此表示由衷地感谢。
|
||||
|
||||
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
|
||||
|
||||
@@ -266,10 +463,21 @@ BGP.TOOLS 提供了本项目的一些数据支持,在此表示由衷地感谢
|
||||
|
||||
其他第三方 API 尽管集成在本项目内,但是具体的 TOS 以及 AUP,请详见第三方 API 官网。如遇到 IP 数据错误,也请直接联系他们纠错。
|
||||
|
||||
如何获取最新commit的新鲜出炉的二进制可执行文件?
|
||||
>请前往GitHub Actions中最新一次 [Build & Release](https://github.com/nxtrace/Ntrace-V1/actions/workflows/build.yml) workflow.
|
||||
|
||||
## IP 数据以及精准度说明
|
||||
|
||||
对于IP相关信息的纠错反馈,我们目前开放了两个渠道:
|
||||
>- 本项目的GITHUB ISSUES区中的[IP 错误报告汇总帖](https://github.com/nxtrace/NTrace-core/issues/41)
|
||||
>- 本项目的纠错专用邮箱: `correction@moeqing.com` (请注意此邮箱仅供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 完成后续的二开和维护工作。
|
||||
|
||||
LeoMoeAPI 是 [Leo](https://github.com/sjlleo) 的作品,归属于 Leo Network,由 [Leo](https://github.com/sjlleo) 完成整套后端 API 编写,该接口未经允许不可用于任何第三方用途。
|
||||
|
||||
LeoMoeAPI 早期数据主要来自 IPInsight、IPInfo,随着项目发展,越来越多的志愿者参与进了这个项目。目前 LeoMoeAPI 有近一半的数据是社区提供的,而另外一半主要来自于包含 IPInfo、IPData、BigDataCloud、IPGeoLocation 在内的多个第三方数据。
|
||||
|
||||
LeoMoeAPI 的骨干网数据有近 70% 是社区自发反馈又或者是项目组成员校准的,这给本项目的路由跟踪基础功能带来了一定的保证,但是全球骨干网的体量庞大,我们并无能力如 IPIP 等商业公司拥有海量监测节点,这使得 LeoMoeAPI 的数据精准度无法和形如 BestTrace(IPIP)相提并论。
|
||||
@@ -279,3 +487,49 @@ LeoMoeAPI 已经尽力校准了比较常见的骨干网路由,这部分在测
|
||||
我们不保证我们的数据一定会及时更新,也不保证数据的精确性,我们希望您在发现数据错误的时候可以前往 issue 页面提交错误报告,谢谢。
|
||||
|
||||
当您使用 LeoMoeAPI 即视为您已经完全了解 NextTrace LeoMoeAPI 的数据精确性,并且同意如果您引用 LeoMoeAPI 其中的数据从而引发的一切问题,均由您自己承担。
|
||||
|
||||
## DN42 模式使用说明
|
||||
|
||||
使用这个模式需要您配置 2 个文件,分别是 geofeed.csv 以及 ptr.csv
|
||||
|
||||
当您初次运行 DN42 模式,NT 会为您生成 nt_config.yaml 文件,您可以自定义 2 个文件的存放位置,默认应该存放在 NT 的运行目录下
|
||||
|
||||
### GeoFeed
|
||||
|
||||
对于 geofeed.csv 来说,格式如下:
|
||||
```
|
||||
IP_CDIR,LtdCode,ISO3166-2,CityName,ASN,IPWhois
|
||||
```
|
||||
|
||||
比如,您可以这么写:
|
||||
|
||||
```
|
||||
58.215.96.0/20,CN,CN-JS,Wuxi,23650,CHINANET-JS
|
||||
```
|
||||
|
||||
如果您有一个大段作为骨干网使用,您也可以不写地理位置信息,如下:
|
||||
|
||||
```
|
||||
202.97.0.0/16,,,4134,CHINANET-BACKBONE
|
||||
```
|
||||
|
||||
### PTR
|
||||
|
||||
对于 ptr.csv 来说,格式如下:
|
||||
```
|
||||
IATA_CODE,LtdCode,RegionName,CityName
|
||||
```
|
||||
|
||||
比如对于美国洛杉矶,您可以这么写
|
||||
|
||||
```
|
||||
LAX,US,California,Los Anegles
|
||||
```
|
||||
|
||||
需要注意的是,NextTrace 支持自动匹配 CSV 中的城市名,如果您的 PTR 记录中有 `losangeles`,您可以只添加上面一条记录就可以正常识别并读取。
|
||||
|
||||
rkflow in GitHub Actions.
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#nxtrace/NTrace-core&Date)
|
||||
|
||||
257
cmd/cmd.go
257
cmd/cmd.go
@@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
@@ -12,21 +13,24 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
"github.com/nxtrace/NTrace-core/config"
|
||||
fastTrace "github.com/nxtrace/NTrace-core/fast_trace"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/printer"
|
||||
"github.com/nxtrace/NTrace-core/reporter"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
"github.com/nxtrace/NTrace-core/tracelog"
|
||||
"github.com/nxtrace/NTrace-core/tracemap"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"github.com/nxtrace/NTrace-core/wshandle"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
fastTrace "github.com/xgadget-lab/nexttrace/fast_trace"
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/printer"
|
||||
"github.com/xgadget-lab/nexttrace/reporter"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/xgadget-lab/nexttrace/tracelog"
|
||||
"github.com/xgadget-lab/nexttrace/tracemap"
|
||||
"github.com/xgadget-lab/nexttrace/util"
|
||||
"github.com/xgadget-lab/nexttrace/wshandle"
|
||||
)
|
||||
|
||||
func Excute() {
|
||||
parser := argparse.NewParser("nexttrace", "An open source visual route tracking CLI tool")
|
||||
// Create string flag
|
||||
ipv4Only := parser.Flag("4", "ipv4", &argparse.Options{Help: "Use IPv4 only"})
|
||||
ipv6Only := parser.Flag("6", "ipv6", &argparse.Options{Help: "Use IPv6 only"})
|
||||
tcp := parser.Flag("T", "tcp", &argparse.Options{Help: "Use TCP SYN for tracerouting (default port is 80)"})
|
||||
udp := parser.Flag("U", "udp", &argparse.Options{Help: "Use UDP SYN for tracerouting (default port is 53)"})
|
||||
fast_trace := parser.Flag("F", "fast-trace", &argparse.Options{Help: "One-Key Fast Trace to China ISPs"})
|
||||
@@ -36,26 +40,38 @@ func Excute() {
|
||||
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{"IP.SB", "IPInfo", "IPInsight", "IPAPI.com"}, &argparse.Options{Default: "LeoMoeAPI",
|
||||
Help: "Choose IP Geograph Data Provider [LeoMoeAPI,IP.SB, IPInfo, IPInsight, IPAPI.com]"})
|
||||
dataOrigin := parser.Selector("d", "data-provider", []string{"Ip2region", "ip2region", "IP.SB", "ip.sb", "IPInfo", "ipinfo", "IPInsight", "ipinsight", "IPAPI.com", "ip-api.com", "IPInfoLocal", "ipinfolocal", "chunzhen", "LeoMoeAPI", "leomoeapi", "disable-geoip"}, &argparse.Options{Default: "LeoMoeAPI",
|
||||
Help: "Choose IP Geograph Data Provider [IP.SB, IPInfo, IPInsight, IP-API.com, Ip2region, IPInfoLocal, CHUNZHEN, disable-geoip]"})
|
||||
powProvider := parser.Selector("", "pow-provider", []string{"api.nxtrace.org", "sakura"}, &argparse.Options{Default: "api.nxtrace.org",
|
||||
Help: "Choose PoW Provider [api.nxtrace.org, sakura] For China mainland users, please use sakura"})
|
||||
noRdns := parser.Flag("n", "no-rdns", &argparse.Options{Help: "Do not resolve IP addresses to their domain names"})
|
||||
alwaysRdns := parser.Flag("a", "always-rdns", &argparse.Options{Help: "Always resolve IP addresses to their domain names"})
|
||||
routePath := parser.Flag("P", "route-path", &argparse.Options{Help: "Print traceroute hop path by ASN and location"})
|
||||
report := parser.Flag("r", "report", &argparse.Options{Help: "output using report mode"})
|
||||
dn42 := parser.Flag("", "dn42", &argparse.Options{Help: "DN42 Mode"})
|
||||
output := parser.Flag("o", "output", &argparse.Options{Help: "Write trace result to file (RealTimePrinter ONLY)"})
|
||||
tablePrint := parser.Flag("t", "table", &argparse.Options{Help: "Output trace results as table"})
|
||||
rawPrint := parser.Flag("", "raw", &argparse.Options{Help: "An Output Easy to Parse"})
|
||||
jsonPrint := parser.Flag("j", "json", &argparse.Options{Help: "Output trace results as JSON"})
|
||||
classicPrint := parser.Flag("c", "classic", &argparse.Options{Help: "Classic Output trace results like BestTrace"})
|
||||
beginHop := parser.Int("f", "first", &argparse.Options{Default: 1, Help: "Start from the first_ttl hop (instead from 1)"})
|
||||
maptrace := parser.Flag("M", "map", &argparse.Options{Help: "No Print Trace Map"})
|
||||
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"})
|
||||
src_addr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr for outgoing packets"})
|
||||
src_dev := 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]"})
|
||||
packet_interval := parser.Int("z", "send-time", &argparse.Options{Default: 100, Help: "Set the time interval for sending every packet. Useful when some routers use rate-limit for ICMP messages"})
|
||||
ttl_interval := parser.Int("i", "ttl-time", &argparse.Options{Default: 500, Help: "Set the time interval for sending packets groups by TTL. Useful when some routers use rate-limit for ICMP messages"})
|
||||
srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr for outgoing packets"})
|
||||
srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the following Network Devices as the source address in outgoing packets"})
|
||||
//router := parser.Flag("R", "route", &argparse.Options{Help: "Show Routing Table [Provided By BGP.Tools]"})
|
||||
packetInterval := parser.Int("z", "send-time", &argparse.Options{Default: 100, Help: "Set how many [milliseconds] between sending each packet.. Useful when some routers use rate-limit for ICMP messages"})
|
||||
ttlInterval := parser.Int("i", "ttl-time", &argparse.Options{Default: 500, Help: "Set how many [milliseconds] between sending packets groups by TTL. Useful when some routers use rate-limit for ICMP messages"})
|
||||
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)"})
|
||||
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"})
|
||||
|
||||
err := parser.Parse(os.Args)
|
||||
if err != nil {
|
||||
@@ -64,20 +80,41 @@ func Excute() {
|
||||
fmt.Print(parser.Usage(err))
|
||||
return
|
||||
}
|
||||
printer.Version()
|
||||
if !*jsonPrint {
|
||||
printer.Version()
|
||||
}
|
||||
if *ver {
|
||||
printer.CopyRight()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *nocolor {
|
||||
color.NoColor = true
|
||||
} else {
|
||||
color.NoColor = false
|
||||
}
|
||||
|
||||
domain := *str
|
||||
|
||||
if *port == 0 {
|
||||
*port = 80
|
||||
}
|
||||
|
||||
if *fast_trace {
|
||||
fastTrace.FastTest(*tcp, *output)
|
||||
if *fast_trace || *file != "" {
|
||||
var paramsFastTrace = fastTrace.ParamsFastTrace{
|
||||
SrcDev: *srcDev,
|
||||
SrcAddr: *srcAddr,
|
||||
BeginHop: *beginHop,
|
||||
MaxHops: *maxHops,
|
||||
RDns: !*noRdns,
|
||||
AlwaysWaitRDNS: *alwaysRdns,
|
||||
Lang: *lang,
|
||||
PktSize: *packetSize,
|
||||
Timeout: time.Duration(*timeout) * time.Millisecond,
|
||||
File: *file,
|
||||
}
|
||||
|
||||
fastTrace.FastTest(*tcp, *output, paramsFastTrace)
|
||||
if *output {
|
||||
fmt.Println("您的追踪日志已经存放在 /tmp/trace.log 中")
|
||||
}
|
||||
@@ -85,11 +122,30 @@ func Excute() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// DOMAIN处理开始
|
||||
if domain == "" {
|
||||
fmt.Print(parser.Usage(err))
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(domain, "/") {
|
||||
parts := strings.Split(domain, "/")
|
||||
if len(parts) < 3 {
|
||||
fmt.Println("Invalid input")
|
||||
return
|
||||
}
|
||||
domain = parts[2]
|
||||
}
|
||||
|
||||
if strings.Contains(domain, "]") {
|
||||
domain = strings.Split(strings.Split(domain, "]")[0], "[")[1]
|
||||
} else if strings.Contains(domain, ":") {
|
||||
if strings.Count(domain, ":") == 1 {
|
||||
domain = strings.Split(domain, ":")[0]
|
||||
}
|
||||
}
|
||||
// DOMAIN处理结束
|
||||
|
||||
capabilities_check()
|
||||
// return
|
||||
|
||||
@@ -99,36 +155,90 @@ func Excute() {
|
||||
fmt.Println("NextTrace 基于 Windows 的路由跟踪还在早期开发阶段,目前还存在诸多问题,TCP/UDP SYN 包请求可能不能正常运行")
|
||||
}
|
||||
|
||||
if *tcp || *udp {
|
||||
ip = util.DomainLookUp(domain, true)
|
||||
} else {
|
||||
ip = util.DomainLookUp(domain, false)
|
||||
if *dn42 {
|
||||
// 初始化配置
|
||||
config.InitConfig()
|
||||
*dataOrigin = "DN42"
|
||||
*disableMaptrace = true
|
||||
}
|
||||
|
||||
if *src_dev != "" {
|
||||
dev, _ := net.InterfaceByName(*src_dev)
|
||||
/**
|
||||
* 此处若使用goroutine同时运行ws的建立与nslookup,
|
||||
* 会导致第一跳的IP信息无法获取,原因不明。
|
||||
*/
|
||||
//var wg sync.WaitGroup
|
||||
//wg.Add(2)
|
||||
//
|
||||
//go func() {
|
||||
// defer wg.Done()
|
||||
if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
|
||||
val, ok := os.LookupEnv("NEXTTRACE_DATAPROVIDER")
|
||||
if strings.ToUpper(*powProvider) != "API.NXTRACE.ORG" {
|
||||
util.PowProviderParam = *powProvider
|
||||
}
|
||||
if ok {
|
||||
*dataOrigin = val
|
||||
} else {
|
||||
w := wshandle.New()
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(w.Interrupt, os.Interrupt)
|
||||
defer func() {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
}
|
||||
}
|
||||
//}()
|
||||
//
|
||||
//go func() {
|
||||
// defer wg.Done()
|
||||
err = nil
|
||||
if *udp {
|
||||
if *ipv6Only {
|
||||
fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.")
|
||||
os.Exit(0)
|
||||
}
|
||||
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
} else {
|
||||
if *ipv6Only {
|
||||
ip, err = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
|
||||
} else if *ipv4Only {
|
||||
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
} else {
|
||||
ip, err = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
//}()
|
||||
//
|
||||
//wg.Wait()
|
||||
|
||||
if *srcDev != "" {
|
||||
dev, _ := net.InterfaceByName(*srcDev)
|
||||
if addrs, err := dev.Addrs(); err == nil {
|
||||
for _, addr := range addrs {
|
||||
if (addr.(*net.IPNet).IP.To4() == nil) == (ip.To4() == nil) {
|
||||
*src_addr = addr.(*net.IPNet).IP.String()
|
||||
*srcAddr = addr.(*net.IPNet).IP.String()
|
||||
// 检查是否是内网IP
|
||||
if !(net.ParseIP(*srcAddr).IsPrivate() ||
|
||||
net.ParseIP(*srcAddr).IsLoopback() ||
|
||||
net.ParseIP(*srcAddr).IsLinkLocalUnicast() ||
|
||||
net.ParseIP(*srcAddr).IsLinkLocalMulticast()) {
|
||||
// 若不是则跳出
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
|
||||
w := wshandle.New()
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(w.Interrupt, os.Interrupt)
|
||||
defer func() {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
if !*jsonPrint {
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize)
|
||||
}
|
||||
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin)
|
||||
|
||||
var m trace.Method = ""
|
||||
var m trace.Method
|
||||
|
||||
switch {
|
||||
case *tcp:
|
||||
@@ -143,26 +253,34 @@ func Excute() {
|
||||
*port = 53
|
||||
}
|
||||
|
||||
util.DestIP = ip.String()
|
||||
var conf = trace.Config{
|
||||
SrcAddr: *src_addr,
|
||||
DN42: *dn42,
|
||||
SrcAddr: *srcAddr,
|
||||
BeginHop: *beginHop,
|
||||
DestIP: ip,
|
||||
DestPort: *port,
|
||||
MaxHops: *maxHops,
|
||||
PacketInterval: *packet_interval,
|
||||
TTLInterval: *ttl_interval,
|
||||
PacketInterval: *packetInterval,
|
||||
TTLInterval: *ttlInterval,
|
||||
NumMeasurements: *numMeasurements,
|
||||
ParallelRequests: *parallelRequests,
|
||||
Lang: *lang,
|
||||
RDns: !*noRdns,
|
||||
AlwaysWaitRDNS: *alwaysRdns,
|
||||
IPGeoSource: ipgeo.GetSource(*dataOrigin),
|
||||
Timeout: 1 * time.Second,
|
||||
Timeout: time.Duration(*timeout) * time.Millisecond,
|
||||
PktSize: *packetSize,
|
||||
}
|
||||
|
||||
// 暂时弃用
|
||||
router := new(bool)
|
||||
*router = false
|
||||
if !*tablePrint {
|
||||
if *classicPrint {
|
||||
conf.RealtimePrinter = printer.ClassicPrinter
|
||||
} else if *rawPrint {
|
||||
conf.RealtimePrinter = printer.EasyPrinter
|
||||
} else {
|
||||
if *output {
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
@@ -179,6 +297,24 @@ func Excute() {
|
||||
}
|
||||
}
|
||||
|
||||
if *jsonPrint {
|
||||
conf.RealtimePrinter = nil
|
||||
conf.AsyncPrinter = nil
|
||||
}
|
||||
|
||||
if util.Uninterrupted != "" && *rawPrint {
|
||||
for {
|
||||
_, err := trace.Traceroute(m, conf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *disableMPLS {
|
||||
util.DisableMPLS = "1"
|
||||
}
|
||||
|
||||
res, err := trace.Traceroute(m, conf)
|
||||
|
||||
if err != nil {
|
||||
@@ -194,9 +330,29 @@ func Excute() {
|
||||
r.Print()
|
||||
}
|
||||
|
||||
if !*maptrace {
|
||||
r, _ := json.Marshal(res)
|
||||
tracemap.GetMapUrl(string(r))
|
||||
r, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
if !*disableMaptrace &&
|
||||
(util.StringInSlice(strings.ToUpper(*dataOrigin), []string{"LEOMOEAPI", "IPINFO", "IPINFO", "IP-API.COM", "IPAPI.COM"})) {
|
||||
url, err := tracemap.GetMapUrl(string(r))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
res.TraceMapUrl = url
|
||||
if !*jsonPrint {
|
||||
tracemap.PrintMapUrl(url)
|
||||
}
|
||||
}
|
||||
r, err = json.Marshal(res)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
if *jsonPrint {
|
||||
fmt.Println(string(r))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +382,12 @@ func capabilities_check() {
|
||||
// NewPid 已经被废弃了,这里改用 NewPid2 方法
|
||||
caps, err := capability.NewPid2(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
// 判断是否为macOS
|
||||
if runtime.GOOS == "darwin" {
|
||||
// macOS下报错有问题
|
||||
} else {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -243,8 +404,8 @@ func capabilities_check() {
|
||||
return
|
||||
} else {
|
||||
// 没权限啦
|
||||
log.Println("您正在以普通用户权限运行 NextTrace,但 NextTrace 未被赋予监听网络套接字的ICMP消息包、修改IP头信息(TTL)等路由跟踪所需的权限")
|
||||
log.Println("请使用管理员用户执行 `sudo setcap cap_net_raw,cap_net_admin+eip ${your_nexttrace_path}/nexttrace` 命令,赋予相关权限后再运行~")
|
||||
log.Fatalln("什么?为什么 ping 普通用户执行不要 root 权限?因为这些工具在管理员安装时就已经被赋予了一些必要的权限,具体请使用 `getcap /usr/bin/ping` 查看")
|
||||
fmt.Println("您正在以普通用户权限运行 NextTrace,但 NextTrace 未被赋予监听网络套接字的ICMP消息包、修改IP头信息(TTL)等路由跟踪所需的权限")
|
||||
fmt.Println("请使用管理员用户执行 `sudo setcap cap_net_raw,cap_net_admin+eip ${your_nexttrace_path}/nexttrace` 命令,赋予相关权限后再运行~")
|
||||
fmt.Println("什么?为什么 ping 普通用户执行不要 root 权限?因为这些工具在管理员安装时就已经被赋予了一些必要的权限,具体请使用 `getcap /usr/bin/ping` 查看")
|
||||
}
|
||||
}
|
||||
|
||||
5
config/basic.go
Normal file
5
config/basic.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package config
|
||||
|
||||
var Version = "v0.0.0.alpha"
|
||||
var BuildDate = ""
|
||||
var CommitID = ""
|
||||
40
config/viper.go
Normal file
40
config/viper.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func InitConfig() {
|
||||
|
||||
// 配置文件名, 不加扩展
|
||||
viper.SetConfigName("nt_config") // name of config file (without extension)
|
||||
// 设置文件的扩展名
|
||||
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
|
||||
// 查找配置文件所在路径
|
||||
viper.AddConfigPath("/etc/bin/nexttrace/")
|
||||
viper.AddConfigPath("/usr/local/bin/nexttrace/")
|
||||
// 在当前路径进行查找
|
||||
viper.AddConfigPath(".")
|
||||
// viper.AddConfigPath("./config/")
|
||||
|
||||
// 配置默认值
|
||||
viper.SetDefault("ptrPath", "./ptr.csv")
|
||||
viper.SetDefault("geoFeedPath", "./geofeed.csv")
|
||||
|
||||
// 开始查找并读取配置文件
|
||||
err := viper.ReadInConfig() // Find and read the config file
|
||||
if err != nil { // Handle errors reading the config file
|
||||
fmt.Println("未能找到配置文件,我们将在您的运行目录为您创建 nt_config.yaml 默认配置")
|
||||
err := viper.SafeWriteConfigAs("./nt_config.yaml")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = viper.ReadInConfig()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
7
dn42/dn42.go
Normal file
7
dn42/dn42.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package dn42
|
||||
|
||||
/***
|
||||
[DN42 Package]
|
||||
谨献给 DN42 所有的小伙伴们,祝你们终有一天能有自己的公网 ASN ~
|
||||
By Leo
|
||||
***/
|
||||
101
dn42/geofeed.go
Normal file
101
dn42/geofeed.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package dn42
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type GeoFeedRow struct {
|
||||
IPNet *net.IPNet
|
||||
CIDR string
|
||||
LtdCode string
|
||||
ISO3166 string
|
||||
City string
|
||||
ASN string
|
||||
IPWhois string
|
||||
}
|
||||
|
||||
func GetGeoFeed(ip string) (GeoFeedRow, bool) {
|
||||
rows, err := ReadGeoFeed()
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
panic(err)
|
||||
}
|
||||
|
||||
row, find := FindGeoFeedRow(ip, rows)
|
||||
return row, find
|
||||
|
||||
}
|
||||
|
||||
func ReadGeoFeed() ([]GeoFeedRow, error) {
|
||||
path := viper.Get("geoFeedPath").(string)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := csv.NewReader(f)
|
||||
rows, err := r.ReadAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 将 CSV 中的每一行转换为 GeoFeedRow 类型,并保存到 rowsSlice 中
|
||||
var rowsSlice []GeoFeedRow
|
||||
for _, row := range rows {
|
||||
cidr := row[0] // 假设第一列是 CIDR 字段
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
// 如果解析 CIDR 失败,跳过这一行
|
||||
continue
|
||||
}
|
||||
if len(row) == 4 {
|
||||
rowsSlice = append(rowsSlice, GeoFeedRow{
|
||||
IPNet: ipnet,
|
||||
CIDR: cidr,
|
||||
LtdCode: row[1],
|
||||
ISO3166: row[2],
|
||||
City: row[3],
|
||||
})
|
||||
} else {
|
||||
rowsSlice = append(rowsSlice, GeoFeedRow{
|
||||
IPNet: ipnet,
|
||||
CIDR: cidr,
|
||||
LtdCode: row[1],
|
||||
ISO3166: row[2],
|
||||
City: row[3],
|
||||
ASN: row[4],
|
||||
IPWhois: row[5],
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
// 根据 CIDR 范围从小到大排序,方便后面查找
|
||||
sort.Slice(rowsSlice, func(i, j int) bool {
|
||||
return rowsSlice[i].IPNet.Mask.String() > rowsSlice[j].IPNet.Mask.String()
|
||||
})
|
||||
|
||||
return rowsSlice, nil
|
||||
}
|
||||
|
||||
func FindGeoFeedRow(ipStr string, rows []GeoFeedRow) (GeoFeedRow, bool) {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
// 如果传入的 IP 无效,直接返回
|
||||
return GeoFeedRow{}, false
|
||||
}
|
||||
|
||||
// 遍历每个 CIDR 范围,找到第一个包含传入的 IP 的 CIDR
|
||||
for _, row := range rows {
|
||||
if row.IPNet.Contains(ip) {
|
||||
return row, true
|
||||
}
|
||||
}
|
||||
|
||||
return GeoFeedRow{}, false
|
||||
}
|
||||
17
dn42/geofeed_test.go
Normal file
17
dn42/geofeed_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package dn42
|
||||
|
||||
// func TestGeoFeed(t *testing.T) {
|
||||
// rows, err := ReadGeoFeed()
|
||||
// if err != nil {
|
||||
// // 处理错误
|
||||
// }
|
||||
|
||||
// row, found := FindGeoFeedRow("2001:0418:1403:8080::6fff", rows)
|
||||
// if found {
|
||||
// // 处理符合条件的 row
|
||||
// log.Println(row)
|
||||
// } else {
|
||||
// // 处理未找到的情况
|
||||
// }
|
||||
|
||||
// }
|
||||
83
dn42/ptr.go
Normal file
83
dn42/ptr.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package dn42
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type PtrRow struct {
|
||||
IATACode string
|
||||
LtdCode string
|
||||
Region string
|
||||
City string
|
||||
}
|
||||
|
||||
func matchesPattern(prefix string, s string) bool {
|
||||
pattern := fmt.Sprintf(`^(.*[-.\d]|^)%s[-.\d].*$`, prefix)
|
||||
|
||||
r, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
fmt.Println("Invalid regular expression:", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return r.MatchString(s)
|
||||
}
|
||||
|
||||
func FindPtrRecord(ptr string) (PtrRow, error) {
|
||||
path := viper.Get("ptrPath").(string)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return PtrRow{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := csv.NewReader(f)
|
||||
rows, err := r.ReadAll()
|
||||
if err != nil {
|
||||
return PtrRow{}, err
|
||||
}
|
||||
// 转小写
|
||||
ptr = strings.ToLower(ptr)
|
||||
// 先查城市名
|
||||
for _, row := range rows {
|
||||
city := row[3]
|
||||
if city == "" {
|
||||
continue
|
||||
}
|
||||
city = strings.ReplaceAll(city, " ", "")
|
||||
city = strings.ToLower(city)
|
||||
|
||||
if matchesPattern(city, ptr) {
|
||||
return PtrRow{
|
||||
LtdCode: row[1],
|
||||
Region: row[2],
|
||||
City: row[3],
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
// 查 IATA Code
|
||||
for _, row := range rows {
|
||||
iata := row[0]
|
||||
if iata == "" {
|
||||
continue
|
||||
}
|
||||
iata = strings.ToLower(iata)
|
||||
if matchesPattern(iata, ptr) {
|
||||
return PtrRow{
|
||||
IATACode: iata,
|
||||
LtdCode: row[1],
|
||||
Region: row[2],
|
||||
City: row[3],
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return PtrRow{}, errors.New("ptr not found")
|
||||
}
|
||||
50
dn42/ptr_test.go
Normal file
50
dn42/ptr_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package dn42
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPTR(t *testing.T) {
|
||||
|
||||
// example_mis := []string{
|
||||
// "sloutravel.com",
|
||||
// "memeslou.org",
|
||||
// "followsloucity.net",
|
||||
// "slouslou.slou",
|
||||
// "slouslou8.slou",
|
||||
// }
|
||||
|
||||
// examples := []string{
|
||||
|
||||
// "1ge.slou.as1299.net",
|
||||
// "1ge.slou2.as1299.net",
|
||||
// "1ge-slou.as1299.net",
|
||||
// "slou-1.as1299.net",
|
||||
// "slou.as1299.com",
|
||||
// "1ge-snge-6.as1299.net",
|
||||
// "c-1.sin.sg.atlas.moeqing.com",
|
||||
// "core.hkg1.hk.atlas.moeqing.com",
|
||||
// "core.losangles.us.atlas.moeqing.com",
|
||||
// }
|
||||
|
||||
// fmt.Println("容易误匹配的 PTR")
|
||||
|
||||
// for _, s := range example_mis {
|
||||
// if r, err := FindPtrRecord("ptr.csv"); err == nil {
|
||||
// fmt.Println(s, r)
|
||||
// } else {
|
||||
// fmt.Println(s, err)
|
||||
// }
|
||||
|
||||
// }
|
||||
// fmt.Println("\n应该正常匹配的 PTR")
|
||||
// for _, s := range examples {
|
||||
// if r, err := FindPtrRecord("ptr.csv"); err == nil {
|
||||
// fmt.Println(s, r)
|
||||
// } else {
|
||||
// fmt.Println(s, err)
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@ type BackBoneCollection struct {
|
||||
CU169 ISPCollection
|
||||
CU9929 ISPCollection
|
||||
CM ISPCollection
|
||||
CMIN2 ISPCollection
|
||||
EDU ISPCollection
|
||||
CST ISPCollection
|
||||
}
|
||||
@@ -32,6 +33,7 @@ const (
|
||||
CU169 string = "联通 169 AS4837"
|
||||
CU9929 string = "联通 A网 AS9929"
|
||||
CM string = "移动 骨干网 AS9808"
|
||||
CMIN2 string = "移动 CMIN2 AS58807"
|
||||
EDU string = "教育网 CERNET AS4538"
|
||||
)
|
||||
|
||||
@@ -47,26 +49,36 @@ var Beijing = BackBoneCollection{
|
||||
Location: "北京",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "106.37.67.1",
|
||||
IPv6: "240e:40:e002:1:a:3ee3:c00:0",
|
||||
IP: "ipv4.pek-4134.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.pek-4134.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "123.125.96.156",
|
||||
IPv6: "2408:8000:1010:2::10",
|
||||
IP: "ipv4.pek-4837.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.pek-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU9929: ISPCollection{
|
||||
ISPName: CU9929,
|
||||
IP: "ipv4.pek-9929.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "211.136.25.153",
|
||||
IPv6: "2409:8000:3800:8::3",
|
||||
IP: "ipv4.pek-9808.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.pek-9808.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CMIN2: ISPCollection{
|
||||
ISPName: CMIN2,
|
||||
IP: "ipv4.pek-58807.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "101.6.15.130",
|
||||
IPv6: "2001:da8::666",
|
||||
IP: "ipv4.pek-4538.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.pek-4538.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -74,37 +86,42 @@ var Shanghai = BackBoneCollection{
|
||||
Location: "上海",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "202.101.21.178",
|
||||
IPv6: "240e:18:2:153::89",
|
||||
IP: "ipv4.sha-4134.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-4134.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CTCN2: ISPCollection{
|
||||
ISPName: CTCN2,
|
||||
IP: "58.32.4.1",
|
||||
IP: "ipv4.sha-4809.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "139.226.206.150",
|
||||
IPv6: "2408:8000:9000:0:4000::437",
|
||||
IP: "ipv4.sha-4837.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU9929: ISPCollection{
|
||||
ISPName: CU9929,
|
||||
IP: "210.13.86.1",
|
||||
IPv6: "2408:8120:2::d6",
|
||||
IP: "ipv4.sha-9929.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-9929.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "120.204.34.85",
|
||||
IPv6: "2409:801e:f0:1::4e1",
|
||||
IP: "ipv4.sha-9808.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-9808.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CMIN2: ISPCollection{
|
||||
ISPName: CMIN2,
|
||||
IP: "ipv4.sha-58807.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "202.120.58.155",
|
||||
IPv6: "2001:da8:8000:1:202:120:2:100",
|
||||
IP: "ipv4.sha-4538.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.sha-4538.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -112,20 +129,20 @@ var Guangzhou = BackBoneCollection{
|
||||
Location: "广州",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "14.116.225.60",
|
||||
IPv6: "240e:f9:8010::3:110:1",
|
||||
IP: "ipv4.can-4134.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.can-4134.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "157.18.0.22",
|
||||
IPv6: "2408:8001:3161:4::1",
|
||||
IP: "ipv4.can-4837.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.can-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "120.198.26.254",
|
||||
IPv6: "2409:8055:3008:1116::150",
|
||||
IP: "ipv4.can-9808.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.can-9808.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -133,24 +150,23 @@ var Hangzhou = BackBoneCollection{
|
||||
Location: "杭州",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "61.164.23.196",
|
||||
IPv6: "240e:f3:c000:201::10",
|
||||
IP: "ipv4.hgh-4134.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hgh-4134.endpoint.nxtrace.org.",
|
||||
},
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "60.12.244.1",
|
||||
IPv6: "",
|
||||
IP: "ipv4.hgh-4837.endpoint.nxtrace.org.",
|
||||
},
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "112.17.224.98",
|
||||
IPv6: "2409:8028:840:2::11",
|
||||
IP: "ipv4.hgh-9808.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hgh-9808.endpoint.nxtrace.org.",
|
||||
},
|
||||
// 浙江大学 教育网
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "210.32.2.1",
|
||||
IPv6: "2001:da8:e000:1::1",
|
||||
IP: "ipv4.hgh-4538.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hgh-4538.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -159,12 +175,12 @@ var Hefei = BackBoneCollection{
|
||||
// 中国科学技术大学 教育网
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "202.38.64.1",
|
||||
IPv6: "2001:da8:d805:ffff:2::1",
|
||||
IP: "ipv4.hfe-4538.endpoint.nxtrace.org.",
|
||||
IPv6: "ipv6.hfe-4538.endpoint.nxtrace.org.",
|
||||
},
|
||||
// 中国科学技术大学 科技网
|
||||
CST: ISPCollection{
|
||||
ISPName: "中国科学技术大学 科技网 AS7497",
|
||||
IP: "210.72.22.2",
|
||||
IP: "ipv4.hfe-7497.endpoint.nxtrace.org.",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,52 +2,63 @@ package fastTrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/printer"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
"github.com/nxtrace/NTrace-core/tracelog"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"github.com/nxtrace/NTrace-core/wshandle"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/printer"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/xgadget-lab/nexttrace/tracelog"
|
||||
"github.com/xgadget-lab/nexttrace/wshandle"
|
||||
)
|
||||
|
||||
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 fp.Close()
|
||||
//var pFastTracer ParamsFastTrace
|
||||
|
||||
log.SetOutput(fp)
|
||||
log.SetFlags(0)
|
||||
func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
|
||||
fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX)
|
||||
log.Printf("『%s %s 』\n", location, ispCollection.ISPName)
|
||||
fmt.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ispCollection.IPv6)
|
||||
log.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ispCollection.IPv6)
|
||||
ip := net.ParseIP(ispCollection.IPv6)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
|
||||
ip, err := util.DomainLookUp(ispCollection.IPv6, "6", "", true)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var conf = trace.Config{
|
||||
BeginHop: 1,
|
||||
BeginHop: f.ParamsFastTrace.BeginHop,
|
||||
DestIP: ip,
|
||||
DestPort: 80,
|
||||
MaxHops: 30,
|
||||
MaxHops: f.ParamsFastTrace.MaxHops,
|
||||
NumMeasurements: 3,
|
||||
ParallelRequests: 18,
|
||||
RDns: true,
|
||||
RDns: f.ParamsFastTrace.RDns,
|
||||
AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS,
|
||||
PacketInterval: 100,
|
||||
TTLInterval: 500,
|
||||
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
|
||||
Timeout: 1 * time.Second,
|
||||
Timeout: f.ParamsFastTrace.Timeout,
|
||||
SrcAddr: f.ParamsFastTrace.SrcAddr,
|
||||
PktSize: f.ParamsFastTrace.PktSize,
|
||||
Lang: f.ParamsFastTrace.Lang,
|
||||
}
|
||||
|
||||
if f.TracerouteMethod == trace.ICMPTrace {
|
||||
if oe {
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
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\n", ispCollection.IPv6, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
}
|
||||
|
||||
_, err = trace.Traceroute(f.TracerouteMethod, conf)
|
||||
@@ -56,7 +67,7 @@ func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
println()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func (f *FastTracer) testAll_v6() {
|
||||
@@ -96,16 +107,28 @@ func (f *FastTracer) testEDU_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.EDU)
|
||||
}
|
||||
|
||||
func FastTestv6(tm bool, outEnable bool) {
|
||||
func (f *FastTracer) testFast_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
}
|
||||
|
||||
func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
var c string
|
||||
|
||||
oe = outEnable
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网")
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网\n6. 全部")
|
||||
fmt.Print("请选择选项:")
|
||||
fmt.Scanln(&c)
|
||||
_, err := fmt.Scanln(&c)
|
||||
if err != nil {
|
||||
c = "1"
|
||||
}
|
||||
|
||||
ft := FastTracer{}
|
||||
ft := FastTracer{
|
||||
ParamsFastTrace: paramsFastTrace,
|
||||
}
|
||||
|
||||
// 建立 WebSocket 连接
|
||||
w := wshandle.New()
|
||||
@@ -115,11 +138,16 @@ func FastTestv6(tm bool, outEnable bool) {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
if !tm {
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数")
|
||||
} else {
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
}
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testAll_v6()
|
||||
ft.testFast_v6()
|
||||
case "2":
|
||||
ft.testCT_v6()
|
||||
case "3":
|
||||
@@ -128,7 +156,9 @@ func FastTestv6(tm bool, outEnable bool) {
|
||||
ft.testCM_v6()
|
||||
case "5":
|
||||
ft.testEDU_v6()
|
||||
default:
|
||||
case "6":
|
||||
ft.testAll_v6()
|
||||
default:
|
||||
ft.testFast_v6()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,90 @@
|
||||
package fastTrace
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/printer"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
"github.com/nxtrace/NTrace-core/tracelog"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"github.com/nxtrace/NTrace-core/wshandle"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/printer"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/xgadget-lab/nexttrace/tracelog"
|
||||
"github.com/xgadget-lab/nexttrace/wshandle"
|
||||
)
|
||||
|
||||
type FastTracer struct {
|
||||
TracerouteMethod trace.Method
|
||||
ParamsFastTrace ParamsFastTrace
|
||||
}
|
||||
|
||||
var oe bool = false
|
||||
type ParamsFastTrace struct {
|
||||
SrcDev string
|
||||
SrcAddr string
|
||||
BeginHop int
|
||||
MaxHops int
|
||||
RDns bool
|
||||
AlwaysWaitRDNS bool
|
||||
Lang string
|
||||
PktSize int
|
||||
Timeout time.Duration
|
||||
File 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 fp.Close()
|
||||
|
||||
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, 30 hops max, 32 byte packets\n", ispCollection.IP)
|
||||
log.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ispCollection.IP)
|
||||
ip := net.ParseIP(ispCollection.IP)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
|
||||
ip, err := util.DomainLookUp(ispCollection.IP, "4", "", true)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var conf = trace.Config{
|
||||
BeginHop: 1,
|
||||
BeginHop: f.ParamsFastTrace.BeginHop,
|
||||
DestIP: ip,
|
||||
DestPort: 80,
|
||||
MaxHops: 30,
|
||||
MaxHops: f.ParamsFastTrace.MaxHops,
|
||||
NumMeasurements: 3,
|
||||
ParallelRequests: 18,
|
||||
RDns: true,
|
||||
RDns: f.ParamsFastTrace.RDns,
|
||||
AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS,
|
||||
PacketInterval: 100,
|
||||
TTLInterval: 500,
|
||||
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
|
||||
Timeout: 1 * time.Second,
|
||||
Timeout: f.ParamsFastTrace.Timeout,
|
||||
SrcAddr: f.ParamsFastTrace.SrcAddr,
|
||||
PktSize: f.ParamsFastTrace.PktSize,
|
||||
Lang: f.ParamsFastTrace.Lang,
|
||||
}
|
||||
|
||||
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\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
@@ -60,7 +95,274 @@ 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) {
|
||||
// tm means tcp mode
|
||||
var c string
|
||||
oe = outEnable
|
||||
|
||||
if paramsFastTrace.File != "" {
|
||||
testFile(paramsFastTrace, tm)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Hi,欢迎使用 Fast Trace 功能,请注意 Fast Trace 功能只适合新手使用\n因为国内网络复杂,我们设置的测试目标有限,建议普通用户自测以获得更加精准的路由情况")
|
||||
fmt.Println("请您选择要测试的 IP 类型\n1. IPv4\n2. IPv6")
|
||||
fmt.Print("请选择选项:")
|
||||
_, err := fmt.Scanln(&c)
|
||||
if err != nil {
|
||||
c = "1"
|
||||
}
|
||||
if c == "2" {
|
||||
if paramsFastTrace.SrcDev != "" {
|
||||
dev, _ := net.InterfaceByName(paramsFastTrace.SrcDev)
|
||||
if addrs, err := dev.Addrs(); err == nil {
|
||||
for _, addr := range addrs {
|
||||
if (addr.(*net.IPNet).IP.To4() == nil) == true {
|
||||
paramsFastTrace.SrcAddr = addr.(*net.IPNet).IP.String()
|
||||
// 检查是否是内网IP
|
||||
if !(net.ParseIP(paramsFastTrace.SrcAddr).IsPrivate() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLoopback() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalUnicast() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalMulticast()) {
|
||||
// 若不是则跳出
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FastTestv6(tm, outEnable, paramsFastTrace)
|
||||
return
|
||||
}
|
||||
if paramsFastTrace.SrcDev != "" {
|
||||
dev, _ := net.InterfaceByName(paramsFastTrace.SrcDev)
|
||||
if addrs, err := dev.Addrs(); err == nil {
|
||||
for _, addr := range addrs {
|
||||
if (addr.(*net.IPNet).IP.To4() == nil) == false {
|
||||
paramsFastTrace.SrcAddr = addr.(*net.IPNet).IP.String()
|
||||
// 检查是否是内网IP
|
||||
if !(net.ParseIP(paramsFastTrace.SrcAddr).IsPrivate() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLoopback() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalUnicast() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalMulticast()) {
|
||||
// 若不是则跳出
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网\n6. 全部")
|
||||
fmt.Print("请选择选项:")
|
||||
_, err = fmt.Scanln(&c)
|
||||
if err != nil {
|
||||
c = "1"
|
||||
}
|
||||
|
||||
ft := FastTracer{
|
||||
ParamsFastTrace: paramsFastTrace,
|
||||
}
|
||||
|
||||
// 建立 WebSocket 连接
|
||||
w := wshandle.New()
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(w.Interrupt, os.Interrupt)
|
||||
defer func() {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
|
||||
if !tm {
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数")
|
||||
} else {
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
}
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testFast()
|
||||
case "2":
|
||||
ft.testCT()
|
||||
case "3":
|
||||
ft.testCU()
|
||||
case "4":
|
||||
ft.testCM()
|
||||
case "5":
|
||||
ft.testEDU()
|
||||
case "6":
|
||||
ft.testAll()
|
||||
default:
|
||||
ft.testFast()
|
||||
}
|
||||
}
|
||||
|
||||
func testFile(paramsFastTrace ParamsFastTrace, tm bool) {
|
||||
// 建立 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
|
||||
if !tm {
|
||||
tracerouteMethod = trace.ICMPTrace
|
||||
fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数")
|
||||
} else {
|
||||
tracerouteMethod = trace.TCPTrace
|
||||
}
|
||||
|
||||
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.Printf("%s『%s』%s\n", printer.YELLOW_PREFIX, ip.Desc, printer.RESET_PREFIX)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ip.Ip, paramsFastTrace.MaxHops, paramsFastTrace.PktSize)
|
||||
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: 80,
|
||||
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
|
||||
}
|
||||
defer func(fp *os.File) {
|
||||
err := fp.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(fp)
|
||||
|
||||
log.SetOutput(fp)
|
||||
log.SetFlags(0)
|
||||
log.Printf("『%s』\n", ip.Desc)
|
||||
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ip.Ip, paramsFastTrace.MaxHops, paramsFastTrace.PktSize)
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
}
|
||||
|
||||
_, err := trace.Traceroute(tracerouteMethod, conf)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (f *FastTracer) testAll() {
|
||||
@@ -83,6 +385,7 @@ func (f *FastTracer) testCT() {
|
||||
|
||||
func (f *FastTracer) testCU() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU9929)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CU169)
|
||||
@@ -91,7 +394,9 @@ func (f *FastTracer) testCU() {
|
||||
|
||||
func (f *FastTracer) testCM() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CMIN2)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CMIN2)
|
||||
f.tracert(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
}
|
||||
@@ -104,52 +409,9 @@ func (f *FastTracer) testEDU() {
|
||||
f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.CST)
|
||||
}
|
||||
|
||||
func FastTest(tm bool, outEnable bool) {
|
||||
var c string
|
||||
|
||||
oe = outEnable
|
||||
fmt.Println("Hi,欢迎使用 Fast Trace 功能,请注意 Fast Trace 功能只适合新手使用\n因为国内网络复杂,我们设置的测试目标有限,建议普通用户自测以获得更加精准的路由情况")
|
||||
fmt.Println("请您选择要测试的 IP 类型\n1. IPv4\n2. IPv6")
|
||||
fmt.Print("请选择选项:")
|
||||
fmt.Scanln(&c)
|
||||
if c == "2" {
|
||||
FastTestv6(tm, outEnable)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网")
|
||||
fmt.Print("请选择选项:")
|
||||
fmt.Scanln(&c)
|
||||
|
||||
ft := FastTracer{}
|
||||
|
||||
// 建立 WebSocket 连接
|
||||
w := wshandle.New()
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(w.Interrupt, os.Interrupt)
|
||||
defer func() {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
|
||||
if !tm {
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数")
|
||||
} else {
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
}
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testAll()
|
||||
case "2":
|
||||
ft.testCT()
|
||||
case "3":
|
||||
ft.testCU()
|
||||
case "4":
|
||||
ft.testCM()
|
||||
case "5":
|
||||
ft.testEDU()
|
||||
default:
|
||||
ft.testAll()
|
||||
}
|
||||
func (f *FastTracer) testFast() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
}
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
package fastTrace
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"testing"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/xgadget-lab/nexttrace/wshandle"
|
||||
)
|
||||
|
||||
// ICMP Use Too Many Time to Wait So we don't test it.
|
||||
func TestTCPTrace(t *testing.T) {
|
||||
ft := FastTracer{}
|
||||
// 建立 WebSocket 连接
|
||||
w := wshandle.New()
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(w.Interrupt, os.Interrupt)
|
||||
defer func() {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
ft.testCM()
|
||||
ft.testEDU()
|
||||
func TestTrace(t *testing.T) {
|
||||
//pFastTrace := ParamsFastTrace{
|
||||
// SrcDev: "",
|
||||
// SrcAddr: "",
|
||||
// BeginHop: 1,
|
||||
// MaxHops: 30,
|
||||
// RDns: false,
|
||||
// AlwaysWaitRDNS: false,
|
||||
// Lang: "",
|
||||
// PktSize: 52,
|
||||
//}
|
||||
//ft := FastTracer{ParamsFastTrace: pFastTrace}
|
||||
//// 建立 WebSocket 连接
|
||||
//w := wshandle.New()
|
||||
//w.Interrupt = make(chan os.Signal, 1)
|
||||
//signal.Notify(w.Interrupt, os.Interrupt)
|
||||
//defer func() {
|
||||
// w.Conn.Close()
|
||||
//}()
|
||||
//fmt.Println("TCP v4")
|
||||
//ft.TracerouteMethod = trace.TCPTrace
|
||||
//ft.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//fmt.Println("TCP v6")
|
||||
//ft.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//fmt.Println("ICMP v4")
|
||||
//ft.TracerouteMethod = trace.ICMPTrace
|
||||
//ft.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//fmt.Println("ICMP v6")
|
||||
//ft.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
}
|
||||
|
||||
3
geofeed.example.csv
Normal file
3
geofeed.example.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
154.48.0.0/12,,,,174,COGENT-NET
|
||||
200.15.12.0/22,BR,BR-SP,Sao Paulo,2914,NTT-BACKBONE
|
||||
2001:0418:1403::/48,US,US-VA,Ashburn,2914,NTT-BACKBONE
|
||||
|
46
go.mod
46
go.mod
@@ -1,30 +1,50 @@
|
||||
module github.com/xgadget-lab/nexttrace
|
||||
module github.com/nxtrace/NTrace-core
|
||||
|
||||
go 1.19
|
||||
go 1.21
|
||||
|
||||
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.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
golang.org/x/net v0.5.0
|
||||
golang.org/x/sync v0.1.0
|
||||
github.com/tsosunchia/powclient v0.1.4
|
||||
golang.org/x/net v0.18.0
|
||||
golang.org/x/sync v0.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/sagikazarmark/locafero v0.3.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.10.0 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/lionsoul2014/ip2region v2.11.0+incompatible
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/lionsoul2014/ip2region v2.11.2+incompatible
|
||||
github.com/rodaine/table v1.1.0
|
||||
github.com/stretchr/testify v1.7.1 // indirect
|
||||
github.com/tidwall/gjson v1.14.4
|
||||
github.com/tidwall/gjson v1.17.0
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
518
go.sum
518
go.sum
@@ -1,68 +1,534 @@
|
||||
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/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/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.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
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/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/lionsoul2014/ip2region v2.11.0+incompatible h1:+L3q50qdDztfPfwKFukSWoOFTpbPhiYRXMmr2cYPPdM=
|
||||
github.com/lionsoul2014/ip2region v2.11.0+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
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.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
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/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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.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/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.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
|
||||
github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
|
||||
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.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.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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/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.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
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/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tsosunchia/powclient v0.1.4 h1:iAXUOiGPLJJTnzVSD0PY+4kBWm+SEMQZzd5ntEk1t0c=
|
||||
github.com/tsosunchia/powclient v0.1.4/go.mod h1:Pm4MP3QqN74SfNskPpFIEyT+NQrcABGoXkkeRwjlMEE=
|
||||
github.com/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=
|
||||
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/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
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.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
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.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20200116001909-b77594299b42/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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/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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
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/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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=
|
||||
|
||||
91
ipgeo/chunzhen.go
Normal file
91
ipgeo/chunzhen.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Chunzhen(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
url := util.GetenvDefault("NEXTTRACE_CHUNZHENURL", "http://127.0.0.1:2060") + "?ip=" + ip
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: timeout,
|
||||
}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
content, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Println("纯真 请求超时(2s),请切换其他API使用")
|
||||
return &IPGeoData{}, err
|
||||
}
|
||||
body, _ := io.ReadAll(content.Body)
|
||||
|
||||
var data map[string]interface{}
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return &IPGeoData{}, err
|
||||
}
|
||||
city := data[ip].(map[string]interface{})["area"].(string)
|
||||
region := data[ip].(map[string]interface{})["country"].(string)
|
||||
var asn string
|
||||
if data[ip].(map[string]interface{})["asn"] != nil {
|
||||
asn = data[ip].(map[string]interface{})["asn"].(string)
|
||||
}
|
||||
// 判断是否前两个字为香港或台湾
|
||||
var country string
|
||||
provinces := []string{
|
||||
"北京",
|
||||
"天津",
|
||||
"河北",
|
||||
"山西",
|
||||
"内蒙古",
|
||||
"辽宁",
|
||||
"吉林",
|
||||
"黑龙江",
|
||||
"上海",
|
||||
"江苏",
|
||||
"浙江",
|
||||
"安徽",
|
||||
"福建",
|
||||
"江西",
|
||||
"山东",
|
||||
"河南",
|
||||
"湖北",
|
||||
"湖南",
|
||||
"广东",
|
||||
"广西",
|
||||
"海南",
|
||||
"重庆",
|
||||
"四川",
|
||||
"贵州",
|
||||
"云南",
|
||||
"西藏",
|
||||
"陕西",
|
||||
"甘肃",
|
||||
"青海",
|
||||
"宁夏",
|
||||
"新疆",
|
||||
"台湾",
|
||||
"香港",
|
||||
"澳门",
|
||||
}
|
||||
for _, province := range provinces {
|
||||
if strings.Contains(region, province) {
|
||||
country = "中国"
|
||||
city = region + city
|
||||
break
|
||||
}
|
||||
}
|
||||
if country == "" {
|
||||
country = region
|
||||
}
|
||||
return &IPGeoData{
|
||||
Asnumber: asn,
|
||||
Country: country,
|
||||
City: city,
|
||||
}, nil
|
||||
}
|
||||
63
ipgeo/dn42.go
Normal file
63
ipgeo/dn42.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/dn42"
|
||||
)
|
||||
|
||||
func LtdCodeToCountryOrAreaName(Code string) string {
|
||||
countryName := []string{"United States", "Afghanistan", "Åland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain", " Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory", "Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada ", "Cape Verde", "Cayman Islands", "Central Africa", "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo (Brazzaville)", "DRC", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", " Denmark", "Djibouti", "Dominica", "Dominica", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", " French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Vatican", "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "British Isles of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "North Korea", "South Korea", " Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "FYROM", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", " Marshall Islands", "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia (Federated States of)", "Moldova", "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Mariana", "Norway", "Oman", "Pakistan", "Palau", "Palestine", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", "Rwanda", "St. Helena", "St. Kitts and Nevis", "St. Lucia", "St. Pierre and Miquelon", "St. Vincent and the Grenadines", "Samoa", "San Marino", "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", "South Georgia and South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen Islands", "Swaziland", "Sweden ", "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "Timor-Leste", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", " United Kingdom", "U.S. Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", "Vietnam", "British Virgin Islands", "U.S. Virgin Islands", "Wallis and Futuna", "Western Sahara", "Yemen", "Zambia", "Zimbabwe"}
|
||||
countryCode := []string{"us", "af", "ax", "al", "dz", "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "au", "at", "az", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm", "bt", "bo", "ba", "bw", "bv", "br", "io", "bn", "bg", "bf", "bi", "kh", "cm", "ca", "cv", "ky", "cf", "td", "cl", "cn", "cx", "cc", "co", "km", "cg", "cd", "ck", "cr", "ci", "hr", "cu", "cy", "cz", "dk", "dj", "dm", "do", "ec", "eg", "sv", "gq", "er", "ee", "et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm", "ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gg", "gn", "gw", "gy", "ht", "hm", "va", "hn", "hk", "hu", "is", "in", "id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo", "kz", "ke", "ki", "kp", "kr", "kw", "kg", "la", "lv", "lb", "ls", "lr", "ly", "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv", "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc", "mn", "me", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "an", "nc", "nz", "ni", "ne", "ng", "nu", "nf", "mp", "no", "om", "pk", "pw", "ps", "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re", "ro", "ru", "rw", "sh", "kn", "lc", "pm", "vc", "ws", "sm", "st", "sa", "sn", "rs", "sc", "sl", "sg", "sk", "si", "sb", "so", "za", "gs", "es", "lk", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj", "tz", "th", "tl", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", "ug", "ua", "ae", "gb", "um", "uy", "uz", "vu", "ve", "vn", "vg", "vi", "wf", "eh", "ye", "zm", "zw"}
|
||||
Code = strings.ToLower(Code)
|
||||
for i, v := range countryCode {
|
||||
if strings.Contains(Code, v) {
|
||||
return countryName[i]
|
||||
}
|
||||
}
|
||||
return Code
|
||||
}
|
||||
|
||||
func DN42(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
data := &IPGeoData{}
|
||||
// 先解析传入过来的数据
|
||||
ipTmp := strings.Split(ip, ",")
|
||||
if len(ipTmp) > 1 {
|
||||
ip = ipTmp[0]
|
||||
}
|
||||
// 先查找 GeoFeed
|
||||
if geo, find := dn42.GetGeoFeed(ip); find {
|
||||
data.Country = geo.LtdCode
|
||||
data.City = geo.City
|
||||
data.Asnumber = geo.ASN
|
||||
data.Whois = geo.IPWhois
|
||||
}
|
||||
// 如果没找到,查找 PTR
|
||||
if len(ipTmp) > 1 {
|
||||
// 存在 PTR 记录
|
||||
if res, err := dn42.FindPtrRecord(ipTmp[1]); err == nil && res.LtdCode != "" {
|
||||
data.Country = res.LtdCode
|
||||
data.Prov = res.Region
|
||||
data.City = res.City
|
||||
}
|
||||
}
|
||||
|
||||
data.Country = LtdCodeToCountryOrAreaName(data.Country)
|
||||
|
||||
switch data.Country {
|
||||
case "Hong Kong":
|
||||
data.Country = "China"
|
||||
data.Prov = "Hong Kong"
|
||||
case "Taiwan":
|
||||
data.Country = "China"
|
||||
data.Prov = "Taiwan"
|
||||
case "Macao":
|
||||
data.Country = "China"
|
||||
data.Prov = "Macao"
|
||||
case "":
|
||||
data.Country = "Unknown"
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
5
ipgeo/dn42_test.go
Normal file
5
ipgeo/dn42_test.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package ipgeo
|
||||
|
||||
// func TestDN42(t *testing.T) {
|
||||
// DN42("")
|
||||
// }
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/lionsoul2014/ip2region/v1.0/binding/golang/ip2region"
|
||||
)
|
||||
@@ -22,21 +23,31 @@ func downloadDataBase() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
// Create the file
|
||||
out, err := os.Create(ipDataBasePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
defer func(out *os.File) {
|
||||
err := out.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}(out)
|
||||
|
||||
// Write the body to file
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func IP2Region(ip string) (*IPGeoData, error) {
|
||||
func IP2Region(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
if _, err := os.Stat(ipDataBasePath); os.IsNotExist(err) {
|
||||
if err = downloadDataBase(); err != nil {
|
||||
panic("Download Failed!")
|
||||
|
||||
@@ -2,20 +2,22 @@ package ipgeo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func IPApiCom(ip string) (*IPGeoData, error) {
|
||||
url := "http://ip-api.com/json/" + ip + "?fields=status,message,country,regionName,city,isp,as"
|
||||
func IPApiCom(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
url := "http://ip-api.com/json/" + ip + "?fields=status,message,country,regionName,city,isp,district,as,lat,lon"
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: 2 * time.Second,
|
||||
Timeout: timeout,
|
||||
}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0")
|
||||
@@ -24,7 +26,7 @@ func IPApiCom(ip string) (*IPGeoData, error) {
|
||||
log.Println("ip-api.com 请求超时(2s),请切换其他API使用")
|
||||
return nil, err
|
||||
}
|
||||
body, _ := ioutil.ReadAll(content.Body)
|
||||
body, _ := io.ReadAll(content.Body)
|
||||
res := gjson.ParseBytes(body)
|
||||
|
||||
if res.Get("status").String() != "success" {
|
||||
@@ -32,12 +34,27 @@ func IPApiCom(ip string) (*IPGeoData, error) {
|
||||
}
|
||||
|
||||
re := regexp.MustCompile("[0-9]+")
|
||||
var country = res.Get("country").String()
|
||||
var prov = res.Get("region").String()
|
||||
var city = res.Get("city").String()
|
||||
var district = res.Get("district").String()
|
||||
if util.StringInSlice(country, []string{"Hong Kong", "Taiwan", "Macao"}) {
|
||||
district = prov + " " + city + " " + district
|
||||
city = country
|
||||
prov = ""
|
||||
country = "China"
|
||||
}
|
||||
lat, _ := strconv.ParseFloat(res.Get("lat").String(), 32)
|
||||
lng, _ := strconv.ParseFloat(res.Get("lon").String(), 32)
|
||||
|
||||
return &IPGeoData{
|
||||
Asnumber: re.FindString(res.Get("as").String()),
|
||||
Country: res.Get("country").String(),
|
||||
City: res.Get("city").String(),
|
||||
Prov: res.Get("regionName").String(),
|
||||
Country: country,
|
||||
City: city,
|
||||
Prov: prov,
|
||||
District: district,
|
||||
Owner: res.Get("isp").String(),
|
||||
Lat: lat,
|
||||
Lng: lng,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -13,23 +13,47 @@ func cidrRangeContains(cidrRange string, checkIP string) bool {
|
||||
return ipNet.Contains(secondIP)
|
||||
}
|
||||
|
||||
// 被选到的返回 geodata, true 否则返回 nil, false
|
||||
// Filter 被选到的返回 geodata, true 否则返回 nil, false
|
||||
func Filter(ip string) (*IPGeoData, bool) {
|
||||
//geodata := &IPGeoData{}
|
||||
asn := ""
|
||||
whois := ""
|
||||
isFiltered := false
|
||||
switch {
|
||||
//rfc1918
|
||||
case net.ParseIP(ip).IsPrivate():
|
||||
case cidrRangeContains("0.0.0.0/8", ip):
|
||||
asn = ""
|
||||
whois = "RFC1918"
|
||||
whois = "RFC1122"
|
||||
isFiltered = true
|
||||
//IANA Reserved Address Space
|
||||
case cidrRangeContains("100.64.0.0/10", ip):
|
||||
asn = ""
|
||||
whois = "RFC6598"
|
||||
isFiltered = true
|
||||
//127.0.0.0/8
|
||||
case cidrRangeContains("127.0.0.0/8", ip):
|
||||
asn = ""
|
||||
whois = "RFC1122"
|
||||
isFiltered = true
|
||||
//169.254.0.0/16
|
||||
case cidrRangeContains("169.254.0.0/16", ip):
|
||||
asn = ""
|
||||
whois = "RFC3927"
|
||||
isFiltered = true
|
||||
//192.0.0.0/24
|
||||
case cidrRangeContains("192.0.0.0/24", ip):
|
||||
asn = ""
|
||||
whois = "RFC6890"
|
||||
isFiltered = true
|
||||
//192.0.2.0/24
|
||||
case cidrRangeContains("192.0.2.0/24", ip):
|
||||
asn = ""
|
||||
whois = "RFC5737"
|
||||
isFiltered = true
|
||||
//192.88.99.0/24
|
||||
case cidrRangeContains("192.88.99.0/24", ip):
|
||||
asn = ""
|
||||
whois = "RFC3068"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("198.18.0.0/15", ip):
|
||||
asn = ""
|
||||
whois = "RFC2544"
|
||||
@@ -40,10 +64,68 @@ func Filter(ip string) (*IPGeoData, bool) {
|
||||
asn = ""
|
||||
whois = "RFC5737"
|
||||
isFiltered = true
|
||||
//224.0.0.0/4
|
||||
case cidrRangeContains("224.0.0.0/4", ip):
|
||||
asn = ""
|
||||
whois = "RFC5771"
|
||||
isFiltered = true
|
||||
//255.255.255.255/32
|
||||
case cidrRangeContains("255.255.255.255/32", ip):
|
||||
asn = ""
|
||||
whois = "RFC0919"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("240.0.0.0/4", ip):
|
||||
asn = ""
|
||||
whois = "RFC1112"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("fe80::/10", ip):
|
||||
asn = ""
|
||||
whois = "RFC4291"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("ff00::/8", ip):
|
||||
asn = ""
|
||||
whois = "RFC4291"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("fec0::/10", ip):
|
||||
asn = ""
|
||||
whois = "RFC3879"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("fe00::/9", ip):
|
||||
asn = ""
|
||||
whois = "RFC4291"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("64:ff9b::/96", ip):
|
||||
asn = ""
|
||||
whois = "RFC6052"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("0::/96", ip):
|
||||
asn = ""
|
||||
whois = "RFC4291"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("64:ff9b:1::/48", ip):
|
||||
asn = ""
|
||||
whois = "RFC6052"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("2001:db8::/32", ip):
|
||||
asn = ""
|
||||
whois = "RFC3849"
|
||||
isFiltered = true
|
||||
case cidrRangeContains("2002::/16", ip):
|
||||
asn = ""
|
||||
whois = "RFC3056"
|
||||
isFiltered = true
|
||||
case net.ParseIP(ip).IsPrivate():
|
||||
//rfc4193
|
||||
if cidrRangeContains("fc00::/7", ip) {
|
||||
asn = ""
|
||||
whois = "RFC4193"
|
||||
isFiltered = true
|
||||
//rfc1918
|
||||
} else {
|
||||
asn = ""
|
||||
whois = "RFC1918"
|
||||
isFiltered = true
|
||||
}
|
||||
//Defense Information System Network
|
||||
case cidrRangeContains("6.0.0.0/8", ip):
|
||||
fallthrough
|
||||
@@ -75,6 +157,12 @@ func Filter(ip string) (*IPGeoData, bool) {
|
||||
isFiltered = true
|
||||
default:
|
||||
}
|
||||
// 判断是否为v6 且不在2000::/3
|
||||
if net.ParseIP(ip).To4() == nil && !cidrRangeContains("2000::/3", ip) && !isFiltered {
|
||||
asn = ""
|
||||
whois = "INVALID"
|
||||
isFiltered = true
|
||||
}
|
||||
if !isFiltered {
|
||||
return nil, false
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,7 @@ package ipgeo
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IPGeoData struct {
|
||||
@@ -25,10 +26,12 @@ type IPGeoData struct {
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type Source = func(ip string) (*IPGeoData, error)
|
||||
type Source = func(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeoData, error)
|
||||
|
||||
func GetSource(s string) Source {
|
||||
switch strings.ToUpper(s) {
|
||||
case "DN42":
|
||||
return DN42
|
||||
case "LEOMOEAPI":
|
||||
return LeoIP
|
||||
case "IP.SB":
|
||||
@@ -37,11 +40,23 @@ func GetSource(s string) Source {
|
||||
return IPInSight
|
||||
case "IPAPI.COM":
|
||||
return IPApiCom
|
||||
case "IP-API.COM":
|
||||
return IPApiCom
|
||||
case "IPINFO":
|
||||
return IPInfo
|
||||
case "IP2REGION":
|
||||
return IP2Region
|
||||
case "IPINFOLOCAL":
|
||||
return IPInfoLocal
|
||||
case "CHUNZHEN":
|
||||
return Chunzhen
|
||||
case "DISABLE-GEOIP":
|
||||
return disableGeoIP
|
||||
default:
|
||||
return LeoIP
|
||||
}
|
||||
}
|
||||
|
||||
func disableGeoIP(string, time.Duration, string, bool) (*IPGeoData, error) {
|
||||
return &IPGeoData{}, nil
|
||||
}
|
||||
|
||||
@@ -37,6 +37,13 @@ func TestXxx(t *testing.T) {
|
||||
log.Println(process_id, ttl_r)
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
res, err := Filter("fd11::1")
|
||||
//打印whois信息
|
||||
fmt.Println(res.Whois)
|
||||
print(err)
|
||||
}
|
||||
|
||||
func reverseID(id string) (int64, int64, error) {
|
||||
ttl, _ := strconv.ParseInt(id[9:15], 2, 32)
|
||||
//process ID
|
||||
|
||||
325
ipgeo/ipinfo.go
325
ipgeo/ipinfo.go
@@ -1,36 +1,339 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func IPInfo(ip string) (*IPGeoData, error) {
|
||||
resp, err := http.Get("https://ipinfo.io/" + ip + "?token=" + token.ipinfo)
|
||||
func IPInfo(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
url := "http://ipinfo.io/" + ip + "?token=" + token.ipinfo
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Get(url)
|
||||
//resp, err := http.Get("https://ipinfo.io/" + ip + "?token=" + token.ipinfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := gjson.ParseBytes(body)
|
||||
|
||||
var country string
|
||||
|
||||
if res.Get("country").String() == "HK" || res.Get("country").String() == "TW" {
|
||||
// ISO-3166 转换
|
||||
var countryMap = map[string]string{
|
||||
"AF": "Afghanistan",
|
||||
"AX": "Åland Islands",
|
||||
"AL": "Albania",
|
||||
"DZ": "Algeria",
|
||||
"AS": "American Samoa",
|
||||
"AD": "Andorra",
|
||||
"AO": "Angola",
|
||||
"AI": "Anguilla",
|
||||
"AQ": "Antarctica",
|
||||
"AG": "Antigua and Barbuda",
|
||||
"AR": "Argentina",
|
||||
"AM": "Armenia",
|
||||
"AW": "Aruba",
|
||||
"AU": "Australia",
|
||||
"AT": "Austria",
|
||||
"AZ": "Azerbaijan",
|
||||
"BH": "Bahrain",
|
||||
"BS": "Bahamas",
|
||||
"BD": "Bangladesh",
|
||||
"BB": "Barbados",
|
||||
"BY": "Belarus",
|
||||
"BE": "Belgium",
|
||||
"BZ": "Belize",
|
||||
"BJ": "Benin",
|
||||
"BM": "Bermuda",
|
||||
"BT": "Bhutan",
|
||||
"BO": "Bolivia",
|
||||
"BQ": "Bonaire",
|
||||
"BA": "Bosnia and Herzegovina",
|
||||
"BW": "Botswana",
|
||||
"BV": "Bouvet Island",
|
||||
"BR": "Brazil",
|
||||
"IO": "British Indian Ocean Territory",
|
||||
"BN": "Brunei Darussalam",
|
||||
"BG": "Bulgaria",
|
||||
"BF": "Burkina Faso",
|
||||
"BI": "Burundi",
|
||||
"KH": "Cambodia",
|
||||
"CM": "Cameroon",
|
||||
"CA": "Canada",
|
||||
"CV": "Cape Verde",
|
||||
"KY": "Cayman Islands",
|
||||
"CF": "Central African Republic",
|
||||
"TD": "Chad",
|
||||
"CL": "Chile",
|
||||
"CN": "China",
|
||||
"CX": "Christmas Island",
|
||||
"CC": "Cocos (Keeling) Islands",
|
||||
"CO": "Colombia",
|
||||
"KM": "Comoros",
|
||||
"CG": "Congo",
|
||||
"CD": "Congo",
|
||||
"CK": "Cook Islands",
|
||||
"CR": "Costa Rica",
|
||||
"CI": "Côte d'Ivoire",
|
||||
"HR": "Croatia",
|
||||
"CU": "Cuba",
|
||||
"CW": "Curaçao",
|
||||
"CY": "Cyprus",
|
||||
"CZ": "Czech Republic",
|
||||
"DK": "Denmark",
|
||||
"DJ": "Djibouti",
|
||||
"DM": "Dominica",
|
||||
"DO": "Dominican Republic",
|
||||
"EC": "Ecuador",
|
||||
"EG": "Egypt",
|
||||
"SV": "El Salvador",
|
||||
"GQ": "Equatorial Guinea",
|
||||
"ER": "Eritrea",
|
||||
"EE": "Estonia",
|
||||
"ET": "Ethiopia",
|
||||
"FK": "Falkland Islands (Malvinas)",
|
||||
"FO": "Faroe Islands",
|
||||
"FJ": "Fiji",
|
||||
"FI": "Finland",
|
||||
"FR": "France",
|
||||
"GF": "French Guiana",
|
||||
"PF": "French Polynesia",
|
||||
"TF": "French Southern Territories",
|
||||
"GA": "Gabon",
|
||||
"GM": "Gambia",
|
||||
"GE": "Georgia",
|
||||
"DE": "Germany",
|
||||
"GH": "Ghana",
|
||||
"GI": "Gibraltar",
|
||||
"GR": "Greece",
|
||||
"GL": "Greenland",
|
||||
"GD": "Grenada",
|
||||
"GP": "Guadeloupe",
|
||||
"GU": "Guam",
|
||||
"GT": "Guatemala",
|
||||
"GG": "Guernsey",
|
||||
"GN": "Guinea",
|
||||
"GW": "Guinea-Bissau",
|
||||
"GY": "Guyana",
|
||||
"HT": "Haiti",
|
||||
"HM": "Heard Island and McDonald Islands",
|
||||
"VA": "Holy See (Vatican City State)",
|
||||
"HN": "Honduras",
|
||||
"HK": "Hong Kong",
|
||||
"HU": "Hungary",
|
||||
"IS": "Iceland",
|
||||
"IN": "India",
|
||||
"ID": "Indonesia",
|
||||
"IR": "Iran",
|
||||
"IQ": "Iraq",
|
||||
"IE": "Ireland",
|
||||
"IM": "Isle of Man",
|
||||
"IL": "Israel",
|
||||
"IT": "Italy",
|
||||
"JM": "Jamaica",
|
||||
"JP": "Japan",
|
||||
"JE": "Jersey",
|
||||
"JO": "Jordan",
|
||||
"KZ": "Kazakhstan",
|
||||
"KE": "Kenya",
|
||||
"KI": "Kiribati",
|
||||
"KP": "Korea",
|
||||
"KR": "Korea",
|
||||
"KW": "Kuwait",
|
||||
"KG": "Kyrgyzstan",
|
||||
"LA": "Lao People's Democratic Republic",
|
||||
"LV": "Latvia",
|
||||
"LB": "Lebanon",
|
||||
"LS": "Lesotho",
|
||||
"LR": "Liberia",
|
||||
"LY": "Libya",
|
||||
"LI": "Liechtenstein",
|
||||
"LT": "Lithuania",
|
||||
"LU": "Luxembourg",
|
||||
"MO": "Macao",
|
||||
"MK": "Macedonia",
|
||||
"MG": "Madagascar",
|
||||
"MW": "Malawi",
|
||||
"MY": "Malaysia",
|
||||
"MV": "Maldives",
|
||||
"ML": "Mali",
|
||||
"MT": "Malta",
|
||||
"MH": "Marshall Islands",
|
||||
"MQ": "Martinique",
|
||||
"MR": "Mauritania",
|
||||
"MU": "Mauritius",
|
||||
"YT": "Mayotte",
|
||||
"MX": "Mexico",
|
||||
"FM": "Micronesia",
|
||||
"MD": "Moldova",
|
||||
"MC": "Monaco",
|
||||
"MN": "Mongolia",
|
||||
"ME": "Montenegro",
|
||||
"MS": "Montserrat",
|
||||
"MA": "Morocco",
|
||||
"MZ": "Mozambique",
|
||||
"MM": "Myanmar",
|
||||
"NA": "Namibia",
|
||||
"NR": "Nauru",
|
||||
"NP": "Nepal",
|
||||
"NL": "Netherlands",
|
||||
"NC": "New Caledonia",
|
||||
"NZ": "New Zealand",
|
||||
"NI": "Nicaragua",
|
||||
"NE": "Niger",
|
||||
"NG": "Nigeria",
|
||||
"NU": "Niue",
|
||||
"NF": "Norfolk Island",
|
||||
"MP": "Northern Mariana Islands",
|
||||
"NO": "Norway",
|
||||
"OM": "Oman",
|
||||
"PK": "Pakistan",
|
||||
"PW": "Palau",
|
||||
"PS": "Palestine",
|
||||
"PA": "Panama",
|
||||
"PG": "Papua New Guinea",
|
||||
"PY": "Paraguay",
|
||||
"PE": "Peru",
|
||||
"PH": "Philippines",
|
||||
"PN": "Pitcairn",
|
||||
"PL": "Poland",
|
||||
"PT": "Portugal",
|
||||
"PR": "Puerto Rico",
|
||||
"QA": "Qatar",
|
||||
"RE": "Réunion",
|
||||
"RO": "Romania",
|
||||
"RU": "Russian Federation",
|
||||
"RW": "Rwanda",
|
||||
"BL": "Saint Barthélemy",
|
||||
"SH": "Saint Helena",
|
||||
"KN": "Saint Kitts and Nevis",
|
||||
"LC": "Saint Lucia",
|
||||
"MF": "Saint Martin (French part)",
|
||||
"PM": "Saint Pierre and Miquelon",
|
||||
"VC": "Saint Vincent and the Grenadines",
|
||||
"WS": "Samoa",
|
||||
"SM": "San Marino",
|
||||
"ST": "Sao Tome and Principe",
|
||||
"SA": "Saudi Arabia",
|
||||
"SN": "Senegal",
|
||||
"RS": "Serbia",
|
||||
"SC": "Seychelles",
|
||||
"SL": "Sierra Leone",
|
||||
"SG": "Singapore",
|
||||
"SX": "Sint Maarten (Dutch part)",
|
||||
"SK": "Slovakia",
|
||||
"SI": "Slovenia",
|
||||
"SB": "Solomon Islands",
|
||||
"SO": "Somalia",
|
||||
"ZA": "South Africa",
|
||||
"GS": "South Georgia and the South Sandwich Islands",
|
||||
"SS": "South Sudan",
|
||||
"ES": "Spain",
|
||||
"LK": "Sri Lanka",
|
||||
"SD": "Sudan",
|
||||
"SR": "Suriname",
|
||||
"SJ": "Svalbard and Jan Mayen",
|
||||
"SZ": "Swaziland",
|
||||
"SE": "Sweden",
|
||||
"CH": "Switzerland",
|
||||
"SY": "Syrian Arab Republic",
|
||||
"TW": "Taiwan",
|
||||
"TJ": "Tajikistan",
|
||||
"TZ": "Tanzania",
|
||||
"TH": "Thailand",
|
||||
"TL": "Timor-Leste",
|
||||
"TG": "Togo",
|
||||
"TK": "Tokelau",
|
||||
"TO": "Tonga",
|
||||
"TT": "Trinidad and Tobago",
|
||||
"TN": "Tunisia",
|
||||
"TR": "Turkey",
|
||||
"TM": "Turkmenistan",
|
||||
"TC": "Turks and Caicos Islands",
|
||||
"TV": "Tuvalu",
|
||||
"UG": "Uganda",
|
||||
"UA": "Ukraine",
|
||||
"AE": "United Arab Emirates",
|
||||
"GB": "United Kingdom",
|
||||
"US": "United States of America",
|
||||
"UM": "United States Minor Outlying Islands",
|
||||
"UY": "Uruguay",
|
||||
"UZ": "Uzbekistan",
|
||||
"VU": "Vanuatu",
|
||||
"VE": "Venezuela",
|
||||
"VN": "Viet Nam",
|
||||
"VG": "Virgin Islands",
|
||||
"VI": "Virgin Islands",
|
||||
"WF": "Wallis and Futuna",
|
||||
"EH": "Western Sahara",
|
||||
"YE": "Yemen",
|
||||
"ZM": "Zambia",
|
||||
"ZW": "Zimbabwe",
|
||||
}
|
||||
var country = res.Get("country").String()
|
||||
var prov = res.Get("region").String()
|
||||
var city = res.Get("city").String()
|
||||
var district = ""
|
||||
if util.StringInSlice(country, []string{"TW", "MO", "HK"}) {
|
||||
district = prov + " " + city
|
||||
city = countryMap[country]
|
||||
prov = ""
|
||||
country = "CN"
|
||||
}
|
||||
country = countryMap[country]
|
||||
|
||||
var anycast = false
|
||||
if res.Get("anycast").String() == "true" {
|
||||
country = "ANYCAST"
|
||||
prov = "ANYCAST"
|
||||
city = ""
|
||||
anycast = true
|
||||
}
|
||||
|
||||
i := strings.Index(res.Get("org").String(), " ")
|
||||
var owner string
|
||||
if i == -1 {
|
||||
owner = ""
|
||||
} else {
|
||||
owner = res.Get("org").String()[i:]
|
||||
}
|
||||
|
||||
var asnumber = ""
|
||||
// 有时候不返回asn或其本身没有asn
|
||||
if strings.HasPrefix(res.Get("org").String(), "AS") {
|
||||
asnumber = strings.Fields(strings.TrimPrefix(res.Get("org").String(), "AS"))[0]
|
||||
}
|
||||
|
||||
//"loc": "34.0522,-118.2437",
|
||||
var lat, lng float64
|
||||
if res.Get("loc").String() != "" {
|
||||
lat, _ = strconv.ParseFloat(strings.Split(res.Get("loc").String(), ",")[0], 32)
|
||||
lng, _ = strconv.ParseFloat(strings.Split(res.Get("loc").String(), ",")[1], 32)
|
||||
}
|
||||
if anycast {
|
||||
lat, lng = 0, 0
|
||||
}
|
||||
|
||||
return &IPGeoData{
|
||||
Asnumber: res.Get("asn").Get("asn").String(),
|
||||
Asnumber: asnumber,
|
||||
Country: country,
|
||||
City: res.Get("city").String(),
|
||||
Prov: res.Get("region").String(),
|
||||
Owner: res.Get("asn").Get("domain").String(),
|
||||
City: city,
|
||||
Prov: prov,
|
||||
District: district,
|
||||
Owner: owner,
|
||||
Lat: lat,
|
||||
Lng: lng,
|
||||
}, nil
|
||||
}
|
||||
|
||||
53
ipgeo/ipinfoLocal.go
Normal file
53
ipgeo/ipinfoLocal.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ipinfoDataBasePath = "./ipinfoLocal.mmdb"
|
||||
)
|
||||
|
||||
func IPInfoLocal(ip string, _ time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
if _, err := os.Stat(ipinfoDataBasePath); os.IsNotExist(err) {
|
||||
panic("Cannot find ipinfoLocal.mmdb")
|
||||
}
|
||||
region, err := maxminddb.Open(ipinfoDataBasePath)
|
||||
if err != nil {
|
||||
panic("Cannot find ipinfoLocal.mmdb")
|
||||
}
|
||||
defer func(region *maxminddb.Reader) {
|
||||
err := region.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}(region)
|
||||
var record interface{}
|
||||
searchErr := region.Lookup(net.ParseIP(ip), &record)
|
||||
if searchErr != nil {
|
||||
return &IPGeoData{}, errors.New("no results")
|
||||
}
|
||||
recordMap := record.(map[string]interface{})
|
||||
country_name := recordMap["country_name"].(string)
|
||||
prov := ""
|
||||
if recordMap["country"].(string) == "HK" {
|
||||
country_name = "China"
|
||||
prov = "Hong Kong"
|
||||
}
|
||||
if recordMap["country"].(string) == "TW" {
|
||||
country_name = "China"
|
||||
prov = "Taiwan"
|
||||
}
|
||||
return &IPGeoData{
|
||||
Asnumber: strings.TrimPrefix(recordMap["asn"].(string), "AS"),
|
||||
Country: country_name,
|
||||
City: "",
|
||||
Prov: prov,
|
||||
Owner: recordMap["as_name"].(string),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,18 +1,23 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func IPInSight(ip string) (*IPGeoData, error) {
|
||||
resp, err := http.Get("https://ipinsight.io/query?ip=" + ip)
|
||||
func IPInSight(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Get("https://api.ipinsight.io/ip/" + ip + "?token=" + token.ipinsight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func IPSB(ip string) (*IPGeoData, error) {
|
||||
func IPSB(ip string, timeout time.Duration, _ string, _ bool) (*IPGeoData, error) {
|
||||
url := "https://api.ip.sb/geoip/" + ip
|
||||
client := &http.Client{
|
||||
// 2 秒超时
|
||||
Timeout: 2 * time.Second,
|
||||
Timeout: timeout,
|
||||
}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
// 设置 UA,ip.sb 默认禁止 go-client User-Agent 的 api 请求
|
||||
@@ -24,7 +24,7 @@ func IPSB(ip string) (*IPGeoData, error) {
|
||||
log.Println("api.ip.sb 请求超时(2s),请切换其他API使用")
|
||||
return nil, err
|
||||
}
|
||||
body, _ := ioutil.ReadAll(content.Body)
|
||||
body, _ := io.ReadAll(content.Body)
|
||||
res := gjson.ParseBytes(body)
|
||||
|
||||
if res.Get("country").String() == "" {
|
||||
|
||||
28
ipgeo/leo.go
28
ipgeo/leo.go
@@ -7,8 +7,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/wshandle"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/xgadget-lab/nexttrace/wshandle"
|
||||
)
|
||||
|
||||
/***
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
* 运作模型可以理解为一个 Node 一直在等待数据,当获得一个新的任务后,转交给下一个协程,不再关注这个 Node 的下一步处理过程,并且回到空闲状态继续等待新的任务
|
||||
***/
|
||||
|
||||
// IP 查询池 map - ip - ip channel
|
||||
// IPPool IP 查询池 map - ip - ip channel
|
||||
type IPPool struct {
|
||||
pool map[string]chan IPGeoData
|
||||
poolMux sync.Mutex
|
||||
@@ -48,14 +48,17 @@ func receiveParse() {
|
||||
// json解析 -> data
|
||||
res := gjson.Parse(data)
|
||||
// 根据返回的IP信息,发送给对应等待回复的IP通道上
|
||||
var domain string = res.Get("domain").String()
|
||||
var domain = res.Get("domain").String()
|
||||
|
||||
if res.Get("domain").String() == "" {
|
||||
domain = res.Get("owner").String()
|
||||
}
|
||||
|
||||
m := make(map[string][]string)
|
||||
json.Unmarshal([]byte(res.Get("router").String()), &m)
|
||||
err := json.Unmarshal([]byte(res.Get("router").String()), &m)
|
||||
if err != nil {
|
||||
// 此处是正常的,因为有些IP没有路由信息
|
||||
}
|
||||
|
||||
lat, _ := strconv.ParseFloat(res.Get("lat").String(), 32)
|
||||
lng, _ := strconv.ParseFloat(res.Get("lng").String(), 32)
|
||||
@@ -80,15 +83,20 @@ func receiveParse() {
|
||||
}
|
||||
}
|
||||
|
||||
func LeoIP(ip string) (*IPGeoData, error) {
|
||||
// 初始化通道 - 向池子里添加IP的Channel,返回IP数据是通过字典中对应键为IP的Channel来获取的
|
||||
func LeoIP(ip string, timeout time.Duration, lang string, maptrace bool) (*IPGeoData, error) {
|
||||
// TODO: 根据lang的值请求中文/英文API
|
||||
// TODO: 根据maptrace的值决定是否请求经纬度信息
|
||||
if timeout < 5*time.Second {
|
||||
timeout = 5 * time.Second
|
||||
}
|
||||
|
||||
// 缓存中没有找到IP信息,需要请求API获取
|
||||
IPPools.poolMux.Lock()
|
||||
defer IPPools.poolMux.Unlock()
|
||||
// 如果之前已经被别的协程初始化过了就不用初始化了
|
||||
if IPPools.pool[ip] == nil {
|
||||
IPPools.pool[ip] = make(chan IPGeoData)
|
||||
}
|
||||
|
||||
IPPools.poolMux.Unlock()
|
||||
// 发送请求
|
||||
sendIPRequest(ip)
|
||||
// 同步开启监听
|
||||
@@ -99,10 +107,8 @@ func LeoIP(ip string) (*IPGeoData, error) {
|
||||
case res := <-IPPools.pool[ip]:
|
||||
return &res, nil
|
||||
// 5秒后依旧没有接收到返回的IP数据,不再等待,超时异常处理
|
||||
case <-time.After(5 * time.Second):
|
||||
// default:
|
||||
case <-time.After(timeout):
|
||||
// 这里不可以返回一个 nil,否则在访问对象内部的键值的时候会报空指针的 Fatal Error
|
||||
return &IPGeoData{}, errors.New("TimeOut")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ipgeo
|
||||
|
||||
import "github.com/nxtrace/NTrace-core/util"
|
||||
|
||||
type tokenData struct {
|
||||
ipinsight string
|
||||
ipinfo string
|
||||
@@ -7,7 +9,7 @@ type tokenData struct {
|
||||
}
|
||||
|
||||
var token = tokenData{
|
||||
ipinsight: "",
|
||||
ipinfo: "",
|
||||
ipinsight: util.GetenvDefault("NEXTTRACE_IPINSIGHT_TOKEN", ""),
|
||||
ipinfo: util.GetenvDefault("NEXTTRACE_IPINFO_TOKEN", ""),
|
||||
ipleo: "NextTraceDemo",
|
||||
}
|
||||
|
||||
2
main.go
2
main.go
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/xgadget-lab/nexttrace/cmd"
|
||||
"github.com/nxtrace/NTrace-core/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
2
nt_config.yaml
Normal file
2
nt_config.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
geofeedpath: ./geofeed.csv
|
||||
ptrpath: ./ptr.csv
|
||||
@@ -93,12 +93,7 @@ checkWgetPackage() {
|
||||
downloadBinrayFile() {
|
||||
echo -e "${Info} 获取最新版的 NextTrace 发行版文件信息"
|
||||
# 简单说明一下,Github提供了一个API,可以获取最新发行版本的二进制文件下载地址(对应的是browser_download_url),根据刚刚测得的osDistribution、archParam,获取对应的下载地址
|
||||
latestURL=$(curl -s https://api.github.com/repos/sjlleo/nexttrace/releases/latest | grep -i "browser_download_url.*${osDistribution}.*${archParam}" | awk -F '"' '{print $4}')
|
||||
|
||||
if [ "$countryCode" == "CN" ]; then
|
||||
echo -e "${Info} 检测到国内环境,正在使用镜像下载"
|
||||
latestURL="https://ghproxy.com/"$latestURL
|
||||
fi
|
||||
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
|
||||
@@ -130,8 +125,6 @@ checkSystemDistribution
|
||||
checkSystemArch
|
||||
checkWgetPackage
|
||||
|
||||
# Download Procedure
|
||||
getLocation
|
||||
downloadBinrayFile
|
||||
|
||||
# Run Procedure
|
||||
|
||||
45
pow/pow.go
Normal file
45
pow/pow.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package pow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"github.com/tsosunchia/powclient"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
baseURL = "/v3/challenge"
|
||||
)
|
||||
|
||||
func GetToken(fastIp string, host string, port string) (string, error) {
|
||||
getTokenParams := powclient.NewGetTokenParams()
|
||||
u := url.URL{Scheme: "https", Host: fastIp + ":" + port, Path: baseURL}
|
||||
getTokenParams.BaseUrl = u.String()
|
||||
getTokenParams.SNI = host
|
||||
getTokenParams.Host = host
|
||||
getTokenParams.UserAgent = util.UserAgent
|
||||
proxyUrl := util.GetProxy()
|
||||
if proxyUrl != nil {
|
||||
getTokenParams.Proxy = proxyUrl
|
||||
}
|
||||
var (
|
||||
token string
|
||||
err error
|
||||
)
|
||||
// 尝试三次RetToken,如果都失败了,异常退出
|
||||
for i := 0; i < 3; i++ {
|
||||
token, err = powclient.RetToken(getTokenParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
//fmt.Println("GetToken success", token, getTokenParams.UserAgent)
|
||||
return token, nil
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println("RetToken failed 3 times, please try again after a while, exit")
|
||||
os.Exit(1)
|
||||
return "", err
|
||||
}
|
||||
19
pow/pow_test.go
Normal file
19
pow/pow_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package pow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetToken(t *testing.T) {
|
||||
// 计时开始
|
||||
start := time.Now()
|
||||
token, err := GetToken("origin-fallback.nxtrace.org", "origin-fallback.nxtrace.org", "443")
|
||||
// 计时结束
|
||||
end := time.Now()
|
||||
fmt.Println("耗时:", end.Sub(start))
|
||||
fmt.Println("token:", token)
|
||||
assert.NoError(t, err, "GetToken() returned an error")
|
||||
}
|
||||
137
printer/basic.go
137
printer/basic.go
@@ -2,14 +2,17 @@ package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/config"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"net"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var version = "v0.0.0.alpha"
|
||||
var buildDate = ""
|
||||
var commitID = ""
|
||||
var version = config.Version
|
||||
var buildDate = config.BuildDate
|
||||
var commitID = config.CommitID
|
||||
|
||||
func Version() {
|
||||
fmt.Fprintf(color.Output, "%s %s %s %s\n",
|
||||
@@ -21,52 +24,106 @@ 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%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", "· Skywolf.cloud"),
|
||||
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) {
|
||||
func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string, maxHops int, packetSize int) {
|
||||
fmt.Println("IP Geo Data Provider: " + dataOrigin)
|
||||
|
||||
if ip.String() == domain {
|
||||
fmt.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ip.String())
|
||||
if util.EnableHidDstIP == "" {
|
||||
if ip.String() == domain {
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", ip.String(), maxHops, packetSize)
|
||||
} else {
|
||||
fmt.Printf("traceroute to %s (%s), %d hops max, %d bytes packets\n", ip.String(), domain, maxHops, packetSize)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("traceroute to %s (%s), 30 hops max, 32 byte packets\n", ip.String(), domain)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", util.HideIPPart(ip.String()), maxHops, packetSize)
|
||||
}
|
||||
}
|
||||
|
||||
func applyLangSetting(h *trace.Hop) {
|
||||
if len(h.Geo.Country) <= 1 {
|
||||
//打印h.geo
|
||||
if h.Geo.Whois != "" {
|
||||
h.Geo.Country = h.Geo.Whois
|
||||
} else {
|
||||
if h.Geo.Source != "LeoMoeAPI" {
|
||||
h.Geo.Country = "网络故障"
|
||||
h.Geo.CountryEn = "Network Error"
|
||||
} else {
|
||||
h.Geo.Country = "未知"
|
||||
h.Geo.CountryEn = "Unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if h.Lang == "en" {
|
||||
if h.Geo.Country == "Anycast" {
|
||||
|
||||
} else if h.Geo.Prov == "骨干网" {
|
||||
h.Geo.Prov = "BackBone"
|
||||
} else if h.Geo.ProvEn == "" {
|
||||
h.Geo.Country = h.Geo.CountryEn
|
||||
} else {
|
||||
if h.Geo.CityEn == "" {
|
||||
h.Geo.Country = h.Geo.ProvEn
|
||||
h.Geo.Prov = h.Geo.CountryEn
|
||||
h.Geo.City = ""
|
||||
} else {
|
||||
h.Geo.Country = h.Geo.CityEn
|
||||
h.Geo.Prov = h.Geo.ProvEn
|
||||
h.Geo.City = h.Geo.CountryEn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
)
|
||||
|
||||
type HopInfo int
|
||||
|
||||
32
printer/easy.go
Normal file
32
printer/easy.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
)
|
||||
|
||||
func EasyPrinter(res *trace.Result, ttl int) {
|
||||
for i := range res.Hops[ttl] {
|
||||
if res.Hops[ttl][i].Address == nil {
|
||||
fmt.Printf("%d|*||||||\n", ttl+1)
|
||||
continue
|
||||
}
|
||||
applyLangSetting(&res.Hops[ttl][i]) // 应用语言设置
|
||||
fmt.Printf(
|
||||
"%d|%s|%s|%.2f|%s|%s|%s|%s|%s|%s|%.4f|%.4f\n",
|
||||
ttl+1,
|
||||
res.Hops[ttl][i].Address.String(),
|
||||
res.Hops[ttl][i].Hostname,
|
||||
float64(res.Hops[ttl][i].RTT.Microseconds())/1000,
|
||||
res.Hops[ttl][i].Geo.Asnumber,
|
||||
res.Hops[ttl][i].Geo.Country,
|
||||
res.Hops[ttl][i].Geo.Prov,
|
||||
res.Hops[ttl][i].Geo.City,
|
||||
res.Hops[ttl][i].Geo.District,
|
||||
res.Hops[ttl][i].Geo.Owner,
|
||||
res.Hops[ttl][i].Geo.Lat,
|
||||
res.Hops[ttl][i].Geo.Lng,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
)
|
||||
|
||||
// var dataOrigin string
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
// }
|
||||
// }
|
||||
|
||||
//此文件目前仅供classic_printer使用
|
||||
|
||||
const (
|
||||
RED_PREFIX = "\033[1;31m"
|
||||
GREEN_PREFIX = "\033[1;32m"
|
||||
@@ -33,6 +35,7 @@ func HopPrinter(h trace.Hop, info HopInfo) {
|
||||
if h.Address == nil {
|
||||
fmt.Println("\t*")
|
||||
} else {
|
||||
applyLangSetting(&h) // 应用语言设置
|
||||
txt := "\t"
|
||||
|
||||
if h.Hostname == "" {
|
||||
@@ -44,6 +47,9 @@ func HopPrinter(h trace.Hop, info HopInfo) {
|
||||
if h.Geo != nil {
|
||||
txt += " " + formatIpGeoData(h.Address.String(), h.Geo)
|
||||
}
|
||||
for _, v := range h.MPLS {
|
||||
txt += " " + v
|
||||
}
|
||||
switch info {
|
||||
case IXP:
|
||||
fmt.Print(CYAN_PREFIX)
|
||||
|
||||
@@ -2,17 +2,17 @@ package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
)
|
||||
|
||||
func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1))
|
||||
|
||||
// 去重
|
||||
var latestIP string
|
||||
tmpMap := make(map[string][]string)
|
||||
@@ -51,18 +51,35 @@ 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])
|
||||
if res.Hops[ttl][i].Geo.Asnumber != "" {
|
||||
// CMIN2, CUII, CN2, CUG 改为壕金色高亮
|
||||
/*** CMIN2, CUG, CN2, CUII, CTG 改为壕金色高亮
|
||||
/* 小孩子不懂事加着玩的
|
||||
/* 此处的高亮不代表任何线路质量
|
||||
/* 仅代表走了这部分的ASN
|
||||
/* 如果使用这些ASN的IP同样会被高亮
|
||||
***/
|
||||
switch {
|
||||
case res.Hops[ttl][i].Geo.Asnumber == "58807":
|
||||
fallthrough
|
||||
@@ -93,7 +110,13 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
}
|
||||
|
||||
if whoisFormat[0] != "" {
|
||||
whoisFormat[0] = "[" + whoisFormat[0] + "]"
|
||||
//如果以RFC或DOD开头那么为空
|
||||
if !(strings.HasPrefix(whoisFormat[0], "RFC") ||
|
||||
strings.HasPrefix(whoisFormat[0], "DOD")) {
|
||||
whoisFormat[0] = "[" + whoisFormat[0] + "]"
|
||||
} else {
|
||||
whoisFormat[0] = ""
|
||||
}
|
||||
}
|
||||
|
||||
// CMIN2, CUII, CN2, CUG 改为壕金色高亮
|
||||
@@ -121,30 +144,7 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
}
|
||||
}
|
||||
|
||||
if len(res.Hops[ttl][i].Geo.Country) <= 1 {
|
||||
res.Hops[ttl][i].Geo.Country = "局域网"
|
||||
res.Hops[ttl][i].Geo.CountryEn = "LAN Address"
|
||||
}
|
||||
|
||||
if res.Hops[ttl][i].Lang == "en" {
|
||||
if res.Hops[ttl][i].Geo.Country == "Anycast" {
|
||||
|
||||
} else if res.Hops[ttl][i].Geo.Prov == "骨干网" {
|
||||
res.Hops[ttl][i].Geo.Prov = "BackBone"
|
||||
} else if res.Hops[ttl][i].Geo.ProvEn == "" {
|
||||
res.Hops[ttl][i].Geo.Country = res.Hops[ttl][i].Geo.CountryEn
|
||||
} else {
|
||||
if res.Hops[ttl][i].Geo.CityEn == "" {
|
||||
res.Hops[ttl][i].Geo.Country = res.Hops[ttl][i].Geo.ProvEn
|
||||
res.Hops[ttl][i].Geo.Prov = res.Hops[ttl][i].Geo.CountryEn
|
||||
res.Hops[ttl][i].Geo.City = ""
|
||||
} else {
|
||||
res.Hops[ttl][i].Geo.Country = res.Hops[ttl][i].Geo.CityEn
|
||||
res.Hops[ttl][i].Geo.Prov = res.Hops[ttl][i].Geo.ProvEn
|
||||
res.Hops[ttl][i].Geo.City = res.Hops[ttl][i].Geo.CountryEn
|
||||
}
|
||||
}
|
||||
}
|
||||
applyLangSetting(&res.Hops[ttl][i]) // 应用语言设置
|
||||
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
|
||||
@@ -178,6 +178,11 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
)
|
||||
}
|
||||
}
|
||||
for _, v := range res.Hops[ttl][i].MPLS {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("\n %s", v),
|
||||
)
|
||||
}
|
||||
fmt.Println()
|
||||
blockDisplay = true
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
)
|
||||
|
||||
func RealtimePrinterWithRouter(res *trace.Result, ttl int) {
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/rodaine/table"
|
||||
|
||||
4
ptr.example.csv
Normal file
4
ptr.example.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
snge,SG,,Singapore
|
||||
CXS,CN,Hunan,Changsha
|
||||
LAX,US,California,Los Angeles
|
||||
SJC,US,California,San Jose
|
||||
|
@@ -6,8 +6,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
)
|
||||
|
||||
type Reporter interface {
|
||||
@@ -45,7 +45,7 @@ func experimentTag() {
|
||||
|
||||
func (r *reporter) generateRouteReportNode(ip string, ipGeoData ipgeo.IPGeoData, ttl uint16) {
|
||||
|
||||
var success bool = true
|
||||
var success = true
|
||||
|
||||
defer r.wg.Done()
|
||||
|
||||
@@ -114,11 +114,13 @@ func (r *reporter) InitialBaseData() Reporter {
|
||||
r.targetTTL = uint16(len(r.routeResult.Hops))
|
||||
|
||||
for i := uint16(0); i < r.targetTTL; i++ {
|
||||
traceHop := r.routeResult.Hops[i][0]
|
||||
if traceHop.Success {
|
||||
currentIP := traceHop.Address.String()
|
||||
r.wg.Add(1)
|
||||
go r.generateRouteReportNode(currentIP, *traceHop.Geo, i)
|
||||
if i < uint16(len(r.routeResult.Hops)) && len(r.routeResult.Hops[i]) > 0 {
|
||||
traceHop := r.routeResult.Hops[i][0]
|
||||
if traceHop.Success {
|
||||
currentIP := traceHop.Address.String()
|
||||
r.wg.Add(1)
|
||||
go r.generateRouteReportNode(currentIP, *traceHop.Geo, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
)
|
||||
|
||||
var testResult = &trace.Result{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,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 {
|
||||
@@ -26,8 +29,11 @@ type ICMPTracer struct {
|
||||
icmpListen net.PacketConn
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
fetchLock sync.Mutex
|
||||
}
|
||||
|
||||
var psize = 52
|
||||
|
||||
func (t *ICMPTracer) PrintFunc() {
|
||||
defer t.wg.Done()
|
||||
var ttl = t.Config.BeginHop - 1
|
||||
@@ -35,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 {
|
||||
@@ -62,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:1", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -116,6 +123,7 @@ func (t *ICMPTracer) Execute() (*Result, error) {
|
||||
|
||||
func (t *ICMPTracer) listenICMP() {
|
||||
lc := NewPacketListener(t.icmpListen, t.ctx)
|
||||
psize = t.Config.PktSize
|
||||
go lc.Start()
|
||||
for {
|
||||
select {
|
||||
@@ -159,6 +167,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)
|
||||
}
|
||||
@@ -171,12 +182,21 @@ 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()
|
||||
|
||||
mpls := extractMPLS(msg, data)
|
||||
if _, ok := t.inflightRequest[ttl]; ok {
|
||||
t.inflightRequest[ttl] <- Hop{
|
||||
Success: true,
|
||||
Address: msg.Peer,
|
||||
MPLS: mpls,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,7 +219,7 @@ func gernerateID(ttl_int int) int {
|
||||
id += "0"
|
||||
}
|
||||
|
||||
res, _ := strconv.ParseInt(id, 2, 64)
|
||||
res, _ := strconv.ParseInt(id, 2, 32)
|
||||
return int(res)
|
||||
}
|
||||
|
||||
@@ -252,8 +272,9 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
icmpHeader := icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
ID: id,
|
||||
//Data: []byte("HELLO-R-U-THERE"),
|
||||
Data: append(bytes.Repeat([]byte{1}, t.Config.PktSize-4), 0x00, 0x00, 0x4f, 0xff),
|
||||
Seq: ttl,
|
||||
},
|
||||
}
|
||||
@@ -298,6 +319,8 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
h.TTL = ttl
|
||||
h.RTT = rtt
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
@@ -12,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 {
|
||||
@@ -25,10 +28,11 @@ type ICMPTracerv6 struct {
|
||||
icmpListen net.PacketConn
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
fetchLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *ICMPTracerv6) PrintFunc() {
|
||||
// defer t.wg.Done()
|
||||
defer t.wg.Done()
|
||||
var ttl = t.Config.BeginHop - 1
|
||||
for {
|
||||
if t.AsyncPrinter != nil {
|
||||
@@ -42,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)
|
||||
}
|
||||
}
|
||||
@@ -63,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
|
||||
}
|
||||
@@ -76,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()
|
||||
@@ -135,6 +140,7 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
|
||||
|
||||
func (t *ICMPTracerv6) listenICMP() {
|
||||
lc := NewPacketListener(t.icmpListen, t.ctx)
|
||||
psize = t.Config.PktSize
|
||||
go lc.Start()
|
||||
for {
|
||||
select {
|
||||
@@ -186,6 +192,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)
|
||||
}
|
||||
@@ -229,12 +237,20 @@ 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()
|
||||
|
||||
mpls := extractMPLS(msg, data)
|
||||
if _, ok := t.inflightRequest[ttl]; ok {
|
||||
t.inflightRequest[ttl] <- Hop{
|
||||
Success: true,
|
||||
Address: msg.Peer,
|
||||
MPLS: mpls,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,8 +265,9 @@ func (t *ICMPTracerv6) send(ttl int) error {
|
||||
icmpHeader := icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
ID: id,
|
||||
//Data: []byte("HELLO-R-U-THERE"),
|
||||
Data: append(bytes.Repeat([]byte{1}, t.Config.PktSize-4), 0x00, 0x00, 0x4f, 0xff),
|
||||
Seq: ttl,
|
||||
},
|
||||
}
|
||||
@@ -298,6 +315,8 @@ func (t *ICMPTracerv6) send(ttl int) error {
|
||||
h.TTL = ttl
|
||||
h.RTT = rtt
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
91
trace/internal/icmp_darwin.go
Normal file
91
trace/internal/icmp_darwin.go
Normal file
@@ -0,0 +1,91 @@
|
||||
//go:build darwin
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:linkname internetSocket net.internetSocket
|
||||
func internetSocket(ctx context.Context, net string, laddr, raddr any, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
|
||||
|
||||
//go:linkname newIPConn net.newIPConn
|
||||
func newIPConn(fd unsafe.Pointer) *net.IPConn
|
||||
|
||||
var (
|
||||
errUnknownNetwork = errors.New("unknown network type")
|
||||
errUnknownIface = errors.New("unknown network interface")
|
||||
|
||||
networkMap = map[string]string{
|
||||
"ip4:icmp": "udp4",
|
||||
"ip4:1": "udp4",
|
||||
"ip6:icmp": "udp6",
|
||||
"ip6:58": "udp6",
|
||||
}
|
||||
)
|
||||
|
||||
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
|
||||
if os.Getuid() == 0 { // root
|
||||
return net.ListenPacket(network, laddr)
|
||||
} else {
|
||||
if nw, ok := networkMap[network]; ok {
|
||||
proto := syscall.IPPROTO_ICMP
|
||||
if nw == "udp6" {
|
||||
proto = syscall.IPPROTO_ICMPV6
|
||||
}
|
||||
|
||||
var ifIndex int = -1
|
||||
if laddr != "" {
|
||||
la := net.ParseIP(laddr)
|
||||
if ifaces, err := net.Interfaces(); err == nil {
|
||||
for _, iface := range ifaces {
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok {
|
||||
if ipnet.IP.Equal(la) {
|
||||
ifIndex = iface.Index
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ifIndex == -1 {
|
||||
return nil, errUnknownIface
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen",
|
||||
func(ctx context.Context, network, address string, c syscall.RawConn) error {
|
||||
if ifIndex != -1 {
|
||||
if proto == syscall.IPPROTO_ICMP {
|
||||
return c.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, ifIndex)
|
||||
})
|
||||
} else {
|
||||
return c.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, ifIndex)
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return newIPConn(isock), nil
|
||||
} else {
|
||||
return nil, errUnknownNetwork
|
||||
}
|
||||
}
|
||||
}
|
||||
9
trace/internal/icmp_general.go
Normal file
9
trace/internal/icmp_general.go
Normal file
@@ -0,0 +1,9 @@
|
||||
//go:build !darwin
|
||||
|
||||
package internal
|
||||
|
||||
import "net"
|
||||
|
||||
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
|
||||
return net.ListenPacket(network, laddr)
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/xgadget-lab/nexttrace/util"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
@@ -31,7 +31,8 @@ type TCPTracer struct {
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
|
||||
sem *semaphore.Weighted
|
||||
sem *semaphore.Weighted
|
||||
fetchLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *TCPTracer) Execute() (*Result, error) {
|
||||
@@ -79,7 +80,7 @@ func (t *TCPTracer) Execute() (*Result, error) {
|
||||
for i := 0; i < t.NumMeasurements; i++ {
|
||||
t.wg.Add(1)
|
||||
go t.send(ttl)
|
||||
|
||||
<-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval))
|
||||
}
|
||||
if t.RealtimePrinter != nil {
|
||||
// 对于实时模式,应该按照TTL进行并发请求
|
||||
@@ -87,7 +88,7 @@ func (t *TCPTracer) Execute() (*Result, error) {
|
||||
t.RealtimePrinter(&t.res, ttl-1)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
|
||||
}
|
||||
go func() {
|
||||
if t.AsyncPrinter != nil {
|
||||
@@ -233,6 +234,11 @@ func (t *TCPTracer) send(ttl int) error {
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
}
|
||||
|
||||
desiredPayloadSize := t.Config.PktSize
|
||||
payload := make([]byte, desiredPayloadSize)
|
||||
copy(buf.Bytes(), payload)
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -251,7 +257,7 @@ func (t *TCPTracer) send(ttl int) error {
|
||||
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)
|
||||
@@ -285,6 +291,8 @@ func (t *TCPTracer) send(ttl int) error {
|
||||
h.TTL = ttl
|
||||
h.RTT = rtt
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"log"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/xgadget-lab/nexttrace/util"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv6"
|
||||
@@ -31,7 +31,8 @@ type TCPTracerv6 struct {
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
|
||||
sem *semaphore.Weighted
|
||||
sem *semaphore.Weighted
|
||||
fetchLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
@@ -39,14 +40,14 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
return &t.res, ErrTracerouteExecuted
|
||||
}
|
||||
|
||||
t.SrcIP, _ = util.LocalIPPort(t.DestIP)
|
||||
log.Println(util.LocalIPPort(t.DestIP))
|
||||
t.SrcIP, _ = util.LocalIPPortv6(t.DestIP)
|
||||
// log.Println(util.LocalIPPortv6(t.DestIP))
|
||||
var err error
|
||||
t.tcp, err = net.ListenPacket("ip6:tcp", t.SrcIP.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.icmp, err = icmp.ListenPacket("ip6:53", "::")
|
||||
t.icmp, err = icmp.ListenPacket("ip6:58", "::")
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -63,15 +64,37 @@ func (t *TCPTracerv6) Execute() (*Result, error) {
|
||||
|
||||
t.sem = semaphore.NewWeighted(int64(t.ParallelRequests))
|
||||
|
||||
for ttl := 1; ttl <= t.MaxHops; ttl++ {
|
||||
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
|
||||
// 如果到达最终跳,则退出
|
||||
if t.final != -1 && ttl > t.final {
|
||||
break
|
||||
}
|
||||
for i := 0; i < t.NumMeasurements; i++ {
|
||||
t.wg.Add(1)
|
||||
go t.send(ttl)
|
||||
|
||||
<-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval))
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
if t.RealtimePrinter != nil {
|
||||
// 对于实时模式,应该按照TTL进行并发请求
|
||||
t.wg.Wait()
|
||||
t.RealtimePrinter(&t.res, ttl-1)
|
||||
}
|
||||
<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
|
||||
}
|
||||
|
||||
go func() {
|
||||
if t.AsyncPrinter != nil {
|
||||
for {
|
||||
t.AsyncPrinter(&t.res)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
if t.RealtimePrinter == nil {
|
||||
t.wg.Wait()
|
||||
}
|
||||
t.wg.Wait()
|
||||
t.res.reduce(t.final)
|
||||
|
||||
return &t.res, nil
|
||||
@@ -85,20 +108,21 @@ func (t *TCPTracerv6) listenICMP() {
|
||||
case <-t.ctx.Done():
|
||||
return
|
||||
case msg := <-lc.Messages:
|
||||
// log.Println(msg)
|
||||
|
||||
if msg.N == nil {
|
||||
continue
|
||||
}
|
||||
rm, err := icmp.ParseMessage(53, msg.Msg[:*msg.N])
|
||||
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
// log.Println(err)
|
||||
continue
|
||||
}
|
||||
log.Println(msg.Peer)
|
||||
switch rm.Type {
|
||||
case ipv6.ICMPTypeTimeExceeded:
|
||||
t.handleICMPMessage(msg, rm.Body.(*icmp.TimeExceeded).Data)
|
||||
t.handleICMPMessage(msg)
|
||||
case ipv6.ICMPTypeDestinationUnreachable:
|
||||
t.handleICMPMessage(msg, rm.Body.(*icmp.DstUnreach).Data)
|
||||
t.handleICMPMessage(msg)
|
||||
default:
|
||||
//log.Println("received icmp message of unknown type", rm.Type)
|
||||
}
|
||||
@@ -118,6 +142,8 @@ func (t *TCPTracerv6) listenTCP() {
|
||||
case <-t.ctx.Done():
|
||||
return
|
||||
case msg := <-lc.Messages:
|
||||
// log.Println(msg)
|
||||
// return
|
||||
if msg.N == nil {
|
||||
continue
|
||||
}
|
||||
@@ -144,23 +170,21 @@ func (t *TCPTracerv6) listenTCP() {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage, data []byte) {
|
||||
header, err := util.GetICMPResponsePayload(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sequenceNumber := util.GetTCPSeq(header)
|
||||
func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) {
|
||||
var sequenceNumber = binary.BigEndian.Uint32(msg.Msg[52:56])
|
||||
|
||||
t.inflightRequestLock.Lock()
|
||||
defer t.inflightRequestLock.Unlock()
|
||||
ch, ok := t.inflightRequest[int(sequenceNumber)]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// log.Println("发送数据", sequenceNumber)
|
||||
ch <- Hop{
|
||||
Success: true,
|
||||
Address: msg.Peer,
|
||||
}
|
||||
|
||||
// log.Println("发送成功")
|
||||
}
|
||||
|
||||
func (t *TCPTracerv6) send(ttl int) error {
|
||||
@@ -176,7 +200,7 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
}
|
||||
// 随机种子
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
_, srcPort := util.LocalIPPort(t.DestIP)
|
||||
_, srcPort := util.LocalIPPortv6(t.DestIP)
|
||||
ipHeader := &layers.IPv6{
|
||||
SrcIP: t.SrcIP,
|
||||
DstIP: t.DestIP,
|
||||
@@ -185,6 +209,7 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
}
|
||||
// 使用Uint16兼容32位系统,防止在rand的时候因使用int32而溢出
|
||||
sequenceNumber := uint32(r.Intn(math.MaxUint16))
|
||||
|
||||
tcpHeader := &layers.TCP{
|
||||
SrcPort: layers.TCPPort(srcPort),
|
||||
DstPort: layers.TCPPort(t.DestPort),
|
||||
@@ -199,6 +224,11 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
}
|
||||
|
||||
desiredPayloadSize := t.Config.PktSize
|
||||
payload := make([]byte, desiredPayloadSize)
|
||||
copy(buf.Bytes(), payload)
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -212,6 +242,7 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
if _, err := t.tcp.WriteTo(buf.Bytes(), &net.IPAddr{IP: t.DestIP}); err != nil {
|
||||
return err
|
||||
}
|
||||
// log.Println(ttl, sequenceNumber)
|
||||
t.inflightRequestLock.Lock()
|
||||
hopCh := make(chan Hop)
|
||||
t.inflightRequest[int(sequenceNumber)] = hopCh
|
||||
@@ -243,6 +274,8 @@ func (t *TCPTracerv6) send(ttl int) error {
|
||||
h.TTL = ttl
|
||||
h.RTT = rtt
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
)
|
||||
|
||||
func HopPrinter(h Hop) {
|
||||
|
||||
168
trace/trace.go
168
trace/trace.go
@@ -2,17 +2,23 @@ package trace
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidMethod = errors.New("invalid method")
|
||||
ErrTracerouteExecuted = errors.New("traceroute already executed")
|
||||
ErrHopLimitTimeout = errors.New("hop timeout")
|
||||
geoCache = sync.Map{}
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -31,8 +37,11 @@ type Config struct {
|
||||
PacketInterval int
|
||||
TTLInterval int
|
||||
Lang string
|
||||
DN42 bool
|
||||
RealtimePrinter func(res *Result, ttl int)
|
||||
AsyncPrinter func(res *Result)
|
||||
PktSize int
|
||||
Maptrace bool
|
||||
}
|
||||
|
||||
type Method string
|
||||
@@ -78,18 +87,23 @@ func Traceroute(method Method, config Config) (*Result, error) {
|
||||
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 = &TCPTracerv6{Config: config}
|
||||
// return nil, errors.New("IPv6 TCP Traceroute is not supported")
|
||||
}
|
||||
default:
|
||||
return &Result{}, ErrInvalidMethod
|
||||
}
|
||||
return tracer.Execute()
|
||||
result, err := tracer.Execute()
|
||||
if err != nil && errors.Is(err, syscall.EPERM) {
|
||||
err = fmt.Errorf("%w, please run as root", err)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Hops [][]Hop
|
||||
lock sync.Mutex
|
||||
Hops [][]Hop
|
||||
lock sync.Mutex
|
||||
TraceMapUrl string
|
||||
}
|
||||
|
||||
func (s *Result) add(hop Hop) {
|
||||
@@ -118,9 +132,24 @@ type Hop struct {
|
||||
Error error
|
||||
Geo *ipgeo.IPGeoData
|
||||
Lang string
|
||||
MPLS []string
|
||||
}
|
||||
|
||||
func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
|
||||
// DN42
|
||||
if c.DN42 {
|
||||
var ip string
|
||||
// 首先查找 PTR 记录
|
||||
r, err := util.LookupAddr(h.Address.String())
|
||||
if err == nil && len(r) > 0 {
|
||||
h.Hostname = r[0][:len(r[0])-1]
|
||||
ip = h.Address.String() + "," + h.Hostname
|
||||
}
|
||||
h.Geo, _ = c.IPGeoSource(ip, c.Timeout, c.Lang, c.Maptrace)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debug Area
|
||||
// c.AlwaysWaitRDNS = true
|
||||
|
||||
@@ -131,7 +160,7 @@ func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
if c.RDns && h.Hostname == "" {
|
||||
// Create a rDNS Query go-routine
|
||||
go func() {
|
||||
r, err := net.LookupAddr(h.Address.String())
|
||||
r, err := util.LookupAddr(h.Address.String())
|
||||
if err != nil {
|
||||
// No PTR Record
|
||||
rDNSChan <- nil
|
||||
@@ -150,7 +179,21 @@ func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
h.Lang = c.Lang
|
||||
h.Geo, res = ipgeo.Filter(h.Address.String())
|
||||
if !res {
|
||||
h.Geo, err = c.IPGeoSource(h.Address.String())
|
||||
timeout := c.Timeout
|
||||
if c.Timeout < 2*time.Second {
|
||||
timeout = 2 * time.Second
|
||||
}
|
||||
//h.Geo, err = c.IPGeoSource(h.Address.String(), timeout, c.Lang, c.Maptrace)
|
||||
if cacheVal, ok := geoCache.Load(h.Address.String()); ok {
|
||||
// 如果缓存中已有结果,直接使用
|
||||
h.Geo = cacheVal.(*ipgeo.IPGeoData)
|
||||
} else {
|
||||
// 如果缓存中无结果,进行查询并将结果存入缓存
|
||||
h.Geo, err = c.IPGeoSource(h.Address.String(), timeout, c.Lang, c.Maptrace)
|
||||
if err == nil {
|
||||
geoCache.Store(h.Address.String(), h.Geo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fetch Done
|
||||
@@ -183,11 +226,116 @@ 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
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func extractMPLS(msg ReceivedMessage, data []byte) []string {
|
||||
if util.DisableMPLS != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if psize != 52 {
|
||||
return nil
|
||||
}
|
||||
|
||||
extensionOffset := 20 + 8 + psize
|
||||
|
||||
if len(data) <= extensionOffset {
|
||||
return nil
|
||||
}
|
||||
|
||||
extensionBody := data[extensionOffset:]
|
||||
if len(extensionBody) < 8 || len(extensionBody)%8 != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp := fmt.Sprintf("%x", msg.Msg[:*msg.N])
|
||||
|
||||
index := strings.Index(tmp, strings.Repeat("01", psize-4)+"00004fff")
|
||||
if index == -1 {
|
||||
return nil
|
||||
}
|
||||
tmp = tmp[index+psize*2:]
|
||||
//由于限制长度了
|
||||
index1 := strings.Index(tmp, "00002000")
|
||||
l := len(tmp[index1+4:])/8 - 2
|
||||
//fmt.Printf("l:%d\n", l)
|
||||
|
||||
if l < 1 {
|
||||
return nil
|
||||
}
|
||||
//去掉扩展头和MPLS头
|
||||
tmp = tmp[index1+4+8*2:]
|
||||
//fmt.Print(tmp)
|
||||
|
||||
var retStrList []string
|
||||
for i := 0; i < l; i++ {
|
||||
label, err := strconv.ParseInt(tmp[i*8+0:i*8+5], 16, 32)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
strSlice := fmt.Sprintf("%s", []byte(tmp[i*8+5:i*8+6]))
|
||||
//fmt.Printf("\nstrSlice: %s\n", strSlice)
|
||||
|
||||
num, err := strconv.ParseUint(strSlice, 16, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
binaryStr := fmt.Sprintf("%04s", strconv.FormatUint(num, 2))
|
||||
|
||||
//fmt.Printf("\nbinaryStr: %s\n", binaryStr)
|
||||
tc, err := strconv.ParseInt(binaryStr[:3], 2, 32)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
s := binaryStr[3:]
|
||||
|
||||
ttlMpls, err := strconv.ParseInt(tmp[i*8+6:i*8+8], 16, 32)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//if i > 0 {
|
||||
// retStr += "\n "
|
||||
//}
|
||||
|
||||
retStrList = append(retStrList, fmt.Sprintf("[MPLS: Lbl %d, TC %d, S %s, TTL %d]", label, tc, s, ttlMpls))
|
||||
}
|
||||
|
||||
//label, err := strconv.ParseInt(tmp[len(tmp)-8:len(tmp)-3], 16, 32)
|
||||
//if err != nil {
|
||||
// return ""
|
||||
//}
|
||||
//
|
||||
//strSlice := fmt.Sprintf("%s", []byte(tmp[len(tmp)-3:len(tmp)-2]))
|
||||
////fmt.Printf("\nstrSlice: %s\n", strSlice)
|
||||
//
|
||||
//num, err := strconv.ParseUint(strSlice, 16, 64)
|
||||
//if err != nil {
|
||||
// return ""
|
||||
//}
|
||||
//binaryStr := fmt.Sprintf("%04s", strconv.FormatUint(num, 2))
|
||||
//
|
||||
////fmt.Printf("\nbinaryStr: %s\n", binaryStr)
|
||||
//tc, err := strconv.ParseInt(binaryStr[:3], 2, 32)
|
||||
//if err != nil {
|
||||
// return ""
|
||||
//}
|
||||
//s := binaryStr[3:]
|
||||
//
|
||||
//ttlMpls, err := strconv.ParseInt(tmp[len(tmp)-2:], 16, 32)
|
||||
//if err != nil {
|
||||
// return ""
|
||||
//}
|
||||
//
|
||||
//retStr := fmt.Sprintf("Lbl %d, TC %d, S %s, TTL %d", label, tc, s, ttlMpls)
|
||||
|
||||
return retStrList
|
||||
}
|
||||
|
||||
18
trace/udp.go
18
trace/udp.go
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/xgadget-lab/nexttrace/util"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
@@ -28,7 +28,8 @@ type UDPTracer struct {
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
|
||||
sem *semaphore.Weighted
|
||||
sem *semaphore.Weighted
|
||||
fetchLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *UDPTracer) Execute() (*Result, error) {
|
||||
@@ -60,14 +61,14 @@ func (t *UDPTracer) Execute() (*Result, error) {
|
||||
for i := 0; i < t.NumMeasurements; i++ {
|
||||
t.wg.Add(1)
|
||||
go t.send(ttl)
|
||||
|
||||
<-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval))
|
||||
}
|
||||
if t.RealtimePrinter != nil {
|
||||
// 对于实时模式,应该按照TTL进行并发请求
|
||||
t.wg.Wait()
|
||||
t.RealtimePrinter(&t.res, ttl-1)
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
|
||||
}
|
||||
go func() {
|
||||
if t.AsyncPrinter != nil {
|
||||
@@ -188,7 +189,12 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
ComputeChecksums: true,
|
||||
FixLengths: true,
|
||||
}
|
||||
if err := gopacket.SerializeLayers(buf, opts, udpHeader, gopacket.Payload("HAJSFJHKAJSHFKJHAJKFHKASHKFHHKAFKHFAHSJK")); err != nil {
|
||||
|
||||
desiredPayloadSize := t.Config.PktSize
|
||||
payload := make([]byte, desiredPayloadSize)
|
||||
copy(buf.Bytes(), payload)
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, udpHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -256,6 +262,8 @@ func (t *UDPTracer) send(ttl int) error {
|
||||
h.TTL = ttl
|
||||
h.RTT = rtt
|
||||
|
||||
t.fetchLock.Lock()
|
||||
defer t.fetchLock.Unlock()
|
||||
h.fetchIPData(t.Config)
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
)
|
||||
|
||||
func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
@@ -17,7 +17,12 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
defer func(f *os.File) {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(f)
|
||||
|
||||
multiWriter := io.MultiWriter(os.Stdout, f)
|
||||
log.SetOutput(multiWriter)
|
||||
|
||||
@@ -1,20 +1,78 @@
|
||||
package tracemap
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetMapUrl(r string) {
|
||||
url := "https://api.leo.moe/tracemap/api"
|
||||
resp, _ := http.Post(url, "application/json", strings.NewReader(string(r)))
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Fprintf(color.Output, "%s %s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "MapTrace URL:"),
|
||||
color.New(color.FgBlue, color.Bold).Sprintf("%s", string(body)),
|
||||
)
|
||||
func GetMapUrl(r string) (string, error) {
|
||||
host, port := util.GetHostAndPort()
|
||||
fastIp := "origin-fallback.nxtrace.org"
|
||||
// 如果 host 是一个 IP 使用默认域名
|
||||
if valid := net.ParseIP(host); valid != nil {
|
||||
fastIp = host
|
||||
if len(strings.Split(fastIp, ":")) > 1 {
|
||||
fastIp = "[" + fastIp + "]"
|
||||
}
|
||||
host = "origin-fallback.nxtrace.org"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
fastIp = util.GetFastIP(host, port, false)
|
||||
}
|
||||
u := url.URL{Scheme: "https", Host: fastIp + ":" + port, Path: "/tracemap/api"}
|
||||
tracemapUrl := u.String()
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
ServerName: host,
|
||||
},
|
||||
},
|
||||
}
|
||||
proxyUrl := util.GetProxy()
|
||||
if proxyUrl != nil {
|
||||
client.Transport.(*http.Transport).Proxy = http.ProxyURL(proxyUrl)
|
||||
}
|
||||
req, err := http.NewRequest("POST", tracemapUrl, strings.NewReader(r))
|
||||
if err != nil {
|
||||
return "", errors.New("an issue occurred while connecting to the tracemap API")
|
||||
}
|
||||
req.Header.Add("User-Agent", util.UserAgent)
|
||||
req.Host = host
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", errors.New("an issue occurred while connecting to the tracemap API")
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", errors.New("an issue occurred while connecting to the tracemap API")
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func PrintMapUrl(r string) {
|
||||
_, err := fmt.Fprintf(color.Output, "%s %s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "MapTrace URL:"),
|
||||
color.New(color.FgBlue, color.Bold).Sprintf("%s", r),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
23
util/common.go
Normal file
23
util/common.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package util
|
||||
|
||||
import "net"
|
||||
|
||||
func Dnspod() *net.Resolver {
|
||||
return newDoTResolver("dot.pub", "dot.pub:853")
|
||||
}
|
||||
|
||||
func Aliyun() *net.Resolver {
|
||||
return newDoTResolver("dns.alidns.com", "dns.alidns.com:853")
|
||||
}
|
||||
|
||||
func DNSSB() *net.Resolver {
|
||||
return newDoTResolver("45.11.45.11", "dot.sb:853")
|
||||
}
|
||||
|
||||
func Cloudflare() *net.Resolver {
|
||||
return newDoTResolver("one.one.one.one", "one.one.one.one:853")
|
||||
}
|
||||
|
||||
func Google() *net.Resolver {
|
||||
return newDoTResolver("dns.google", "dns.google:853")
|
||||
}
|
||||
20
util/dns_test.go
Normal file
20
util/dns_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDNS(t *testing.T) {
|
||||
resolver := DNSSB()
|
||||
ips, _ := resolver.LookupHost(context.Background(), "www.bing.com")
|
||||
fmt.Println(ips)
|
||||
}
|
||||
|
||||
func TestDomainLookUp(t *testing.T) {
|
||||
ips, _ := DomainLookUp("pek-4134.endpoint.nxtrace.org.", "all", "", false)
|
||||
fmt.Println(ips)
|
||||
ips, _ = DomainLookUp("pek-4134.endpoint.nxtrace.org.", "4", "", false)
|
||||
fmt.Println(ips)
|
||||
}
|
||||
32
util/dot.go
Normal file
32
util/dot.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newDoTResolver(serverName string, addrs string) *net.Resolver {
|
||||
|
||||
d := &net.Dialer{
|
||||
// 设置超时时间
|
||||
Timeout: 1000 * time.Millisecond,
|
||||
}
|
||||
|
||||
tls_config := &tls.Config{
|
||||
// 设置 TLS Server Name 以确保证书能和域名对应
|
||||
ServerName: serverName,
|
||||
}
|
||||
return &net.Resolver{
|
||||
// 指定使用 Go Build-in 的 DNS Resolver 来解析
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
conn, err := d.DialContext(ctx, "tcp", addrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tls.Client(conn, tls_config), nil
|
||||
},
|
||||
}
|
||||
}
|
||||
128
util/latency.go
Normal file
128
util/latency.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
type ResponseInfo struct {
|
||||
IP string
|
||||
Latency string
|
||||
Content string
|
||||
}
|
||||
|
||||
var (
|
||||
results = make(chan ResponseInfo)
|
||||
timeout = 5 * time.Second
|
||||
)
|
||||
var FastIpCache = ""
|
||||
|
||||
func GetFastIP(domain string, port string, enableOutput bool) string {
|
||||
proxyUrl := GetProxy()
|
||||
if proxyUrl != nil {
|
||||
return "origin-fallback.nxtrace.org"
|
||||
}
|
||||
if FastIpCache != "" {
|
||||
return FastIpCache
|
||||
}
|
||||
|
||||
var ips []net.IP
|
||||
var err error
|
||||
if domain == "origin-fallback.nxtrace.org" {
|
||||
ips, err = net.LookupIP("api.nxtrace.org")
|
||||
} else {
|
||||
ips, err = net.LookupIP(domain)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("DNS resolution failed, please check your system DNS Settings")
|
||||
}
|
||||
|
||||
if len(ips) == 0 {
|
||||
// 添加默认IP 45.88.195.154
|
||||
ips = append(ips, net.ParseIP("45.88.195.154"))
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
go checkLatency(domain, ip.String(), port)
|
||||
}
|
||||
|
||||
var result ResponseInfo
|
||||
|
||||
select {
|
||||
case result = <-results:
|
||||
//等待5s没有结果 视为连不上API了
|
||||
case <-time.After(timeout):
|
||||
log.Println("IP connection has been timeout, please check your network")
|
||||
|
||||
}
|
||||
|
||||
//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 = result.IP
|
||||
return result.IP
|
||||
}
|
||||
|
||||
func checkLatency(domain string, ip string, port string) {
|
||||
start := time.Now()
|
||||
if !strings.Contains(ip, ".") {
|
||||
ip = "[" + ip + "]"
|
||||
}
|
||||
|
||||
// 自定义DialContext以使用指定的IP连接
|
||||
transport := &http.Transport{
|
||||
//DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
// return net.DialTimeout(network, addr, 1*time.Second)
|
||||
//},
|
||||
TLSClientConfig: &tls.Config{
|
||||
ServerName: domain,
|
||||
},
|
||||
TLSHandshakeTimeout: timeout,
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
//此处虽然是 https://domain/ 但是实际上会使用指定的IP连接
|
||||
req, err := http.NewRequest("GET", "https://"+ip+":"+port+"/", nil)
|
||||
if err != nil {
|
||||
// !!! 此处不要给results返回任何值
|
||||
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
|
||||
return
|
||||
}
|
||||
req.Host = domain
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
|
||||
return
|
||||
}
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
latency := fmt.Sprintf("%.2f", float64(time.Since(start))/float64(time.Millisecond))
|
||||
results <- ResponseInfo{IP: ip, Latency: latency, Content: bodyString}
|
||||
}
|
||||
6
util/latency_test.go
Normal file
6
util/latency_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package util
|
||||
|
||||
//github action test 不支持v6 这里会报错
|
||||
//func TestGetFastIP(t *testing.T) {
|
||||
// GetFastIP("origin-fallback.nxtrace.org", "443", true)
|
||||
//}
|
||||
12
util/udp.go
Normal file
12
util/udp.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func newUDPResolver() *net.Resolver {
|
||||
return &net.Resolver{
|
||||
// 指定使用 Go Build-in 的 DNS Resolver 来解析
|
||||
PreferGo: true,
|
||||
}
|
||||
}
|
||||
215
util/util.go
215
util/util.go
@@ -1,15 +1,49 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/config"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// get the local ip and port based on our destination ip
|
||||
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 DestIP string
|
||||
|
||||
func LookupAddr(addr string) ([]string, error) {
|
||||
// 如果在缓存中找到,直接返回
|
||||
if hostname, ok := RdnsCache.Load(addr); ok {
|
||||
//fmt.Println("hit RdnsCache for", addr, hostname)
|
||||
return []string{hostname.(string)}, nil
|
||||
}
|
||||
// 如果缓存中未找到,进行 DNS 查询
|
||||
names, err := net.LookupAddr(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 将查询结果存入缓存
|
||||
if len(names) > 0 {
|
||||
RdnsCache.Store(addr, names[0])
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// LocalIPPort get the local ip and port based on our destination ip
|
||||
func LocalIPPort(dstip net.IP) (net.IP, int) {
|
||||
serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345")
|
||||
if err != nil {
|
||||
@@ -27,41 +61,81 @@ func LocalIPPort(dstip net.IP) (net.IP, int) {
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func DomainLookUp(host string, ipv4Only bool) net.IP {
|
||||
ips, err := net.LookupIP(host)
|
||||
func LocalIPPortv6(dstip net.IP) (net.IP, int) {
|
||||
serverAddr, err := net.ResolveUDPAddr("udp", "["+dstip.String()+"]:12345")
|
||||
if err != nil {
|
||||
fmt.Println("Domain " + host + " Lookup Fail.")
|
||||
os.Exit(1)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var ipSlice = []net.IP{}
|
||||
var ipv6Flag = false
|
||||
// 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
|
||||
}
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if ipv4Only {
|
||||
// 仅返回ipv4的ip
|
||||
if ip.To4() != nil {
|
||||
ipSlice = append(ipSlice, ip)
|
||||
} else {
|
||||
ipv6Flag = true
|
||||
func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput bool) (net.IP, error) {
|
||||
// ipVersion: 4, 6, all
|
||||
var (
|
||||
r *net.Resolver
|
||||
ips []net.IP
|
||||
)
|
||||
|
||||
switch dotServer {
|
||||
case "dnssb":
|
||||
r = DNSSB()
|
||||
case "aliyun":
|
||||
r = Aliyun()
|
||||
case "dnspod":
|
||||
r = Dnspod()
|
||||
case "google":
|
||||
r = Google()
|
||||
case "cloudflare":
|
||||
r = Cloudflare()
|
||||
default:
|
||||
r = newUDPResolver()
|
||||
}
|
||||
ipsStr, err := r.LookupHost(context.Background(), host)
|
||||
for _, v := range ipsStr {
|
||||
ips = append(ips, net.ParseIP(v))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.New("DNS lookup failed")
|
||||
}
|
||||
|
||||
//var ipv6Flag = false
|
||||
//TODO: 此处代码暂无意义
|
||||
//if ipv6Flag {
|
||||
// fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.")
|
||||
// if len(ips) == 0 {
|
||||
// os.Exit(0)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Filter by IPv4/IPv6
|
||||
if ipVersion != "all" {
|
||||
var filteredIPs []net.IP
|
||||
for _, ip := range ips {
|
||||
if ipVersion == "4" && ip.To4() != nil {
|
||||
filteredIPs = []net.IP{ip}
|
||||
break
|
||||
} else if ipVersion == "6" && strings.Contains(ip.String(), ":") {
|
||||
filteredIPs = []net.IP{ip}
|
||||
break
|
||||
}
|
||||
} else {
|
||||
ipSlice = append(ipSlice, ip)
|
||||
}
|
||||
ips = filteredIPs
|
||||
}
|
||||
|
||||
if ipv6Flag {
|
||||
fmt.Println("[Info] IPv6 TCP/UDP Traceroute is not supported right now.")
|
||||
if len(ipSlice) == 0 {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ipSlice) == 1 {
|
||||
return ipSlice[0]
|
||||
if (len(ips) == 1) || (disableOutput) {
|
||||
return ips[0], nil
|
||||
} else {
|
||||
fmt.Println("Please Choose the IP You Want To TraceRoute")
|
||||
for i, ip := range ipSlice {
|
||||
for i, ip := range ips {
|
||||
fmt.Fprintf(color.Output, "%s %s\n",
|
||||
color.New(color.FgHiYellow, color.Bold).Sprintf("%d.", i),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", ip),
|
||||
@@ -69,19 +143,102 @@ func DomainLookUp(host string, ipv4Only bool) net.IP {
|
||||
}
|
||||
var index int
|
||||
fmt.Printf("Your Option: ")
|
||||
fmt.Scanln(&index)
|
||||
if index >= len(ipSlice) || index < 0 {
|
||||
_, err := fmt.Scanln(&index)
|
||||
if err != nil {
|
||||
index = 0
|
||||
}
|
||||
if index >= len(ips) || index < 0 {
|
||||
fmt.Println("Your Option is invalid")
|
||||
os.Exit(3)
|
||||
}
|
||||
return ipSlice[index]
|
||||
return ips[index], nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetenvDefault(key, defVal string) string {
|
||||
val, ok := os.LookupEnv(key)
|
||||
if ok {
|
||||
_, ok := os.LookupEnv("NEXTTRACE_DEBUG")
|
||||
if ok {
|
||||
fmt.Println("ENV", key, "detected as", val)
|
||||
}
|
||||
return val
|
||||
}
|
||||
return defVal
|
||||
}
|
||||
|
||||
func GetHostAndPort() (host string, port string) {
|
||||
var hostP = GetenvDefault("NEXTTRACE_HOSTPORT", "origin-fallback.nxtrace.org")
|
||||
// 解析域名
|
||||
hostArr := strings.Split(hostP, ":")
|
||||
// 判断是否有指定端口
|
||||
if len(hostArr) > 1 {
|
||||
// 判断是否为 IPv6
|
||||
if strings.HasPrefix(hostP, "[") {
|
||||
tmp := strings.Split(hostP, "]")
|
||||
host = tmp[0]
|
||||
host = host[1:]
|
||||
if port = tmp[1]; port != "" {
|
||||
port = port[1:]
|
||||
}
|
||||
} else {
|
||||
host, port = hostArr[0], hostArr[1]
|
||||
}
|
||||
} else {
|
||||
host = hostP
|
||||
}
|
||||
if port == "" {
|
||||
// 默认端口
|
||||
port = "443"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetProxy() *url.URL {
|
||||
proxyURLStr := GetenvDefault("NEXTTRACE_PROXY", "")
|
||||
if proxyURLStr == "" {
|
||||
return nil
|
||||
}
|
||||
proxyURL, err := url.Parse(proxyURLStr)
|
||||
if err != nil {
|
||||
log.Println("Failed to parse proxy URL:", err)
|
||||
return nil
|
||||
}
|
||||
return proxyURL
|
||||
}
|
||||
|
||||
func GetPowProvider() string {
|
||||
var powProvider = ""
|
||||
if PowProviderParam == "" {
|
||||
powProvider = GetenvDefault("NEXTTRACE_POWPROVIDER", "api.nxtrace.org")
|
||||
} else {
|
||||
powProvider = PowProviderParam
|
||||
}
|
||||
if powProvider == "sakura" {
|
||||
return "pow.nexttrace.owo.13a.com"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func StringInSlice(val string, list []string) bool {
|
||||
for _, v := range list {
|
||||
if v == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func HideIPPart(ip string) string {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if parsedIP.To4() != nil {
|
||||
// IPv4: 隐藏后16位
|
||||
return strings.Join(strings.Split(ip, ".")[:2], ".") + ".0.0/16"
|
||||
}
|
||||
// IPv6: 隐藏后96位
|
||||
return parsedIP.Mask(net.CIDRMask(32, 128)).String() + "/32"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package wshandle
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/nxtrace/NTrace-core/pow"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -13,7 +15,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/xgadget-lab/nexttrace/util"
|
||||
)
|
||||
|
||||
type WsConn struct {
|
||||
@@ -29,8 +30,10 @@ type WsConn struct {
|
||||
}
|
||||
|
||||
var wsconn *WsConn
|
||||
var hostP = util.GetenvDefault("NEXTTRACE_HOSTPORT", "api.leo.moe")
|
||||
var host, port, fast_ip string
|
||||
var host, port, fastIp string
|
||||
var envToken = util.EnvToken
|
||||
var cacheToken string
|
||||
var cacheTokenFailedTimes int
|
||||
|
||||
func (c *WsConn) keepAlive() {
|
||||
go func() {
|
||||
@@ -116,15 +119,44 @@ func (c *WsConn) messageSendHandler() {
|
||||
}
|
||||
|
||||
func (c *WsConn) recreateWsConn() {
|
||||
u := url.URL{Scheme: "wss", Host: fast_ip + ":" + port, Path: "/v2/ipGeoWs"}
|
||||
// 尝试重新连线
|
||||
u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"}
|
||||
// log.Printf("connecting to %s", u.String())
|
||||
jwtToken, ua := envToken, []string{"Privileged Client"}
|
||||
err := error(nil)
|
||||
if envToken == "" {
|
||||
// 无环境变量 token
|
||||
if cacheToken == "" {
|
||||
// 无cacheToken, 重新获取 token
|
||||
if util.GetPowProvider() == "" {
|
||||
jwtToken, err = pow.GetToken(fastIp, host, port)
|
||||
} else {
|
||||
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
// 使用 cacheToken
|
||||
jwtToken = cacheToken
|
||||
}
|
||||
ua = []string{util.UserAgent}
|
||||
}
|
||||
cacheToken = jwtToken
|
||||
requestHeader := http.Header{
|
||||
"Host": []string{host},
|
||||
"Host": []string{host},
|
||||
"User-Agent": ua,
|
||||
"Authorization": []string{"Bearer " + jwtToken},
|
||||
}
|
||||
dialer := websocket.DefaultDialer
|
||||
dialer.TLSClientConfig = &tls.Config{
|
||||
ServerName: host,
|
||||
}
|
||||
proxyUrl := util.GetProxy()
|
||||
if proxyUrl != nil {
|
||||
dialer.Proxy = http.ProxyURL(proxyUrl)
|
||||
}
|
||||
ws, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader)
|
||||
c.Conn = ws
|
||||
if err != nil {
|
||||
@@ -132,6 +164,11 @@ func (c *WsConn) recreateWsConn() {
|
||||
// <-time.After(time.Second * 1)
|
||||
c.Connected = false
|
||||
c.Connecting = false
|
||||
if cacheTokenFailedTimes > 3 {
|
||||
cacheToken = ""
|
||||
}
|
||||
cacheTokenFailedTimes += 1
|
||||
//fmt.Println("重连失败", cacheTokenFailedTimes, "次")
|
||||
return
|
||||
} else {
|
||||
c.Connected = true
|
||||
@@ -143,47 +180,52 @@ func (c *WsConn) recreateWsConn() {
|
||||
}
|
||||
|
||||
func createWsConn() *WsConn {
|
||||
proxyUrl := util.GetProxy()
|
||||
//fmt.Println("正在连接 WS")
|
||||
// 设置终端中断通道
|
||||
interrupt := make(chan os.Signal, 1)
|
||||
signal.Notify(interrupt, os.Interrupt)
|
||||
// 解析域名
|
||||
hostArr := strings.Split(hostP, ":")
|
||||
// 判断是否有指定端口
|
||||
if len(hostArr) > 1 {
|
||||
// 判断是否为 IPv6
|
||||
if strings.HasPrefix(hostP, "[") {
|
||||
tmp := strings.Split(hostP, "]")
|
||||
host = tmp[0]
|
||||
host = host[1:]
|
||||
if port = tmp[1]; port != "" {
|
||||
port = port[1:]
|
||||
}
|
||||
} else {
|
||||
host, port = hostArr[0], hostArr[1]
|
||||
}
|
||||
} else {
|
||||
host = hostP
|
||||
}
|
||||
if port == "" {
|
||||
// 默认端口
|
||||
port = "443"
|
||||
}
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
fast_ip = GetFastIP(host, port)
|
||||
|
||||
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 = "origin-fallback.nxtrace.org"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
fastIp = util.GetFastIP(host, port, true)
|
||||
}
|
||||
// 判断是否是一个 IP
|
||||
jwtToken, ua := envToken, []string{"Privileged Client"}
|
||||
err := error(nil)
|
||||
if envToken == "" {
|
||||
if util.GetPowProvider() == "" {
|
||||
jwtToken, err = pow.GetToken(fastIp, host, port)
|
||||
} else {
|
||||
jwtToken, err = pow.GetToken(util.GetPowProvider(), util.GetPowProvider(), port)
|
||||
}
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
ua = []string{util.UserAgent}
|
||||
}
|
||||
cacheToken = jwtToken
|
||||
cacheTokenFailedTimes = 0
|
||||
requestHeader := http.Header{
|
||||
"Host": []string{host},
|
||||
"Host": []string{host},
|
||||
"User-Agent": ua,
|
||||
"Authorization": []string{"Bearer " + jwtToken},
|
||||
}
|
||||
dialer := websocket.DefaultDialer
|
||||
dialer.TLSClientConfig = &tls.Config{
|
||||
ServerName: host,
|
||||
}
|
||||
u := url.URL{Scheme: "wss", Host: fast_ip + ":" + port, Path: "/v2/ipGeoWs"}
|
||||
if proxyUrl != nil {
|
||||
dialer.Proxy = http.ProxyURL(proxyUrl)
|
||||
}
|
||||
u := url.URL{Scheme: "wss", Host: fastIp + ":" + port, Path: "/v3/ipGeoWs"}
|
||||
// log.Printf("connecting to %s", u.String())
|
||||
|
||||
c, _, err := websocket.DefaultDialer.Dial(u.String(), requestHeader)
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package wshandle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var (
|
||||
result string
|
||||
results = make(chan string)
|
||||
)
|
||||
|
||||
func GetFastIP(domain string, port string) string {
|
||||
|
||||
ips, err := net.LookupIP(domain)
|
||||
if err != nil {
|
||||
log.Fatal("DNS resolution failed, please check your system DNS Settings")
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
go checkLatency(ip.String(), port)
|
||||
}
|
||||
|
||||
select {
|
||||
case result = <-results:
|
||||
case <-time.After(1 * time.Second):
|
||||
|
||||
}
|
||||
if result == "" {
|
||||
log.Fatal("IP connection has been timeout, please check your network")
|
||||
}
|
||||
res := strings.Split(result, "-")
|
||||
|
||||
if len(ips) > 1 {
|
||||
_, _ = 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]),
|
||||
)
|
||||
}
|
||||
|
||||
return res[0]
|
||||
}
|
||||
|
||||
func checkLatency(ip string, port string) {
|
||||
start := time.Now()
|
||||
if !strings.Contains(ip, ".") {
|
||||
ip = "[" + ip + "]"
|
||||
}
|
||||
conn, err := net.DialTimeout("tcp", ip+":"+port, time.Second*1)
|
||||
if err != nil {
|
||||
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
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package wshandle
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetFastIP(t *testing.T) {
|
||||
GetFastIP("api.leo.moe", "443")
|
||||
}
|
||||
Reference in New Issue
Block a user