mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bef8b918a | ||
|
|
690f7346d0 | ||
|
|
d82a521954 | ||
|
|
0b7d01fb21 | ||
|
|
2fed1d152f | ||
|
|
98c0fa38ca | ||
|
|
cb63e9b61c | ||
|
|
e4626c4f82 | ||
|
|
47698be2ed | ||
|
|
ee0ebb126c | ||
|
|
1b59210eb1 | ||
|
|
ed518d175b | ||
|
|
9f67ea5e9b | ||
|
|
d98d2f7ee5 | ||
|
|
e0b874a687 | ||
|
|
b39657ca5b | ||
|
|
c79387ff75 | ||
|
|
e6b2d975b4 | ||
|
|
33c1af9f50 | ||
|
|
da825c18cc | ||
|
|
4adba2b59d | ||
|
|
65d8ac5c14 | ||
|
|
7472398c26 | ||
|
|
b2870a823f | ||
|
|
f82af5f9c5 | ||
|
|
9eda2d2a30 | ||
|
|
1a4a15eb74 | ||
|
|
f5556fea5d | ||
|
|
d760e75810 | ||
|
|
99409089b2 | ||
|
|
2040497d89 | ||
|
|
29ce61b24b | ||
|
|
701abc3447 | ||
|
|
f08778c862 | ||
|
|
8697325193 | ||
|
|
32b0f15e78 | ||
|
|
90e349eeed | ||
|
|
f275edba3a | ||
|
|
2974002c02 | ||
|
|
9b2fc9b570 | ||
|
|
960ab9687c | ||
|
|
07623ce4fd | ||
|
|
3164acfccf | ||
|
|
8b30ef39dd | ||
|
|
239b1c2f8d | ||
|
|
ed3c158e87 | ||
|
|
02348f08c0 | ||
|
|
9c7402accb | ||
|
|
a6848f8f23 | ||
|
|
29b7c668a4 | ||
|
|
86eacc006e | ||
|
|
81d6df8b82 | ||
|
|
dbe42d669c | ||
|
|
a7089e8d54 | ||
|
|
bcda750a66 | ||
|
|
1580c6111f | ||
|
|
ae16731bbe | ||
|
|
c532dfd05c | ||
|
|
bb522ed859 | ||
|
|
9e5cd736c8 | ||
|
|
b11d2334c2 | ||
|
|
84a03e8a10 | ||
|
|
761d506613 | ||
|
|
e33548d2ce | ||
|
|
1266f62aba | ||
|
|
8e837072c6 | ||
|
|
cf0639111d | ||
|
|
56cd0022f7 | ||
|
|
1407c499b9 | ||
|
|
f25662d481 | ||
|
|
4f94f01e87 | ||
|
|
1460ad67c0 | ||
|
|
d6dcfc8dc5 | ||
|
|
190b3ab94e | ||
|
|
87fa850d2d | ||
|
|
c71a26d018 | ||
|
|
79c39b655a | ||
|
|
580612ce08 | ||
|
|
ebe2a0f8e9 | ||
|
|
ca2a7a8dc8 | ||
|
|
b68b768a65 |
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -17,6 +17,11 @@ copyright: [v2fly](https://github.com/v2fly)
|
||||
除非特殊情况,请完整填写所有问题。不按模板发的 issue 将直接被关闭。
|
||||
如果你遇到的问题不是 nexttrace 的 bug,比如你不清楚如何配置,请在 https://github.com/nxtrace/NTrace-core/discussions 进行讨论。
|
||||
-->
|
||||
## 本项目是基于Linux/macOS的,请确认您遇到的问题是否在Linux或macOS上存在。
|
||||
|
||||
<!-- 是/否 -->
|
||||
<!-- 对于只出现在Windows上的问题,本项目有时无法解决,请知悉 -->
|
||||
|
||||
|
||||
## 你正在使用哪个版本的 nexttrace?
|
||||
|
||||
@@ -39,6 +44,8 @@ copyright: [v2fly](https://github.com/v2fly)
|
||||
|
||||
## 请附上出错时软件输出的错误信息
|
||||
|
||||
<!-- 是/否 -->
|
||||
|
||||
## 是否查询过本仓库wiki有没有类似错误
|
||||
|
||||
<!-- wiki: https://github.com/sjlleo/nexttrace/wiki -->
|
||||
<!-- wiki: https://github.com/nxtrace/NTrace-core/wiki -->
|
||||
|
||||
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@@ -75,6 +75,12 @@ jobs:
|
||||
goarch: mipsle
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
- goos: linux
|
||||
goarch: mipsle
|
||||
gomips: softfloat
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
gomips: softfloat
|
||||
# END MIPS
|
||||
# BEGIN PPC
|
||||
- goos: linux
|
||||
@@ -104,6 +110,7 @@ jobs:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GOMIPS: ${{ matrix.gomips }}
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
@@ -117,7 +124,10 @@ jobs:
|
||||
if [ "$GOOS" == "windows" ]; then
|
||||
export _NAME="$_NAME.exe"
|
||||
fi
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, RELEASE_NAME: $_NAME"
|
||||
if [ "$GOMIPS" == "softfloat" ]; then
|
||||
export _NAME="${_NAME}_softfolat"
|
||||
fi
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||
echo "BUILD_VERSION=$(git describe --tags --always)" >> $GITHUB_ENV
|
||||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||
@@ -166,7 +176,7 @@ jobs:
|
||||
git config --global user.name "${{ secrets.git_name }}"
|
||||
- name: Clone repo
|
||||
run: |
|
||||
git clone https://github.com/xgadget-lab/homebrew-nexttrace.git
|
||||
git clone https://github.com/nxtrace/homebrew-nexttrace.git
|
||||
- name: Exec scipt
|
||||
run: |
|
||||
cd homebrew-nexttrace
|
||||
@@ -183,7 +193,7 @@ jobs:
|
||||
run: |
|
||||
cd homebrew-nexttrace
|
||||
git commit -am 'Publish a new version with Formula' || true
|
||||
git remote set-url origin https://${{ secrets.gt_token }}@github.com/xgadget-lab/homebrew-nexttrace.git
|
||||
git remote set-url origin https://${{ secrets.gt_token }}@github.com/nxtrace/homebrew-nexttrace.git
|
||||
git push
|
||||
# env:
|
||||
# SSH_AUTH_SOCK: /tmp/ssh_agent.sock
|
||||
|
||||
4
.github/workflows/publishNewFormula.yml
vendored
4
.github/workflows/publishNewFormula.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
git config --global user.name "${{ secrets.git_name }}"
|
||||
- name: Clone repo
|
||||
run: |
|
||||
git clone https://github.com/xgadget-lab/homebrew-nexttrace.git
|
||||
git clone https://github.com/nxtrace/homebrew-nexttrace.git
|
||||
- name: Exec scipt
|
||||
run: |
|
||||
cd homebrew-nexttrace
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
run: |
|
||||
cd homebrew-nexttrace
|
||||
git commit -am 'Publish a new version with Formula' || true
|
||||
git remote set-url origin https://${{ secrets.gt_token }}@github.com/xgadget-lab/homebrew-nexttrace.git
|
||||
git remote set-url origin https://${{ secrets.gt_token }}@github.com/nxtrace/homebrew-nexttrace.git
|
||||
git push
|
||||
# env:
|
||||
# SSH_AUTH_SOCK: /tmp/ssh_agent.sock
|
||||
|
||||
77
README.md
77
README.md
@@ -37,7 +37,7 @@
|
||||
</a>
|
||||
|
||||
<a href="https://skywolf.cloud" >
|
||||
<img src="https://github.com/nxtrace/Ntrace-core/assets/59512455/19b659f4-31f5-4816-9821-bf2a73c60336" width="170.7" height="62.9">
|
||||
<img src="https://hk.skywolf.cloud/assets/img/skywolf.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,10 @@ We are extremely grateful to [DMIT](https://dmit.io) and [Misaka](https://misaka
|
||||
|
||||
Document Language: English | [简体中文](README_zh_CN.md)
|
||||
|
||||
⚠️ This is the README for the V1 Enhanced version of NextTrace.
|
||||
⚠️ Please note: We welcome PR submissions from the community, but please submit your PRs to the [NTrace-V1](https://github.com/nxtrace/NTrace-V1) repository instead of [NTrace-core](https://github.com/nxtrace/NTrace-core) repository.<br>
|
||||
Regarding the NTrace-V1 and NTrace-core repositories:<br>
|
||||
Both will largely remain consistent with each other. All development work is done within the NTrace-V1 repository. The NTrace-V1 repository releases new versions first. After running stably for an undetermined period, we will synchronize that version to NTrace-core. This means that the NTrace-V1 repository serves as a "beta" or "testing" version.<br>
|
||||
Please note, there are exceptions to this synchronization. If a version of NTrace-V1 encounters a serious bug, NTrace-core will skip that flawed version and synchronize directly to the next version that resolves the issue.
|
||||
|
||||
### Automated Install
|
||||
|
||||
@@ -58,20 +61,25 @@ Document Language: English | [简体中文](README_zh_CN.md)
|
||||
bash -c "$(curl http://nexttrace-io-leomoe-api-a0.shop/nt_install_v1.sh)"
|
||||
```
|
||||
* Arch Linux AUR installation command
|
||||
* Build from source
|
||||
|
||||
```shell
|
||||
yay -S nexttrace
|
||||
```
|
||||
* Directly download bin package (only supports amd64)
|
||||
|
||||
```shell
|
||||
yay -S nexttrace-bin
|
||||
```
|
||||
* The two types of AUR builds are maintained by huyz and ouuan, respectively
|
||||
* The AUR builds are maintained by ouuan
|
||||
* Linuxbrew's installation command
|
||||
|
||||
Same as the macOS Homebrew's installation method (homebrew-core version only supports amd64)
|
||||
* Deepin installation command
|
||||
|
||||
```shell
|
||||
apt install nexttrace
|
||||
```
|
||||
* Termux installation command
|
||||
|
||||
```shell
|
||||
pkg install nexttrace-enhanced
|
||||
```
|
||||
|
||||
* macOS
|
||||
* macOS Homebrew's installation command
|
||||
@@ -83,7 +91,7 @@ Document Language: English | [简体中文](README_zh_CN.md)
|
||||
* This repository's ACTIONS automatically built version (updates faster)
|
||||
|
||||
```shell
|
||||
brew tap xgadget-lab/nexttrace && brew install xgadget-lab/nexttrace/nexttrace
|
||||
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
|
||||
|
||||
@@ -112,10 +120,15 @@ Document Language: English | [简体中文](README_zh_CN.md)
|
||||
After installing Go >= 1.20 yourself, you can use the following command to install
|
||||
|
||||
```shell
|
||||
go install github.com/nxtrace/Ntrace-V1@latest
|
||||
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
|
||||
|
||||
@@ -134,7 +147,7 @@ nexttrace --table 1.0.0.1
|
||||
nexttrace --raw 1.0.0.1
|
||||
nexttrace --json 1.0.0.1
|
||||
|
||||
# IPv4/IPv6 Resolve Only
|
||||
# IPv4/IPv6 Resolve Only, and automatically select the first IP when there are multiple IPs
|
||||
nexttrace --ipv4 g.co
|
||||
nexttrace --ipv6 g.co
|
||||
|
||||
@@ -164,6 +177,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
|
||||
@@ -203,6 +227,8 @@ nexttrace --parallel-requests 1 www.hkix.net
|
||||
|
||||
# Start Trace with TTL of 5, end at TTL of 10
|
||||
nexttrace --first 5 --max-hops 10 www.decix.net
|
||||
# In addition, an ENV is provided to set whether to hide the destination IP
|
||||
export NEXTTRACE_ENABLEHIDDENDSTIP=1
|
||||
|
||||
# Turn off the IP reverse parsing function
|
||||
nexttrace --no-rdns www.bbix.net
|
||||
@@ -251,8 +277,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
|
||||
@@ -278,15 +304,16 @@ Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
|
||||
<integer>] [--parallel-requests <integer>] [-m|--max-hops
|
||||
<integer>] [-d|--data-provider
|
||||
(Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)]
|
||||
[-n|--no-rdns] [-a|--always-rdns] [-P|--route-path]
|
||||
[-r|--report] [--dn42] [-o|--output] [-t|--table] [--raw]
|
||||
[-j|--json] [-c|--classic] [-f|--first <integer>] [-M|--map]
|
||||
[--pow-provider (api.leo.moe|sakura)] [-n|--no-rdns]
|
||||
[-a|--always-rdns] [-P|--route-path] [-r|--report] [--dn42]
|
||||
[-o|--output] [-t|--table] [--raw] [-j|--json] [-c|--classic]
|
||||
[-f|--first <integer>] [-M|--map] [-e|--disable-mpls]
|
||||
[-v|--version] [-s|--source "<value>"] [-D|--dev "<value>"]
|
||||
[-R|--route] [-z|--send-time <integer>] [-i|--ttl-time
|
||||
<integer>] [--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_31 "<value>"] [--dot-server
|
||||
[-z|--send-time <integer>] [-i|--ttl-time <integer>]
|
||||
[--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_32 "<value>"] [--dot-server
|
||||
(dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language
|
||||
(en|cn)]
|
||||
(en|cn)] [--file "<value>"]
|
||||
|
||||
Arguments:
|
||||
|
||||
@@ -343,7 +370,6 @@ Arguments:
|
||||
-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 how many [milliseconds] between
|
||||
sending each packet.. Useful when some
|
||||
routers use rate-limit for ICMP messages.
|
||||
@@ -357,11 +383,12 @@ Arguments:
|
||||
connection.. Default: 1000
|
||||
--psize Set the packet size (payload size).
|
||||
Default: 52
|
||||
--_positionalArg_nexttrace_31 IP Address or domain name
|
||||
--_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
|
||||
```
|
||||
|
||||
## Project screenshot
|
||||
@@ -428,7 +455,7 @@ We hope you can give us as much feedback as possible on IP geolocation errors (s
|
||||
Although other third-party APIs are integrated in this project, please refer to the official website of the third-party APIs for specific TOS and AUP. If you encounter IP data errors, please contact them directly to correct them.
|
||||
|
||||
For feedback related to corrections about IP information, we currently have two channels available:
|
||||
>- [IP 错误报告汇总帖](https://github.com/sjlleo/nexttrace/issues/41) in the GITHUB ISSUES section of this project (Recommended)
|
||||
>- [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?
|
||||
@@ -436,4 +463,4 @@ How to obtain the freshly baked binary executable of the latest commit?
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#sjlleo/nexttrace&Date)
|
||||
[](https://star-history.com/#nxtrace/NTrace-core&Date)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</a>
|
||||
|
||||
<a href="https://skywolf.cloud" >
|
||||
<img src="https://github.com/nxtrace/Ntrace-core/assets/59512455/19b659f4-31f5-4816-9821-bf2a73c60336" width="170.7" height="62.9">
|
||||
<img src="https://hk.skywolf.cloud/assets/img/skywolf.svg" width="170.7" height="62.9">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -50,11 +50,14 @@
|
||||
|
||||
Document Language: [English](README.md) | 简体中文
|
||||
|
||||
⚠️ 这是NextTrace V1 增强版的README文件。
|
||||
⚠️ 请注意:我们欢迎来自社区的PR提交,但是请将您的PR提交至 [NTrace-V1](https://github.com/nxtrace/NTrace-V1) 仓库,而不是 [NTrace-core](https://github.com/nxtrace/NTrace-core) 仓库。<br>
|
||||
关于NTrace-V1和NTrace-core两个仓库的说明:<br>
|
||||
二者将大体上保持一致。所有的开发工作均在NTrace-V1仓库中进行。NTrace-V1仓库首先发布新版本,在稳定运行一段时间后(时长不定),我们会把版本同步至NTrace-core。这意味着NTrace-V1仓库充当了一个“测试版”的角色。<br>
|
||||
请注意,版本同步也存在例外。如果NTrace-V1的某个版本出现了严重的bug,NTrace-core会跳过这一有缺陷的版本,直接同步到下一个修复了该问题的版本。
|
||||
|
||||
### Before Using
|
||||
|
||||
使用 NextTrace 之前,我们建议您先阅读 [#IP 数据以及精准度说明](https://github.com/sjlleo/nexttrace/blob/main/README_zh_CN.md#ip-%E6%95%B0%E6%8D%AE%E4%BB%A5%E5%8F%8A%E7%B2%BE%E5%87%86%E5%BA%A6%E8%AF%B4%E6%98%8E),在了解您自己的对数据精准度需求以后再进行抉择。
|
||||
使用 NextTrace 之前,我们建议您先阅读 [#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 中。
|
||||
|
||||
@@ -68,20 +71,26 @@ Document Language: [English](README.md) | 简体中文
|
||||
```
|
||||
|
||||
* Arch Linux AUR 安装命令
|
||||
* 由源码构建
|
||||
|
||||
```shell
|
||||
yay -S nexttrace
|
||||
```
|
||||
* 直接下载bin包(仅支持amd64)
|
||||
|
||||
```shell
|
||||
yay -S nexttrace-bin`
|
||||
```
|
||||
* AUR 的2种构建分别由 huyz 和 ouuan 维护
|
||||
* AUR 的构建分别由 ouuan 维护
|
||||
* Linuxbrew 安装命令
|
||||
|
||||
同macOS Homebrew安装方法(homebrew-core版仅支持amd64)
|
||||
* Deepin 安装命令
|
||||
|
||||
```shell
|
||||
apt install nexttrace
|
||||
```
|
||||
* Termux 安装命令
|
||||
|
||||
```shell
|
||||
pkg install nexttrace-enhanced
|
||||
```
|
||||
|
||||
|
||||
* macOS
|
||||
* macOS Homebrew 安装命令
|
||||
@@ -93,7 +102,7 @@ Document Language: [English](README.md) | 简体中文
|
||||
* 本仓库ACTIONS自动构建版(更新更快)
|
||||
|
||||
```shell
|
||||
brew tap xgadget-lab/nexttrace && brew install xgadget-lab/nexttrace/nexttrace
|
||||
brew tap nxtrace/nexttrace && brew install nxtrace/nexttrace/nexttrace
|
||||
```
|
||||
* homebrew-core 构建由 chenrui333 维护,请注意该版本更新可能会落后仓库Action自动构建版本
|
||||
|
||||
@@ -122,9 +131,16 @@ Document Language: [English](README.md) | 简体中文
|
||||
您可在自行安装Go >= 1.20后,使用以下命令安装
|
||||
|
||||
```shell
|
||||
go install github.com/nxtrace/Ntrace-V1@latest
|
||||
go install github.com/nxtrace/NTrace-core@latest
|
||||
```
|
||||
安装后可执行文件在`$GOPATH/bin`目录下,如果您没有设置`GOPATH`,则在`$HOME/go/bin`目录下。
|
||||
*由于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
|
||||
```
|
||||
|
||||
|
||||
### Get Started
|
||||
@@ -144,7 +160,7 @@ nexttrace --table 1.0.0.1
|
||||
nexttrace --raw 1.0.0.1
|
||||
nexttrace --json 1.0.0.1
|
||||
|
||||
# 只进行IPv4/IPv6解析
|
||||
# 只进行IPv4/IPv6解析,且当多个IP时自动选择第一个IP
|
||||
nexttrace --ipv4 g.co
|
||||
nexttrace --ipv6 g.co
|
||||
|
||||
@@ -174,6 +190,17 @@ nexttrace --fast-trace
|
||||
|
||||
# 也可以使用 TCP SYN 而非 ICMP 进行测试
|
||||
nexttrace --fast-trace --tcp
|
||||
|
||||
# 也可以通过自定义的IP/DOMAIN列表文件进行快速测试
|
||||
nexttrace --file /path/to/your/iplist.txt
|
||||
# 自定义的IP/DOMAIN列表文件格式
|
||||
## 一行一个IP/DOMAIN + 空格 + 描述信息(可选)
|
||||
## 例如:
|
||||
## 106.37.67.1 北京电信
|
||||
## 240e:928:101:31a::1 北京电信
|
||||
## bj.10086.cn 北京移动
|
||||
## 2409:8080:0:1::1
|
||||
## 223.5.5.5
|
||||
```
|
||||
|
||||
`NextTrace` 已支持指定网卡进行路由跟踪
|
||||
@@ -216,6 +243,8 @@ nexttrace --parallel-requests 1 www.hkix.net
|
||||
|
||||
# 从TTL为5开始发送探测包,直到TTL为10结束
|
||||
nexttrace --first 5 --max-hops 10 www.decix.net
|
||||
# 此外还提供了一个ENV,可以设置是否隐匿目的IP
|
||||
export NEXTTRACE_ENABLEHIDDENDSTIP=1
|
||||
|
||||
# 关闭IP反向解析功能
|
||||
nexttrace --no-rdns www.bbix.net
|
||||
@@ -264,7 +293,7 @@ nexttrace -tcp --queries 2 --parallel-requests 1 --table --route-path 2001:4860:
|
||||
|
||||
Equivalent to:
|
||||
nexttrace -d ip-api.com -m 20 -T -p 443 -q 5 -n 1.1.1.1
|
||||
nexttrace -T -q 2 --parallel-requests 1 -t -R 2001:4860:4860::8888
|
||||
nexttrace -T -q 2 --parallel-requests 1 -t -P 2001:4860:4860::8888
|
||||
```
|
||||
|
||||
### 全部用法详见 Usage 菜单
|
||||
@@ -275,15 +304,16 @@ Usage: nexttrace [-h|--help] [-4|--ipv4] [-6|--ipv6] [-T|--tcp] [-U|--udp]
|
||||
<integer>] [--parallel-requests <integer>] [-m|--max-hops
|
||||
<integer>] [-d|--data-provider
|
||||
(Ip2region|ip2region|IP.SB|ip.sb|IPInfo|ipinfo|IPInsight|ipinsight|IPAPI.com|ip-api.com|IPInfoLocal|ipinfolocal|chunzhen|LeoMoeAPI|leomoeapi|disable-geoip)]
|
||||
[-n|--no-rdns] [-a|--always-rdns] [-P|--route-path]
|
||||
[-r|--report] [--dn42] [-o|--output] [-t|--table] [--raw]
|
||||
[-j|--json] [-c|--classic] [-f|--first <integer>] [-M|--map]
|
||||
[--pow-provider (api.leo.moe|sakura)] [-n|--no-rdns]
|
||||
[-a|--always-rdns] [-P|--route-path] [-r|--report] [--dn42]
|
||||
[-o|--output] [-t|--table] [--raw] [-j|--json] [-c|--classic]
|
||||
[-f|--first <integer>] [-M|--map] [-e|--disable-mpls]
|
||||
[-v|--version] [-s|--source "<value>"] [-D|--dev "<value>"]
|
||||
[-R|--route] [-z|--send-time <integer>] [-i|--ttl-time
|
||||
<integer>] [--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_31 "<value>"] [--dot-server
|
||||
[-z|--send-time <integer>] [-i|--ttl-time <integer>]
|
||||
[--timeout <integer>] [--psize <integer>]
|
||||
[_positionalArg_nexttrace_32 "<value>"] [--dot-server
|
||||
(dnssb|aliyun|dnspod|google|cloudflare)] [-g|--language
|
||||
(en|cn)]
|
||||
(en|cn)] [--file "<value>"]
|
||||
|
||||
Arguments:
|
||||
|
||||
@@ -340,7 +370,6 @@ Arguments:
|
||||
-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 how many [milliseconds] between
|
||||
sending each packet.. Useful when some
|
||||
routers use rate-limit for ICMP messages.
|
||||
@@ -354,11 +383,12 @@ Arguments:
|
||||
connection.. Default: 1000
|
||||
--psize Set the packet size (payload size).
|
||||
Default: 52
|
||||
--_positionalArg_nexttrace_31 IP Address or domain name
|
||||
--_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
|
||||
```
|
||||
|
||||
## 项目截图
|
||||
@@ -369,7 +399,7 @@ Arguments:
|
||||
|
||||
## 第三方 IP 数据库 API 开发接口
|
||||
|
||||
NextTrace 所有的的 IP 地理位置 `API DEMO` 可以参考[这里](https://github.com/sjlleo/nexttrace/blob/main/ipgeo/)
|
||||
NextTrace 所有的的 IP 地理位置 `API DEMO` 可以参考[这里](https://github.com/nxtrace/NTrace-core/blob/main/ipgeo/)
|
||||
|
||||
你可以在这里添加你自己的 API 接口,为了 NextTrace 能够正确显示你接口中的内容,请参考 `leo.go` 中所需要的信息
|
||||
|
||||
@@ -430,7 +460,7 @@ nexttrace --pow-provider sakura
|
||||
## IP 数据以及精准度说明
|
||||
|
||||
对于IP相关信息的纠错反馈,我们目前开放了两个渠道:
|
||||
>- 本项目的GITHUB ISSUES区中的[IP 错误报告汇总帖](https://github.com/sjlleo/nexttrace/issues/41)
|
||||
>- 本项目的GITHUB ISSUES区中的[IP 错误报告汇总帖](https://github.com/nxtrace/NTrace-core/issues/41)
|
||||
>- 本项目的纠错专用邮箱: `correction@moeqing.com` (请注意此邮箱仅供IP相关信息纠错专用,其他反馈请发送ISSUE)
|
||||
|
||||
NextTrace 有多个数据源可以选择,目前默认使用的 LeoMoeAPI 为我们项目维护的数据源。
|
||||
@@ -488,3 +518,9 @@ LAX,US,California,Los Anegles
|
||||
```
|
||||
|
||||
需要注意的是,NextTrace 支持自动匹配 CSV 中的城市名,如果您的 PTR 记录中有 `losangeles`,您可以只添加上面一条记录就可以正常识别并读取。
|
||||
|
||||
rkflow in GitHub Actions.
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#nxtrace/NTrace-core&Date)
|
||||
|
||||
32
cmd/cmd.go
32
cmd/cmd.go
@@ -59,7 +59,7 @@ func Excute() {
|
||||
ver := parser.Flag("v", "version", &argparse.Options{Help: "Print version info and exit"})
|
||||
srcAddr := parser.String("s", "source", &argparse.Options{Help: "Use source src_addr for outgoing packets"})
|
||||
srcDev := parser.String("D", "dev", &argparse.Options{Help: "Use the following Network Devices as the source address in outgoing packets"})
|
||||
router := parser.Flag("R", "route", &argparse.Options{Help: "Show Routing Table [Provided By BGP.Tools]"})
|
||||
//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."})
|
||||
@@ -69,6 +69,7 @@ func Excute() {
|
||||
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"})
|
||||
|
||||
err := parser.Parse(os.Args)
|
||||
if err != nil {
|
||||
@@ -91,7 +92,7 @@ func Excute() {
|
||||
*port = 80
|
||||
}
|
||||
|
||||
if *fast_trace {
|
||||
if *fast_trace || *file != "" {
|
||||
var paramsFastTrace = fastTrace.ParamsFastTrace{
|
||||
SrcDev: *srcDev,
|
||||
SrcAddr: *srcAddr,
|
||||
@@ -102,6 +103,7 @@ func Excute() {
|
||||
Lang: *lang,
|
||||
PktSize: *packetSize,
|
||||
Timeout: time.Duration(*timeout) * time.Millisecond,
|
||||
File: *file,
|
||||
}
|
||||
|
||||
fastTrace.FastTest(*tcp, *output, paramsFastTrace)
|
||||
@@ -119,7 +121,12 @@ func Excute() {
|
||||
}
|
||||
|
||||
if strings.Contains(domain, "/") {
|
||||
domain = strings.Split(domain, "/")[2]
|
||||
parts := strings.Split(domain, "/")
|
||||
if len(parts) < 3 {
|
||||
fmt.Println("Invalid input")
|
||||
return
|
||||
}
|
||||
domain = parts[2]
|
||||
}
|
||||
|
||||
if strings.Contains(domain, "]") {
|
||||
@@ -176,21 +183,26 @@ func Excute() {
|
||||
//
|
||||
//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 = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
} else {
|
||||
if *ipv6Only {
|
||||
ip = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
|
||||
ip, err = util.DomainLookUp(domain, "6", *dot, *jsonPrint)
|
||||
} else if *ipv4Only {
|
||||
ip = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
ip, err = util.DomainLookUp(domain, "4", *dot, *jsonPrint)
|
||||
} else {
|
||||
ip = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
|
||||
ip, err = util.DomainLookUp(domain, "all", *dot, *jsonPrint)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
//}()
|
||||
//
|
||||
//wg.Wait()
|
||||
@@ -218,7 +230,7 @@ func Excute() {
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin, *maxHops, *packetSize)
|
||||
}
|
||||
|
||||
var m trace.Method = ""
|
||||
var m trace.Method
|
||||
|
||||
switch {
|
||||
case *tcp:
|
||||
@@ -233,6 +245,7 @@ func Excute() {
|
||||
*port = 53
|
||||
}
|
||||
|
||||
util.DestIP = ip.String()
|
||||
var conf = trace.Config{
|
||||
DN42: *dn42,
|
||||
SrcAddr: *srcAddr,
|
||||
@@ -252,6 +265,9 @@ func Excute() {
|
||||
PktSize: *packetSize,
|
||||
}
|
||||
|
||||
// 暂时弃用
|
||||
router := new(bool)
|
||||
*router = false
|
||||
if !*tablePrint {
|
||||
if *classicPrint {
|
||||
conf.RealtimePrinter = printer.ClassicPrinter
|
||||
|
||||
@@ -13,45 +13,49 @@ import (
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
var pFastTracer ParamsFastTrace
|
||||
//var pFastTracer ParamsFastTrace
|
||||
|
||||
func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
|
||||
fp, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func(fp *os.File) {
|
||||
err := fp.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(fp)
|
||||
|
||||
log.SetOutput(fp)
|
||||
log.SetFlags(0)
|
||||
fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX)
|
||||
log.Printf("『%s %s 』\n", location, ispCollection.ISPName)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, pFastTracer.MaxHops, pFastTracer.PktSize)
|
||||
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IPv6, pFastTracer.MaxHops, pFastTracer.PktSize)
|
||||
ip := util.DomainLookUp(ispCollection.IPv6, "6", "", true)
|
||||
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: pFastTracer.BeginHop,
|
||||
BeginHop: f.ParamsFastTrace.BeginHop,
|
||||
DestIP: ip,
|
||||
DestPort: 80,
|
||||
MaxHops: pFastTracer.MaxHops,
|
||||
MaxHops: f.ParamsFastTrace.MaxHops,
|
||||
NumMeasurements: 3,
|
||||
ParallelRequests: 18,
|
||||
RDns: pFastTracer.RDns,
|
||||
AlwaysWaitRDNS: pFastTracer.AlwaysWaitRDNS,
|
||||
RDns: f.ParamsFastTrace.RDns,
|
||||
AlwaysWaitRDNS: f.ParamsFastTrace.AlwaysWaitRDNS,
|
||||
PacketInterval: 100,
|
||||
TTLInterval: 500,
|
||||
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
|
||||
Timeout: pFastTracer.Timeout,
|
||||
PktSize: pFastTracer.PktSize,
|
||||
Lang: pFastTracer.Lang,
|
||||
Timeout: f.ParamsFastTrace.Timeout,
|
||||
SrcAddr: f.ParamsFastTrace.SrcAddr,
|
||||
PktSize: f.ParamsFastTrace.PktSize,
|
||||
Lang: f.ParamsFastTrace.Lang,
|
||||
}
|
||||
|
||||
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
|
||||
@@ -63,7 +67,7 @@ func (f *FastTracer) tracert_v6(location string, ispCollection ISPCollection) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
println()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func (f *FastTracer) testAll_v6() {
|
||||
@@ -114,7 +118,6 @@ func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
var c string
|
||||
|
||||
oe = outEnable
|
||||
pFastTracer = paramsFastTrace
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网\n6. 全部")
|
||||
fmt.Print("请选择选项:")
|
||||
@@ -123,7 +126,9 @@ func FastTestv6(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
c = "1"
|
||||
}
|
||||
|
||||
ft := FastTracer{}
|
||||
ft := FastTracer{
|
||||
ParamsFastTrace: paramsFastTrace,
|
||||
}
|
||||
|
||||
// 建立 WebSocket 连接
|
||||
w := wshandle.New()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fastTrace
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
"github.com/nxtrace/NTrace-core/printer"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -30,29 +32,25 @@ type ParamsFastTrace struct {
|
||||
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 func(fp *os.File) {
|
||||
err := fp.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(fp)
|
||||
|
||||
log.SetOutput(fp)
|
||||
log.SetFlags(0)
|
||||
fmt.Printf("%s『%s %s 』%s\n", printer.YELLOW_PREFIX, location, ispCollection.ISPName, printer.RESET_PREFIX)
|
||||
log.Printf("『%s %s 』\n", location, ispCollection.ISPName)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
log.Printf("traceroute to %s, %d hops max, %d byte packets\n", ispCollection.IP, f.ParamsFastTrace.MaxHops, f.ParamsFastTrace.PktSize)
|
||||
ip := util.DomainLookUp(ispCollection.IP, "4", "", true)
|
||||
|
||||
ip, err := util.DomainLookUp(ispCollection.IP, "4", "", true)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var conf = trace.Config{
|
||||
BeginHop: f.ParamsFastTrace.BeginHop,
|
||||
DestIP: ip,
|
||||
@@ -72,6 +70,21 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -82,13 +95,19 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
println()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
// tm means tcp mode
|
||||
var c string
|
||||
pFastTrace := paramsFastTrace
|
||||
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("请选择选项:")
|
||||
@@ -97,16 +116,41 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
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 pFastTrace.SrcDev != "" {
|
||||
dev, _ := net.InterfaceByName(pFastTrace.SrcDev)
|
||||
if paramsFastTrace.SrcDev != "" {
|
||||
dev, _ := net.InterfaceByName(paramsFastTrace.SrcDev)
|
||||
if addrs, err := dev.Addrs(); err == nil {
|
||||
for _, addr := range addrs {
|
||||
if addr.(*net.IPNet).IP.To4() != nil {
|
||||
pFastTrace.SrcAddr = addr.(*net.IPNet).IP.String()
|
||||
if (addr.(*net.IPNet).IP.To4() == nil) == false {
|
||||
paramsFastTrace.SrcAddr = addr.(*net.IPNet).IP.String()
|
||||
// 检查是否是内网IP
|
||||
if !(net.ParseIP(paramsFastTrace.SrcAddr).IsPrivate() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLoopback() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalUnicast() ||
|
||||
net.ParseIP(paramsFastTrace.SrcAddr).IsLinkLocalMulticast()) {
|
||||
// 若不是则跳出
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +164,7 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
}
|
||||
|
||||
ft := FastTracer{
|
||||
ParamsFastTrace: pFastTrace,
|
||||
ParamsFastTrace: paramsFastTrace,
|
||||
}
|
||||
|
||||
// 建立 WebSocket 连接
|
||||
@@ -156,6 +200,171 @@ func FastTest(tm bool, outEnable bool, paramsFastTrace ParamsFastTrace) {
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
f.testCT()
|
||||
println()
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
package fastTrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"testing"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/trace"
|
||||
"github.com/nxtrace/NTrace-core/wshandle"
|
||||
)
|
||||
|
||||
func TestTrace(t *testing.T) {
|
||||
pFastTrace := ParamsFastTrace{
|
||||
SrcDev: "",
|
||||
SrcAddr: "",
|
||||
BeginHop: 1,
|
||||
MaxHops: 30,
|
||||
RDns: false,
|
||||
AlwaysWaitRDNS: false,
|
||||
Lang: "",
|
||||
PktSize: 52,
|
||||
}
|
||||
ft := FastTracer{ParamsFastTrace: pFastTrace}
|
||||
// 建立 WebSocket 连接
|
||||
w := wshandle.New()
|
||||
w.Interrupt = make(chan os.Signal, 1)
|
||||
signal.Notify(w.Interrupt, os.Interrupt)
|
||||
defer func() {
|
||||
w.Conn.Close()
|
||||
}()
|
||||
fmt.Println("TCP v4")
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
//pFastTrace := ParamsFastTrace{
|
||||
// SrcDev: "",
|
||||
// SrcAddr: "",
|
||||
// BeginHop: 1,
|
||||
// MaxHops: 30,
|
||||
// RDns: false,
|
||||
// AlwaysWaitRDNS: false,
|
||||
// Lang: "",
|
||||
// PktSize: 52,
|
||||
//}
|
||||
//ft := FastTracer{ParamsFastTrace: pFastTrace}
|
||||
//// 建立 WebSocket 连接
|
||||
//w := wshandle.New()
|
||||
//w.Interrupt = make(chan os.Signal, 1)
|
||||
//signal.Notify(w.Interrupt, os.Interrupt)
|
||||
//defer func() {
|
||||
// w.Conn.Close()
|
||||
//}()
|
||||
//fmt.Println("TCP v4")
|
||||
//ft.TracerouteMethod = trace.TCPTrace
|
||||
//ft.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
//fmt.Println("TCP v6")
|
||||
//ft.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
|
||||
30
go.mod
30
go.mod
@@ -6,41 +6,45 @@ require (
|
||||
github.com/akamensky/argparse v1.4.0
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/oschwald/maxminddb-golang v1.12.0
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/spf13/viper v1.17.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
github.com/tsosunchia/powclient v0.1.3
|
||||
golang.org/x/net v0.15.0
|
||||
golang.org/x/sync v0.3.0
|
||||
github.com/tsosunchia/powclient v0.1.4
|
||||
golang.org/x/net v0.18.0
|
||||
golang.org/x/sync v0.5.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
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.19 // 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.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/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
golang.org/x/text v0.13.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/fatih/color v1.15.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
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/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.12.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
65
go.sum
65
go.sum
@@ -49,20 +49,21 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
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/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.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
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=
|
||||
@@ -124,8 +125,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/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=
|
||||
@@ -150,8 +151,8 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -162,30 +163,32 @@ github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/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/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/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/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
||||
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
||||
github.com/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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -205,8 +208,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tsosunchia/powclient v0.1.3 h1:L29HQdyZZ0Vcrn0ZzNIyrTpLOnycm3oHF46sYJ+vkhQ=
|
||||
github.com/tsosunchia/powclient v0.1.3/go.mod h1:Pm4MP3QqN74SfNskPpFIEyT+NQrcABGoXkkeRwjlMEE=
|
||||
github.com/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=
|
||||
@@ -217,6 +220,8 @@ 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=
|
||||
@@ -234,6 +239,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
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=
|
||||
@@ -289,8 +296,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/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=
|
||||
@@ -310,8 +317,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/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=
|
||||
@@ -348,10 +355,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/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=
|
||||
@@ -361,8 +367,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/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=
|
||||
@@ -506,8 +512,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/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=
|
||||
|
||||
@@ -158,7 +158,7 @@ func Filter(ip string) (*IPGeoData, bool) {
|
||||
default:
|
||||
}
|
||||
// 判断是否为v6 且不在2000::/3
|
||||
if net.ParseIP(ip).To4() == nil && !cidrRangeContains("2000::/3", ip) {
|
||||
if net.ParseIP(ip).To4() == nil && !cidrRangeContains("2000::/3", ip) && !isFiltered {
|
||||
asn = ""
|
||||
whois = "INVALID"
|
||||
isFiltered = true
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestXxx(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
res, err := Filter("::1")
|
||||
res, err := Filter("fd11::1")
|
||||
//打印whois信息
|
||||
fmt.Println(res.Whois)
|
||||
print(err)
|
||||
|
||||
@@ -23,10 +23,13 @@ func GetToken(fastIp string, host string, port string) (string, error) {
|
||||
if proxyUrl != nil {
|
||||
getTokenParams.Proxy = proxyUrl
|
||||
}
|
||||
var err error
|
||||
var (
|
||||
token string
|
||||
err error
|
||||
)
|
||||
// 尝试三次RetToken,如果都失败了,异常退出
|
||||
for i := 0; i < 3; i++ {
|
||||
token, err := powclient.RetToken(getTokenParams)
|
||||
token, err = powclient.RetToken(getTokenParams)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -38,5 +41,5 @@ func GetToken(fastIp string, host string, port string) (string, error) {
|
||||
}
|
||||
fmt.Println("RetToken failed 3 times, please try again after a while, exit")
|
||||
os.Exit(1)
|
||||
return "", nil
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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"
|
||||
@@ -23,53 +24,69 @@ 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%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", "· MoeQing.io"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", formatted),
|
||||
)
|
||||
}
|
||||
|
||||
func PluginCopyRight() {
|
||||
fmt.Fprintf(color.Output, "%s\n%s %s\n\n",
|
||||
color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Map Plugin Author"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", "Tso"),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"),
|
||||
)
|
||||
}
|
||||
//func PluginCopyRight() {
|
||||
// fmt.Fprintf(color.Output, "%s\n%s %s\n\n",
|
||||
// color.New(color.FgGreen, color.Bold).Sprintf("%s", "NextTrace Map Plugin Author"),
|
||||
// color.New(color.FgWhite, color.Bold).Sprintf("%s", "Tso"),
|
||||
// color.New(color.FgHiBlack, color.Bold).Sprintf("%s", "tsosunchia@gmail.com"),
|
||||
// )
|
||||
//}
|
||||
|
||||
func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string, maxHops int, packetSize int) {
|
||||
fmt.Println("IP Geo Data Provider: " + dataOrigin)
|
||||
|
||||
if ip.String() == domain {
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", ip.String(), maxHops, packetSize)
|
||||
if 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), %d hops max, %d bytes packets\n", ip.String(), domain, maxHops, packetSize)
|
||||
fmt.Printf("traceroute to %s, %d hops max, %d bytes packets\n", util.HideIPPart(ip.String()), maxHops, packetSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/util"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
|
||||
func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1))
|
||||
|
||||
// 去重
|
||||
var latestIP string
|
||||
tmpMap := make(map[string][]string)
|
||||
@@ -51,13 +51,25 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
fmt.Printf("%4s", "")
|
||||
}
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip),
|
||||
)
|
||||
if util.EnableHidDstIP == "" || ip != util.DestIP {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", util.HideIPPart(ip)),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
|
||||
)
|
||||
if util.EnableHidDstIP == "" || ip != util.DestIP {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", util.HideIPPart(ip)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
i, _ := strconv.Atoi(v[0])
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/trace/internal"
|
||||
)
|
||||
|
||||
type ICMPTracer struct {
|
||||
@@ -39,6 +41,7 @@ func (t *ICMPTracer) PrintFunc() {
|
||||
if t.AsyncPrinter != nil {
|
||||
t.AsyncPrinter(&t.res)
|
||||
}
|
||||
// 接收的时候检查一下是不是 3 跳都齐了
|
||||
if len(t.res.Hops)-1 > ttl {
|
||||
if len(t.res.Hops[ttl]) == t.NumMeasurements {
|
||||
if t.RealtimePrinter != nil {
|
||||
@@ -66,7 +69,7 @@ func (t *ICMPTracer) Execute() (*Result, error) {
|
||||
|
||||
var err error
|
||||
|
||||
t.icmpListen, err = net.ListenPacket("ip4:1", t.SrcAddr)
|
||||
t.icmpListen, err = internal.ListenICMP("ip4:1", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -164,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)
|
||||
}
|
||||
@@ -176,6 +182,12 @@ func (t *ICMPTracer) listenICMP() {
|
||||
}
|
||||
|
||||
func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte, ttl int) {
|
||||
if icmpType == 2 {
|
||||
if t.DestIP.String() != msg.Peer.String() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.inflightRequestRWLock.RLock()
|
||||
defer t.inflightRequestRWLock.RUnlock()
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv6"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/trace/internal"
|
||||
)
|
||||
|
||||
type ICMPTracerv6 struct {
|
||||
@@ -30,7 +32,7 @@ type ICMPTracerv6 struct {
|
||||
}
|
||||
|
||||
func (t *ICMPTracerv6) PrintFunc() {
|
||||
// defer t.wg.Done()
|
||||
defer t.wg.Done()
|
||||
var ttl = t.Config.BeginHop - 1
|
||||
for {
|
||||
if t.AsyncPrinter != nil {
|
||||
@@ -44,12 +46,12 @@ func (t *ICMPTracerv6) PrintFunc() {
|
||||
t.RealtimePrinter(&t.res, ttl)
|
||||
}
|
||||
ttl++
|
||||
if ttl == t.final {
|
||||
|
||||
if ttl == t.final-1 || ttl >= t.MaxHops-1 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<-time.After(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -65,7 +67,7 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
|
||||
|
||||
var err error
|
||||
|
||||
t.icmpListen, err = net.ListenPacket("ip6:58", t.SrcAddr)
|
||||
t.icmpListen, err = internal.ListenICMP("ip6:58", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -78,6 +80,7 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
|
||||
t.final = -1
|
||||
|
||||
go t.listenICMP()
|
||||
t.wg.Add(1)
|
||||
go t.PrintFunc()
|
||||
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
|
||||
t.inflightRequestRWLock.Lock()
|
||||
@@ -189,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)
|
||||
}
|
||||
@@ -232,6 +237,11 @@ func (t *ICMPTracerv6) listenICMP() {
|
||||
}
|
||||
|
||||
func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte, ttl int) {
|
||||
if icmpType == 2 {
|
||||
if t.DestIP.String() != msg.Peer.String() {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.inflightRequestRWLock.RLock()
|
||||
defer t.inflightRequestRWLock.RUnlock()
|
||||
|
||||
|
||||
@@ -12,13 +12,14 @@ import (
|
||||
)
|
||||
|
||||
//go:linkname internetSocket net.internetSocket
|
||||
func internetSocket(ctx context.Context, net string, laddr, raddr interface{}, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
|
||||
func internetSocket(ctx context.Context, net string, laddr, raddr any, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
|
||||
|
||||
//go:linkname newIPConn net.newIPConn
|
||||
func newIPConn(fd unsafe.Pointer) *net.IPConn
|
||||
|
||||
var (
|
||||
errUnknownNetwork = errors.New("unknown network type")
|
||||
errUnknownIface = errors.New("unknown network interface")
|
||||
|
||||
networkMap = map[string]string{
|
||||
"ip4:icmp": "udp4",
|
||||
@@ -28,7 +29,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// ListenICMP 会造成指定出口IP功能不可使用
|
||||
func ListenICMP(network string, laddr string) (net.PacketConn, error) {
|
||||
if os.Getuid() == 0 { // root
|
||||
return net.ListenPacket(network, laddr)
|
||||
@@ -38,7 +38,48 @@ func ListenICMP(network string, laddr string) (net.PacketConn, error) {
|
||||
if nw == "udp6" {
|
||||
proto = syscall.IPPROTO_ICMPV6
|
||||
}
|
||||
isock, err := internetSocket(context.Background(), nw, nil, nil, syscall.SOCK_DGRAM, proto, "listen", nil)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -257,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)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/nxtrace/NTrace-core/ipgeo"
|
||||
@@ -92,7 +93,11 @@ func Traceroute(method Method, config Config) (*Result, error) {
|
||||
default:
|
||||
return &Result{}, ErrInvalidMethod
|
||||
}
|
||||
return tracer.Execute()
|
||||
result, err := tracer.Execute()
|
||||
if err != nil && errors.Is(err, syscall.EPERM) {
|
||||
err = fmt.Errorf("%w, please run as root", err)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
@@ -141,7 +146,7 @@ func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
h.Hostname = r[0][:len(r[0])-1]
|
||||
ip = h.Address.String() + "," + h.Hostname
|
||||
}
|
||||
h.Geo, err = c.IPGeoSource(ip, c.Timeout, c.Lang, c.Maptrace)
|
||||
h.Geo, _ = c.IPGeoSource(ip, c.Timeout, c.Lang, c.Maptrace)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -221,9 +226,9 @@ func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
selectClose = true
|
||||
}
|
||||
|
||||
// When Select Close, fetchDoneChan Reciever will also be closed
|
||||
// When Select Close, fetchDoneChan Received will also be closed
|
||||
if selectClose {
|
||||
// New a reciever to prevent channel congestion
|
||||
// New a receiver to prevent channel congestion
|
||||
<-fetchDoneChan
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ func GetMapUrl(r string) (string, error) {
|
||||
fastIp := "api.leo.moe"
|
||||
// 如果 host 是一个 IP 使用默认域名
|
||||
if valid := net.ParseIP(host); valid != nil {
|
||||
fastIp = host
|
||||
if len(strings.Split(fastIp, ":")) > 1 {
|
||||
fastIp = "[" + fastIp + "]"
|
||||
}
|
||||
host = "api.leo.moe"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
|
||||
@@ -13,8 +13,8 @@ func TestDNS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDomainLookUp(t *testing.T) {
|
||||
ips := DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "all", "", false)
|
||||
ips, _ := DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "all", "", false)
|
||||
fmt.Println(ips)
|
||||
ips = DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "4", "", false)
|
||||
ips, _ = DomainLookUp("pek-4134.nexttrace-io-fasttrace-endpoint.win.", "4", "", false)
|
||||
fmt.Println(ips)
|
||||
}
|
||||
|
||||
104
util/latency.go
104
util/latency.go
@@ -1,18 +1,27 @@
|
||||
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 (
|
||||
result string
|
||||
results = make(chan string)
|
||||
results = make(chan ResponseInfo)
|
||||
timeout = 5 * time.Second
|
||||
)
|
||||
var FastIpCache = ""
|
||||
|
||||
@@ -30,50 +39,83 @@ func GetFastIP(domain string, port string, enableOutput bool) string {
|
||||
log.Fatal("DNS resolution failed, please check your system DNS Settings")
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
go checkLatency(ip.String(), port)
|
||||
if len(ips) == 0 {
|
||||
// 添加默认IP 45.88.195.154
|
||||
ips = append(ips, net.ParseIP("45.88.195.154"))
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
go checkLatency(domain, ip.String(), port)
|
||||
}
|
||||
|
||||
var result ResponseInfo
|
||||
|
||||
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, "-")
|
||||
//等待5s没有结果 视为连不上API了
|
||||
case <-time.After(timeout):
|
||||
log.Println("IP connection has been timeout, please check your network")
|
||||
|
||||
if len(ips) > 1 {
|
||||
if enableOutput {
|
||||
_, _ = fmt.Fprintf(color.Output, "%s prefered API IP - %s - %s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("[NextTrace API]"),
|
||||
color.New(color.FgGreen, color.Bold).Sprintf("%s", res[0]),
|
||||
color.New(color.FgCyan, color.Bold).Sprintf("%sms", res[1]),
|
||||
)
|
||||
}
|
||||
}
|
||||
FastIpCache = res[0]
|
||||
return res[0]
|
||||
|
||||
//if len(ips) > 0 {
|
||||
if enableOutput {
|
||||
_, _ = fmt.Fprintf(color.Output, "%s preferred API IP - %s - %s - %s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("[NextTrace API]"),
|
||||
color.New(color.FgGreen, color.Bold).Sprintf("%s", result.IP),
|
||||
color.New(color.FgCyan, color.Bold).Sprintf("%sms", result.Latency),
|
||||
color.New(color.FgGreen, color.Bold).Sprintf("%s", result.Content),
|
||||
)
|
||||
}
|
||||
//}
|
||||
FastIpCache = result.IP
|
||||
return result.IP
|
||||
}
|
||||
|
||||
func checkLatency(ip string, port string) {
|
||||
func checkLatency(domain string, ip string, port string) {
|
||||
start := time.Now()
|
||||
if !strings.Contains(ip, ".") {
|
||||
ip = "[" + ip + "]"
|
||||
}
|
||||
conn, err := net.DialTimeout("tcp", ip+":"+port, time.Second*1)
|
||||
|
||||
// 自定义DialContext以使用指定的IP连接
|
||||
transport := &http.Transport{
|
||||
//DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
// return net.DialTimeout(network, addr, 1*time.Second)
|
||||
//},
|
||||
TLSClientConfig: &tls.Config{
|
||||
ServerName: domain,
|
||||
},
|
||||
TLSHandshakeTimeout: timeout,
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
//此处虽然是 https://domain/ 但是实际上会使用指定的IP连接
|
||||
req, err := http.NewRequest("GET", "https://"+ip+":"+port+"/", nil)
|
||||
if err != nil {
|
||||
// !!! 此处不要给results返回任何值
|
||||
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
|
||||
return
|
||||
}
|
||||
defer func(conn net.Conn) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(conn)
|
||||
if result == "" {
|
||||
result = fmt.Sprintf("%s-%.2f", ip, float64(time.Since(start))/float64(time.Millisecond))
|
||||
results <- result
|
||||
req.Host = domain
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
//results <- ResponseInfo{IP: ip, Latency: "error", Content: ""}
|
||||
return
|
||||
}
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
latency := fmt.Sprintf("%.2f", float64(time.Since(start))/float64(time.Millisecond))
|
||||
results <- ResponseInfo{IP: ip, Latency: latency, Content: bodyString}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetFastIP(t *testing.T) {
|
||||
GetFastIP("api.leo.moe", "443", true)
|
||||
}
|
||||
//github action test 不支持v6 这里会报错
|
||||
//func TestGetFastIP(t *testing.T) {
|
||||
// GetFastIP("api.leo.moe", "443", true)
|
||||
//}
|
||||
|
||||
36
util/util.go
36
util/util.go
@@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/nxtrace/NTrace-core/config"
|
||||
"log"
|
||||
@@ -21,6 +22,8 @@ var UserAgent = fmt.Sprintf("NextTrace %s/%s/%s", config.Version, runtime.GOOS,
|
||||
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) {
|
||||
// 如果在缓存中找到,直接返回
|
||||
@@ -75,7 +78,7 @@ func LocalIPPortv6(dstip net.IP) (net.IP, int) {
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput bool) net.IP {
|
||||
func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput bool) (net.IP, error) {
|
||||
// ipVersion: 4, 6, all
|
||||
var (
|
||||
r *net.Resolver
|
||||
@@ -101,8 +104,7 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput
|
||||
ips = append(ips, net.ParseIP(v))
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("Domain " + host + " Lookup Fail.")
|
||||
os.Exit(1)
|
||||
return nil, errors.New("DNS lookup failed")
|
||||
}
|
||||
|
||||
//var ipv6Flag = false
|
||||
@@ -119,16 +121,18 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput
|
||||
var filteredIPs []net.IP
|
||||
for _, ip := range ips {
|
||||
if ipVersion == "4" && ip.To4() != nil {
|
||||
filteredIPs = append(filteredIPs, ip)
|
||||
filteredIPs = []net.IP{ip}
|
||||
break
|
||||
} else if ipVersion == "6" && strings.Contains(ip.String(), ":") {
|
||||
filteredIPs = append(filteredIPs, ip)
|
||||
filteredIPs = []net.IP{ip}
|
||||
break
|
||||
}
|
||||
}
|
||||
ips = filteredIPs
|
||||
}
|
||||
|
||||
if (len(ips) == 1) || (disableOutput) {
|
||||
return ips[0]
|
||||
return ips[0], nil
|
||||
} else {
|
||||
fmt.Println("Please Choose the IP You Want To TraceRoute")
|
||||
for i, ip := range ips {
|
||||
@@ -147,13 +151,17 @@ func DomainLookUp(host string, ipVersion string, dotServer string, disableOutput
|
||||
fmt.Println("Your Option is invalid")
|
||||
os.Exit(3)
|
||||
}
|
||||
return ips[index]
|
||||
return ips[index], nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetenvDefault(key, defVal string) string {
|
||||
val, ok := os.LookupEnv(key)
|
||||
if ok {
|
||||
_, ok := os.LookupEnv("NEXTTRACE_DEBUG")
|
||||
if ok {
|
||||
fmt.Println("ENV", key, "detected as", val)
|
||||
}
|
||||
return val
|
||||
}
|
||||
return defVal
|
||||
@@ -220,3 +228,17 @@ func StringInSlice(val string, list []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func HideIPPart(ip string) string {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if parsedIP.To4() != nil {
|
||||
// IPv4: 隐藏后16位
|
||||
return strings.Join(strings.Split(ip, ".")[:2], ".") + ".0.0/16"
|
||||
}
|
||||
// IPv6: 隐藏后96位
|
||||
return parsedIP.Mask(net.CIDRMask(32, 128)).String() + "/32"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -187,6 +188,10 @@ func createWsConn() *WsConn {
|
||||
host, port = util.GetHostAndPort()
|
||||
// 如果 host 是一个 IP 使用默认域名
|
||||
if valid := net.ParseIP(host); valid != nil {
|
||||
fastIp = host
|
||||
if len(strings.Split(fastIp, ":")) > 1 {
|
||||
fastIp = "[" + fastIp + "]"
|
||||
}
|
||||
host = "api.leo.moe"
|
||||
} else {
|
||||
// 默认配置完成,开始寻找最优 IP
|
||||
|
||||
Reference in New Issue
Block a user