mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9b18f5efa | ||
|
|
326034b41e | ||
|
|
09a5b42443 | ||
|
|
b8542489b6 | ||
|
|
a73a306d0a | ||
|
|
801f42801a | ||
|
|
08c4a5ceae | ||
|
|
ce1c773996 | ||
|
|
cdec6cbd8d | ||
|
|
aa891ebd7c | ||
|
|
5d132e73ab | ||
|
|
63ca4d0418 | ||
|
|
a6da078eb0 | ||
|
|
7ee76591b4 | ||
|
|
2d95fed6b2 | ||
|
|
29b1d7b283 | ||
|
|
1e654d1400 | ||
|
|
1746068302 | ||
|
|
45c30ddb8d | ||
|
|
88fef52e71 | ||
|
|
491f774336 | ||
|
|
b435a36ee4 | ||
|
|
ba87933580 | ||
|
|
6bf243794b | ||
|
|
44d7d5a024 | ||
|
|
5f096964dc | ||
|
|
69b893a587 | ||
|
|
ab2462bead | ||
|
|
d9d60d09b2 | ||
|
|
91cd4fb8f4 | ||
|
|
c592c14f28 | ||
|
|
15829c7041 | ||
|
|
336151dc1b | ||
|
|
66ee62f22b | ||
|
|
3afd28cb89 | ||
|
|
690f546ff0 | ||
|
|
d561063a7c | ||
|
|
473ce3c5f2 | ||
|
|
a1783e3563 | ||
|
|
7b9912f23f | ||
|
|
fdeaf112f5 | ||
|
|
07a2aac7c7 | ||
|
|
08f8daf9ce | ||
|
|
7872e9ee0f | ||
|
|
991f66cfe4 | ||
|
|
5b91fac860 | ||
|
|
62e877e248 | ||
|
|
9e4b2ae577 | ||
|
|
b02572d6ff | ||
|
|
e5742e1603 | ||
|
|
70a727bee6 | ||
|
|
1261e243f2 | ||
|
|
49ce0cba8e | ||
|
|
9764533c8e | ||
|
|
d752385c29 | ||
|
|
bf005fb37a | ||
|
|
8dc5960d98 |
@@ -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 openbsd/amd64 openbsd/arm64 freebsd/amd64 freebsd/arm64"
|
||||
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"
|
||||
|
||||
BUILD_VERSION="$(git describe --tags --always)"
|
||||
BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -41,6 +41,8 @@ jobs:
|
||||
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
|
||||
|
||||
1
.github/workflows/publishNewFormula.yml
vendored
1
.github/workflows/publishNewFormula.yml
vendored
@@ -3,6 +3,7 @@ name: Publish New Formula
|
||||
# Controls when the action will run. Workflow runs when manually triggered using the UI
|
||||
# or API.
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
|
||||
146
README.md
146
README.md
@@ -4,30 +4,73 @@
|
||||
|
||||
</div>
|
||||
|
||||
## NextTrace Lite
|
||||
|
||||
Document Language: English | [简体中文](README_zh_CN.md)
|
||||
## NextTrace
|
||||
|
||||
An open source visual routing tool that pursues light weight, developed using Golang.
|
||||
|
||||
NextTrace has a total of 2 versions, the Lite version focusing on lightweight and the [Enhanced version](#nexttrace-enhanced) which is more enthusiast-oriented.
|
||||
2022/12/18: Due to time and effort, it is becoming more and more difficult to maintain 2 branches at the same time, so I will be phasing out support for the NextTrace Enhanced version in the near future. I will resume updating the `Enhanced` version when I have more time.
|
||||
|
||||
PS: Our Lite version does not provide OSM based geolocation visualization, we provide this parameter in the enhanced version if needed.
|
||||
## 公告
|
||||
|
||||
我今天看到了一些非常难过的事情,一些用户在 BestTrace 和 WorstTrace 下面宣传 NextTrace 的完全可替代性。
|
||||
|
||||
这么做是不正确的,NextTrace 从来都不是一个从零开始的软件,NextTrace 之所以能够拥有某些功能特性,是因为吸取了 BestTrace 、WorstTrace 的一些想法。
|
||||
|
||||
我们希望您在使用的时候知晓这一点,**我们是站在巨人的肩膀上,而尊重其他软件作者,向他们或者是我们提交 Bug 或贡献代码,才是推动整个 traceroute 工具的软件多样化发展的最好方式**。
|
||||
|
||||
NextTrace 并不追求成为一个替代者,同类软件越多样化,才能满足更多人的需求,这才是我们希望看到的,而去诋毁其他软件,这违背了我们对于开发 NextTrace 的初衷。
|
||||
|
||||
我们希望看到这条公告的朋友应该主动删除自己过激的言论,如果您有任何问题或建议,请随时在我们的社区中发表。
|
||||
|
||||
## 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.
|
||||
|
||||
3. In terms of development, I would like to credit missuo and zhshch for their help with Go cross-compilation, design concepts and TCP/UDP Traceroute refactoring, and tsosunchia for their support on TraceMap.
|
||||
|
||||
4. I would also like to credit FFEE_CO, TheresaQWQ, stydxm and others for their help. leoMoeAPI has received a lot of support since its first release, so I would like to credit them all!
|
||||
|
||||
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.
|
||||
|
||||
## How To Use
|
||||
|
||||
Document Language: English | [简体中文](README_zh_CN.md)
|
||||
|
||||
### Automated Installation
|
||||
|
||||
```bash
|
||||
# Linux one-click install script
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)
|
||||
|
||||
# macOS brew install command
|
||||
brew tap xgadget-lab/nexttrace && brew install nexttrace
|
||||
```
|
||||
|
||||
Windows users please go to [Release Page](https://github.com/sjlleo/nexttrace/releases/latest) directly and download exe file.
|
||||
|
||||
- `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 not available on `Windows` platform.
|
||||
- Some of the necessary dependencies of this project are not fully implemented in `Golang` on `Windows`, so currently `NextTrace` is experimental on `Windows` platform.
|
||||
|
||||
### Get Started
|
||||
|
||||
@@ -38,20 +81,41 @@ brew tap xgadget-lab/nexttrace && brew install nexttrace
|
||||
nexttrace 1.0.0.1
|
||||
|
||||
# Form printing (output all hops at one time, wait 20-40 seconds)
|
||||
nexttrace -table 1.0.0.1
|
||||
nexttrace --table 1.0.0.1
|
||||
|
||||
# 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
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
Note that in LeoMoeAPI 2.0, due to the addition of geographical location data, **we have deprecated the online query part of the OpenStreetMap API in the traceMap plugin and are using location information from our own database**.
|
||||
|
||||
The routing visualization function requires the geographical coordinates of each Hop, but third-party APIs generally do not provide this information, so this function is currently only supported when used with LeoMoeAPI.
|
||||
|
||||
`NextTrace` now supports quick testing, and friends who have a one-time backhaul routing test requirement can use it
|
||||
|
||||
```bash
|
||||
# IPv4 ICMP Fast Test (Beijing + Shanghai + Guangzhou + Hangzhou) in China Telecom / Unicom / Mobile / Education Network
|
||||
nexttrace -f
|
||||
nexttrace -F
|
||||
|
||||
# You can also use TCP SYN for testing
|
||||
nexttrace -f -T
|
||||
nexttrace -F -T
|
||||
```
|
||||
|
||||
`NextTrace` already supports route tracing for specified Network Devices
|
||||
|
||||
```bash
|
||||
# Use eth0 network interface
|
||||
nexttrace --dev eth0 2606:4700:4700::1111
|
||||
|
||||
# Use eth0 network interface's IP
|
||||
# When using the network interface's IP for route tracing, note that the IP type to be traced should be the same as network interface's IP type (e.g. both IPv4)
|
||||
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
|
||||
@@ -79,7 +143,7 @@ nexttrace -q 2 www.hkix.net
|
||||
nexttrace -r 1 www.hkix.net
|
||||
|
||||
# Start Trace with TTL of 5, end at TTL of 10
|
||||
nexttrace -b 5 -m 10 www.decix.net
|
||||
nexttrace -f 5 -m 10 www.decix.net
|
||||
|
||||
# Turn off the IP reverse parsing function
|
||||
nexttrace -n www.bbix.net
|
||||
@@ -93,7 +157,7 @@ nexttrace -n www.bbix.net
|
||||
# ╰AS36776 Five9 Inc.「Philippines『Metro Manila』」
|
||||
# ╭╯
|
||||
# ╰AS37963 Aliyun「ALIDNS.COM『ALIDNS.COM』」
|
||||
nexttrace -report www.time.com.my
|
||||
nexttrace --route-path www.time.com.my
|
||||
```
|
||||
|
||||
`NextTrace` supports users to select their own IP API (currently supports: `LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`)
|
||||
@@ -111,12 +175,14 @@ nexttrace -d IP.SB
|
||||
|
||||
```bash
|
||||
Example:
|
||||
nexttrace -d IPInsight -m 20 -p 443 -q 5 -r 20 -rdns 1.1.1.1
|
||||
nexttrace -T -q 2 -r 1 -table -report 2001:4860:4860::8888
|
||||
nexttrace --data-provider LeoMoeAPI -m 20 -p 443 -q 5 -n 1.1.1.1
|
||||
nexttrace -T -q 2 -r 1 --table --route-path 2001:4860:4860::8888
|
||||
```
|
||||
|
||||
### IP Database
|
||||
|
||||
#### We use [bgp.tools](https://bgp.tools) as a data provider for routing tables.
|
||||
|
||||
NextTrace BackEnd is now open-source.
|
||||
|
||||
https://github.com/sjlleo/nexttrace-backend
|
||||
@@ -154,7 +220,10 @@ Options:
|
||||
|
||||
## Project screenshot
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
## NextTrace Enhanced
|
||||
|
||||
@@ -164,6 +233,8 @@ The `Enhanced` version supports many functions that the `lite` version does not
|
||||
|
||||
https://github.com/OwO-Network/nexttrace-enhanced
|
||||
|
||||
#
|
||||
|
||||
## 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
|
||||
@@ -175,12 +246,14 @@ Here is our recommended troubleshooting process:
|
||||
|
||||
## JetBrain Support
|
||||
|
||||
### This Project uses [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport). We Proudly Develop By Goland.
|
||||
#### This Project uses [JetBrain Open-Source Project License](https://jb.gg/OpenSourceSupport). We Proudly Develop By Goland.
|
||||
|
||||

|
||||
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" title="" alt="GoLand logo" width="331">
|
||||
|
||||
## Credits
|
||||
|
||||
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)
|
||||
|
||||
[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
|
||||
@@ -189,44 +262,7 @@ Here is our recommended troubleshooting process:
|
||||
|
||||
[waiting4new](https://github.com/waiting4new)
|
||||
|
||||
[FFEE_CO](https://github.com/fkx4-p)
|
||||
|
||||
## IP Database Copyright
|
||||
|
||||
### IPv4 Database
|
||||
|
||||
#### China
|
||||
|
||||
| ISP | Type | Data Source | Proportion |
|
||||
|:---------------------------:|:--------:|:--------------------:|:----------:|
|
||||
| China Telecom/Unicom/Mobile | Backbone | Internet Enthusiasts | 10% |
|
||||
| China Telecom/Unicom/Mobile | Local | Avon Technology | 90% |
|
||||
|
||||
#### WorldWide
|
||||
|
||||
##### Tier 1
|
||||
|
||||
| ISP | Type | Data Source | Proportion |
|
||||
|:------:|:--------:|:---------------:|:----------:|
|
||||
| Tier 1 | Backbone | IPInfo | 2% |
|
||||
| Tier 1 | Backbone | Avon Technology | 3% |
|
||||
| Tier 1 | Backbone | IPInSight | 5% |
|
||||
| Tier 1 | Local | IPInSight | 90% |
|
||||
|
||||
##### General
|
||||
|
||||
| ISP | Type | Data Source | Proportion |
|
||||
|:-------:|:--------:|:-----------:|:----------:|
|
||||
| General | Backbone | IPInSight | 5% |
|
||||
| General | Local | IPInSight | 95% |
|
||||
|
||||
### IPv6 Database
|
||||
|
||||
| ISP | Type | Data Source | Proportion |
|
||||
|:---:|:----:|:----------------:|:----------:|
|
||||
| All | All | IP2Location Lite | 100% |
|
||||
|
||||
This product includes IP2Location LITE data available from <a href="https://lite.ip2location.com">https://lite.ip2location.com</a>.
|
||||
[FFEE_CO](https://github.com/fkx4-p)
|
||||
|
||||
### Others
|
||||
|
||||
|
||||
112
README_zh_CN.md
112
README_zh_CN.md
@@ -4,13 +4,13 @@
|
||||
|
||||
</div>
|
||||
|
||||
## NextTrace Lite
|
||||
## NextTrace
|
||||
|
||||
一款追求轻量的开源可视化路由跟踪工具,使用 Golang 开发。
|
||||
|
||||
NextTrace 一共有2个版本,专注于轻量的 Lite 版本以及更面向发烧友的 [Enhanced 版本](#nexttrace-enhanced)。
|
||||
如果您对 NextTrace 项目本身感兴趣,可以阅读 [有关 NextTrace 的一些碎碎念](https://leo.moe/annoucement/nexttrace.html) 或许可以帮您解决疑惑。
|
||||
|
||||
PS: Lite 版本追求轻量化,并不提供基于高德地图 / OpenStreetMap 的路由可视化功能,如有需要,请使用 Enhanced 版本。
|
||||
2022/12/18: 由于时间精力的关系,同时维护2个分支变得愈发困难,近期我将逐步停止 NextTrace Enhanced 版本的支持,如有觉得 NextTrace Enhanced 一些不错的功能,可以在 issue 里面提出来。待我重新有富裕时间时重新恢复对 `Enhanced` 版本的更新。
|
||||
|
||||
## How To Use
|
||||
|
||||
@@ -18,17 +18,19 @@ PS: Lite 版本追求轻量化,并不提供基于高德地图 / OpenStreetMap
|
||||
|
||||
```bash
|
||||
# Linux 一键安装脚本
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)
|
||||
|
||||
# GHPROXY 镜像(国内使用)
|
||||
bash <(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
|
||||
bash <(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/sjlleo/nexttrace/main/nt_install.sh)
|
||||
|
||||
# macOS brew 安装命令
|
||||
brew tap xgadget-lab/nexttrace && brew install nexttrace
|
||||
```
|
||||
|
||||
Windows 用户请直接前往 [Release](https://github.com/sjlleo/nexttrace/releases/latest) 下载编译后的二进制 exe 文件。
|
||||
|
||||
- `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
|
||||
- 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台不可用。
|
||||
- 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台出于实验性支持阶段。
|
||||
|
||||
### Get Started
|
||||
|
||||
@@ -39,20 +41,43 @@ brew tap xgadget-lab/nexttrace && brew install nexttrace
|
||||
nexttrace 1.0.0.1
|
||||
|
||||
# 表格打印(一次性输出全部跳数,需等待20-40秒)
|
||||
nexttrace -table 1.0.0.1
|
||||
nexttrace --table 1.0.0.1
|
||||
|
||||
# IPv6 ICMP Trace
|
||||
nexttrace 2606:4700:4700::1111
|
||||
|
||||
# 路径可视化 使用 -M 参数,将返回一个地图 URL
|
||||
nexttrace -M koreacentral.blob.core.windows.net
|
||||
# MapTrace URL: https://api.leo.moe/tracemap/html/c14e439e-3250-5310-8965-42a1e3545266.html
|
||||
```
|
||||
|
||||
PS: 路由可视化的绘制模块由 [@tsosunchia](https://github.com/tsosunchia) 同学编写,具体代码可在 [tsosunchia/traceMap](https://github.com/tsosunchia/traceMap) 查看
|
||||
|
||||
需要注意的是,在 LeoMoeAPI 2.0 中,由于新增了了地理位置数据,**我们已经弃用 traceMap 插件中 OpenStreetMap API 的在线查询的部分,并且使用自己数据库内的位置信息**。
|
||||
|
||||
路由可视化功能因为需要每个 Hop 的地理位置坐标,而第三方 API 通常不提供此类信息,所以此功能目前只支持搭配 LeoMoeAPI 使用。
|
||||
|
||||
`NextTrace` 现已经支持快速测试,有一次性测试回程路由需求的朋友可以使用
|
||||
|
||||
```bash
|
||||
# 北上广(电信+联通+移动+教育网)IPv4 ICMP 快速测试
|
||||
nexttrace -f
|
||||
nexttrace -F
|
||||
|
||||
# 也可以使用 TCP SYN 而非 ICMP 进行测试
|
||||
nexttrace -f -T
|
||||
nexttrace -F -T
|
||||
```
|
||||
|
||||
`NextTrace` 已支持指定网卡进行路由跟踪
|
||||
|
||||
```bash
|
||||
# 请注意 Lite 版本此参数不能和快速测试联用,如有需要请使用 enhanced 版本
|
||||
# 使用 eth0 网卡
|
||||
nexttrace -D eth0 2606:4700:4700::1111
|
||||
|
||||
# 使用 eth0 网卡IP
|
||||
# 网卡 IP 可以使用 ip a 或者 ifconfig 获取
|
||||
# 使用网卡IP进行路由跟踪时需要注意跟踪的IP类型应该和网卡IP类型一致(如都为 IPv4)
|
||||
nexttrace -S 204.98.134.56 9.9.9.9
|
||||
```
|
||||
|
||||
`NextTrace` 也可以使用`TCP`和`UDP`协议发起`Traceroute`请求,不过目前只支持`IPv4`
|
||||
@@ -94,7 +119,7 @@ nexttrace -n www.bbix.net
|
||||
# ╰AS36776 Five9 Inc.「Philippines『Metro Manila』」
|
||||
# ╭╯
|
||||
# ╰AS37963 阿里云「ALIDNS.COM『ALIDNS.COM』」
|
||||
nexttrace -report www.time.com.my
|
||||
nexttrace --route-path www.time.com.my
|
||||
```
|
||||
|
||||
`NextTrace`支持用户自主选择 IP 数据库(目前支持:`LeoMoeAPI`, `IP.SB`, `IPInfo`, `IPInsight`, `IPAPI.com`)
|
||||
@@ -112,12 +137,14 @@ nexttrace -d IP.SB
|
||||
|
||||
```bash
|
||||
Example:
|
||||
nexttrace -d IPInsight -m 20 -p 443 -q 5 -r 20 -rdns 1.1.1.1
|
||||
nexttrace -T -q 2 -r 1 -table -report 2001:4860:4860::8888
|
||||
nexttrace --data-provider LeoMoeAPI -m 20 -p 443 -q 5 --parallel-requests 20 -n 1.1.1.1
|
||||
nexttrace -T -q 2 --table --route-path 2001:4860:4860::8888
|
||||
```
|
||||
|
||||
### IP 数据库
|
||||
|
||||
我们使用[bgp.tools](https://bgp.tools)作为路由表功能的数据提供者。
|
||||
|
||||
✨NextTrace `LeoMoeAPI` 的后端也开源啦
|
||||
|
||||
[GitHub - sjlleo/nexttrace-backend: NextTrace BackEnd](https://github.com/sjlleo/nexttrace-backend)
|
||||
@@ -130,6 +157,10 @@ NextTrace 所有的的 IP 地理位置`API DEMO`可以参考[这里](https://git
|
||||
Usage of nexttrace:
|
||||
'nexttrace [options] <hostname>' or 'nexttrace <hostname> [option...]'
|
||||
Options:
|
||||
-D string
|
||||
Use the following Network Devices as the source address in outgoing packets
|
||||
-S string
|
||||
Use the following IP address as the source address in outgoing packets
|
||||
-T Use TCP SYN for tracerouting (default port is 80)
|
||||
-U Use UDP Package for tracerouting (default port is 53 in UDP)
|
||||
-V Print Version
|
||||
@@ -155,7 +186,9 @@ Options:
|
||||
|
||||
## 项目截图
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
## NextTrace Enhanced
|
||||
|
||||
@@ -163,19 +196,14 @@ Options:
|
||||
|
||||
`Enhanced` 版本支持很多`lite`版本没有的功能,如能够自定义设置超时时间,也能指定TTL作为起点进行路由跟踪等,对于普通用户来说,通常`lite`版本已经足够完成大部分需要。
|
||||
|
||||
**很遗憾,由于时间精力的关系,`Enhanced` 版本将在很长一段时间不会再收到新的更新补丁,我们将继续维护 `Standard` 版本。**
|
||||
|
||||
https://github.com/OwO-Network/nexttrace-enhanced
|
||||
|
||||
## FAQ 常见问题
|
||||
|
||||
如果你在安装或者使用的时候遇到了问题,我们建议你不要把新建一个 `issue` 作为首选项
|
||||
|
||||
以下是我们推荐的排错流程:
|
||||
|
||||
1. 查看是否为常见问题 -> [前往 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. 疑似 BUG、或者功能建议 -> [前往 Github Issues](https://github.com/xgadget-lab/nexttrace/issues)
|
||||
|
||||
## Thanks
|
||||
|
||||
BGP.TOOLS 提供了本项目的一些数据支持,在此表示由衷地感谢。
|
||||
|
||||
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
|
||||
|
||||
[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
|
||||
@@ -186,46 +214,6 @@ https://github.com/OwO-Network/nexttrace-enhanced
|
||||
|
||||
[FFEE_CO](https://github.com/fkx4-p)
|
||||
|
||||
## IP Database Copyright
|
||||
|
||||
### IPv4 Database
|
||||
|
||||
#### China
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
|:--------:|:---:|:-----:|:---:|
|
||||
| 电信/联通/移动 | 骨干网 | 网络爱好者 | 10% |
|
||||
| 电信/联通/移动 | 城域网 | 埃文科技 | 90% |
|
||||
|
||||
- 参与骨干网维护的朋友都是网络爱好者群体,尽管我们多名志愿者通过自己的网络进行了大量的勘测,但是由于信息不足,依旧存在很多错误。
|
||||
- 对于更高精度的朋友,我们依旧强烈推荐IPIP.NET,他们开发的Besttrace是目前质量最好的路由可视化软件,我们多数爱好者能有今天这样的骨干网初步认知都是归功于他们,在此特表感谢。
|
||||
|
||||
#### WorldWide
|
||||
|
||||
##### Tier 01
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
|:-------:|:---:|:---------:|:---:|
|
||||
| Tier-01 | 骨干网 | IPInfo | 2% |
|
||||
| Tier-01 | 骨干网 | 埃文科技 | 3% |
|
||||
| Tier-01 | 骨干网 | IPInSight | 5% |
|
||||
| Tier-01 | 城域网 | IPInSight | 90% |
|
||||
|
||||
##### Other ISP
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
|:------:|:---:|:---------:|:---:|
|
||||
| Others | 骨干网 | IPInSight | 5% |
|
||||
| Others | 城域网 | IPInSight | 95% |
|
||||
|
||||
### IPv6 Database
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
|:---:|:---:|:----------------:|:----:|
|
||||
| All | 全部 | IP2Location Lite | 100% |
|
||||
|
||||
This product includes IP2Location LITE data available from <a href="https://lite.ip2location.com">https://lite.ip2location.com</a>.
|
||||
|
||||
### Others
|
||||
|
||||
其他第三方 API 尽管集成在本项目内,但是具体的 TOS 以及 AUP,请详见第三方 API 官网。如遇到 IP 数据错误,也请直接联系他们纠错。
|
||||
|
||||
BIN
asset/nexttrace021.png
Normal file
BIN
asset/nexttrace021.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
242
cmd/cmd.go
Normal file
242
cmd/cmd.go
Normal file
@@ -0,0 +1,242 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
"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
|
||||
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"})
|
||||
port := parser.Int("p", "port", &argparse.Options{Help: "Set the destination port to use. It is either initial udp port value for \"default\"" +
|
||||
"method (incremented by each probe, default is 33434), or initial seq for \"icmp\" (incremented as well, default from 1), or some constant" +
|
||||
"destination port for other methods (with default of 80 for \"tcp\", 53 for \"udp\", etc.)"})
|
||||
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]"})
|
||||
noRdns := parser.Flag("n", "no-rdns", &argparse.Options{Help: " Do not resolve IP addresses to their domain names"})
|
||||
routePath := parser.Flag("r", "route-path", &argparse.Options{Help: "Print traceroute hop path by ASN and location"})
|
||||
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"})
|
||||
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: "Print Trace Map. This will return a Trace Map URL"})
|
||||
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: 0, 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."})
|
||||
str := parser.StringPositional(&argparse.Options{Help: "IP Address or domain name"})
|
||||
|
||||
err := parser.Parse(os.Args)
|
||||
if err != nil {
|
||||
// In case of error print error and print usage
|
||||
// This can also be done by passing -h or --help flags
|
||||
fmt.Print(parser.Usage(err))
|
||||
return
|
||||
}
|
||||
printer.Version()
|
||||
if *ver {
|
||||
printer.CopyRight()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
domain := *str
|
||||
|
||||
if *port == 0 {
|
||||
*port = 80
|
||||
}
|
||||
|
||||
if *fast_trace {
|
||||
fastTrace.FastTest(*tcp, *output)
|
||||
if *output {
|
||||
fmt.Println("您的追踪日志已经存放在 /tmp/trace.log 中")
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if domain == "" {
|
||||
fmt.Print(parser.Usage(err))
|
||||
return
|
||||
}
|
||||
|
||||
capabilities_check()
|
||||
// return
|
||||
|
||||
var ip net.IP
|
||||
|
||||
if runtime.GOOS == "windows" && (*tcp || *udp) {
|
||||
fmt.Println("NextTrace 基于 Windows 的路由跟踪还在早期开发阶段,目前还存在诸多问题,TCP/UDP SYN 包请求可能不能正常运行")
|
||||
}
|
||||
|
||||
if *tcp || *udp {
|
||||
ip = util.DomainLookUp(domain, true)
|
||||
} else {
|
||||
ip = util.DomainLookUp(domain, false)
|
||||
}
|
||||
|
||||
if *src_dev != "" {
|
||||
dev, _ := net.InterfaceByName(*src_dev)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}()
|
||||
}
|
||||
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin)
|
||||
|
||||
var m trace.Method = ""
|
||||
|
||||
switch {
|
||||
case *tcp:
|
||||
m = trace.TCPTrace
|
||||
case *udp:
|
||||
m = trace.UDPTrace
|
||||
default:
|
||||
m = trace.ICMPTrace
|
||||
}
|
||||
|
||||
if !*tcp && *port == 80 {
|
||||
*port = 53
|
||||
}
|
||||
|
||||
var conf = trace.Config{
|
||||
SrcAddr: *src_addr,
|
||||
BeginHop: *beginHop,
|
||||
DestIP: ip,
|
||||
DestPort: *port,
|
||||
MaxHops: *maxHops,
|
||||
PacketInterval: *packet_interval,
|
||||
TTLInterval: *ttl_interval,
|
||||
NumMeasurements: *numMeasurements,
|
||||
ParallelRequests: *parallelRequests,
|
||||
RDns: !*noRdns,
|
||||
IPGeoSource: ipgeo.GetSource(*dataOrigin),
|
||||
Timeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
if !*tablePrint {
|
||||
if *classicPrint {
|
||||
conf.RealtimePrinter = printer.ClassicPrinter
|
||||
} else {
|
||||
if *output {
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
} else if *router {
|
||||
conf.RealtimePrinter = printer.RealtimePrinterWithRouter
|
||||
fmt.Println("路由表数据源由 BGP.Tools 提供,在此特表感谢")
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
}
|
||||
}
|
||||
} else {
|
||||
conf.AsyncPrinter = printer.TracerouteTablePrinter
|
||||
}
|
||||
|
||||
res, err := trace.Traceroute(m, conf)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if *tablePrint {
|
||||
printer.TracerouteTablePrinter(res)
|
||||
}
|
||||
|
||||
if *routePath {
|
||||
r := reporter.New(res, ip.String())
|
||||
r.Print()
|
||||
}
|
||||
|
||||
if *maptrace {
|
||||
r, _ := json.Marshal(res)
|
||||
tracemap.GetMapUrl(string(r))
|
||||
}
|
||||
}
|
||||
|
||||
func capabilities_check() {
|
||||
|
||||
// Windows 判断放在前面,防止遇到一些奇奇怪怪的问题
|
||||
if runtime.GOOS == "windows" {
|
||||
// Running on Windows, skip checking capabilities
|
||||
return
|
||||
}
|
||||
|
||||
uid := os.Getuid()
|
||||
if uid == 0 {
|
||||
// Running as root, skip checking capabilities
|
||||
return
|
||||
}
|
||||
|
||||
/***
|
||||
* 检查当前进程是否有两个关键的权限
|
||||
==== 看不到我 ====
|
||||
* 没办法啦
|
||||
* 自己之前承诺的坑补全篇
|
||||
* 被迫填坑系列 qwq
|
||||
==== 看不到我 ====
|
||||
***/
|
||||
|
||||
// NewPid 已经被废弃了,这里改用 NewPid2 方法
|
||||
caps, err := capability.NewPid2(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// load 获取全部的 caps 信息
|
||||
err = caps.Load()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断一下权限有木有
|
||||
if caps.Get(capability.EFFECTIVE, capability.CAP_NET_RAW) && caps.Get(capability.EFFECTIVE, capability.CAP_NET_ADMIN) {
|
||||
// 有权限啦
|
||||
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` 查看")
|
||||
}
|
||||
}
|
||||
7
cmd/cmd_test.go
Normal file
7
cmd/cmd_test.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCMD(t *testing.T) {
|
||||
Excute()
|
||||
}
|
||||
@@ -23,6 +23,7 @@ type BackBoneCollection struct {
|
||||
type ISPCollection struct {
|
||||
ISPName string
|
||||
IP string
|
||||
IPv6 string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -47,21 +48,25 @@ var Beijing = BackBoneCollection{
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "106.37.67.1",
|
||||
IPv6: "240e:40:e002:1:a:3ee3:c00:0",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "123.125.96.156",
|
||||
IPv6: "2408:8000:1010:2::10",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "211.136.25.153",
|
||||
IPv6: "2409:8000:3800:8::3",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "101.6.15.130",
|
||||
IPv6: "2001:da8::666",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -70,6 +75,7 @@ var Shanghai = BackBoneCollection{
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "101.226.28.198",
|
||||
IPv6: "240e:18:2:153::89",
|
||||
},
|
||||
|
||||
CTCN2: ISPCollection{
|
||||
@@ -80,21 +86,25 @@ var Shanghai = BackBoneCollection{
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "139.226.206.150",
|
||||
IPv6: "2408:8000:9000:0:4000::437",
|
||||
},
|
||||
|
||||
CU9929: ISPCollection{
|
||||
ISPName: CU9929,
|
||||
IP: "210.13.86.1",
|
||||
IPv6: "2408:8120:2::d6",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "120.204.34.85",
|
||||
IPv6: "2409:801e:f0:1::4e1",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "202.120.58.155",
|
||||
IPv6: "2001:da8:8000:1:202:120:2:100",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -102,17 +112,20 @@ var Guangzhou = BackBoneCollection{
|
||||
Location: "广州",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "106.37.67.1",
|
||||
IP: "14.116.225.60",
|
||||
IPv6: "240e:f9:8010::3:110:1",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "157.18.0.22",
|
||||
IPv6: "2408:8001:3161:4::1",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "120.198.26.254",
|
||||
IPv6: "2409:8055:3008:1116::150",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -121,19 +134,23 @@ var Hangzhou = BackBoneCollection{
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "61.164.23.196",
|
||||
IPv6: "240e:f3:c000:201::10",
|
||||
},
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "60.12.244.1",
|
||||
IPv6: "",
|
||||
},
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "112.17.224.98",
|
||||
IPv6: "2409:8028:840:2::11",
|
||||
},
|
||||
// 浙江大学 教育网
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "210.32.2.1",
|
||||
IPv6: "2001:da8:e000:1::1",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -143,6 +160,7 @@ var Hefei = BackBoneCollection{
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "202.38.64.1",
|
||||
IPv6: "2001:da8:d805:ffff:2::1",
|
||||
},
|
||||
// 中国科学技术大学 科技网
|
||||
CST: ISPCollection{
|
||||
|
||||
134
fast_trace/fast_trace ipv6.go
Normal file
134
fast_trace/fast_trace ipv6.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package fastTrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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()
|
||||
|
||||
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.IPv6)
|
||||
log.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ispCollection.IPv6)
|
||||
ip := net.ParseIP(ispCollection.IPv6)
|
||||
var conf = trace.Config{
|
||||
BeginHop: 1,
|
||||
DestIP: ip,
|
||||
DestPort: 80,
|
||||
MaxHops: 30,
|
||||
NumMeasurements: 3,
|
||||
ParallelRequests: 18,
|
||||
RDns: true,
|
||||
IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
|
||||
Timeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
if f.TracerouteMethod == trace.ICMPTrace {
|
||||
if oe {
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_, err = trace.Traceroute(f.TracerouteMethod, conf)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
println()
|
||||
}
|
||||
|
||||
func (f *FastTracer) testAll_v6() {
|
||||
f.testCT_v6()
|
||||
println()
|
||||
f.testCU_v6()
|
||||
println()
|
||||
f.testCM_v6()
|
||||
println()
|
||||
f.testEDU_v6()
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCT_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CT163)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCU_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCM_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.CM)
|
||||
f.tracert_v6(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testEDU_v6() {
|
||||
f.tracert_v6(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
f.tracert_v6(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.EDU)
|
||||
f.tracert_v6(TestIPsCollection.Hangzhou.Location, TestIPsCollection.Hangzhou.EDU)
|
||||
}
|
||||
|
||||
func FastTestv6(tm bool, outEnable bool) {
|
||||
var c string
|
||||
|
||||
oe = outEnable
|
||||
|
||||
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()
|
||||
}()
|
||||
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testAll_v6()
|
||||
case "2":
|
||||
ft.testCT_v6()
|
||||
case "3":
|
||||
ft.testCU_v6()
|
||||
case "4":
|
||||
ft.testCM_v6()
|
||||
case "5":
|
||||
ft.testEDU_v6()
|
||||
default:
|
||||
ft.testAll_v6()
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -18,9 +19,21 @@ type FastTracer struct {
|
||||
TracerouteMethod trace.Method
|
||||
}
|
||||
|
||||
var oe bool = 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)
|
||||
var conf = trace.Config{
|
||||
BeginHop: 1,
|
||||
@@ -34,21 +47,17 @@ func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
|
||||
Timeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
if f.TracerouteMethod == trace.ICMPTrace {
|
||||
if oe {
|
||||
conf.RealtimePrinter = tracelog.RealtimePrinter
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
}
|
||||
|
||||
res, err := trace.Traceroute(f.TracerouteMethod, conf)
|
||||
_, err = trace.Traceroute(f.TracerouteMethod, conf)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if f.TracerouteMethod == trace.TCPTrace {
|
||||
printer.TracerouteTablePrinter(res)
|
||||
// 单次测试结束阻塞 3 秒,仅阻塞 TCP
|
||||
<-time.After(time.Second * 3)
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
@@ -93,9 +102,19 @@ func (f *FastTracer) testEDU() {
|
||||
f.tracert(TestIPsCollection.Hefei.Location, TestIPsCollection.Hefei.CST)
|
||||
}
|
||||
|
||||
func FastTest(tm bool) {
|
||||
func FastTest(tm bool, outEnable bool) {
|
||||
var c string
|
||||
|
||||
oe = outEnable
|
||||
|
||||
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)
|
||||
|
||||
24
go.mod
24
go.mod
@@ -1,28 +1,30 @@
|
||||
module github.com/xgadget-lab/nexttrace
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/akamensky/argparse v1.4.0
|
||||
github.com/google/gopacket v1.1.19
|
||||
golang.org/x/net v0.0.0-20220809012201-f428fae20770
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
|
||||
golang.org/x/net v0.5.0
|
||||
golang.org/x/sync v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // 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/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rodaine/table v1.0.1
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/tidwall/gjson v1.14.2
|
||||
github.com/lionsoul2014/ip2region v2.11.0+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/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // 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
|
||||
)
|
||||
|
||||
51
go.sum
51
go.sum
@@ -1,66 +1,67 @@
|
||||
github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
|
||||
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
|
||||
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/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/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
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/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
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 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
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-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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
|
||||
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
|
||||
github.com/rodaine/table v1.1.0 h1:/fUlCSdjamMY8VifdQRIu3VWZXYLY7QHFkVorS8NTr4=
|
||||
github.com/rodaine/table v1.1.0/go.mod h1:Qu3q5wi1jTQD6B6HsP6szie/S4w1QUQ8pq22pz9iL8g=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
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.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
|
||||
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
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/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 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
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=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220809012201-f428fae20770 h1:dIi4qVdvjZEjiMDv7vhokAZNGnz3kepwuXqFKYDdDMs=
|
||||
golang.org/x/net v0.0.0-20220809012201-f428fae20770/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs=
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
||||
|
||||
77
ipgeo/ip2region.go
Normal file
77
ipgeo/ip2region.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/lionsoul2014/ip2region/v1.0/binding/golang/ip2region"
|
||||
)
|
||||
|
||||
const (
|
||||
ipDataBasePath = "./ip2region.db"
|
||||
defaultDownURL = "1"
|
||||
originURL = "https://ghproxy.com/?q=https://github.com/bqf9979/ip2region/blob/master/data/ip2region.db?raw=true"
|
||||
)
|
||||
|
||||
func downloadDataBase() error {
|
||||
fmt.Println("Downloading DataBase...")
|
||||
resp, err := http.Get(originURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Create the file
|
||||
out, err := os.Create(ipDataBasePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Write the body to file
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func IP2Region(ip string) (*IPGeoData, error) {
|
||||
if _, err := os.Stat(ipDataBasePath); os.IsNotExist(err) {
|
||||
if err = downloadDataBase(); err != nil {
|
||||
panic("Download Failed!")
|
||||
}
|
||||
}
|
||||
region, err := ip2region.New(ipDataBasePath)
|
||||
if err != nil {
|
||||
panic("Cannot find ip2region.db")
|
||||
}
|
||||
defer region.Close()
|
||||
info, searchErr := region.MemorySearch(ip)
|
||||
if searchErr != nil {
|
||||
return &IPGeoData{}, errors.New("no results")
|
||||
}
|
||||
|
||||
if info.Country == "0" {
|
||||
info.Country = ""
|
||||
}
|
||||
|
||||
if info.Province == "0" {
|
||||
info.Province = ""
|
||||
}
|
||||
|
||||
if info.City == "0" {
|
||||
info.City = ""
|
||||
}
|
||||
|
||||
if info.ISP == "0" {
|
||||
info.ISP = ""
|
||||
}
|
||||
|
||||
return &IPGeoData{
|
||||
Owner: info.ISP,
|
||||
Country: info.Country,
|
||||
Prov: info.Province,
|
||||
City: info.City,
|
||||
}, nil
|
||||
}
|
||||
@@ -38,6 +38,6 @@ func IPApiCom(ip string) (*IPGeoData, error) {
|
||||
Country: res.Get("country").String(),
|
||||
City: res.Get("city").String(),
|
||||
Prov: res.Get("regionName").String(),
|
||||
Isp: res.Get("isp").String(),
|
||||
Owner: res.Get("isp").String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -5,13 +5,24 @@ import (
|
||||
)
|
||||
|
||||
type IPGeoData struct {
|
||||
Asnumber string
|
||||
Country string
|
||||
Prov string
|
||||
City string
|
||||
District string
|
||||
Owner string
|
||||
Isp string
|
||||
IP string `json:"ip"`
|
||||
Asnumber string `json:"asnumber"`
|
||||
Country string `json:"country"`
|
||||
CountryEn string `json:"country_en"`
|
||||
Prov string `json:"prov"`
|
||||
ProvEn string `json:"prov_en"`
|
||||
City string `json:"city"`
|
||||
CityEn string `json:"city_en"`
|
||||
District string `json:"district"`
|
||||
Owner string `json:"owner"`
|
||||
Isp string `json:"isp"`
|
||||
Domain string `json:"domain"`
|
||||
Whois string `json:"whois"`
|
||||
Lat float64 `json:"lat"`
|
||||
Lng float64 `json:"lng"`
|
||||
Prefix string `json:"prefix"`
|
||||
Router map[string][]string `json:"router"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type Source = func(ip string) (*IPGeoData, error)
|
||||
@@ -28,6 +39,8 @@ func GetSource(s string) Source {
|
||||
return IPApiCom
|
||||
case "IPINFO":
|
||||
return IPInfo
|
||||
case "IP2REGION":
|
||||
return IP2Region
|
||||
default:
|
||||
return LeoIP
|
||||
}
|
||||
|
||||
@@ -1,11 +1,72 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// import (
|
||||
// "testing"
|
||||
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
|
||||
func TestXxx(t *testing.T) {
|
||||
const ID_FIXED_HEADER = "10"
|
||||
var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位
|
||||
var ttl = fmt.Sprintf("%06b", 95) //取TTL的后6位
|
||||
fmt.Println(os.Getpid()&0x7f, 95)
|
||||
|
||||
var parity int
|
||||
id := ID_FIXED_HEADER + processID + ttl
|
||||
for _, c := range id {
|
||||
if c == '1' {
|
||||
parity++
|
||||
}
|
||||
}
|
||||
if parity%2 == 0 {
|
||||
id += "1"
|
||||
} else {
|
||||
id += "0"
|
||||
}
|
||||
process_id, ttl_r, _ := reverseID(id)
|
||||
log.Println(process_id, ttl_r)
|
||||
}
|
||||
|
||||
func reverseID(id string) (int64, int64, error) {
|
||||
ttl, _ := strconv.ParseInt(id[9:15], 2, 32)
|
||||
//process ID
|
||||
processID, _ := strconv.ParseInt(id[2:9], 2, 32)
|
||||
|
||||
parity := 0
|
||||
for i := 0; i < len(id)-1; i++ {
|
||||
if id[i] == '1' {
|
||||
parity++
|
||||
}
|
||||
}
|
||||
|
||||
if parity%2 == 1 {
|
||||
if id[len(id)-1] == '0' {
|
||||
fmt.Println("Parity check passed.")
|
||||
} else {
|
||||
fmt.Println("Parity check failed.")
|
||||
return 0, 0, errors.New("err")
|
||||
}
|
||||
} else {
|
||||
if id[len(id)-1] == '1' {
|
||||
fmt.Println("Parity check passed.")
|
||||
} else {
|
||||
fmt.Println("Parity check failed.")
|
||||
return 0, 0, errors.New("err")
|
||||
}
|
||||
}
|
||||
return processID, ttl, nil
|
||||
}
|
||||
|
||||
// func TestLeoIP(t *testing.T) {
|
||||
// // res, err := LeoIP("1.1.1.1")
|
||||
// // assert.Nil(t, err)
|
||||
|
||||
@@ -31,6 +31,6 @@ func IPInfo(ip string) (*IPGeoData, error) {
|
||||
Country: country,
|
||||
City: res.Get("city").String(),
|
||||
Prov: res.Get("region").String(),
|
||||
Isp: res.Get("asn").Get("domain").String(),
|
||||
Owner: res.Get("asn").Get("domain").String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -37,6 +37,6 @@ func IPSB(ip string) (*IPGeoData, error) {
|
||||
Country: res.Get("country").String(),
|
||||
City: res.Get("city").String(),
|
||||
Prov: res.Get("region").String(),
|
||||
Isp: res.Get("isp").String(),
|
||||
Owner: res.Get("isp").String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
30
ipgeo/leo.go
30
ipgeo/leo.go
@@ -1,7 +1,9 @@
|
||||
package ipgeo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -52,14 +54,28 @@ func receiveParse() {
|
||||
domain = res.Get("owner").String()
|
||||
}
|
||||
|
||||
m := make(map[string][]string)
|
||||
json.Unmarshal([]byte(res.Get("router").String()), &m)
|
||||
|
||||
lat, _ := strconv.ParseFloat(res.Get("lat").String(), 32)
|
||||
lng, _ := strconv.ParseFloat(res.Get("lng").String(), 32)
|
||||
|
||||
IPPools.pool[gjson.Parse(data).Get("ip").String()] <- IPGeoData{
|
||||
Asnumber: res.Get("asnumber").String(),
|
||||
Country: res.Get("country").String(),
|
||||
Prov: res.Get("prov").String(),
|
||||
City: res.Get("city").String(),
|
||||
District: res.Get("district").String(),
|
||||
Owner: domain,
|
||||
Isp: res.Get("isp").String(),
|
||||
Asnumber: res.Get("asnumber").String(),
|
||||
Country: res.Get("country").String(),
|
||||
CountryEn: res.Get("country_en").String(),
|
||||
Prov: res.Get("prov").String(),
|
||||
ProvEn: res.Get("prov_en").String(),
|
||||
City: res.Get("city").String(),
|
||||
CityEn: res.Get("city_en").String(),
|
||||
District: res.Get("district").String(),
|
||||
Owner: domain,
|
||||
Lat: lat,
|
||||
Lng: lng,
|
||||
Isp: res.Get("isp").String(),
|
||||
Whois: res.Get("whois").String(),
|
||||
Prefix: res.Get("prefix").String(),
|
||||
Router: m,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
150
main.go
150
main.go
@@ -1,155 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
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/util"
|
||||
"github.com/xgadget-lab/nexttrace/wshandle"
|
||||
"github.com/xgadget-lab/nexttrace/cmd"
|
||||
)
|
||||
|
||||
var fSet = flag.NewFlagSet("", flag.ExitOnError)
|
||||
var fastTest = fSet.Bool("f", false, "One-Key Fast Traceroute")
|
||||
var tcpSYNFlag = fSet.Bool("T", false, "Use TCP SYN for tracerouting (default port is 80)")
|
||||
var udpPackageFlag = fSet.Bool("U", false, "Use UDP Package for tracerouting (default port is 53 in UDP)")
|
||||
var port = fSet.Int("p", 80, "Set SYN Traceroute Port")
|
||||
var numMeasurements = fSet.Int("q", 3, "Set the number of probes per each hop.")
|
||||
var parallelRequests = fSet.Int("r", 18, "Set ParallelRequests number. It should be 1 when there is a multi-routing.")
|
||||
var maxHops = fSet.Int("m", 30, "Set the max number of hops (max TTL to be reached).")
|
||||
var dataOrigin = fSet.String("d", "LeoMoeAPI", "Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight, IPAPI.com]")
|
||||
var noRdns = fSet.Bool("n", false, "Disable IP Reverse DNS lookup")
|
||||
var routePath = fSet.Bool("report", false, "Route Path")
|
||||
var tablePrint = fSet.Bool("table", false, "Output trace results as table")
|
||||
var classicPrint = fSet.Bool("classic", false, "Classic Output trace results like BestTrace")
|
||||
var beginHop = fSet.Int("b", 1, "Set The Begin TTL")
|
||||
var ver = fSet.Bool("V", false, "Print Version")
|
||||
|
||||
func printArgHelp() {
|
||||
fmt.Println("\nArgs Error\nUsage : 'nexttrace [option...] HOSTNAME' or 'nexttrace HOSTNAME [option...]'\nOPTIONS: [-VTU] [-d DATAORIGIN.STR ] [ -m TTL ] [ -p PORT ] [ -q PROBES.COUNT ] [ -r PARALLELREQUESTS.COUNT ] [-rdns] [ -table ] -report")
|
||||
fSet.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func flagApply() string {
|
||||
printer.Version()
|
||||
|
||||
target := ""
|
||||
if len(os.Args) < 2 {
|
||||
printArgHelp()
|
||||
}
|
||||
|
||||
// flag parse
|
||||
if !strings.HasPrefix(os.Args[1], "-") {
|
||||
target = os.Args[1]
|
||||
fSet.Parse(os.Args[2:])
|
||||
} else {
|
||||
fSet.Parse(os.Args[1:])
|
||||
target = fSet.Arg(0)
|
||||
}
|
||||
|
||||
// Print Version
|
||||
if *ver {
|
||||
printer.CopyRight()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// -f Fast Test
|
||||
if *fastTest {
|
||||
fastTrace.FastTest(*tcpSYNFlag)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if target == "" {
|
||||
printArgHelp()
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
domain := flagApply()
|
||||
|
||||
if os.Getuid() != 0 {
|
||||
log.Fatalln("Traceroute requires root/sudo privileges.")
|
||||
}
|
||||
|
||||
var ip net.IP
|
||||
|
||||
if *tcpSYNFlag || *udpPackageFlag {
|
||||
ip = util.DomainLookUp(domain, true)
|
||||
} else {
|
||||
ip = util.DomainLookUp(domain, false)
|
||||
}
|
||||
|
||||
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()
|
||||
}()
|
||||
}
|
||||
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin)
|
||||
|
||||
var m trace.Method = ""
|
||||
|
||||
switch {
|
||||
case *tcpSYNFlag:
|
||||
m = trace.TCPTrace
|
||||
case *udpPackageFlag:
|
||||
m = trace.UDPTrace
|
||||
default:
|
||||
m = trace.ICMPTrace
|
||||
}
|
||||
|
||||
if !*tcpSYNFlag && *port == 80 {
|
||||
*port = 53
|
||||
}
|
||||
|
||||
var conf = trace.Config{
|
||||
BeginHop: *beginHop,
|
||||
DestIP: ip,
|
||||
DestPort: *port,
|
||||
MaxHops: *maxHops,
|
||||
NumMeasurements: *numMeasurements,
|
||||
ParallelRequests: *parallelRequests,
|
||||
RDns: !*noRdns,
|
||||
IPGeoSource: ipgeo.GetSource(*dataOrigin),
|
||||
Timeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
if !*tablePrint {
|
||||
if *classicPrint {
|
||||
conf.RealtimePrinter = printer.ClassicPrinter
|
||||
} else {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
}
|
||||
}
|
||||
|
||||
res, err := trace.Traceroute(m, conf)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if *tablePrint {
|
||||
printer.TracerouteTablePrinter(res)
|
||||
}
|
||||
|
||||
if *routePath {
|
||||
r := reporter.New(res, ip.String())
|
||||
r.Print()
|
||||
}
|
||||
cmd.Excute()
|
||||
}
|
||||
|
||||
@@ -17,10 +17,14 @@ checkSystemArch() {
|
||||
arch=$(uname -m)
|
||||
if [[ $arch == "x86_64" ]]; then
|
||||
archParam="amd64"
|
||||
fi
|
||||
|
||||
if [[ $arch == "aarch64" ]]; then
|
||||
elif [[ $arch == "i386" ]]; then
|
||||
archParam="386"
|
||||
elif [[ $arch == "aarch64" ]]; then
|
||||
archParam="arm64"
|
||||
elif [[ $arch == "armv7l" ]] || [[ $arch == "armv7ml" ]]; then
|
||||
archParam="armv7"
|
||||
elif [[ $arch == "mips" ]]; then
|
||||
archParam="mips"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -28,7 +32,14 @@ checkSystemDistribution() {
|
||||
case "$OSTYPE" in
|
||||
linux*)
|
||||
osDistribution="linux"
|
||||
|
||||
if [ ! -d "/usr/local" ];
|
||||
then
|
||||
downPath="/usr/bin/nexttrace"
|
||||
else
|
||||
downPath="/usr/local/bin/nexttrace"
|
||||
fi
|
||||
|
||||
;;
|
||||
*)
|
||||
echo "unknown: $OSTYPE"
|
||||
@@ -82,7 +93,7 @@ checkWgetPackage() {
|
||||
downloadBinrayFile() {
|
||||
echo -e "${Info} 获取最新版的 NextTrace 发行版文件信息"
|
||||
# 简单说明一下,Github提供了一个API,可以获取最新发行版本的二进制文件下载地址(对应的是browser_download_url),根据刚刚测得的osDistribution、archParam,获取对应的下载地址
|
||||
latestURL=$(curl -s https://api.github.com/repos/xgadget-lab/nexttrace/releases/latest | grep -i "browser_download_url.*${osDistribution}.*${archParam}" | awk -F '"' '{print $4}')
|
||||
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} 检测到国内环境,正在使用镜像下载"
|
||||
@@ -109,7 +120,7 @@ changeMode() {
|
||||
runBinrayFileHelp() {
|
||||
if [ -e ${downPath} ]; then
|
||||
${downPath} -V
|
||||
echo -e "${Tips} 一切准备就绪!使用命令 nexttrace 1.1.1.1 开始您的第一次路由测试吧~ 更多进阶命令玩法可以用 nexttrace -h 查看哦\n 关于软件卸载,因为nexttrace是绿色版单文件,卸载只需输入命令 rm /usr/local/bin/nexttrace 即可"
|
||||
echo -e "${Tips} 一切准备就绪!使用命令 nexttrace 1.1.1.1 开始您的第一次路由测试吧~ 更多进阶命令玩法可以用 nexttrace -h 查看哦\n 关于软件卸载,因为nexttrace是绿色版单文件,卸载只需输入命令 rm ${downPath} 即可"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -1,105 +1,94 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/xgadget-lab/nexttrace/util"
|
||||
)
|
||||
|
||||
func TestPrintTraceRouteNav(t *testing.T) {
|
||||
PrintTraceRouteNav(util.DomainLookUp("1.1.1.1", false), "1.1.1.1", "dataOrigin")
|
||||
}
|
||||
|
||||
var testGeo = &ipgeo.IPGeoData{
|
||||
Asnumber: "TestAsnumber",
|
||||
Country: "TestCountry",
|
||||
Prov: "TestProv",
|
||||
City: "TestCity",
|
||||
District: "TestDistrict",
|
||||
Owner: "TestOwner",
|
||||
Isp: "TestIsp",
|
||||
}
|
||||
|
||||
var testResult = &trace.Result{
|
||||
Hops: [][]trace.Hop{
|
||||
{
|
||||
{
|
||||
Success: true,
|
||||
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
Hostname: "test",
|
||||
TTL: 0,
|
||||
RTT: 10 * time.Millisecond,
|
||||
Error: nil,
|
||||
Geo: testGeo,
|
||||
},
|
||||
{
|
||||
Success: true,
|
||||
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
Hostname: "test",
|
||||
TTL: 0,
|
||||
RTT: 10 * time.Millisecond,
|
||||
Error: nil,
|
||||
Geo: testGeo,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
Success: false,
|
||||
Address: nil,
|
||||
Hostname: "",
|
||||
TTL: 0,
|
||||
RTT: 0,
|
||||
Error: errors.New("test error"),
|
||||
Geo: nil,
|
||||
},
|
||||
{
|
||||
Success: true,
|
||||
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
Hostname: "test",
|
||||
TTL: 0,
|
||||
RTT: 10 * time.Millisecond,
|
||||
Error: nil,
|
||||
Geo: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
Success: true,
|
||||
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
Hostname: "test",
|
||||
TTL: 0,
|
||||
RTT: 0,
|
||||
Error: nil,
|
||||
Geo: &ipgeo.IPGeoData{},
|
||||
},
|
||||
{
|
||||
Success: true,
|
||||
Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
Hostname: "",
|
||||
TTL: 0,
|
||||
RTT: 10 * time.Millisecond,
|
||||
Error: nil,
|
||||
Geo: testGeo,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// func TestTraceroutePrinter(t *testing.T) {
|
||||
// TraceroutePrinter(testResult)
|
||||
// func TestPrintTraceRouteNav(t *testing.T) {
|
||||
// PrintTraceRouteNav(util.DomainLookUp("1.1.1.1", false), "1.1.1.1", "dataOrigin")
|
||||
// }
|
||||
|
||||
func TestTracerouteTablePrinter(t *testing.T) {
|
||||
TracerouteTablePrinter(testResult)
|
||||
}
|
||||
// var testGeo = &ipgeo.IPGeoData{
|
||||
// Asnumber: "TestAsnumber",
|
||||
// Country: "TestCountry",
|
||||
// Prov: "TestProv",
|
||||
// City: "TestCity",
|
||||
// District: "TestDistrict",
|
||||
// Owner: "TestOwner",
|
||||
// Isp: "TestIsp",
|
||||
// }
|
||||
|
||||
func TestRealtimePrinter(t *testing.T) {
|
||||
RealtimePrinter(testResult, 0)
|
||||
// RealtimePrinter(testResult, 1)
|
||||
// RealtimePrinter(testResult, 2)
|
||||
}
|
||||
// var testResult = &trace.Result{
|
||||
// Hops: [][]trace.Hop{
|
||||
// {
|
||||
// {
|
||||
// Success: true,
|
||||
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
// Hostname: "test",
|
||||
// TTL: 0,
|
||||
// RTT: 10 * time.Millisecond,
|
||||
// Error: nil,
|
||||
// Geo: testGeo,
|
||||
// },
|
||||
// {
|
||||
// Success: true,
|
||||
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
// Hostname: "test",
|
||||
// TTL: 0,
|
||||
// RTT: 10 * time.Millisecond,
|
||||
// Error: nil,
|
||||
// Geo: testGeo,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// {
|
||||
// Success: false,
|
||||
// Address: nil,
|
||||
// Hostname: "",
|
||||
// TTL: 0,
|
||||
// RTT: 0,
|
||||
// Error: errors.New("test error"),
|
||||
// Geo: nil,
|
||||
// },
|
||||
// {
|
||||
// Success: true,
|
||||
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
// Hostname: "test",
|
||||
// TTL: 0,
|
||||
// RTT: 10 * time.Millisecond,
|
||||
// Error: nil,
|
||||
// Geo: nil,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// {
|
||||
// Success: true,
|
||||
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
// Hostname: "test",
|
||||
// TTL: 0,
|
||||
// RTT: 0,
|
||||
// Error: nil,
|
||||
// Geo: &ipgeo.IPGeoData{},
|
||||
// },
|
||||
// {
|
||||
// Success: true,
|
||||
// Address: &net.IPAddr{IP: net.ParseIP("192.168.3.1")},
|
||||
// Hostname: "",
|
||||
// TTL: 0,
|
||||
// RTT: 10 * time.Millisecond,
|
||||
// Error: nil,
|
||||
// Geo: testGeo,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
|
||||
// // func TestTraceroutePrinter(t *testing.T) {
|
||||
// // TraceroutePrinter(testResult)
|
||||
// // }
|
||||
|
||||
// func TestTracerouteTablePrinter(t *testing.T) {
|
||||
// TracerouteTablePrinter(testResult)
|
||||
// }
|
||||
|
||||
// func TestRealtimePrinter(t *testing.T) {
|
||||
// RealtimePrinter(testResult, 0)
|
||||
// // RealtimePrinter(testResult, 1)
|
||||
// // RealtimePrinter(testResult, 2)
|
||||
// }
|
||||
|
||||
@@ -2,7 +2,9 @@ package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
@@ -48,9 +50,15 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
if blockDisplay {
|
||||
fmt.Printf("%4s", "")
|
||||
}
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
|
||||
)
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
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("%-15s", ip),
|
||||
)
|
||||
}
|
||||
|
||||
i, _ := strconv.Atoi(v[0])
|
||||
|
||||
@@ -60,18 +68,41 @@ func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
fmt.Printf(" %-8s", "*")
|
||||
}
|
||||
|
||||
if res.Hops[ttl][i].Geo.Country == "" {
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-")
|
||||
if len(whoisFormat) > 1 {
|
||||
whoisFormat[0] = strings.Join(whoisFormat[:2], "-")
|
||||
}
|
||||
|
||||
if whoisFormat[0] != "" {
|
||||
whoisFormat[0] = "[" + whoisFormat[0] + "]"
|
||||
}
|
||||
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-16s", whoisFormat[0]))
|
||||
}
|
||||
if len(res.Hops[ttl][i].Geo.Country) <= 1 {
|
||||
res.Hops[ttl][i].Geo.Country = "LAN Address"
|
||||
}
|
||||
|
||||
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
|
||||
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%-22s", res.Hops[ttl][0].Hostname),
|
||||
)
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
|
||||
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
|
||||
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%-39s", res.Hops[ttl][i].Hostname),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
|
||||
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%-32s", res.Hops[ttl][i].Hostname),
|
||||
)
|
||||
}
|
||||
|
||||
for j := 1; j < len(v); j++ {
|
||||
if len(v) == 2 || j == 1 {
|
||||
|
||||
152
printer/realtime_printer_router.go
Normal file
152
printer/realtime_printer_router.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
)
|
||||
|
||||
func RealtimePrinterWithRouter(res *trace.Result, ttl int) {
|
||||
fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1))
|
||||
|
||||
// 去重
|
||||
var latestIP string
|
||||
tmpMap := make(map[string][]string)
|
||||
for i, v := range res.Hops[ttl] {
|
||||
if v.Address == nil && latestIP != "" {
|
||||
tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*"))
|
||||
continue
|
||||
} else if v.Address == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, exist := tmpMap[v.Address.String()]; !exist {
|
||||
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i))
|
||||
// 首次进入
|
||||
if latestIP == "" {
|
||||
for j := 0; j < i; j++ {
|
||||
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*"))
|
||||
}
|
||||
}
|
||||
latestIP = v.Address.String()
|
||||
}
|
||||
|
||||
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))
|
||||
}
|
||||
|
||||
if latestIP == "" {
|
||||
fmt.Fprintf(color.Output, "%s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("*"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var blockDisplay = false
|
||||
for ip, v := range tmpMap {
|
||||
if blockDisplay {
|
||||
fmt.Printf("%4s", "")
|
||||
}
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
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("%-15s", ip),
|
||||
)
|
||||
}
|
||||
|
||||
i, _ := strconv.Atoi(v[0])
|
||||
|
||||
if res.Hops[ttl][i].Geo.Asnumber != "" {
|
||||
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber))
|
||||
} else {
|
||||
fmt.Printf(" %-8s", "*")
|
||||
}
|
||||
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-")
|
||||
if len(whoisFormat) > 1 {
|
||||
whoisFormat[0] = strings.Join(whoisFormat[:2], "-")
|
||||
}
|
||||
|
||||
if whoisFormat[0] != "" {
|
||||
whoisFormat[0] = "[" + whoisFormat[0] + "]"
|
||||
}
|
||||
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-16s", whoisFormat[0]))
|
||||
}
|
||||
|
||||
if res.Hops[ttl][i].Geo.Country == "" {
|
||||
res.Hops[ttl][i].Geo.Country = "LAN Address"
|
||||
}
|
||||
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
|
||||
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
|
||||
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%-39s", res.Hops[ttl][i].Hostname),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
|
||||
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
|
||||
color.New(color.FgHiBlack, color.Bold).Sprintf("%-32s", res.Hops[ttl][i].Hostname),
|
||||
)
|
||||
}
|
||||
|
||||
for j := 1; j < len(v); j++ {
|
||||
if len(v) == 2 || j == 1 {
|
||||
fmt.Fprintf(color.Output, "%s",
|
||||
color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, " / %s",
|
||||
color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]),
|
||||
)
|
||||
}
|
||||
}
|
||||
i = 0
|
||||
fmt.Println()
|
||||
if res.Hops[ttl][i].Geo != nil && !blockDisplay {
|
||||
fmt.Fprintf(color.Output, "%s %s %s %s %s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("-"),
|
||||
color.New(color.FgHiYellow, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prefix),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("路由表"),
|
||||
color.New(color.FgHiCyan, color.Bold).Sprintf("Beta"),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("-"),
|
||||
)
|
||||
GetRouter(&res.Hops[ttl][i].Geo.Router, "AS"+res.Hops[ttl][i].Geo.Asnumber)
|
||||
}
|
||||
blockDisplay = true
|
||||
}
|
||||
}
|
||||
|
||||
func GetRouter(r *map[string][]string, node string) {
|
||||
routeMap := *r
|
||||
for _, v := range routeMap[node] {
|
||||
if len(routeMap[v]) != 0 {
|
||||
fmt.Fprintf(color.Output, " %s %s %s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", routeMap[v][0]),
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", v),
|
||||
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", node),
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(color.Output, " %s %s\n",
|
||||
color.New(color.FgWhite, color.Bold).Sprintf("%s", v),
|
||||
color.New(color.FgHiBlue, color.Bold).Sprintf("%s", node),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -37,14 +37,17 @@ func TracerouteTablePrinter(res *trace.Result) {
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, "", data.Owner)
|
||||
} else {
|
||||
if data.City != "" {
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country+", "+data.Prov+", "+data.City, data.Owner)
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.City+", "+data.Prov+", "+data.Country, data.Owner)
|
||||
} else if data.Prov != "" {
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Prov+", "+data.Country, data.Owner)
|
||||
} else {
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country + ", " + data.Prov, data.Owner)
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country, data.Owner)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Print("\033[H\033[2J")
|
||||
// 打印表格
|
||||
tbl.Print()
|
||||
}
|
||||
@@ -75,7 +78,7 @@ func tableDataGenerator(h trace.Hop) *rowData {
|
||||
IP: IP,
|
||||
Latency: lantency,
|
||||
Country: "LAN Address",
|
||||
Prov: "LAN Address",
|
||||
Prov: "",
|
||||
Owner: "",
|
||||
}
|
||||
} else if strings.HasPrefix(IP, "11.") {
|
||||
@@ -84,7 +87,7 @@ func tableDataGenerator(h trace.Hop) *rowData {
|
||||
IP: IP,
|
||||
Latency: lantency,
|
||||
Country: "LAN Address",
|
||||
Prov: "LAN Address",
|
||||
Prov: "",
|
||||
Owner: "",
|
||||
}
|
||||
}
|
||||
@@ -102,9 +105,9 @@ func tableDataGenerator(h trace.Hop) *rowData {
|
||||
IP: IP,
|
||||
Latency: lantency,
|
||||
Asnumber: h.Geo.Asnumber,
|
||||
Country: h.Geo.Country,
|
||||
Prov: h.Geo.Prov,
|
||||
City: h.Geo.City,
|
||||
Country: h.Geo.CountryEn,
|
||||
Prov: h.Geo.ProvEn,
|
||||
City: h.Geo.CityEn,
|
||||
District: h.Geo.District,
|
||||
Owner: h.Geo.Owner,
|
||||
}
|
||||
@@ -117,9 +120,9 @@ func tableDataGenerator(h trace.Hop) *rowData {
|
||||
h.Geo.Owner = h.Geo.Isp
|
||||
}
|
||||
r.Asnumber = h.Geo.Asnumber
|
||||
r.Country = h.Geo.Country
|
||||
r.Prov = h.Geo.Prov
|
||||
r.City = h.Geo.City
|
||||
r.Country = h.Geo.CountryEn
|
||||
r.Prov = h.Geo.ProvEn
|
||||
r.City = h.Geo.CityEn
|
||||
r.District = h.Geo.District
|
||||
r.Owner = h.Geo.Owner
|
||||
return r
|
||||
|
||||
@@ -2,9 +2,12 @@ package trace
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -15,23 +18,49 @@ import (
|
||||
|
||||
type ICMPTracer struct {
|
||||
Config
|
||||
wg sync.WaitGroup
|
||||
res Result
|
||||
ctx context.Context
|
||||
resCh chan Hop
|
||||
icmpListen net.PacketConn
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
res Result
|
||||
ctx context.Context
|
||||
inflightRequest map[int]chan Hop
|
||||
inflightRequestLock sync.Mutex
|
||||
icmpListen net.PacketConn
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *ICMPTracer) PrintFunc() {
|
||||
defer t.wg.Done()
|
||||
var ttl = t.Config.BeginHop - 1
|
||||
for {
|
||||
if t.AsyncPrinter != nil {
|
||||
t.AsyncPrinter(&t.res)
|
||||
}
|
||||
if len(t.res.Hops)-1 > ttl {
|
||||
if len(t.res.Hops[ttl]) == t.NumMeasurements {
|
||||
if t.RealtimePrinter != nil {
|
||||
t.RealtimePrinter(&t.res, ttl)
|
||||
}
|
||||
ttl++
|
||||
|
||||
if ttl == t.final-1 || ttl >= t.MaxHops-1 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
<-time.After(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ICMPTracer) Execute() (*Result, error) {
|
||||
t.inflightRequest = make(map[int]chan Hop)
|
||||
|
||||
if len(t.res.Hops) > 0 {
|
||||
return &t.res, ErrTracerouteExecuted
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
t.icmpListen, err = net.ListenPacket("ip4:1", "0.0.0.0")
|
||||
t.icmpListen, err = net.ListenPacket("ip4:1", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -40,27 +69,46 @@ func (t *ICMPTracer) Execute() (*Result, error) {
|
||||
var cancel context.CancelFunc
|
||||
t.ctx, cancel = context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
t.resCh = make(chan Hop)
|
||||
t.final = -1
|
||||
|
||||
go t.listenICMP()
|
||||
|
||||
t.wg.Add(1)
|
||||
go t.PrintFunc()
|
||||
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
|
||||
t.inflightRequestLock.Lock()
|
||||
t.inflightRequest[ttl] = make(chan Hop, t.NumMeasurements)
|
||||
t.inflightRequestLock.Unlock()
|
||||
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))
|
||||
}
|
||||
// 一组TTL全部退出(收到应答或者超时终止)以后,再进行下一个TTL的包发送
|
||||
t.wg.Wait()
|
||||
<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
|
||||
}
|
||||
|
||||
t.wg.Wait()
|
||||
t.res.reduce(t.final)
|
||||
if t.final != -1 {
|
||||
if t.RealtimePrinter != nil {
|
||||
t.RealtimePrinter(&t.res, ttl-1)
|
||||
t.RealtimePrinter(&t.res, t.final-1)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < t.NumMeasurements; i++ {
|
||||
t.res.add(Hop{
|
||||
Success: false,
|
||||
Address: nil,
|
||||
TTL: 30,
|
||||
RTT: 0,
|
||||
Error: ErrHopLimitTimeout,
|
||||
})
|
||||
}
|
||||
if t.RealtimePrinter != nil {
|
||||
t.RealtimePrinter(&t.res, t.MaxHops-1)
|
||||
}
|
||||
}
|
||||
t.res.reduce(t.final)
|
||||
|
||||
return &t.res, nil
|
||||
}
|
||||
|
||||
@@ -75,55 +123,136 @@ func (t *ICMPTracer) listenICMP() {
|
||||
if msg.N == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if binary.BigEndian.Uint16(msg.Msg[32:34]) != uint16(os.Getpid()&0xffff) {
|
||||
// 如果类型为应答消息,且应答消息包的进程ID与主进程相同时不跳过
|
||||
if msg.Msg[0] != 0 || binary.BigEndian.Uint16(msg.Msg[4:6]) != uint16(os.Getpid()&0xffff) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
dstip := net.IP(msg.Msg[24:28])
|
||||
if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv4zero) {
|
||||
// 匹配再继续解析包,否则直接丢弃
|
||||
// log.Println(msg.Msg)
|
||||
if msg.Msg[0] == 0 {
|
||||
rm, err := icmp.ParseMessage(1, msg.Msg[:*msg.N])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
switch rm.Type {
|
||||
case ipv4.ICMPTypeTimeExceeded:
|
||||
t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data)
|
||||
case ipv4.ICMPTypeEchoReply:
|
||||
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
|
||||
default:
|
||||
// log.Println("received icmp message of unknown type", rm.Type)
|
||||
echoReply := rm.Body.(*icmp.Echo)
|
||||
ttl := echoReply.Seq // This is the TTL value
|
||||
if ttl > 100 {
|
||||
continue
|
||||
}
|
||||
if msg.Peer.String() == t.DestIP.String() {
|
||||
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, ttl)
|
||||
}
|
||||
continue
|
||||
}
|
||||
packet_id := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[32:34])), 2)
|
||||
if process_id, ttl, err := reverseID(packet_id); err == nil {
|
||||
if process_id == int64(os.Getpid()&0x7f) {
|
||||
dstip := net.IP(msg.Msg[24:28])
|
||||
if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv4zero) {
|
||||
// 匹配再继续解析包,否则直接丢弃
|
||||
rm, err := icmp.ParseMessage(1, msg.Msg[:*msg.N])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch rm.Type {
|
||||
case ipv4.ICMPTypeTimeExceeded:
|
||||
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))
|
||||
default:
|
||||
// log.Println("received icmp message of unknown type", rm.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte) {
|
||||
t.resCh <- Hop{
|
||||
Success: true,
|
||||
Address: msg.Peer,
|
||||
func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte, ttl int) {
|
||||
t.inflightRequestLock.Lock()
|
||||
defer t.inflightRequestLock.Unlock()
|
||||
if _, ok := t.inflightRequest[ttl]; ok {
|
||||
t.inflightRequest[ttl] <- Hop{
|
||||
Success: true,
|
||||
Address: msg.Peer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func gernerateID(ttl_int int) int {
|
||||
const ID_FIXED_HEADER = "10"
|
||||
var processID = fmt.Sprintf("%07b", os.Getpid()&0x7f) //取进程ID的前7位
|
||||
var ttl = fmt.Sprintf("%06b", ttl_int) //取TTL的后6位
|
||||
|
||||
var parity int
|
||||
id := ID_FIXED_HEADER + processID + ttl
|
||||
for _, c := range id {
|
||||
if c == '1' {
|
||||
parity++
|
||||
}
|
||||
}
|
||||
if parity%2 == 0 {
|
||||
id += "1"
|
||||
} else {
|
||||
id += "0"
|
||||
}
|
||||
|
||||
res, _ := strconv.ParseInt(id, 2, 64)
|
||||
return int(res)
|
||||
}
|
||||
|
||||
func reverseID(id string) (int64, int64, error) {
|
||||
if len(id) < 16 {
|
||||
return 0, 0, errors.New("err")
|
||||
}
|
||||
ttl, err := strconv.ParseInt(id[9:15], 2, 32)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
//process ID
|
||||
processID, _ := strconv.ParseInt(id[2:9], 2, 32)
|
||||
|
||||
parity := 0
|
||||
for i := 0; i < len(id)-1; i++ {
|
||||
if id[i] == '1' {
|
||||
parity++
|
||||
}
|
||||
}
|
||||
|
||||
if parity%2 == 1 {
|
||||
if id[len(id)-1] == '0' {
|
||||
// fmt.Println("Parity check passed.")
|
||||
} else {
|
||||
// fmt.Println("Parity check failed.")
|
||||
return 0, 0, errors.New("err")
|
||||
}
|
||||
} else {
|
||||
if id[len(id)-1] == '1' {
|
||||
// fmt.Println("Parity check passed.")
|
||||
} else {
|
||||
// fmt.Println("Parity check failed.")
|
||||
return 0, 0, errors.New("err")
|
||||
}
|
||||
}
|
||||
return processID, ttl, nil
|
||||
}
|
||||
|
||||
func (t *ICMPTracer) send(ttl int) error {
|
||||
|
||||
defer t.wg.Done()
|
||||
if t.final != -1 && ttl > t.final {
|
||||
return nil
|
||||
}
|
||||
|
||||
id := gernerateID(ttl)
|
||||
// log.Println("发送的", id)
|
||||
|
||||
icmpHeader := icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: os.Getpid() & 0xffff,
|
||||
ID: id,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
Seq: ttl,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -145,7 +274,7 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
select {
|
||||
case <-t.ctx.Done():
|
||||
return nil
|
||||
case h := <-t.resCh:
|
||||
case h := <-t.inflightRequest[ttl]:
|
||||
rtt := time.Since(start)
|
||||
if t.final != -1 && ttl > t.final {
|
||||
return nil
|
||||
@@ -153,6 +282,7 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
if addr, ok := h.Address.(*net.IPAddr); ok && addr.IP.Equal(t.DestIP) {
|
||||
t.finalLock.Lock()
|
||||
if t.final == -1 || ttl < t.final {
|
||||
|
||||
t.final = ttl
|
||||
}
|
||||
t.finalLock.Unlock()
|
||||
@@ -170,7 +300,6 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
h.fetchIPData(t.Config)
|
||||
|
||||
t.res.add(h)
|
||||
|
||||
case <-time.After(t.Timeout):
|
||||
if t.final != -1 && ttl > t.final {
|
||||
return nil
|
||||
@@ -183,6 +312,7 @@ func (t *ICMPTracer) send(ttl int) error {
|
||||
RTT: 0,
|
||||
Error: ErrHopLimitTimeout,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -14,23 +16,52 @@ import (
|
||||
|
||||
type ICMPTracerv6 struct {
|
||||
Config
|
||||
wg sync.WaitGroup
|
||||
res Result
|
||||
ctx context.Context
|
||||
resCh chan Hop
|
||||
icmpListen net.PacketConn
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
res Result
|
||||
ctx context.Context
|
||||
resCh chan Hop
|
||||
inflightRequest map[int]chan Hop
|
||||
inflightRequestLock sync.Mutex
|
||||
icmpListen net.PacketConn
|
||||
final int
|
||||
finalLock sync.Mutex
|
||||
}
|
||||
|
||||
func (t *ICMPTracerv6) PrintFunc() {
|
||||
// defer t.wg.Done()
|
||||
var ttl = t.Config.BeginHop - 1
|
||||
for {
|
||||
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 {
|
||||
t.RealtimePrinter(&t.res, ttl)
|
||||
}
|
||||
ttl++
|
||||
if ttl == t.final {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<-time.After(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ICMPTracerv6) Execute() (*Result, error) {
|
||||
t.inflightRequest = make(map[int]chan Hop)
|
||||
|
||||
if len(t.res.Hops) > 0 {
|
||||
return &t.res, ErrTracerouteExecuted
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
t.icmpListen, err = net.ListenPacket("ip6:58", "::")
|
||||
t.icmpListen, err = net.ListenPacket("ip6:58", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -43,22 +74,57 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
|
||||
t.final = -1
|
||||
|
||||
go t.listenICMP()
|
||||
|
||||
go t.PrintFunc()
|
||||
for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
|
||||
t.inflightRequestLock.Lock()
|
||||
t.inflightRequest[ttl] = make(chan Hop, t.NumMeasurements)
|
||||
t.inflightRequestLock.Unlock()
|
||||
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.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
|
||||
}
|
||||
// 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)
|
||||
// }
|
||||
// // 一组TTL全部退出(收到应答或者超时终止)以后,再进行下一个TTL的包发送
|
||||
// t.wg.Wait()
|
||||
// if t.RealtimePrinter != nil {
|
||||
// t.RealtimePrinter(&t.res, ttl-1)
|
||||
// }
|
||||
|
||||
// if t.AsyncPrinter != nil {
|
||||
// t.AsyncPrinter(&t.res)
|
||||
// }
|
||||
// }
|
||||
t.wg.Wait()
|
||||
t.res.reduce(t.final)
|
||||
if t.final != -1 {
|
||||
t.RealtimePrinter(&t.res, t.final-1)
|
||||
} else {
|
||||
for i := 0; i < t.NumMeasurements; i++ {
|
||||
t.res.add(Hop{
|
||||
Success: false,
|
||||
Address: nil,
|
||||
TTL: 30,
|
||||
RTT: 0,
|
||||
Error: ErrHopLimitTimeout,
|
||||
})
|
||||
}
|
||||
// 一组TTL全部退出(收到应答或者超时终止)以后,再进行下一个TTL的包发送
|
||||
t.wg.Wait()
|
||||
if t.RealtimePrinter != nil {
|
||||
t.RealtimePrinter(&t.res, ttl-1)
|
||||
t.RealtimePrinter(&t.res, t.MaxHops-1)
|
||||
}
|
||||
}
|
||||
t.res.reduce(t.final)
|
||||
|
||||
return &t.res, nil
|
||||
}
|
||||
@@ -74,29 +140,98 @@ func (t *ICMPTracerv6) listenICMP() {
|
||||
if msg.N == nil {
|
||||
continue
|
||||
}
|
||||
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
if msg.Msg[0] == 129 {
|
||||
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
echoReply, ok := rm.Body.(*icmp.Echo)
|
||||
|
||||
if ok {
|
||||
ttl := echoReply.Seq // This is the TTL value
|
||||
|
||||
if ttl > 100 {
|
||||
continue
|
||||
}
|
||||
if msg.Peer.String() == t.DestIP.String() {
|
||||
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, ttl)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// log.Println(msg.Peer)
|
||||
switch rm.Type {
|
||||
case ipv6.ICMPTypeTimeExceeded:
|
||||
t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data)
|
||||
case ipv6.ICMPTypeEchoReply:
|
||||
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
|
||||
default:
|
||||
// log.Println("received icmp message of unknown type", rm.Type)
|
||||
packet_id := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[52:54])), 2)
|
||||
if process_id, ttl, err := reverseID(packet_id); err == nil {
|
||||
if process_id == int64(os.Getpid()&0x7f) {
|
||||
dstip := net.IP(msg.Msg[32:48])
|
||||
// 无效包本地环回包
|
||||
if dstip.String() == "::" {
|
||||
continue
|
||||
}
|
||||
if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv6zero) {
|
||||
// 匹配再继续解析包,否则直接丢弃
|
||||
rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch rm.Type {
|
||||
case ipv6.ICMPTypeTimeExceeded:
|
||||
t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data, int(ttl))
|
||||
case ipv6.ICMPTypeEchoReply:
|
||||
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, int(ttl))
|
||||
default:
|
||||
// log.Println("received icmp message of unknown type", rm.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// dstip := net.IP(msg.Msg[32:48])
|
||||
// if binary.BigEndian.Uint16(msg.Msg[52:54]) != uint16(os.Getpid()&0xffff) {
|
||||
// // // 如果类型为应答消息,且应答消息包的进程ID与主进程相同时不跳过
|
||||
// if binary.BigEndian.Uint16(msg.Msg[52:54]) != 0 {
|
||||
// continue
|
||||
// } else {
|
||||
// if dstip.String() != "::" {
|
||||
// continue
|
||||
// }
|
||||
// if msg.Peer.String() != t.DestIP.String() {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv6zero) {
|
||||
// rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// continue
|
||||
// }
|
||||
// // log.Println(msg.Peer)
|
||||
// switch rm.Type {
|
||||
// case ipv6.ICMPTypeTimeExceeded:
|
||||
// t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data)
|
||||
// case ipv6.ICMPTypeEchoReply:
|
||||
// t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
|
||||
// default:
|
||||
// // log.Println("received icmp message of unknown type", rm.Type)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte) {
|
||||
t.resCh <- Hop{
|
||||
Success: true,
|
||||
Address: msg.Peer,
|
||||
func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte, ttl int) {
|
||||
t.inflightRequestLock.Lock()
|
||||
defer t.inflightRequestLock.Unlock()
|
||||
if _, ok := t.inflightRequest[ttl]; ok {
|
||||
t.inflightRequest[ttl] <- Hop{
|
||||
Success: true,
|
||||
Address: msg.Peer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,12 +240,14 @@ func (t *ICMPTracerv6) send(ttl int) error {
|
||||
if t.final != -1 && ttl > t.final {
|
||||
return nil
|
||||
}
|
||||
id := gernerateID(ttl)
|
||||
|
||||
icmpHeader := icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: os.Getpid() & 0xffff,
|
||||
ID: id,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
Seq: ttl,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -135,7 +272,7 @@ func (t *ICMPTracerv6) send(ttl int) error {
|
||||
select {
|
||||
case <-t.ctx.Done():
|
||||
return nil
|
||||
case h := <-t.resCh:
|
||||
case h := <-t.inflightRequest[ttl]:
|
||||
rtt := time.Since(start)
|
||||
if t.final != -1 && ttl > t.final {
|
||||
return nil
|
||||
|
||||
@@ -42,11 +42,16 @@ func (t *TCPTracer) Execute() (*Result, error) {
|
||||
t.SrcIP, _ = util.LocalIPPort(t.DestIP)
|
||||
|
||||
var err error
|
||||
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcIP.String())
|
||||
if t.SrcAddr != "" {
|
||||
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcAddr)
|
||||
} else {
|
||||
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcIP.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.icmp, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
||||
t.icmp, err = icmp.ListenPacket("ip4:icmp", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -78,8 +83,19 @@ func (t *TCPTracer) Execute() (*Result, error) {
|
||||
t.wg.Wait()
|
||||
t.RealtimePrinter(&t.res, ttl-1)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
go func() {
|
||||
if t.AsyncPrinter != nil {
|
||||
for {
|
||||
t.AsyncPrinter(&t.res)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
// 如果是表格模式,则一次性并发请求
|
||||
if t.RealtimePrinter == nil {
|
||||
t.wg.Wait()
|
||||
|
||||
@@ -16,6 +16,7 @@ var (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
SrcAddr string
|
||||
BeginHop int
|
||||
MaxHops int
|
||||
NumMeasurements int
|
||||
@@ -26,7 +27,10 @@ type Config struct {
|
||||
Quic bool
|
||||
IPGeoSource ipgeo.Source
|
||||
RDns bool
|
||||
PacketInterval int
|
||||
TTLInterval int
|
||||
RealtimePrinter func(res *Result, ttl int)
|
||||
AsyncPrinter func(res *Result)
|
||||
}
|
||||
|
||||
type Method string
|
||||
@@ -114,10 +118,25 @@ type Hop struct {
|
||||
}
|
||||
|
||||
func (h *Hop) fetchIPData(c Config) (err error) {
|
||||
timeout := time.Millisecond * 800
|
||||
if c.RDns && h.Hostname == "" {
|
||||
ptr, err := net.LookupAddr(h.Address.String())
|
||||
if err == nil && len(ptr) > 0 {
|
||||
h.Hostname = ptr[0]
|
||||
result := make(chan []string)
|
||||
go func() {
|
||||
r, err := net.LookupAddr(h.Address.String())
|
||||
if err != nil {
|
||||
result <- nil
|
||||
} else {
|
||||
result <- r
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case ptr := <-result:
|
||||
// process result
|
||||
if err == nil && len(ptr) > 0 {
|
||||
h.Hostname = ptr[0]
|
||||
}
|
||||
case <-time.After(timeout):
|
||||
// handle timeout
|
||||
}
|
||||
}
|
||||
if c.IPGeoSource != nil && h.Geo == nil {
|
||||
|
||||
14
trace/udp.go
14
trace/udp.go
@@ -37,7 +37,7 @@ func (t *UDPTracer) Execute() (*Result, error) {
|
||||
}
|
||||
|
||||
var err error
|
||||
t.icmp, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
||||
t.icmp, err = icmp.ListenPacket("ip4:icmp", t.SrcAddr)
|
||||
if err != nil {
|
||||
return &t.res, err
|
||||
}
|
||||
@@ -69,8 +69,16 @@ func (t *UDPTracer) Execute() (*Result, error) {
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
go func() {
|
||||
if t.AsyncPrinter != nil {
|
||||
for {
|
||||
t.AsyncPrinter(&t.res)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}()
|
||||
// 如果是表格模式,则一次性并发请求
|
||||
if t.RealtimePrinter == nil {
|
||||
if t.AsyncPrinter != nil {
|
||||
t.wg.Wait()
|
||||
}
|
||||
t.res.reduce(t.final)
|
||||
@@ -100,7 +108,7 @@ func (t *UDPTracer) listenICMP() {
|
||||
case ipv4.ICMPTypeDestinationUnreachable:
|
||||
t.handleICMPMessage(msg, rm.Body.(*icmp.DstUnreach).Data)
|
||||
default:
|
||||
log.Println("received icmp message of unknown type", rm.Type)
|
||||
// log.Println("received icmp message of unknown type", rm.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
111
tracelog/log.go
Normal file
111
tracelog/log.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package tracelog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
)
|
||||
|
||||
func RealtimePrinter(res *trace.Result, ttl int) {
|
||||
f, err := os.OpenFile("/tmp/trace.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
multiWriter := io.MultiWriter(os.Stdout, f)
|
||||
log.SetOutput(multiWriter)
|
||||
log.SetFlags(0)
|
||||
var res_str string
|
||||
res_str += fmt.Sprintf("%-2d ", ttl+1)
|
||||
|
||||
// 去重
|
||||
var latestIP string
|
||||
tmpMap := make(map[string][]string)
|
||||
for i, v := range res.Hops[ttl] {
|
||||
if v.Address == nil && latestIP != "" {
|
||||
tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*"))
|
||||
continue
|
||||
} else if v.Address == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, exist := tmpMap[v.Address.String()]; !exist {
|
||||
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i))
|
||||
// 首次进入
|
||||
if latestIP == "" {
|
||||
for j := 0; j < i; j++ {
|
||||
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*"))
|
||||
}
|
||||
}
|
||||
latestIP = v.Address.String()
|
||||
}
|
||||
|
||||
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))
|
||||
}
|
||||
|
||||
if latestIP == "" {
|
||||
res_str += fmt.Sprintf("%s\n", "*")
|
||||
log.Print(res_str)
|
||||
return
|
||||
}
|
||||
|
||||
var blockDisplay = false
|
||||
for ip, v := range tmpMap {
|
||||
if blockDisplay {
|
||||
res_str += fmt.Sprintf("%4s", "")
|
||||
}
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
res_str += fmt.Sprintf("%-25s ", ip)
|
||||
} else {
|
||||
res_str += fmt.Sprintf("%-15s ", ip)
|
||||
}
|
||||
|
||||
i, _ := strconv.Atoi(v[0])
|
||||
|
||||
if res.Hops[ttl][i].Geo.Asnumber != "" {
|
||||
res_str += fmt.Sprintf("AS%-7s", res.Hops[ttl][i].Geo.Asnumber)
|
||||
} else {
|
||||
res_str += fmt.Sprintf(" %-8s", "*")
|
||||
}
|
||||
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-")
|
||||
if len(whoisFormat) > 1 {
|
||||
whoisFormat[0] = strings.Join(whoisFormat[:2], "-")
|
||||
}
|
||||
|
||||
if whoisFormat[0] != "" {
|
||||
whoisFormat[0] = "[" + whoisFormat[0] + "]"
|
||||
}
|
||||
res_str += fmt.Sprintf("%-16s", whoisFormat[0])
|
||||
}
|
||||
|
||||
if res.Hops[ttl][i].Geo.Country == "" {
|
||||
res.Hops[ttl][i].Geo.Country = "LAN Address"
|
||||
}
|
||||
|
||||
if net.ParseIP(ip).To4() != nil {
|
||||
|
||||
res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-39s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
|
||||
} else {
|
||||
res_str += fmt.Sprintf(" %s %s %s %s %-6s\n %-35s ", res.Hops[ttl][i].Geo.Country, res.Hops[ttl][i].Geo.Prov, res.Hops[ttl][i].Geo.City, res.Hops[ttl][i].Geo.District, res.Hops[ttl][i].Geo.Owner, res.Hops[ttl][i].Hostname)
|
||||
}
|
||||
|
||||
for j := 1; j < len(v); j++ {
|
||||
if len(v) == 2 || j == 1 {
|
||||
res_str += v[j]
|
||||
} else {
|
||||
res_str += fmt.Sprintf("/ %s", v[j])
|
||||
}
|
||||
}
|
||||
log.Print(res_str)
|
||||
blockDisplay = true
|
||||
}
|
||||
}
|
||||
20
tracemap/tracemap.go
Normal file
20
tracemap/tracemap.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package tracemap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
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)),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user