mirror of
https://github.com/nxtrace/NTrace-core.git
synced 2025-08-12 06:26:39 +00:00
Compare commits
39 Commits
v0.1.5-bet
...
v0.1.6-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50cc9858d4 | ||
|
|
27f49f9cd0 | ||
|
|
e4320da08d | ||
|
|
7f16a27580 | ||
|
|
7db77024a3 | ||
|
|
c4ea506c35 | ||
|
|
39e2471845 | ||
|
|
18a9eefec9 | ||
|
|
1b743d1f17 | ||
|
|
3f0b14341a | ||
|
|
2ffd7fdb58 | ||
|
|
8f77050fca | ||
|
|
65652bd4e2 | ||
|
|
4176407a8a | ||
|
|
6bc4abeaf8 | ||
|
|
af0d886a02 | ||
|
|
09fdd2ac37 | ||
|
|
2b9d8176d4 | ||
|
|
ed2f89310f | ||
|
|
bd5a8902d4 | ||
|
|
e2a1bfe8cf | ||
|
|
356b782b3d | ||
|
|
40922ae13a | ||
|
|
8e90795a54 | ||
|
|
bbbb2377e1 | ||
|
|
efdfd9d612 | ||
|
|
016f06bafd | ||
|
|
1049986ebc | ||
|
|
80a75288d2 | ||
|
|
986b8ce300 | ||
|
|
ab774406ac | ||
|
|
9604b7befe | ||
|
|
dc6537005a | ||
|
|
839d227770 | ||
|
|
84e989e71b | ||
|
|
3b74b302cc | ||
|
|
9b0e58359f | ||
|
|
f0d7151144 | ||
|
|
3c65c29eff |
62
README.md
62
README.md
@@ -13,20 +13,18 @@
|
||||
### Automated Install
|
||||
|
||||
```bash
|
||||
# Note: This Script Only Supports Linux/macOS, Other Unix-Like Systems are UNAVAILABLE
|
||||
curl -Ls https://github.com/xgadget-lab/nexttrace/raw/main/nt_install.sh -O && sudo bash nt_install.sh
|
||||
# Linux 一键安装脚本
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
|
||||
|
||||
# GHPROXY 镜像(国内使用)
|
||||
bash <(curl -Ls https://ghproxy.com/https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)
|
||||
|
||||
# macOS brew 安装命令
|
||||
brew tap xgadget-lab/nexttrace && brew install nexttrace
|
||||
```
|
||||
|
||||
* `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
|
||||
* 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台不可用。
|
||||
|
||||
### Fast Test
|
||||
|
||||
此脚本旨在快速测试服务器的到中国内地的线路,建议新手或者没有自定义`NextTrace`功能需求的用户使用。
|
||||
|
||||
```bash
|
||||
curl -Ls https://github.com/xgadget-lab/nexttrace/raw/main/quicklytest.sh -O && sudo bash quicklytest.sh
|
||||
```
|
||||
- `Release`里面为很多系统以及不同架构提供了编译好的二进制可执行文件,如果没有可以自行编译。
|
||||
- 一些本项目的必要依赖在`Windows`上`Golang`底层实现不完全,所以目前`NextTrace`在`Windows`平台不可用。
|
||||
|
||||
### Get Started
|
||||
|
||||
@@ -139,7 +137,11 @@ Options:
|
||||
|
||||
## 项目截图
|
||||
|
||||
<img src=asset/screenshot.png alt="NextTrace Screenshot" width="683" height="688" />
|
||||
<div align="center">
|
||||
|
||||
<img src=asset/screenshot.png alt="NextTrace Screenshot" height="688" />
|
||||
|
||||
</div>
|
||||
|
||||
## FAQ 常见问题
|
||||
|
||||
@@ -149,7 +151,7 @@ Options:
|
||||
|
||||
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. 不会软件的参数使用 -> [前往 Github Discussions](https://github.com/xgadget-lab/nexttrace/discussions)
|
||||
3. 疑似BUG、或者功能建议 -> [前往 Github Issues](https://github.com/xgadget-lab/nexttrace/issues)
|
||||
3. 疑似 BUG、或者功能建议 -> [前往 Github Issues](https://github.com/xgadget-lab/nexttrace/issues)
|
||||
|
||||
## Thanks
|
||||
|
||||
@@ -169,30 +171,30 @@ Options:
|
||||
|
||||
#### China
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :------------: | :----: | :--------------: | :--: |
|
||||
| 电信/联通/移动 | 骨干网 | NextTrace | 10% |
|
||||
| 电信/联通/移动 | 城域网 | 埃文科技 | 90% |
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :------------: | :----: | :-------: | :--: |
|
||||
| 电信/联通/移动 | 骨干网 | NextTrace | 10% |
|
||||
| 电信/联通/移动 | 城域网 | 埃文科技 | 90% |
|
||||
|
||||
#### WorldWide
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :------------: | :----: | :--------------: | :--: |
|
||||
| Tier-01 | 骨干网 | IPInfo | 2% |
|
||||
| Tier-01 | 骨干网 | 埃文科技 | 3% |
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :-----: | :----: | :-------: | :--: |
|
||||
| Tier-01 | 骨干网 | IPInfo | 2% |
|
||||
| Tier-01 | 骨干网 | 埃文科技 | 3% |
|
||||
| Tier-01 | 骨干网 | IPInSight | 5% |
|
||||
| Tier-01 | 城域网 | IPInSight | 90% |
|
||||
| Tier-01 | 城域网 | IPInSight | 90% |
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :------------: | :----: | :--------------: | :--: |
|
||||
| Others | 骨干网 | IPInSight | 5% |
|
||||
| Others | 城域网 | IPInSight | 95% |
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :----: | :----: | :-------: | :--: |
|
||||
| Others | 骨干网 | IPInSight | 5% |
|
||||
| Others | 城域网 | IPInSight | 95% |
|
||||
|
||||
### IPv6 Database
|
||||
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :------------: | :----: | :--------------: | :--: |
|
||||
| All | 全部 | IP2Location Lite | 100% |
|
||||
| ISP | 类型 | 数据源 | 占比 |
|
||||
| :-: | :--: | :--------------: | :--: |
|
||||
| All | 全部 | IP2Location Lite | 100% |
|
||||
|
||||
This product includes IP2Location LITE data available from <a href="https://lite.ip2location.com">https://lite.ip2location.com</a>.
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 907 KiB After Width: | Height: | Size: 1.3 MiB |
@@ -3,8 +3,8 @@ package config
|
||||
import "os"
|
||||
|
||||
type tracerConfig struct {
|
||||
Token `yaml:"Token"`
|
||||
Preference `yaml:"Preference"`
|
||||
Token `yaml:"Token"`
|
||||
Preference `yaml:"Preference"`
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
@@ -13,19 +13,23 @@ type Token struct {
|
||||
}
|
||||
|
||||
type Preference struct {
|
||||
AlwaysRoutePath bool `yaml:"AlwaysRoutePath"`
|
||||
NoRDNS bool `yaml:"NoRDNS"`
|
||||
DataOrigin string `yaml:"DataOrigin"`
|
||||
AlwaysRoutePath bool `yaml:"AlwaysRoutePath"`
|
||||
TablePrintDefault bool `yaml:"TablePrintDefault"`
|
||||
TraceMethod string `yaml:"TraceMethod"`
|
||||
}
|
||||
|
||||
type configPath func() (string, error)
|
||||
|
||||
func configFromRunDir() (string, error) {
|
||||
return "./", nil
|
||||
return "./", nil
|
||||
}
|
||||
|
||||
func configFromUserHomeDir() (string, error) {
|
||||
dir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir + "/.nexttrace/", nil
|
||||
}
|
||||
dir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir + "/.nexttrace/", nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -36,24 +37,54 @@ func writeFile(content []byte) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(path + "ntraceConfig.yml", []byte(content), 0644); err != nil{
|
||||
if err = ioutil.WriteFile(path+"ntraceConfig.yml", []byte(content), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AutoGenerate() (*tracerConfig, error) {
|
||||
token := Token{
|
||||
LeoMoeAPI: "NextTraceDemo",
|
||||
IPInfo: "",
|
||||
}
|
||||
|
||||
preference := Preference{
|
||||
AlwaysRoutePath: false,
|
||||
TablePrintDefault: false,
|
||||
DataOrigin: "LEOMOEAPI",
|
||||
}
|
||||
|
||||
finalConfig := tracerConfig{
|
||||
Token: token,
|
||||
Preference: preference,
|
||||
}
|
||||
|
||||
yamlData, err := yaml.Marshal(&finalConfig)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = writeFile(yamlData); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &finalConfig, nil
|
||||
}
|
||||
}
|
||||
|
||||
func Generate() (*tracerConfig, error) {
|
||||
var leotoken string
|
||||
var iPInfoToken string
|
||||
var routePathEnable string
|
||||
|
||||
fmt.Println("这是一个配置向导,我们会帮助您生成配置文件,它是一次性的,除非您主动要求重新生成,否则它将不会再出现")
|
||||
var tmpInput string
|
||||
|
||||
fmt.Println("请输入您的LeoMoeAPI Token,如果您没有,请到 Telegram Bot @NextTraceBot 获取一个")
|
||||
fmt.Println("欢迎使用高阶自定义功能,这是一个配置向导,我们会帮助您生成配置文件。您的配置文件会被放在 ~/.nexttrace/ntraceConfig.yml 中,您也可以通过编辑这个文件来自定义配置。")
|
||||
|
||||
fmt.Println("请输入您的LeoMoeAPI Token,您可以回车,以便继续使用公共Token")
|
||||
fmt.Scanln(&leotoken)
|
||||
if leotoken == "" {
|
||||
fmt.Println("检测到您的输入为空,您将使用公共Token。这意味着您将和所有使用此Token的客户端共用每分钟150次IP查询的额度")
|
||||
fmt.Println("检测到您的输入为空,您将使用公共Token。")
|
||||
leotoken = "NextTraceDemo"
|
||||
}
|
||||
|
||||
@@ -66,12 +97,47 @@ func Generate() (*tracerConfig, error) {
|
||||
}
|
||||
|
||||
var preference Preference
|
||||
fmt.Print("您是否希望在每次Traceroute结束后显示Route-Path图? (y/n)")
|
||||
fmt.Scanln(&routePathEnable)
|
||||
if routePathEnable == "n" || routePathEnable == "N" || routePathEnable == "no" || routePathEnable == "No" || routePathEnable == "NO" {
|
||||
preference = Preference{AlwaysRoutePath: false}
|
||||
var AlwaysRoutePath bool
|
||||
var tablePrintDefault bool
|
||||
var dataOrigin string
|
||||
fmt.Print("我希望默认在路由跟踪完毕后,不绘制Route-Path图 (y/n) [y]")
|
||||
fmt.Scanln(&tmpInput)
|
||||
if tmpInput == "n" || tmpInput == "N" || tmpInput == "no" || tmpInput == "No" || tmpInput == "NO" {
|
||||
AlwaysRoutePath = true
|
||||
} else {
|
||||
preference = Preference{AlwaysRoutePath: true}
|
||||
AlwaysRoutePath = false
|
||||
}
|
||||
|
||||
fmt.Print("我希望路由跟踪默认实时显示,而不使用制表模式 (y/n) [y]")
|
||||
fmt.Scanln(&tmpInput)
|
||||
if tmpInput == "n" || tmpInput == "N" || tmpInput == "no" || tmpInput == "No" || tmpInput == "NO" {
|
||||
tablePrintDefault = true
|
||||
} else {
|
||||
tablePrintDefault = false
|
||||
}
|
||||
|
||||
fmt.Println("请选择默认的IP地理位置API数据源:\n1. LeoMoe\n2. IPInfo\n3. IPInsight\n4. IP.SB\n5. IP-API.COM")
|
||||
fmt.Print("请输入您的选择:")
|
||||
fmt.Scanln(&tmpInput)
|
||||
switch tmpInput {
|
||||
case "1":
|
||||
dataOrigin = "LEOMOEAPI"
|
||||
case "2":
|
||||
dataOrigin = "IPINFO"
|
||||
case "3":
|
||||
dataOrigin = "IPINSIGHT"
|
||||
case "4":
|
||||
dataOrigin = "IP.SB"
|
||||
case "5":
|
||||
dataOrigin = "IPAPI.COM"
|
||||
default:
|
||||
dataOrigin = "LEOMOEAPI"
|
||||
}
|
||||
|
||||
preference = Preference{
|
||||
AlwaysRoutePath: AlwaysRoutePath,
|
||||
TablePrintDefault: tablePrintDefault,
|
||||
DataOrigin: dataOrigin,
|
||||
}
|
||||
|
||||
finalConfig := tracerConfig{
|
||||
@@ -82,13 +148,13 @@ func Generate() (*tracerConfig, error) {
|
||||
yamlData, err := yaml.Marshal(&finalConfig)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = writeFile(yamlData); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
fmt.Println("配置文件创建成功")
|
||||
fmt.Println("配置文件已经更新,在下次路由跟踪时,将会使用您的偏好。")
|
||||
return &finalConfig, nil
|
||||
}
|
||||
}
|
||||
|
||||
111
fast_trace/basic.go
Normal file
111
fast_trace/basic.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package fastTrace
|
||||
|
||||
type AllLocationCollection struct {
|
||||
Beijing BackBoneCollection
|
||||
Shanghai BackBoneCollection
|
||||
Guangzhou BackBoneCollection
|
||||
}
|
||||
|
||||
type BackBoneCollection struct {
|
||||
Location string
|
||||
CT163 ISPCollection
|
||||
CTCN2 ISPCollection
|
||||
CU169 ISPCollection
|
||||
CU9929 ISPCollection
|
||||
CM ISPCollection
|
||||
EDU ISPCollection
|
||||
}
|
||||
|
||||
type ISPCollection struct {
|
||||
ISPName string
|
||||
IP string
|
||||
}
|
||||
|
||||
const (
|
||||
CT163 string = "电信 163 AS4134"
|
||||
CTCN2 string = "电信 CN2 AS4809"
|
||||
CU169 string = "联通 169 AS4837"
|
||||
CU9929 string = "联通 A网 AS9929"
|
||||
CM string = "移动 骨干网 AS9808"
|
||||
EDU string = "教育网 CERNET AS4538"
|
||||
)
|
||||
|
||||
var TestIPsCollection = AllLocationCollection{
|
||||
Beijing: Beijing,
|
||||
Shanghai: Shanghai,
|
||||
Guangzhou: Guangzhou,
|
||||
}
|
||||
|
||||
var Beijing = BackBoneCollection{
|
||||
Location: "北京",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "106.37.67.1",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "123.125.96.156",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "211.136.25.153",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "101.6.15.130",
|
||||
},
|
||||
}
|
||||
|
||||
var Shanghai = BackBoneCollection{
|
||||
Location: "上海",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "101.226.28.198",
|
||||
},
|
||||
|
||||
CTCN2: ISPCollection{
|
||||
ISPName: CTCN2,
|
||||
IP: "58.32.4.1",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "139.226.206.150",
|
||||
},
|
||||
|
||||
CU9929: ISPCollection{
|
||||
ISPName: CU9929,
|
||||
IP: "210.13.86.1",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "120.204.34.85",
|
||||
},
|
||||
|
||||
EDU: ISPCollection{
|
||||
ISPName: EDU,
|
||||
IP: "202.120.58.155",
|
||||
},
|
||||
}
|
||||
|
||||
var Guangzhou = BackBoneCollection{
|
||||
Location: "广州",
|
||||
CT163: ISPCollection{
|
||||
ISPName: CT163,
|
||||
IP: "106.37.67.1",
|
||||
},
|
||||
|
||||
CU169: ISPCollection{
|
||||
ISPName: CU169,
|
||||
IP: "123.125.96.156",
|
||||
},
|
||||
|
||||
CM: ISPCollection{
|
||||
ISPName: CM,
|
||||
IP: "120.198.26.254",
|
||||
},
|
||||
}
|
||||
136
fast_trace/fast_trace.go
Normal file
136
fast_trace/fast_trace.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package fastTrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/config"
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/printer"
|
||||
"github.com/xgadget-lab/nexttrace/reporter"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
)
|
||||
|
||||
type FastTracer struct {
|
||||
Preference config.Preference
|
||||
TracerouteMethod trace.Method
|
||||
}
|
||||
|
||||
func (f *FastTracer) tracert(location string, ispCollection ISPCollection) {
|
||||
fmt.Printf("『%s %s 』\n", location, ispCollection.ISPName)
|
||||
fmt.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ispCollection.IP)
|
||||
ip := net.ParseIP(ispCollection.IP)
|
||||
var conf = trace.Config{
|
||||
DestIP: ip,
|
||||
DestPort: 80,
|
||||
MaxHops: 30,
|
||||
NumMeasurements: 3,
|
||||
ParallelRequests: 18,
|
||||
RDns: !f.Preference.NoRDNS,
|
||||
IPGeoSource: ipgeo.GetSource(f.Preference.DataOrigin),
|
||||
Timeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
if f.TracerouteMethod == trace.ICMPTrace {
|
||||
conf.RealtimePrinter = printer.RealtimePrinter
|
||||
}
|
||||
|
||||
res, err := trace.Traceroute(f.TracerouteMethod, conf)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if f.TracerouteMethod == trace.TCPTrace {
|
||||
printer.TracerouteTablePrinter(res)
|
||||
}
|
||||
|
||||
if f.Preference.AlwaysRoutePath {
|
||||
r := reporter.New(res, ip.String())
|
||||
r.Print()
|
||||
}
|
||||
}
|
||||
|
||||
func initialize() *FastTracer {
|
||||
configData, err := config.Read()
|
||||
|
||||
// Initialize Default Config
|
||||
if err != nil || configData.DataOrigin == "" {
|
||||
if configData, err = config.AutoGenerate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set Token from Config
|
||||
ipgeo.SetToken(configData.Token)
|
||||
|
||||
return &FastTracer{
|
||||
Preference: configData.Preference,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FastTracer) testAll() {
|
||||
f.testCT()
|
||||
f.testCU()
|
||||
f.testCM()
|
||||
f.testEDU()
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCT() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CT163)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CT163)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CTCN2)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CT163)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCU() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CU169)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU169)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CU9929)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CU169)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testCM() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.CM)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.CM)
|
||||
f.tracert(TestIPsCollection.Guangzhou.Location, TestIPsCollection.Guangzhou.CM)
|
||||
}
|
||||
|
||||
func (f *FastTracer) testEDU() {
|
||||
f.tracert(TestIPsCollection.Beijing.Location, TestIPsCollection.Beijing.EDU)
|
||||
f.tracert(TestIPsCollection.Shanghai.Location, TestIPsCollection.Shanghai.EDU)
|
||||
}
|
||||
|
||||
func FastTest(tm bool) {
|
||||
var c string
|
||||
|
||||
fmt.Println("您想测试哪些ISP的路由?\n1. 国内四网\n2. 电信\n3. 联通\n4. 移动\n5. 教育网")
|
||||
fmt.Print("请选择选项:")
|
||||
fmt.Scanln(&c)
|
||||
|
||||
ft := initialize()
|
||||
|
||||
if !tm {
|
||||
ft.TracerouteMethod = trace.ICMPTrace
|
||||
fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数")
|
||||
} else {
|
||||
ft.TracerouteMethod = trace.TCPTrace
|
||||
}
|
||||
|
||||
switch c {
|
||||
case "1":
|
||||
ft.testAll()
|
||||
case "2":
|
||||
ft.testCT()
|
||||
case "3":
|
||||
ft.testCU()
|
||||
case "4":
|
||||
ft.testCM()
|
||||
case "5":
|
||||
ft.testEDU()
|
||||
default:
|
||||
ft.testAll()
|
||||
}
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -11,7 +11,7 @@ require (
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // direct
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
58
main.go
58
main.go
@@ -4,11 +4,13 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/config"
|
||||
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"
|
||||
@@ -17,14 +19,16 @@ import (
|
||||
)
|
||||
|
||||
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 manualConfig = fSet.Bool("c", false, "Manual Config [Advanced]")
|
||||
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 rdnsenable = fSet.Bool("rdns", false, "Set whether rDNS will be display")
|
||||
var dataOrigin = fSet.String("d", "", "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 ver = fSet.Bool("V", false, "Check Version")
|
||||
@@ -42,6 +46,8 @@ func flagApply() string {
|
||||
if len(os.Args) < 2 {
|
||||
printArgHelp()
|
||||
}
|
||||
|
||||
// flag parse
|
||||
if !strings.HasPrefix(os.Args[1], "-") {
|
||||
target = os.Args[1]
|
||||
fSet.Parse(os.Args[2:])
|
||||
@@ -49,9 +55,26 @@ func flagApply() string {
|
||||
fSet.Parse(os.Args[1:])
|
||||
target = fSet.Arg(0)
|
||||
}
|
||||
|
||||
// Print Version
|
||||
if *ver {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Advanced Config
|
||||
if *manualConfig {
|
||||
if _, err := config.Generate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// -f Fast Test
|
||||
if *fastTest {
|
||||
fastTrace.FastTest(*tcpSYNFlag)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if target == "" {
|
||||
printArgHelp()
|
||||
}
|
||||
@@ -66,17 +89,32 @@ func main() {
|
||||
log.Fatalln("Traceroute requires root/sudo privileges.")
|
||||
}
|
||||
|
||||
configData, err := config.Read();
|
||||
configData, err := config.Read()
|
||||
|
||||
if err != nil {
|
||||
if configData, err = config.Generate(); err != nil {
|
||||
// Initialize Default Config
|
||||
if err != nil || configData.DataOrigin == "" {
|
||||
if configData, err = config.AutoGenerate(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set Token from Config
|
||||
ipgeo.SetToken(configData.Token)
|
||||
|
||||
ip := util.DomainLookUp(domain)
|
||||
// Check Whether User has specified IP Geograph Data Provider
|
||||
if *dataOrigin == "" {
|
||||
// Use Default Data Origin with Config
|
||||
*dataOrigin = configData.DataOrigin
|
||||
}
|
||||
|
||||
var ip net.IP
|
||||
|
||||
if *tcpSYNFlag || *udpPackageFlag {
|
||||
ip = util.DomainLookUp(domain, true)
|
||||
} else {
|
||||
ip = util.DomainLookUp(domain, false)
|
||||
}
|
||||
|
||||
printer.PrintTraceRouteNav(ip, domain, *dataOrigin)
|
||||
|
||||
var m trace.Method = ""
|
||||
@@ -100,7 +138,7 @@ func main() {
|
||||
MaxHops: *maxHops,
|
||||
NumMeasurements: *numMeasurements,
|
||||
ParallelRequests: *parallelRequests,
|
||||
RDns: *rdnsenable,
|
||||
RDns: !*noRdns,
|
||||
IPGeoSource: ipgeo.GetSource(*dataOrigin),
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
@@ -115,15 +153,13 @@ func main() {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if (*tcpSYNFlag && *udpPackageFlag) || *tablePrint {
|
||||
if (*tcpSYNFlag && *udpPackageFlag) || *tablePrint || configData.TablePrintDefault {
|
||||
printer.TracerouteTablePrinter(res)
|
||||
} else if *tcpSYNFlag || *udpPackageFlag {
|
||||
printer.TraceroutePrinter(res)
|
||||
}
|
||||
|
||||
p := configData.Preference
|
||||
|
||||
if *routePath || p.AlwaysRoutePath {
|
||||
if *routePath || configData.AlwaysRoutePath {
|
||||
r := reporter.New(res, ip.String())
|
||||
r.Print()
|
||||
}
|
||||
|
||||
304
nt_install.sh
304
nt_install.sh
@@ -1,270 +1,123 @@
|
||||
#!/bin/bash
|
||||
|
||||
auto=False
|
||||
#是否忽略一切警告,按默认执行
|
||||
if [[ $1 == "--auto" ]]; then
|
||||
auto=True
|
||||
echo "自动运行中"
|
||||
fi
|
||||
|
||||
usrPath="/usr/local/bin"
|
||||
|
||||
function red() {
|
||||
echo -e "\033[31m""${1}""\033[0m"
|
||||
}
|
||||
Green_font="\033[32m"
|
||||
Yellow_font="\033[33m"
|
||||
Red_font="\033[31m"
|
||||
Font_suffix="\033[0m"
|
||||
Info="${Green_font}[Info]${Font_suffix}"
|
||||
Error="${Red_font}[Error]${Font_suffix}"
|
||||
Tips="${Green_font}[Tips]${Font_suffix}"
|
||||
Temp_path="/var/tmp/nexttrace"
|
||||
|
||||
checkRootPermit() {
|
||||
[[ $EUID -ne 0 ]] && red "请使用sudo/root权限运行本脚本" && exit 1
|
||||
[[ $EUID -ne 0 ]] && echo -e "${Error} 请使用sudo/root权限运行本脚本" && exit 1
|
||||
}
|
||||
|
||||
checkSystemArch() {
|
||||
arch=$(uname -m)
|
||||
case $arch in
|
||||
'x86_64')
|
||||
archParam='amd64'
|
||||
;;
|
||||
'mips')
|
||||
archParam='mips'
|
||||
;;
|
||||
'arm64' | 'aarch64')
|
||||
arch=$(uname -m)
|
||||
if [[ $arch == "x86_64" ]]; then
|
||||
archParam="amd64"
|
||||
fi
|
||||
|
||||
if [[ $arch == "aarch64" ]]; then
|
||||
archParam="arm64"
|
||||
;;
|
||||
'armv7l')
|
||||
archParam='armv7'
|
||||
;;
|
||||
'i386')
|
||||
archParam="386"
|
||||
;;
|
||||
*)
|
||||
red "未知的系统架构,请联系开发者."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
checkSystemDistribution() {
|
||||
case "$OSTYPE" in
|
||||
darwin*)
|
||||
osDistribution="darwin"
|
||||
downPath="/var/tmp/nexttrace"
|
||||
;;
|
||||
linux*)
|
||||
case "$OSTYPE" in
|
||||
linux*)
|
||||
osDistribution="linux"
|
||||
downPath="/var/tmp/nexttrace"
|
||||
downPath="/usr/local/bin/nexttrace"
|
||||
;;
|
||||
*)
|
||||
red "安装脚本暂不支持的操作系统: $OSTYPE"
|
||||
*)
|
||||
echo "unknown: $OSTYPE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ask_if() {
|
||||
local choice=""
|
||||
red "${1}"
|
||||
read -r choice
|
||||
[[ $choice == y ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
#检查脚本更新
|
||||
check_script_update() {
|
||||
if [[ ${osDistribution} == "darwin" ]]; then
|
||||
[ "$(md5 <"${BASH_SOURCE[0]}")" == "$(curl -sL "https://github.com/xgadget-lab/nexttrace/raw/main/nt_install.sh" | md5)" ] && return 1 || return 0
|
||||
else
|
||||
[ "$(md5sum "${BASH_SOURCE[0]}" | awk '{print $1}')" == "$(md5sum <(curl -sL "https://github.com/xgadget-lab/nexttrace/raw/main/nt_install.sh") | awk '{print $1}')" ] && return 1 || return 0
|
||||
fi
|
||||
}
|
||||
|
||||
#更新脚本
|
||||
update_script() {
|
||||
if curl -sL -o "${BASH_SOURCE[0]}" "https://github.com/xgadget-lab/nexttrace/raw/main/nt_install.sh" || curl -sL -o "${BASH_SOURCE[0]}" "https://github.com/xgadget-lab/nexttrace/raw/main/nt_install.sh"; then
|
||||
red "nt_install.sh更新完成,正在重启脚本..."
|
||||
exec bash "${BASH_SOURCE[0]}" --auto
|
||||
else
|
||||
red "更新nt_install.sh失败!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ask_update_script() {
|
||||
if check_script_update; then
|
||||
red "nt_install.sh可升级"
|
||||
[[ $auto == True ]] && update_script
|
||||
ask_if "是否升级脚本?(n/y):[n]" && update_script
|
||||
else
|
||||
red "nt_install.sh已经是最新版本"
|
||||
fi
|
||||
esac
|
||||
}
|
||||
|
||||
getLocation() {
|
||||
red "正在获取地理位置信息..."
|
||||
countryCode=$(curl -s "http://ip-api.com/line/?fields=countryCode")
|
||||
countryCode=$(curl -s "http://ip-api.com/line/?fields=countryCode")
|
||||
}
|
||||
|
||||
checkPackageManger() {
|
||||
if [[ ${osDistribution} == "darwin" ]]; then
|
||||
# brew update
|
||||
PACKAGE_MANAGEMENT_INSTALL='brew install'
|
||||
PACKAGE_MANAGEMENT_REMOVE='brew uninstall'
|
||||
return 0
|
||||
fi
|
||||
if [[ "$(which apt)" ]]; then
|
||||
apt-get update
|
||||
PACKAGE_MANAGEMENT_INSTALL='apt-get -y --no-install-recommends install'
|
||||
PACKAGE_MANAGEMENT_REMOVE='apt-get purge'
|
||||
elif [[ "$(which dnf)" ]]; then
|
||||
dnf check-update
|
||||
PACKAGE_MANAGEMENT_INSTALL='dnf -y install'
|
||||
PACKAGE_MANAGEMENT_REMOVE='dnf remove'
|
||||
elif [[ "$(which yum)" ]]; then
|
||||
PACKAGE_MANAGEMENT_INSTALL='yum -y install'
|
||||
PACKAGE_MANAGEMENT_REMOVE='yum remove'
|
||||
elif [[ "$(which zypper)" ]]; then
|
||||
zypper refresh
|
||||
PACKAGE_MANAGEMENT_INSTALL='zypper install -y --no-recommends'
|
||||
PACKAGE_MANAGEMENT_REMOVE='zypper remove'
|
||||
elif [[ "$(which pacman)" ]]; then
|
||||
PACKAGE_MANAGEMENT_INSTALL='pacman -Syu --noconfirm'
|
||||
PACKAGE_MANAGEMENT_REMOVE='pacman -Rsn'
|
||||
else
|
||||
red "error: The script does not support the package manager in this operating system."
|
||||
exit 1
|
||||
fi
|
||||
installWgetPackage() {
|
||||
echo -e "${Info} wget 正在安装中..."
|
||||
# try apt
|
||||
apt -h &> /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
# 先更新一下数据源,有些机器数据源比较老可能会404
|
||||
apt update -y &> /dev/null
|
||||
apt install wget -y &> /dev/null
|
||||
fi
|
||||
|
||||
# try yum
|
||||
yum -h &> /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
yum install wget -y &> /dev/null
|
||||
fi
|
||||
|
||||
# try dnf
|
||||
dnf -h &> /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
dnf install wget -y &> /dev/null
|
||||
fi
|
||||
|
||||
# try pacman
|
||||
pacman -h &> /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
pacman -Sy
|
||||
pacman -S wget
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
install_software() {
|
||||
package_name="$1"
|
||||
which "$package_name" >/dev/null 2>&1 && return
|
||||
[[ ${osDistribution} == "darwin" ]] && echo -e "由于macOS brew的权限限制,请以非root权限执行下面一行提示的命令后再次运行本脚本(注意不要在该命令加sudo!):\nbrew update && ${PACKAGE_MANAGEMENT_INSTALL} $package_name " && exit 0
|
||||
red "${package_name} 正在安装中...(此步骤时间可能较长,请耐心等待)"
|
||||
if ${PACKAGE_MANAGEMENT_INSTALL} "$package_name"; then
|
||||
red "info: $package_name is installed."
|
||||
else
|
||||
red "error: Installation of $package_name failed, please check your network."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
checkVersion() {
|
||||
which nexttrace >/dev/null 2>&1 || return
|
||||
red "正在检查版本..."
|
||||
version=$(curl -sL https://api.github.com/repos/xgadget-lab/nexttrace/releases/latest | jq -r '.tag_name')
|
||||
if [[ $version == "" ]]; then
|
||||
red "获取版本失败,请检查网络连接"
|
||||
exit 1
|
||||
fi
|
||||
currentVersion=$(nexttrace -V | head -n 1 | awk '{print $2}')
|
||||
if [[ $currentVersion == "$version" ]]; then
|
||||
red "当前版本已是最新版本"
|
||||
exit 0
|
||||
fi
|
||||
red "当前最新release版本:${version}"
|
||||
red "您当前的版本:${currentVersion}"
|
||||
if [[ $auto == True ]]; then
|
||||
return 0
|
||||
fi
|
||||
read -r -p "是否更新软件? (n/y):[n]" input
|
||||
case $input in
|
||||
[yY][eE][sS] | [yY])
|
||||
return 0
|
||||
;;
|
||||
[nN][oO] | [nN])
|
||||
red "您选择了取消更新,脚本即将退出"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
red "您选择了取消更新,脚本即将退出"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
checkWgetPackage() {
|
||||
wget -h &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
installWgetPackage
|
||||
fi
|
||||
}
|
||||
|
||||
downloadBinrayFile() {
|
||||
red "正在获取最新版的 NextTrace 发行版文件信息..."
|
||||
# 简单说明一下,Github提供了一个API,可以获取最新发行版本的二进制文件下载地址(对应的是browser_download_url),根据刚刚测得的osDistribution、archParam,获取对应的下载地址
|
||||
# red nexttrace_${osDistribution}_${archParam}
|
||||
latestURL=$(curl -s https://api.github.com/repos/xgadget-lab/nexttrace/releases/latest | jq ".assets[] | select(.name == \"nexttrace_${osDistribution}_${archParam}\") | .browser_download_url")
|
||||
latestURL=${latestURL:1:$((${#latestURL} - 1 - 1))}
|
||||
if [ "$countryCode" == "CN" ]; then
|
||||
if [[ $auto == True ]]; then
|
||||
latestURL="https://ghproxy.com/"$latestURL
|
||||
else
|
||||
read -r -p "检测到国内网络环境,是否使用镜像下载以加速(n/y)[y]" input
|
||||
case $input in
|
||||
[yY][eE][sS] | [yY])
|
||||
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}')
|
||||
|
||||
if [ "$countryCode" == "CN" ]; then
|
||||
echo -e "${Info} 检测到国内环境,正在使用镜像下载"
|
||||
latestURL="https://ghproxy.com/"$latestURL
|
||||
;;
|
||||
|
||||
[nN][oO] | [nN])
|
||||
red "您选择了不使用镜像,下载可能会变得异常缓慢,或者失败"
|
||||
;;
|
||||
|
||||
*)
|
||||
latestURL="https://ghproxy.com/"$latestURL
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
red "正在下载 NextTrace 二进制文件..."
|
||||
if wget -O ${downPath} "${latestURL}"; then
|
||||
red "NextTrace 现在已经在您的系统中可用"
|
||||
|
||||
echo -e "${Info} 正在下载 NextTrace 二进制文件..."
|
||||
wget -O ${Temp_path} ${latestURL} &> /dev/null
|
||||
if [ $? -eq 0 ];
|
||||
then
|
||||
changeMode
|
||||
mv ${downPath} ${usrPath}
|
||||
else
|
||||
red "NextTrace 下载失败,请检查您的网络是否正常"
|
||||
mv ${Temp_path} ${downPath}
|
||||
echo -e "${Info} NextTrace 现在已经在您的系统中可用"
|
||||
else
|
||||
echo -e "${Error} NextTrace 下载失败,请检查您的网络是否正常"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
changeMode() {
|
||||
chmod +x ${downPath}
|
||||
[[ ${osDistribution} == "darwin" ]] && xattr -r -d com.apple.quarantine ${downPath}
|
||||
chmod +x ${Temp_path} &> /dev/null
|
||||
}
|
||||
|
||||
runBinrayFileHelp() {
|
||||
if [ -e ${usrPath} ]; then
|
||||
${usrPath}/nexttrace -h
|
||||
fi
|
||||
red "You may need to execute a command to remove dependent software: $PACKAGE_MANAGEMENT_REMOVE wget jq"
|
||||
}
|
||||
|
||||
addCronTask() {
|
||||
read -r -p "是否添加自动更新任务?(n/y):[n]" input
|
||||
case $input in
|
||||
[yY][eE][sS] | [yY])
|
||||
if [[ ${osDistribution} == "darwin" ]]; then
|
||||
crontab -l >crontab.bak 2>/dev/null
|
||||
sed -i '' '/nt_install.sh/d' crontab.bak
|
||||
elif [[ ${osDistribution} == "linux" ]]; then
|
||||
crontab -l >crontab.bak 2>/dev/null
|
||||
sed -i '/nt_install.sh/d' crontab.bak
|
||||
else
|
||||
red "暂不支持您的系统,无法自动添加crontab任务"
|
||||
return
|
||||
if [ -e ${downPath} ]; then
|
||||
${downPath} -V
|
||||
echo -e "${Tips} 一切准备就绪!使用命令 nexttrace 1.1.1.1 开始您的第一次路由测试吧~ 更多进阶命令玩法可以用 nexttrace -h 查看哦\n 关于软件卸载,因为nexttrace是绿色版单文件,卸载只需输入命令 rm /usr/local/bin/nexttrace 即可"
|
||||
fi
|
||||
echo "1 1 * * * $(dirname "$(readlink -f "$0")")/nt_install.sh --auto >> /var/log/nt_install.log" >>crontab.bak
|
||||
crontab crontab.bak
|
||||
rm -f crontab.bak
|
||||
;;
|
||||
[nN][oO] | [nN])
|
||||
red "您选择了不添加自动更新任务,您也可以通过命令 再次执行此脚本 手动更新"
|
||||
;;
|
||||
*)
|
||||
red "您选择了不添加自动更新任务,您可以通过命令 再次执行此脚本 手动更新"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check Procedure
|
||||
checkRootPermit
|
||||
checkSystemDistribution
|
||||
checkSystemArch
|
||||
ask_update_script
|
||||
checkPackageManger
|
||||
install_software wget
|
||||
install_software jq
|
||||
checkVersion
|
||||
checkWgetPackage
|
||||
|
||||
# Download Procedure
|
||||
getLocation
|
||||
@@ -272,4 +125,3 @@ downloadBinrayFile
|
||||
|
||||
# Run Procedure
|
||||
runBinrayFileHelp
|
||||
[[ $auto != True ]] && addCronTask
|
||||
|
||||
@@ -52,11 +52,11 @@ func formatIpGeoData(ip string, data *ipgeo.IPGeoData) string {
|
||||
// TODO: 判断阿里云和腾讯云内网,数据不足,有待进一步完善
|
||||
// TODO: 移动IDC判断到Hop.fetchIPData函数,减少API调用
|
||||
if strings.HasPrefix(ip, "9.") {
|
||||
res = append(res, "局域网", "腾讯云")
|
||||
res = append(res, "LAN Address")
|
||||
} else if strings.HasPrefix(ip, "11.") {
|
||||
res = append(res, "局域网", "阿里云")
|
||||
res = append(res, "LAN Address")
|
||||
} else if data.Country == "" {
|
||||
res = append(res, "局域网")
|
||||
res = append(res, "LAN Address")
|
||||
} else {
|
||||
// 有些IP的归属信息为空,这个时候将ISP的信息填入
|
||||
if data.Owner == "" {
|
||||
|
||||
@@ -2,16 +2,17 @@ package printer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
"github.com/xgadget-lab/nexttrace/util"
|
||||
"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"), "1.1.1.1", "dataOrigin")
|
||||
PrintTraceRouteNav(util.DomainLookUp("1.1.1.1", false), "1.1.1.1", "dataOrigin")
|
||||
}
|
||||
|
||||
var testGeo = &ipgeo.IPGeoData{
|
||||
|
||||
@@ -2,9 +2,10 @@ package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
"strings"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/ipgeo"
|
||||
|
||||
"github.com/xgadget-lab/nexttrace/trace"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@@ -38,7 +39,7 @@ func TracerouteTablePrinter(res *trace.Result) {
|
||||
if data.City != "" {
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country+", "+data.Prov+", "+data.City, data.Owner)
|
||||
} else {
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country, data.Owner)
|
||||
tbl.AddRow(data.Hop, data.IP, data.Latency, data.Asnumber, data.Country + ", " + data.Prov, data.Owner)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,16 +74,18 @@ func tableDataGenerator(h trace.Hop) *rowData {
|
||||
Hop: fmt.Sprint(h.TTL),
|
||||
IP: IP,
|
||||
Latency: lantency,
|
||||
Country: "局域网",
|
||||
Owner: "腾讯云",
|
||||
Country: "LAN Address",
|
||||
Prov: "LAN Address",
|
||||
Owner: "",
|
||||
}
|
||||
} else if strings.HasPrefix(IP, "11.") {
|
||||
return &rowData{
|
||||
Hop: fmt.Sprint(h.TTL),
|
||||
IP: IP,
|
||||
Latency: lantency,
|
||||
Country: "局域网",
|
||||
Owner: "阿里云",
|
||||
Country: "LAN Address",
|
||||
Prov: "LAN Address",
|
||||
Owner: "",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
271
quicklytest.sh
271
quicklytest.sh
@@ -1,271 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
Green_font="\033[32m" && Red_font="\033[31m" && Font_suffix="\033[0m"
|
||||
Info="${Green_font}[Info]${Font_suffix}"
|
||||
Error="${Red_font}[Error]${Font_suffix}"
|
||||
echo -e "${Green_font}
|
||||
#======================================
|
||||
# Project: NextTrace https://github.com/xgadget-lab/nexttrace
|
||||
# Copyright Notice:
|
||||
# This script is ported from @KANIKIG https://github.com/KANIKIG/
|
||||
# The developer team made some modifications to adapt to NextTrace under the GPL-3.0 LICENSE
|
||||
# NextTrace:
|
||||
# XGadget-lab Leo (leo.moe) & Vincent (vincent.moe) & zhshch (xzhsh.ch)
|
||||
# IP Geo Data Provider: LeoMoeAPI
|
||||
#======================================
|
||||
${Font_suffix}"
|
||||
|
||||
check_root() {
|
||||
[[ "$(id -u)" != "0" ]] && echo -e "${Error} must be root user !" && exit 1
|
||||
}
|
||||
|
||||
checkNexttrace() {
|
||||
echo -e "${Info} 正在检查Nexttrace...(若未安装NextTrace则开始安装)"
|
||||
if curl -sL -O "https://github.com/xgadget-lab/nexttrace/raw/main/nt_install.sh" || curl -sL -O "https://github.com/xgadget-lab/nexttrace/raw/main/nt_install.sh"; then
|
||||
bash nt_install.sh #--auto #>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
ask_if() {
|
||||
local choice=""
|
||||
echo -e "${Info} $1"
|
||||
read -r choice
|
||||
[[ $choice == y ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
checkSystemDistribution() {
|
||||
case "$OSTYPE" in
|
||||
darwin*)
|
||||
osDistribution="darwin"
|
||||
;;
|
||||
linux*)
|
||||
osDistribution="linux"
|
||||
;;
|
||||
*)
|
||||
red "unknown: $OSTYPE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
#检查脚本更新
|
||||
check_script_update() {
|
||||
if [[ ${osDistribution} == "darwin" ]]; then
|
||||
[ "$(md5 <"${BASH_SOURCE[0]}")" == "$(curl -sL "https://github.com/xgadget-lab/nexttrace/raw/main/quicklytest.sh" | md5)" ] && return 1 || return 0
|
||||
else
|
||||
[ "$(md5sum "${BASH_SOURCE[0]}" | awk '{print $1}')" == "$(md5sum <(curl -sL "https://github.com/xgadget-lab/nexttrace/raw/main/quicklytest.sh") | awk '{print $1}')" ] && return 1 || return 0
|
||||
fi
|
||||
}
|
||||
|
||||
#更新脚本
|
||||
update_script() {
|
||||
if curl -sL -o "${BASH_SOURCE[0]}" "https://github.com/xgadget-lab/nexttrace/raw/main/quicklytest.sh" || curl -sL -o "${BASH_SOURCE[0]}" "https://github.com/xgadget-lab/nexttrace/raw/main/quicklytest.sh"; then
|
||||
echo -e "${Info} quickylytest.sh更新完成,正在重启脚本..."
|
||||
exec bash "${BASH_SOURCE[0]}"
|
||||
else
|
||||
echo -e "${Info} 更新quickylytest.sh失败!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ask_update_script() {
|
||||
if check_script_update; then
|
||||
echo -e "${Info} quickylytest.sh可升级"
|
||||
ask_if "是否升级脚本?(n/y)[n]" && update_script
|
||||
else
|
||||
echo -e "${Info} quickylytest.sh已经是最新版本"
|
||||
fi
|
||||
}
|
||||
|
||||
check_mode() {
|
||||
echo -e "${Info} Nexttrace目前支持以下三种协议发起Traceroute请求:\n1.ICMP\n2.TCP(速度最快,但部分节点不支持)\n3.UDP\n(IPv6暂只支持ICMP模式)" && read -r -p "输入数字以选择:" node
|
||||
|
||||
while [[ ! "${node}" =~ ^[1-3]$ ]]; do
|
||||
echo -e "${Error} 无效输入"
|
||||
echo -e "${Info} 请重新选择" && read -r -p "输入数字以选择:" node
|
||||
done
|
||||
|
||||
[[ "${node}" == "1" ]] && TRACECMD="nexttrace"
|
||||
[[ "${node}" == "2" ]] && TRACECMD="nexttrace -T"
|
||||
[[ "${node}" == "3" ]] && TRACECMD="nexttrace -U"
|
||||
|
||||
echo -e "${Info} 结果是否制表?(制表模式为非实时显示)"
|
||||
if ask_if "输入n/y以选择模式:[n]"; then
|
||||
TRACECMD=${TRACECMD}" -rdns -table"
|
||||
# ##Route-Path功能还未完善,临时替代:
|
||||
# [[ "${node}" == "2" ]] && TRACECMD=${TRACECMD}" -report"
|
||||
# ##
|
||||
else
|
||||
TRACECMD=${TRACECMD}" -rdns -realtime"
|
||||
# ##Route-Path功能还未完善,临时替代:
|
||||
# [[ "${node}" == "1" ]] && TRACECMD=${TRACECMD}" -report"
|
||||
# [[ "${node}" == "2" ]] && TRACECMD=${TRACECMD}" -report"
|
||||
# ##
|
||||
fi
|
||||
|
||||
echo -e "${Info} 是否输出Route-Path?"
|
||||
ask_if "输入n/y以选择模式:[n]" && TRACECMD=${TRACECMD}" -report"
|
||||
|
||||
}
|
||||
|
||||
test_single() {
|
||||
echo -e "${Info} 请输入你要测试的目标 ip :"
|
||||
read -r -p "输入 ip 地址:" ip
|
||||
|
||||
while [[ -z "${ip}" ]]; do
|
||||
echo -e "${Error} 无效输入"
|
||||
echo -e "${Info} 请重新输入" && read -r -p "输入 ip 地址:" ip
|
||||
done
|
||||
|
||||
${TRACECMD} "${ip}" | grep -v -E 'NextTrace|XGadget-lab|Data\ Provider'
|
||||
|
||||
repeat_test_single
|
||||
}
|
||||
|
||||
repeat_test_single() {
|
||||
echo -e "${Info} 是否继续测试其他目标 ip ?"
|
||||
if ask_if "输入n/y以选择:[n]"; then
|
||||
test_single
|
||||
else
|
||||
echo -e "${Info} 退出脚本 ..." && exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
test_alternative() {
|
||||
select_alternative
|
||||
set_alternative
|
||||
result_alternative
|
||||
}
|
||||
|
||||
select_alternative() {
|
||||
echo -e "${Info} 选择需要测速的目标网络: \n1.中国电信\n2.中国联通\n3.中国移动\n4.教育网"
|
||||
read -r -p "输入数字以选择:" ISP
|
||||
|
||||
while [[ ! "${ISP}" =~ ^[1-4]$ ]]; do
|
||||
echo -e "${Error} 无效输入"
|
||||
echo -e "${Info} 请重新选择" && read -r -p "输入数字以选择:" ISP
|
||||
done
|
||||
}
|
||||
|
||||
set_alternative() {
|
||||
[[ "${ISP}" == "1" ]] && node_1
|
||||
[[ "${ISP}" == "2" ]] && node_2
|
||||
[[ "${ISP}" == "3" ]] && node_3
|
||||
[[ "${ISP}" == "4" ]] && node_4
|
||||
}
|
||||
|
||||
node_1() {
|
||||
echo -e "1.上海电信(天翼云)\n2.厦门电信CN2\n3.北京电信\n4.江苏电信\n5.广东深圳电信\n6.广州电信(天翼云)\n7.浙江电信" && read -r -p "输入数字以选择:" node
|
||||
|
||||
while [[ ! "${node}" =~ ^[1-7]$ ]]; do
|
||||
echo -e "${Error} 无效输入"
|
||||
echo -e "${Info} 请重新选择" && read -r -p "输入数字以选择:" node
|
||||
done
|
||||
|
||||
[[ "${node}" == "1" ]] && ISP_name="上海电信" && ip=101.89.132.9
|
||||
[[ "${node}" == "2" ]] && ISP_name="厦门电信CN2" && ip=117.28.254.129
|
||||
[[ "${node}" == "3" ]] && ISP_name="北京电信" && ip=120.92.180.135
|
||||
[[ "${node}" == "4" ]] && ISP_name="江苏电信" && ip=221.229.173.233
|
||||
[[ "${node}" == "5" ]] && ISP_name="广东深圳电信" && ip=116.6.211.41
|
||||
[[ "${node}" == "6" ]] && ISP_name="广州电信(天翼云)" && ip=14.215.116.1
|
||||
[[ "${node}" == "7" ]] && ISP_name="浙江电信" && ip=115.236.169.86
|
||||
}
|
||||
|
||||
node_2() {
|
||||
echo -e "1.上海联通\n2.重庆联通\n3.北京联通\n4.安徽合肥联通\n5.江苏南京联通\n6.浙江杭州联通\n7.广东联通" && read -r -p "输入数字以选择:" node
|
||||
|
||||
while [[ ! "${node}" =~ ^[1-7]$ ]]; do
|
||||
echo -e "${Error} 无效输入"
|
||||
echo -e "${Info} 请重新选择" && read -r -p "输入数字以选择:" node
|
||||
done
|
||||
|
||||
[[ "${node}" == "1" ]] && ISP_name="上海联通" && ip=220.196.252.174
|
||||
[[ "${node}" == "2" ]] && ISP_name="重庆联通" && ip=113.207.32.65
|
||||
[[ "${node}" == "3" ]] && ISP_name="北京联通" && ip=202.106.54.150
|
||||
[[ "${node}" == "4" ]] && ISP_name="安徽合肥联通" && ip=112.122.10.26
|
||||
[[ "${node}" == "5" ]] && ISP_name="江苏联通" && ip=112.85.231.129
|
||||
[[ "${node}" == "6" ]] && ISP_name="浙江联通" && ip=60.12.214.156
|
||||
[[ "${node}" == "7" ]] && ISP_name="广东联通" && ip=58.252.2.194
|
||||
}
|
||||
|
||||
node_3() {
|
||||
echo -e "1.上海移动\n2.四川成都移动\n3.北京移动\n4.浙江杭州移动\n5.广东移动\n6.江苏移动\n7.浙江移动" && read -r -p "输入数字以选择:" node
|
||||
|
||||
while [[ ! "${node}" =~ ^[1-7]$ ]]; do
|
||||
echo -e "${Error} 无效输入"
|
||||
echo -e "${Info} 请重新选择" && read -r -p "输入数字以选择:" node
|
||||
done
|
||||
|
||||
[[ "${node}" == "1" ]] && ISP_name="上海移动" && ip=117.184.42.114
|
||||
[[ "${node}" == "2" ]] && ISP_name="四川成都移动" && ip=183.221.247.9
|
||||
[[ "${node}" == "3" ]] && ISP_name="北京移动" && ip=111.13.217.125
|
||||
[[ "${node}" == "4" ]] && ISP_name="浙江移动" && ip=183.246.69.139
|
||||
[[ "${node}" == "5" ]] && ISP_name="广东移动" && ip=221.179.44.57
|
||||
[[ "${node}" == "6" ]] && ISP_name="江苏移动" && ip=120.195.6.129
|
||||
[[ "${node}" == "7" ]] && ISP_name="浙江移动" && ip=183.246.69.139
|
||||
}
|
||||
|
||||
node_4() {
|
||||
ISP_name="北京教育网" && ip=211.68.69.240
|
||||
}
|
||||
|
||||
result_alternative() {
|
||||
echo -e "${Info} 测试路由 到 ${ISP_name} 中 ..."
|
||||
${TRACECMD} ${ip} | grep -v -E 'NextTrace|XGadget-lab|Data\ Provider'
|
||||
echo -e "${Info} 测试路由 到 ${ISP_name} 完成 !"
|
||||
|
||||
repeat_test_alternative
|
||||
}
|
||||
|
||||
repeat_test_alternative() {
|
||||
echo -e "${Info} 是否继续测试其他节点?"
|
||||
if ask_if "输入n/y以选择:[n]"; then
|
||||
test_alternative
|
||||
else
|
||||
echo -e "${Info} 退出脚本 ..." && exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
test_all() {
|
||||
result_all '116.6.211.41' '广东东莞CN2'
|
||||
|
||||
result_all '101.95.110.149' '上海电信'
|
||||
|
||||
result_all '112.85.231.129' '江苏徐州联通'
|
||||
|
||||
result_all '120.199.239.1' '浙江杭州移动'
|
||||
|
||||
result_all '211.68.69.240' '北京教育网'
|
||||
|
||||
echo -e "${Info} 四网路由快速测试 已完成 !"
|
||||
}
|
||||
|
||||
result_all() {
|
||||
ISP_name=$2
|
||||
echo -e "${Info} 测试路由 到 ${ISP_name} 中 ..."
|
||||
${TRACECMD} "${1}" | grep -v -E 'NextTrace|XGadget-lab|Data\ Provider'
|
||||
echo -e "${Info} 测试路由 到 ${ISP_name} 完成 !"
|
||||
}
|
||||
|
||||
check_root
|
||||
checkSystemDistribution
|
||||
ask_update_script
|
||||
checkNexttrace
|
||||
check_mode
|
||||
echo -e "${Info} 选择你要使用的功能: "
|
||||
echo -e "1.选择一个节点进行测试\n2.四网路由快速测试\n3.手动输入 ip 进行测试"
|
||||
read -r -p "输入数字以选择:" function
|
||||
|
||||
while [[ ! "${function}" =~ ^[1-3]$ ]]; do
|
||||
echo -e "${Error} 缺少或无效输入"
|
||||
echo -e "${Info} 请重新选择" && read -r -p "输入数字以选择:" function
|
||||
done
|
||||
|
||||
if [[ "${function}" == "1" ]]; then
|
||||
test_alternative
|
||||
elif [[ "${function}" == "2" ]]; then
|
||||
test_all
|
||||
else
|
||||
test_single
|
||||
fi
|
||||
@@ -1,6 +1,7 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
@@ -74,19 +75,32 @@ func (t *ICMPTracer) listenICMP() {
|
||||
if msg.N == nil {
|
||||
continue
|
||||
}
|
||||
rm, err := icmp.ParseMessage(1, msg.Msg[:*msg.N])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
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
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
||||
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)
|
||||
case ipv4.ICMPTypeEchoReply:
|
||||
t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
|
||||
default:
|
||||
// log.Println("received icmp message of unknown type", rm.Type)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,18 +88,21 @@ func (t *TCPTracer) listenICMP() {
|
||||
if msg.N == nil {
|
||||
continue
|
||||
}
|
||||
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, rm.Body.(*icmp.TimeExceeded).Data)
|
||||
case ipv4.ICMPTypeDestinationUnreachable:
|
||||
t.handleICMPMessage(msg, rm.Body.(*icmp.DstUnreach).Data)
|
||||
default:
|
||||
//log.Println("received icmp message of unknown type", rm.Type)
|
||||
dstip := net.IP(msg.Msg[24:28])
|
||||
if dstip.Equal(t.DestIP) {
|
||||
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, rm.Body.(*icmp.TimeExceeded).Data)
|
||||
case ipv4.ICMPTypeDestinationUnreachable:
|
||||
t.handleICMPMessage(msg, rm.Body.(*icmp.DstUnreach).Data)
|
||||
default:
|
||||
//log.Println("received icmp message of unknown type", rm.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ func formatIpGeoData(ip string, data *ipgeo.IPGeoData) string {
|
||||
// TODO: 判断阿里云和腾讯云内网,数据不足,有待进一步完善
|
||||
// TODO: 移动IDC判断到Hop.fetchIPData函数,减少API调用
|
||||
if strings.HasPrefix(ip, "9.") {
|
||||
res = append(res, "局域网", "腾讯云")
|
||||
res = append(res, "LAN Address", "")
|
||||
} else if strings.HasPrefix(ip, "11.") {
|
||||
res = append(res, "局域网", "阿里云")
|
||||
res = append(res, "LAN Address", "")
|
||||
} else if data.Country == "" {
|
||||
res = append(res, "局域网")
|
||||
res = append(res, "LAN Address")
|
||||
} else {
|
||||
// 有些IP的归属信息为空,这个时候将ISP的信息填入
|
||||
if data.Owner == "" {
|
||||
|
||||
21
util/util.go
21
util/util.go
@@ -25,7 +25,7 @@ func LocalIPPort(dstip net.IP) (net.IP, int) {
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func DomainLookUp(host string) net.IP {
|
||||
func DomainLookUp(host string, ipv4Only bool) net.IP {
|
||||
ips, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
fmt.Println("Domain " + host + " Lookup Fail.")
|
||||
@@ -36,17 +36,20 @@ func DomainLookUp(host string) net.IP {
|
||||
var ipv6Flag = false
|
||||
|
||||
for _, ip := range ips {
|
||||
ipSlice = append(ipSlice, ip)
|
||||
// 仅返回ipv4的ip
|
||||
// if ip.To4() != nil {
|
||||
// ipSlice = append(ipSlice, ip)
|
||||
// } else {
|
||||
// ipv6Flag = true
|
||||
// }
|
||||
if ipv4Only {
|
||||
// 仅返回ipv4的ip
|
||||
if ip.To4() != nil {
|
||||
ipSlice = append(ipSlice, ip)
|
||||
} else {
|
||||
ipv6Flag = true
|
||||
}
|
||||
} else {
|
||||
ipSlice = append(ipSlice, ip)
|
||||
}
|
||||
}
|
||||
|
||||
if ipv6Flag {
|
||||
fmt.Println("[Info] IPv6 Traceroute is not supported right now.")
|
||||
fmt.Println("[Info] IPv6 TCP/UDP Traceroute is not supported right now.")
|
||||
if len(ipSlice) == 0 {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user