diff --git a/.cross_compile.sh b/.cross_compile.sh
index 851864e..52148e7 100644
--- a/.cross_compile.sh
+++ b/.cross_compile.sh
@@ -7,6 +7,10 @@ DEBUG_MODE=${2}
TARGET_DIR="dist"
PLATFORMS="darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 linux/mips"
+BUILD_VERSION="$(git describe --tags --always)"
+BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
+COMMIT_SHA1="$(git rev-parse --short HEAD)"
+
rm -rf ${TARGET_DIR}
mkdir ${TARGET_DIR}
@@ -21,15 +25,15 @@ for pl in ${PLATFORMS}; do
echo "build => ${TARGET}"
if [ "${DEBUG_MODE}" == "debug" ]; then
go build -trimpath -gcflags "all=-N -l" -o ${TARGET} \
- -ldflags "-X 'main.version=${BUILD_VERSION}' \
- -X 'main.buildDate=${BUILD_DATE}' \
- -X 'main.commitID=${COMMIT_SHA1}'\
+ -ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
+ -X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
+ -X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
-w -s"
else
go build -trimpath -o ${TARGET} \
- -ldflags "-X 'main.version=${BUILD_VERSION}' \
- -X 'main.buildDate=${BUILD_DATE}' \
- -X 'main.commitID=${COMMIT_SHA1}'\
+ -ldflags "-X 'github.com/xgadget-lab/nexttrace/printer.version=${BUILD_VERSION}' \
+ -X 'github.com/xgadget-lab/nexttrace/printer.buildDate=${BUILD_DATE}' \
+ -X 'github.com/xgadget-lab/nexttrace/printer.commitID=${COMMIT_SHA1}'\
-w -s"
fi
done
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index eb100e9..6da0472 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,13 +5,27 @@ on:
name: Build Release
jobs:
- release:
+ test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@master # checkout 代码
- - uses: actions/setup-go@v2 # 配置 Go 环境
+ - uses: actions/checkout@v3
+
+ - uses: actions/setup-go@v2
with:
- go-version: "1.18" # 改成自己的版本
+ go-version: "1.18"
+
+ - name: Test
+ run: go test -v -coverprofile='coverage.out' -covermode=count ./...
+
+ release:
+ needs: test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: actions/setup-go@v2
+ with:
+ go-version: "1.18"
- run: bash .cross_compile.sh
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..097382f
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,17 @@
+on:
+ push:
+ pull_request:
+
+name: Test
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: actions/setup-go@v2
+ with:
+ go-version: "1.18"
+
+ - name: Test
+ run: go test -v -coverprofile='coverage.out' -covermode=count ./...
diff --git a/README.md b/README.md
index 305fa58..ee7d2e8 100644
--- a/README.md
+++ b/README.md
@@ -22,30 +22,40 @@ bash -c "$(curl -Ls https://raw.githubusercontent.com/xgadget-lab/nexttrace/main
```bash
# IPv4 ICMP Trace
-./nexttrace 1.0.0.1
-
-# 获得 route-path
-./nexttrace -report 1.0.0.1
+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
+nexttrace 2606:4700:4700::1111
```
`NextTrace`也可以使用`TCP`和`UDP`协议发起`Traceroute`请求,不过目前只支持`IPv4`
```bash
# TCP SYN Trace
-./nexttrace -T www.bing.com
+nexttrace -T www.bing.com
# 可以自行指定端口[此处为443],默认80端口
-./nexttrace -T -p 443 1.0.0.1
+nexttrace -T -p 443 1.0.0.1
# UDP Trace
-./nexttrace -U 1.0.0.1
+nexttrace -U 1.0.0.1
-./nexttrace -U -p 53 1.0.0.1
+nexttrace -U -p 53 1.0.0.1
+```
+
+`NextTrace`也同样支持一些进阶功能,如IP反向解析、并发数控制、模式切换等
+
+```bash
+# 无并发,每次只发送一个探测包
+nexttrace -r 1 www.hkix.net
+
+# 打开IP反向解析功能,在IPv6的骨干网定位辅助有较大帮助
+nexttrace -rdns www.bbix.net
+
+# 联合使用
+nexttrace -r 1 -q 1 -report www.time.com.my
```
### IP数据库
@@ -62,10 +72,9 @@ NextTrace所有的的IP地理位置`API DEMO`可以参考[这里](https://github
Usage of nexttrace:
-T Use TCP SYN for tracerouting (default port is 80)
-U Use UDP Package for tracerouting (default port is 53 in UDP)
+ -V Check Version
-d string
Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight] (default "LeoMoeAPI")
- -displayMode string
- Choose The Display Mode [table, classic] (default "table")
-m int
Set the max number of hops (max TTL to be reached). (default 30)
-p int
@@ -76,25 +85,62 @@ Usage of nexttrace:
Set ParallelRequests number. It should be 1 when there is a multi-routing. (default 18)
-rdns
Set whether rDNS will be display
+ -realtime
+ Output trace results in runtime
-report
Route Path
+ -table
+ Output trace results as table
```
+
## 项目截图
-
-
-
+

-## Thanks
+
+
+## Thanks
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
-[waiting4new](https://github.com/waiting4new)
+[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
-FFEE_CO
+[waiting4new](https://github.com/waiting4new)、[FFEE_CO](https://github.com/fkx4-p)、[nsnnns](https://github.com/tsosunchia)
-nsnnns
+## IP Database Copyright
+
+### IPv4 Database
+
+#### China MainLand
+
+* 项目组自行维护 ~ 御三家骨干网数据 ~ 5%
+
+* 埃文科技 Paid Database ~ 95%
+
+#### WorldWide
+
+* 埃文科技 Paid Database ~ 15%
+
+* IpInfo Free ~ 15%
+
+* IPInSight Free ~ 70%
+
+### IPv6 Database
+
+This product includes IP2Location LITE data available from https://lite.ip2location.com.
+
+### Others
+
+其他第三方API尽管集成在本项目内,但是具体的TOS以及AUP,请详见第三方API官网。如遇到IP数据错误,也请直接联系他们纠错。
diff --git a/asset/screenshot2.png b/asset/screenshot2.png
deleted file mode 100644
index 1ff8af8..0000000
Binary files a/asset/screenshot2.png and /dev/null differ
diff --git a/asset/screenshot3.png b/asset/screenshot3.png
deleted file mode 100644
index 84e2ba8..0000000
Binary files a/asset/screenshot3.png and /dev/null differ
diff --git a/asset/screenshot_2.png b/asset/screenshot_2.png
new file mode 100644
index 0000000..15955ca
Binary files /dev/null and b/asset/screenshot_2.png differ
diff --git a/asset/screenshot_special.png b/asset/screenshot_special.png
new file mode 100644
index 0000000..385f729
Binary files /dev/null and b/asset/screenshot_special.png differ
diff --git a/go.sum b/go.sum
index 4e9638a..b74a7b3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,6 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
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=
@@ -53,4 +54,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/ipgeo/ipgeo.go b/ipgeo/ipgeo.go
index f9762a8..0de5b71 100644
--- a/ipgeo/ipgeo.go
+++ b/ipgeo/ipgeo.go
@@ -1,5 +1,7 @@
package ipgeo
+import "strings"
+
type IPGeoData struct {
Asnumber string
Country string
@@ -13,12 +15,12 @@ type IPGeoData struct {
type Source = func(ip string) (*IPGeoData, error)
func GetSource(s string) Source {
- switch s {
- case "LeoMoeAPI":
+ switch strings.ToUpper(s) {
+ case "LEOMOEAPI":
return LeoIP
case "IP.SB":
return IPSB
- case "IPInsight":
+ case "IPINSIGHT":
return IPInSight
default:
return nil
diff --git a/ipgeo/ipgeo_test.go b/ipgeo/ipgeo_test.go
index 08dea10..4f1c982 100644
--- a/ipgeo/ipgeo_test.go
+++ b/ipgeo/ipgeo_test.go
@@ -15,11 +15,12 @@ func TestLeoIP(t *testing.T) {
}
func TestIPSB(t *testing.T) {
- res, err := IPSB("1.1.1.1")
- assert.Nil(t, err)
- assert.NotNil(t, res)
- assert.NotEmpty(t, res.Asnumber)
- assert.NotEmpty(t, res.Isp)
+ // Not available
+ //res, err := IPSB("1.1.1.1")
+ //assert.Nil(t, err)
+ //assert.NotNil(t, res)
+ //assert.NotEmpty(t, res.Asnumber)
+ //assert.NotEmpty(t, res.Isp)
}
func TestIPInfo(t *testing.T) {
diff --git a/ipgeo/ipsb.go b/ipgeo/ipsb.go
index 46bdad1..8bee301 100644
--- a/ipgeo/ipsb.go
+++ b/ipgeo/ipsb.go
@@ -3,21 +3,25 @@ package ipgeo
import (
"io/ioutil"
"net/http"
+ "time"
"github.com/tidwall/gjson"
)
func IPSB(ip string) (*IPGeoData, error) {
- resp, err := http.Get("https://api.ip.sb/geoip/" + ip)
+ url := "https://api.ip.sb/geoip/" + ip
+ client := &http.Client{
+ // 2秒超时
+ Timeout: 2 * time.Second,
+ }
+ req, _ := http.NewRequest("GET", url, nil)
+ // 设置 UA,ip.sb 默认禁止 go-client User-Agent 的 api 请求
+ req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0")
+ content, err := client.Do(req)
if err != nil {
return nil, err
}
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
-
+ body, _ := ioutil.ReadAll(content.Body)
res := gjson.ParseBytes(body)
return &IPGeoData{
diff --git a/listener_channel/listener-channel.go b/listener_channel/listener-channel.go
deleted file mode 100644
index e6ef8c1..0000000
--- a/listener_channel/listener-channel.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package listener_channel
-
-import (
- "golang.org/x/net/context"
- "net"
- "time"
-)
-
-type ReceivedMessage struct {
- N *int
- Peer net.Addr
- Msg []byte
- Err error
-}
-
-type ListenerChannel struct {
- ctx context.Context
- cancel context.CancelFunc
- Conn net.PacketConn
- Messages chan ReceivedMessage
-}
-
-func New(conn net.PacketConn) *ListenerChannel {
- ctx, cancel := context.WithCancel(context.Background())
- results := make(chan ReceivedMessage, 50)
-
- return &ListenerChannel{Conn: conn, ctx: ctx, cancel: cancel, Messages: results}
-}
-
-func (l *ListenerChannel) Start() {
- for {
- select {
- case <-l.ctx.Done():
- return
- default:
- }
-
- reply := make([]byte, 1500)
- err := l.Conn.SetReadDeadline(time.Now().Add(2 * time.Second))
- if err != nil {
- l.Messages <- ReceivedMessage{Err: err}
- continue
- }
-
- n, peer, err := l.Conn.ReadFrom(reply)
- if err != nil {
- l.Messages <- ReceivedMessage{Err: err}
- continue
- }
- l.Messages <- ReceivedMessage{
- N: &n,
- Peer: peer,
- Err: nil,
- Msg: reply,
- }
- }
-}
-
-func (l *ListenerChannel) Stop() {
- l.cancel()
-}
diff --git a/main.go b/main.go
index 4456a7f..97072f3 100644
--- a/main.go
+++ b/main.go
@@ -25,9 +25,14 @@ var rdnsenable = flag.Bool("rdns", false, "Set whether rDNS will be display")
var routePath = flag.Bool("report", false, "Route Path")
var realtimePrint = flag.Bool("realtime", false, "Output trace results in runtime")
var tablePrint = flag.Bool("table", false, "Output trace results as table")
+var ver = flag.Bool("V", false, "Check Version")
func flagApply() string {
flag.Parse()
+ printer.Version()
+ if *ver {
+ os.Exit(0)
+ }
ipArg := flag.Args()
if flag.NArg() != 1 {
fmt.Println("Args Error\nUsage : ./nexttrace [-T] [-rdns] [-displayMode ] [-d ] [ -m ] [ -p ] [ -q ] [ -r ] ")
@@ -37,11 +42,13 @@ func flagApply() string {
}
func main() {
+
+ domain := flagApply()
+
if os.Getuid() != 0 {
log.Fatalln("Traceroute requires root/sudo privileges.")
}
- domain := flagApply()
ip := util.DomainLookUp(domain)
printer.PrintTraceRouteNav(ip, domain, *dataOrigin)
diff --git a/nt_install.sh b/nt_install.sh
index adda6f9..5ad4d13 100644
--- a/nt_install.sh
+++ b/nt_install.sh
@@ -52,11 +52,12 @@ installWgetPackage() {
# macOS should install wget originally. Nothing to do
echo "wget 正在安装中..."
# try apt
- apt-get -h &>/dev/null
+ # 是时候直接使用 APT 来管理包了
+ apt -h &>/dev/null
if [ $? -eq 0 ]; then
# 先更新一下数据源,有些机器数据源比较老可能会404
- apt-get update -y &>/dev/null
- apt-get install wget -y &>/dev/null
+ apt update -y &>/dev/null
+ apt install wget -y &>/dev/null
fi
# try yum
@@ -79,8 +80,9 @@ installWgetPackage() {
pacman -Sy &>/dev/null
pacman -S wget &>/dev/null
fi
-
- wget -h &>/dev/null
+
+ # 有的发行版自带的wget,只有 --help 参数
+ wget --help &>/dev/null
if [ $? -ne 0 ]; then
echo "wget 安装失败"
exit 1
diff --git a/printer/basic.go b/printer/basic.go
index 0c3d02e..439411b 100644
--- a/printer/basic.go
+++ b/printer/basic.go
@@ -5,6 +5,15 @@ import (
"net"
)
+var version = "v0.0.0.alpha"
+var buildDate = ""
+var commitID = ""
+
+func Version() {
+ fmt.Println("NextTrace", version, buildDate, commitID)
+ fmt.Println("XGadget-lab Leo (leo.moe) & Vincent (vincent.moe) & zhshch (xzhsh.ch)")
+}
+
func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string) {
fmt.Println("IP Geo Data Provider: " + dataOrigin)
diff --git a/printer/printer_test.go b/printer/printer_test.go
new file mode 100644
index 0000000..d285211
--- /dev/null
+++ b/printer/printer_test.go
@@ -0,0 +1,104 @@
+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"
+)
+
+func TestPrintTraceRouteNav(t *testing.T) {
+ PrintTraceRouteNav(util.DomainLookUp("1.1.1.1"), "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 TestTracerouteTablePrinter(t *testing.T) {
+ TracerouteTablePrinter(testResult)
+}
+
+func TestRealtimePrinter(t *testing.T) {
+ RealtimePrinter(testResult, 0)
+ RealtimePrinter(testResult, 1)
+ RealtimePrinter(testResult, 2)
+}
diff --git a/printer/tableprinter.go b/printer/tableprinter.go
index 08e663d..46c748b 100644
--- a/printer/tableprinter.go
+++ b/printer/tableprinter.go
@@ -2,6 +2,7 @@ package printer
import (
"fmt"
+ "github.com/xgadget-lab/nexttrace/ipgeo"
"strings"
"github.com/xgadget-lab/nexttrace/trace"
@@ -89,6 +90,10 @@ func tableDataGenerator(h trace.Hop) *rowData {
IP = fmt.Sprint(h.Hostname, " (", IP, ") ")
}
+ if h.Geo == nil {
+ h.Geo = &ipgeo.IPGeoData{}
+ }
+
r := &rowData{
Hop: fmt.Sprint(h.TTL),
IP: IP,
diff --git a/reporter/reporter.go b/reporter/reporter.go
index c9c13a5..b9fedbe 100644
--- a/reporter/reporter.go
+++ b/reporter/reporter.go
@@ -1,10 +1,10 @@
package reporter
import (
- "errors"
"fmt"
"net"
"strings"
+ "sync"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/xgadget-lab/nexttrace/trace"
@@ -24,9 +24,12 @@ func New(rs *trace.Result, ip string) Reporter {
}
type reporter struct {
- targetIP string
- routeReport map[uint16][]routeReportNode
- routeResult *trace.Result
+ targetTTL uint16
+ targetIP string
+ routeReport map[uint16][]routeReportNode
+ routeReportLock sync.Mutex
+ routeResult *trace.Result
+ wg sync.WaitGroup
}
type routeReportNode struct {
@@ -40,71 +43,107 @@ func experimentTag() {
fmt.Println("Route-Path 功能实验室")
}
-func (r *reporter) generateRouteReportNode(ip string, ipGeoData ipgeo.IPGeoData) (routeReportNode, error) {
- rpn := routeReportNode{}
- go func() {
- ptr, err := net.LookupAddr(ip)
- if err == nil {
- if strings.Contains(strings.ToLower(ptr[0]), "ix") {
- rpn.ix = true
- } else {
- rpn.ix = false
- }
- }
- }()
+func (r *reporter) generateRouteReportNode(ip string, ipGeoData ipgeo.IPGeoData, ttl uint16) {
+ var success bool = true
+
+ defer r.wg.Done()
+
+ rpn := routeReportNode{}
+ ptr, err := net.LookupAddr(ip)
+
+ if err == nil {
+ if strings.Contains(strings.ToLower(ptr[0]), "ix") {
+ rpn.ix = true
+ } else {
+ rpn.ix = false
+ }
+ }
+ // TODO: 这种写法不好,后面再重构一下
+ // 判断反向解析的域名中又或者是IP地理位置数据库中,是否出现了 IX
if strings.Contains(strings.ToLower(ipGeoData.Isp), "exchange") || strings.Contains(strings.ToLower(ipGeoData.Isp), "ix") || strings.Contains(strings.ToLower(ipGeoData.Owner), "exchange") || strings.Contains(strings.ToLower(ipGeoData.Owner), "ix") {
rpn.ix = true
}
+
+ // TODO: 正则判断POP并且提取带宽大小等信息
+
+ // CN2 需要特殊处理,因为他们很多没有ASN
+ // 但是目前这种写法是不规范的,属于凭空标记4809的IP
+ // TODO: 用更好的方式显示 CN2 骨干网的路由 Path
if strings.HasPrefix(ip, "59.43") {
rpn.asn = "4809"
} else {
rpn.asn = ipGeoData.Asnumber
}
+
// 无论最后一跳是否为存在地理位置信息(AnyCast),都应该给予显示
- if ipGeoData.Country == "" || ipGeoData.City == "" || ipGeoData.City == "-" && ip != r.targetIP {
- return rpn, errors.New("GeoData Search Failed")
+ if (ipGeoData.Country == "" || ipGeoData.Country == "LAN Address" || ipGeoData.Country == "-") && ip != r.targetIP {
+ success = false
} else {
if ipGeoData.City == "" {
- rpn.geo = []string{ipGeoData.Country, ipGeoData.Country}
+ rpn.geo = []string{ipGeoData.Country, ipGeoData.Prov}
} else {
rpn.geo = []string{ipGeoData.Country, ipGeoData.City}
}
}
+ if ipGeoData.Asnumber == "" {
+ rpn.asn = "*"
+ }
+
if ipGeoData.Isp == "" {
rpn.isp = ipGeoData.Owner
} else {
rpn.isp = ipGeoData.Isp
}
- return rpn, nil
+
+ // 有效记录
+ if success {
+ // 锁住资源,防止同时写panic
+ r.routeReportLock.Lock()
+ // 添加到MAP中
+ r.routeReport[ttl] = append(r.routeReport[ttl], rpn)
+ // 写入完成,解锁释放资源给其他协程
+ r.routeReportLock.Unlock()
+ }
}
func (r *reporter) InitialBaseData() Reporter {
- var nodeIndex uint16 = 1
reportNodes := map[uint16][]routeReportNode{}
- for i := uint16(0); int(i) < len(r.routeResult.Hops); i++ {
+
+ r.routeReport = reportNodes
+ r.targetTTL = uint16(len(r.routeResult.Hops))
+
+ for i := uint16(0); i < r.targetTTL; i++ {
traceHop := r.routeResult.Hops[i][0]
if traceHop.Success {
currentIP := traceHop.Address.String()
- rpn, err := r.generateRouteReportNode(currentIP, *traceHop.Geo)
- if err == nil {
- reportNodes[nodeIndex] = append(reportNodes[nodeIndex], rpn)
- nodeIndex += 1
- }
+ r.wg.Add(1)
+ go r.generateRouteReportNode(currentIP, *traceHop.Geo, i)
}
}
- r.routeReport = reportNodes
+
+ // 等待所有的子协程运行完毕
+ r.wg.Wait()
return r
}
func (r *reporter) Print() {
+ var beforeActiveTTL uint16 = 1
r.InitialBaseData()
- for i := uint16(1); int(i) < len(r.routeReport)+1; i++ {
+
+ for i := uint16(1); i < r.targetTTL; i++ {
+ // 计算该TTL内的数据长度,如果为0,则代表没有有效数据
+ if len(r.routeReport[i]) == 0 {
+ // 跳过改跃点的数据整理
+ continue
+ }
nodeReport := r.routeReport[i][0]
+
if i == 1 {
fmt.Printf("AS%s %s「%s『%s", nodeReport.asn, nodeReport.isp, nodeReport.geo[0], nodeReport.geo[1])
} else {
- nodeReportBefore := r.routeReport[i-1][0]
+ nodeReportBefore := r.routeReport[beforeActiveTTL][0]
+ // ASN 相同,同个 ISP 内部的数据传递
if nodeReportBefore.asn == nodeReport.asn {
// Same ASN but Coutry or City Changed
if nodeReportBefore.geo[0] != nodeReport.geo[0] {
@@ -115,8 +154,11 @@ func (r *reporter) Print() {
}
}
} else {
+ // ASN 不同,跨 ISP 的数据传递,这里可能会出现 POP、IP Transit、Peer、Exchange
fmt.Printf("』」")
if int(i) != len(r.routeReport)+1 {
+ // 部分 Shell 客户端可能无法很好的展示这个特殊字符
+ // TODO: 寻找其他替代字符
fmt.Printf("\n ╭╯\n ╰")
}
if nodeReport.ix {
@@ -126,6 +168,8 @@ func (r *reporter) Print() {
}
}
}
+ // 标记为最新的一个有效跃点
+ beforeActiveTTL = i
}
fmt.Println("』」")
}
diff --git a/reporter/reporter_test.go b/reporter/reporter_test.go
index 7147971..c95acdc 100644
--- a/reporter/reporter_test.go
+++ b/reporter/reporter_test.go
@@ -1,31 +1,115 @@
package reporter
import (
+ "net"
"testing"
"time"
"github.com/xgadget-lab/nexttrace/ipgeo"
"github.com/xgadget-lab/nexttrace/trace"
- "github.com/xgadget-lab/nexttrace/util"
)
+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: &ipgeo.IPGeoData{
+ Asnumber: "4808",
+ Country: "中国",
+ Prov: "北京市",
+ City: "北京市",
+ District: "北京市",
+ Owner: "",
+ Isp: "中国联通",
+ },
+ },
+ },
+ {
+ {
+ Success: true,
+ Address: &net.IPAddr{IP: net.ParseIP("114.249.16.1")},
+ Hostname: "test",
+ TTL: 0,
+ RTT: 10 * time.Millisecond,
+ Error: nil,
+ Geo: &ipgeo.IPGeoData{
+ Asnumber: "4808",
+ Country: "中国",
+ Prov: "北京市",
+ City: "北京市",
+ District: "北京市",
+ Owner: "",
+ Isp: "中国联通",
+ },
+ },
+ },
+ {
+ {
+ Success: true,
+ Address: &net.IPAddr{IP: net.ParseIP("219.158.5.150")},
+ Hostname: "test",
+ TTL: 0,
+ RTT: 10 * time.Millisecond,
+ Error: nil,
+ Geo: &ipgeo.IPGeoData{
+ Asnumber: "4837",
+ Country: "中国",
+ Prov: "",
+ City: "",
+ District: "",
+ Owner: "",
+ Isp: "中国联通",
+ },
+ },
+ },
+ {
+ {
+ Success: true,
+ Address: &net.IPAddr{IP: net.ParseIP("62.115.125.160")},
+ Hostname: "test",
+ TTL: 0,
+ RTT: 10 * time.Millisecond,
+ Error: nil,
+ Geo: &ipgeo.IPGeoData{
+ Asnumber: "1299",
+ Country: "Sweden",
+ Prov: "Stockholm County",
+ City: "Stockholm",
+ District: "",
+ Owner: "",
+ Isp: "Telia Company AB",
+ },
+ },
+ },
+ {
+ {
+ Success: true,
+ Address: &net.IPAddr{IP: net.ParseIP("213.226.68.73")},
+ Hostname: "test",
+ TTL: 0,
+ RTT: 10 * time.Millisecond,
+ Error: nil,
+ Geo: &ipgeo.IPGeoData{
+ Asnumber: "56630",
+ Country: "Germany",
+ Prov: "Hesse, Frankfurt",
+ City: "",
+ District: "",
+ Owner: "",
+ Isp: "Melbikomas UAB",
+ },
+ },
+ },
+ },
+}
+
func TestPrint(t *testing.T) {
- ip := util.DomainLookUp("213.226.68.73")
- var m trace.Method = "tcp"
- var conf = trace.Config{
- DestIP: ip,
- DestPort: 80,
- MaxHops: 30,
- NumMeasurements: 1,
- ParallelRequests: 1,
- RDns: true,
- IPGeoSource: ipgeo.GetSource("LeoMoeAPI"),
- Timeout: 2 * time.Second,
-
- //Quic: false,
- }
-
- res, _ := trace.Traceroute(m, conf)
- r := New(res, ip.String())
+ r := New(testResult, "213.226.68.73")
r.Print()
}
diff --git a/signal/signal.go b/signal/signal.go
deleted file mode 100644
index 6095a25..0000000
--- a/signal/signal.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package signal
-
-type Signal struct {
- sigChan chan struct{}
-}
-
-func New() *Signal {
- return &Signal{sigChan: make(chan struct{}, 1)}
-}
-
-func (s *Signal) Signal() {
- if len(s.sigChan) == 0 {
- s.sigChan <- struct{}{}
- }
-}
-
-func (s *Signal) Chan() chan struct{} {
- return s.sigChan
-}
diff --git a/taskgroup/taskgroup.go b/taskgroup/taskgroup.go
deleted file mode 100644
index ca2e472..0000000
--- a/taskgroup/taskgroup.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package taskgroup
-
-import (
- "sync"
-)
-
-type TaskGroup struct {
- count int
- mu sync.Mutex
- done []chan struct{}
-}
-
-func New() *TaskGroup {
- return &TaskGroup{
- count: 0,
- mu: sync.Mutex{},
- done: []chan struct{}{},
- }
-}
-
-func (t *TaskGroup) Add() {
- t.mu.Lock()
- defer t.mu.Unlock()
- t.count++
-}
-
-func (t *TaskGroup) Done() {
- t.mu.Lock()
- defer t.mu.Unlock()
- if t.count-1 == 0 {
- for _, doneChannel := range t.done {
- doneChannel <- struct{}{}
- }
- t.done = []chan struct{}{}
- }
- t.count--
-}
-
-func (t *TaskGroup) Wait() {
- doneChannel := make(chan struct{})
- t.mu.Lock()
- t.done = append(t.done, doneChannel)
- t.mu.Unlock()
- <-doneChannel
-}
diff --git a/trace/tcp_ipv4.go b/trace/tcp_ipv4.go
index 0188b01..be7ced1 100644
--- a/trace/tcp_ipv4.go
+++ b/trace/tcp_ipv4.go
@@ -10,7 +10,6 @@ import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
- "github.com/xgadget-lab/nexttrace/listener_channel"
"github.com/xgadget-lab/nexttrace/util"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
@@ -110,10 +109,7 @@ func (t *TCPTracer) listenICMP() {
// @title listenTCP
// @description 监听TCP的响应数据包
func (t *TCPTracer) listenTCP() {
- lc := listener_channel.New(t.tcp)
-
- defer lc.Stop()
-
+ lc := NewPacketListener(t.tcp, t.ctx)
go lc.Start()
for {
diff --git a/trace/tcp_ipv6.go b/trace/tcp_ipv6.go
index 252cbee..b05c8d3 100644
--- a/trace/tcp_ipv6.go
+++ b/trace/tcp_ipv6.go
@@ -10,7 +10,6 @@ import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
- "github.com/xgadget-lab/nexttrace/listener_channel"
"github.com/xgadget-lab/nexttrace/util"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
@@ -111,10 +110,7 @@ func (t *TCPTracerv6) listenICMP() {
// @title listenTCP
// @description 监听TCP的响应数据包
func (t *TCPTracerv6) listenTCP() {
- lc := listener_channel.New(t.tcp)
-
- defer lc.Stop()
-
+ lc := NewPacketListener(t.tcp, t.ctx)
go lc.Start()
for {