diff --git a/main.go b/main.go index 02ecc1e..06ab13b 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "github.com/xgadget-lab/nexttrace/methods/udp" "github.com/xgadget-lab/nexttrace/util" "github.com/xgadget-lab/nexttrace/util/printer" + "github.com/xgadget-lab/nexttrace/util/reporter" ) var tcpSYNFlag = flag.Bool("T", false, "Use TCP SYN for tracerouting (default port is 80 in TCP, 53 in UDP)") @@ -21,6 +22,7 @@ var maxHops = flag.Int("m", 30, "Set the max number of hops (max TTL to be reach var dataOrigin = flag.String("d", "LeoMoeAPI", "Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight]") var displayMode = flag.String("displayMode", "table", "Choose The Display Mode [table, classic]") var rdnsenable = flag.Bool("rdns", false, "Set whether rDNS will be display") +var routeReport = flag.Bool("report", false, "Auto-Generate a Route-Path Report by TCPTraceroute") func main() { printer.PrintCopyRight() @@ -41,13 +43,18 @@ func main() { if err != nil { fmt.Println("请赋予 sudo (root) 权限运行本程序") } else { - util.Printer(&util.PrinterConfig{ - IP: ip, - DisplayMode: *displayMode, - DataOrigin: *dataOrigin, - Rdnsenable: *rdnsenable, - Results: *res, - }) + if *routeReport { + r := reporter.New(*res, ip.String()) + r.Print() + } else { + util.Printer(&util.PrinterConfig{ + IP: ip, + DisplayMode: *displayMode, + DataOrigin: *dataOrigin, + Rdnsenable: *rdnsenable, + Results: *res, + }) + } } } else { @@ -66,13 +73,18 @@ func main() { if err != nil { fmt.Println("请赋予 sudo (root) 权限运行本程序") } else { - util.Printer(&util.PrinterConfig{ - IP: ip, - DisplayMode: *displayMode, - DataOrigin: *dataOrigin, - Rdnsenable: *rdnsenable, - Results: *res, - }) + if *routeReport { + r := reporter.New(*res, ip.String()) + r.Print() + } else { + util.Printer(&util.PrinterConfig{ + IP: ip, + DisplayMode: *displayMode, + DataOrigin: *dataOrigin, + Rdnsenable: *rdnsenable, + Results: *res, + }) + } } } } diff --git a/util/reporter/reporter.go b/util/reporter/reporter.go index 85a0acb..db3316a 100644 --- a/util/reporter/reporter.go +++ b/util/reporter/reporter.go @@ -1,8 +1,12 @@ package reporter import ( + "errors" "fmt" + "net" + "strings" + "github.com/xgadget-lab/nexttrace/ipgeo" "github.com/xgadget-lab/nexttrace/methods" ) @@ -10,18 +14,114 @@ type Reporter interface { Print() } -func New(rs map[uint16][]methods.TracerouteHop) Reporter { +func New(rs map[uint16][]methods.TracerouteHop, ip string) Reporter { + experimentTag() r := reporter{ routeResult: rs, + targetIP: ip, } - fmt.Println(r) return &r } type reporter struct { + targetIP string + routeReport map[uint16][]routeReportNode routeResult map[uint16][]methods.TracerouteHop } -func (r *reporter) Print() { - fmt.Println(r) +type routeReportNode struct { + asn string + isp string + geo []string + ix bool +} + +func experimentTag() { + fmt.Println("Route-Path是一个实验性功能,我们的IP库不能很好的支持我们提供骨干网的地理位置信息,所以IP位置有时候会异常") +} + +func reduceRouteReportNode(ip string, ipGeoData ipgeo.IPGeoData) (routeReportNode, error) { + 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 + } + } + + 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 + } + if strings.HasPrefix(ip, "59.43") { + rpn.asn = "4809" + } else { + rpn.asn = ipGeoData.Asnumber + } + + if ipGeoData.Country == "" || ipGeoData.City == "" { + return rpn, errors.New("GeoData Search Failed") + } else { + rpn.geo = []string{ipGeoData.Country, ipGeoData.City} + } + if ipGeoData.Isp == "" { + rpn.isp = ipGeoData.Owner + } else { + rpn.isp = ipGeoData.Isp + } + return rpn, nil +} + +func (r *reporter) InitialBaseData() Reporter { + var nodeIndex uint16 = 1 + reportNodes := map[uint16][]routeReportNode{} + for i := uint16(1); int(i) < len(r.routeResult)+1; i++ { + traceHop := r.routeResult[i][0] + if traceHop.Success { + currentIP := traceHop.Address.String() + ipGeoData, _ := ipgeo.LeoIP(currentIP) + rpn, err := reduceRouteReportNode(currentIP, *ipGeoData) + if err == nil { + reportNodes[nodeIndex] = append(reportNodes[nodeIndex], rpn) + nodeIndex += 1 + } + } + } + r.routeReport = reportNodes + return r +} + +func (r *reporter) Print() { + r.InitialBaseData() + //fmt.Println(r.routeReport) + for i := uint16(1); int(i) < len(r.routeReport)+1; i++ { + 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] + if nodeReportBefore.asn == nodeReport.asn { + // Same ASN but Coutry or City Changed + if nodeReportBefore.geo[0] != nodeReport.geo[0] { + fmt.Printf("』→ %s『%s", nodeReport.geo[0], nodeReport.geo[1]) + } else { + if nodeReportBefore.geo[1] != nodeReport.geo[1] { + fmt.Printf(" → %s", nodeReport.geo[1]) + } + } + } else { + fmt.Printf("』」") + if int(i) != len(r.routeReport)+1 { + fmt.Printf("\n ╭╯\n ╰") + } + if nodeReport.ix { + fmt.Printf("AS%s \033[42;37mIXP\033[0m %s「%s『%s", nodeReport.asn, nodeReport.isp, nodeReport.geo[0], nodeReport.geo[1]) + } else { + fmt.Printf("AS%s %s「%s『%s", nodeReport.asn, nodeReport.isp, nodeReport.geo[0], nodeReport.geo[1]) + } + } + } + } + fmt.Println("』」") } diff --git a/util/reporter/reporter_test.go b/util/reporter/reporter_test.go index 8aeb9f0..ba931e3 100644 --- a/util/reporter/reporter_test.go +++ b/util/reporter/reporter_test.go @@ -10,15 +10,22 @@ import ( ) func TestPrint(t *testing.T) { - ip := util.DomainLookUp("1.1.1.1") + ip := util.DomainLookUp("213.226.68.73") tcpTraceroute := tcp.New(ip, methods.TracerouteConfig{ MaxHops: uint16(30), NumMeasurements: uint16(1), - ParallelRequests: uint16(1), + ParallelRequests: uint16(12), Port: 80, Timeout: time.Second / 2, }) res, _ := tcpTraceroute.Start() - r := New(*res) + util.Printer(&util.PrinterConfig{ + IP: ip, + DisplayMode: "classic", + DataOrigin: "LeoMoeAPI", + Rdnsenable: true, + Results: *res, + }) + r := New(*res, ip.String()) r.Print() }