package main import ( "encoding/json" "flag" "fmt" "log" "net" "os" "os/signal" "runtime" "strings" "time" "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" ) 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 output = fSet.Bool("o", false, "Ouput trace result to file (RealTimePrinter ONLY") 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 maptrace = fSet.Bool("M", false, "Print Trace Map") var ver = fSet.Bool("V", false, "Print Version") var src_addr = fSet.String("S", "", "Use the following IP address as the source address in outgoing packets") var src_dev = fSet.String("D", "", "Use the following Network Devices as the source address in outgoing packets") var router = fSet.Bool("R", false, "Show Routing Table [Provided By BGP.Tools]") 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, *output) if *output { fmt.Println("您的追踪日志已经存放在 /tmp/trace.log 中") } os.Exit(0) } if target == "" { printArgHelp() } return target } 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` 查看") } } func main() { domain := flagApply() capabilities_check() // return var ip net.IP if runtime.GOOS == "windows" && (*tcpSYNFlag || *udpPackageFlag) { fmt.Println("NextTrace 基于 Windows 的路由跟踪还在早期开发阶段,目前还存在诸多问题,TCP/UDP SYN 包请求可能不能正常运行") } if *tcpSYNFlag || *udpPackageFlag { 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 *tcpSYNFlag: m = trace.TCPTrace case *udpPackageFlag: m = trace.UDPTrace default: m = trace.ICMPTrace } if !*tcpSYNFlag && *port == 80 { *port = 53 } var conf = trace.Config{ SrcAddr: *src_addr, 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 { 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)) } }