diff --git a/fast_trace/basic.go b/fast_trace/basic.go index 17407d3..0557022 100644 --- a/fast_trace/basic.go +++ b/fast_trace/basic.go @@ -16,7 +16,7 @@ type BackBoneCollection struct { CU169 ISPCollection CU9929 ISPCollection CM ISPCollection - CMIN2 ISPCollection + CMIN2 ISPCollection EDU ISPCollection CST ISPCollection } @@ -24,6 +24,7 @@ type BackBoneCollection struct { type ISPCollection struct { ISPName string IP string + IPv6 string } const ( @@ -42,7 +43,6 @@ var TestIPsCollection = AllLocationCollection{ Guangzhou: Guangzhou, Hangzhou: Hangzhou, Hefei: Hefei, - Changsha: Changsha, } var Beijing = BackBoneCollection{ @@ -50,13 +50,15 @@ 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", }, - + CU9929: ISPCollection{ ISPName: CU9929, IP: "218.105.131.125", @@ -65,8 +67,9 @@ var Beijing = BackBoneCollection{ CM: ISPCollection{ ISPName: CM, IP: "211.136.25.153", + IPv6: "2409:8000:3800:8::3", }, - + CMIN2: ISPCollection{ ISPName: CMIN2, IP: "223.70.155.55", @@ -75,6 +78,7 @@ var Beijing = BackBoneCollection{ EDU: ISPCollection{ ISPName: EDU, IP: "101.6.15.130", + IPv6: "2001:da8::666", }, } @@ -82,7 +86,8 @@ var Shanghai = BackBoneCollection{ Location: "上海", CT163: ISPCollection{ ISPName: CT163, - IP: "101.226.28.198", + IP: "202.101.21.178", + IPv6: "240e:18:2:153::89", }, CTCN2: ISPCollection{ @@ -93,18 +98,21 @@ 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", }, - + CMIN2: ISPCollection{ ISPName: CMIN2, IP: "183.194.134.1", @@ -113,6 +121,7 @@ var Shanghai = BackBoneCollection{ EDU: ISPCollection{ ISPName: EDU, IP: "202.120.58.155", + IPv6: "2001:da8:8000:1:202:120:2:100", }, } @@ -120,17 +129,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", }, } @@ -139,19 +151,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", }, } @@ -161,6 +177,7 @@ var Hefei = BackBoneCollection{ EDU: ISPCollection{ ISPName: EDU, IP: "202.38.64.1", + IPv6: "2001:da8:d805:ffff:2::1", }, // 中国科学技术大学 科技网 CST: ISPCollection{ @@ -168,12 +185,3 @@ var Hefei = BackBoneCollection{ IP: "210.72.22.2", }, } - -var Changsha = BackBoneCollection{ - Location: "长沙", - // 中南大学 教育网 - EDU: ISPCollection{ - ISPName: EDU, - IP: "202.197.61.221", - }, -} diff --git a/fast_trace/fast_trace ipv6.go b/fast_trace/fast_trace ipv6.go new file mode 100644 index 0000000..b22c552 --- /dev/null +++ b/fast_trace/fast_trace ipv6.go @@ -0,0 +1,136 @@ +package fastTrace + +import ( + "fmt" + "log" + "net" + "os" + "os/signal" + "time" + + "github.com/OwO-Network/nexttrace-enhanced/ipgeo" + "github.com/OwO-Network/nexttrace-enhanced/printer" + "github.com/OwO-Network/nexttrace-enhanced/trace" + "github.com/OwO-Network/nexttrace-enhanced/wshandle" + //"github.com/xgadget-lab/nexttrace/tracelog" +) + +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, + } + oe := false + 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() + }() + + if !tm { + ft.TracerouteMethod = trace.ICMPTrace + fmt.Println("您将默认使用ICMP协议进行路由跟踪,如果您想使用TCP SYN进行路由跟踪,可以加入 -T 参数") + } else { + ft.TracerouteMethod = trace.TCPTrace + } + + 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() + } +} diff --git a/fast_trace/fast_trace.go b/fast_trace/fast_trace.go index e852953..68958b0 100644 --- a/fast_trace/fast_trace.go +++ b/fast_trace/fast_trace.go @@ -144,6 +144,16 @@ func (f *FastTracer) testEDU() { func FastTest(tm bool, srcDev string, srcAddr string) { var c string + outEnable := false + fmt.Println("Hi,欢迎使用 Fast Trace 功能,请注意 Fast Trace 功能只适合新手使用\n因为国内网络复杂,我们设置的测试目标有限,建议普通用户自测以获得更加精准的路由情况") + fmt.Println("请您选择要测试的 IP 类型\n1. IPv4\n2. IPv6") + fmt.Print("请选择选项:") + fmt.Scanln(&c) + if c == "2" { + FastTestv6(tm, outEnable) + return + } + specificDev = srcDev specificAddr = srcAddr diff --git a/main.go b/main.go index bc6fe22..7ca1b21 100644 --- a/main.go +++ b/main.go @@ -113,11 +113,11 @@ func flagApply() string { if target == "" { printArgHelp() } - + if strings.Contains(target, "/") { target = strings.Split(target, "/")[2] } - + if strings.Contains(target, "]") { target = strings.Split(strings.Split(target, "]")[0], "[")[1] } else if strings.Contains(target, ":") { @@ -125,7 +125,7 @@ func flagApply() string { target = strings.Split(target, ":")[0] } } - + return target } @@ -155,10 +155,15 @@ func main() { var ip net.IP - if *tcpSYNFlag || *udpPackageFlag { - ip = util.DomainLookUp(domain, *dns_ip, true, false, *jsonEnable) + //if *tcpSYNFlag || *udpPackageFlag { + // ip = util.DomainLookUp(domain, *dns_ip, true, false, *jsonEnable) + //} else { + // ip = util.DomainLookUp(domain, *dns_ip, *ipv4Only, *ipv6Only, *jsonEnable) + //} + if *udpPackageFlag { + ip = util.DomainLookUp(domain, true) } else { - ip = util.DomainLookUp(domain, *dns_ip, *ipv4Only, *ipv6Only, *jsonEnable) + ip = util.DomainLookUp(domain, false) } // if ip.To4() == nil && strings.ToUpper(*dataOrigin) == "LEOMOEAPI" { diff --git a/printer/printer.go b/printer/printer.go index 9b45343..d743540 100644 --- a/printer/printer.go +++ b/printer/printer.go @@ -11,6 +11,15 @@ import ( var dataOrigin string +const ( + RED_PREFIX = "\033[1;31m" + GREEN_PREFIX = "\033[1;32m" + YELLOW_PREFIX = "\033[1;33m" + BLUE_PREFIX = "\033[1;34m" + CYAN_PREFIX = "\033[1;36m" + RESET_PREFIX = "\033[0m" +) + func TraceroutePrinter(res *trace.Result) { for i, hop := range res.Hops { fmt.Print(i + 1) diff --git a/trace/tcp_ipv6.go b/trace/tcp_ipv6.go index b6af897..fd9bf58 100644 --- a/trace/tcp_ipv6.go +++ b/trace/tcp_ipv6.go @@ -1,16 +1,16 @@ package trace import ( - "log" + "encoding/binary" "math" "math/rand" "net" "sync" "time" + "github.com/OwO-Network/nexttrace-enhanced/util" "github.com/google/gopacket" "github.com/google/gopacket/layers" - "github.com/OwO-Network/nexttrace-enhanced/util" "golang.org/x/net/context" "golang.org/x/net/icmp" "golang.org/x/net/ipv6" @@ -39,14 +39,14 @@ func (t *TCPTracerv6) Execute() (*Result, error) { return &t.res, ErrTracerouteExecuted } - t.SrcIP, _ = util.LocalIPPort(t.DestIP) - log.Println(util.LocalIPPort(t.DestIP)) + t.SrcIP, _ = util.LocalIPPortv6(t.DestIP) + // log.Println(util.LocalIPPortv6(t.DestIP)) var err error t.tcp, err = net.ListenPacket("ip6:tcp", t.SrcIP.String()) if err != nil { return nil, err } - t.icmp, err = icmp.ListenPacket("ip6:53", "::") + t.icmp, err = icmp.ListenPacket("ip6:58", "::") if err != nil { return &t.res, err } @@ -64,14 +64,36 @@ func (t *TCPTracerv6) Execute() (*Result, error) { t.sem = semaphore.NewWeighted(int64(t.ParallelRequests)) for ttl := 1; 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) - + } + if t.RealtimePrinter != nil { + // 对于实时模式,应该按照TTL进行并发请求 + 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() } - t.wg.Wait() t.res.reduce(t.final) return &t.res, nil @@ -85,20 +107,21 @@ func (t *TCPTracerv6) listenICMP() { case <-t.ctx.Done(): return case msg := <-lc.Messages: + // log.Println(msg) + if msg.N == nil { continue } - rm, err := icmp.ParseMessage(53, msg.Msg[:*msg.N]) + rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N]) if err != nil { - log.Println(err) + // log.Println(err) continue } - log.Println(msg.Peer) switch rm.Type { case ipv6.ICMPTypeTimeExceeded: - t.handleICMPMessage(msg, rm.Body.(*icmp.TimeExceeded).Data) + t.handleICMPMessage(msg) case ipv6.ICMPTypeDestinationUnreachable: - t.handleICMPMessage(msg, rm.Body.(*icmp.DstUnreach).Data) + t.handleICMPMessage(msg) default: //log.Println("received icmp message of unknown type", rm.Type) } @@ -118,6 +141,8 @@ func (t *TCPTracerv6) listenTCP() { case <-t.ctx.Done(): return case msg := <-lc.Messages: + // log.Println(msg) + // return if msg.N == nil { continue } @@ -144,23 +169,21 @@ func (t *TCPTracerv6) listenTCP() { } } -func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage, data []byte) { - header, err := util.GetICMPResponsePayload(data) - if err != nil { - return - } - sequenceNumber := util.GetTCPSeq(header) +func (t *TCPTracerv6) handleICMPMessage(msg ReceivedMessage) { + var sequenceNumber = binary.BigEndian.Uint32(msg.Msg[52:56]) + t.inflightRequestLock.Lock() defer t.inflightRequestLock.Unlock() ch, ok := t.inflightRequest[int(sequenceNumber)] if !ok { return } + // log.Println("发送数据", sequenceNumber) ch <- Hop{ Success: true, Address: msg.Peer, } - + // log.Println("发送成功") } func (t *TCPTracerv6) send(ttl int) error { @@ -176,7 +199,7 @@ func (t *TCPTracerv6) send(ttl int) error { } // 随机种子 r := rand.New(rand.NewSource(time.Now().UnixNano())) - _, srcPort := util.LocalIPPort(t.DestIP) + _, srcPort := util.LocalIPPortv6(t.DestIP) ipHeader := &layers.IPv6{ SrcIP: t.SrcIP, DstIP: t.DestIP, @@ -185,6 +208,7 @@ func (t *TCPTracerv6) send(ttl int) error { } // 使用Uint16兼容32位系统,防止在rand的时候因使用int32而溢出 sequenceNumber := uint32(r.Intn(math.MaxUint16)) + tcpHeader := &layers.TCP{ SrcPort: layers.TCPPort(srcPort), DstPort: layers.TCPPort(t.DestPort), @@ -212,6 +236,7 @@ func (t *TCPTracerv6) send(ttl int) error { if _, err := t.tcp.WriteTo(buf.Bytes(), &net.IPAddr{IP: t.DestIP}); err != nil { return err } + // log.Println(ttl, sequenceNumber) t.inflightRequestLock.Lock() hopCh := make(chan Hop) t.inflightRequest[int(sequenceNumber)] = hopCh diff --git a/trace/trace.go b/trace/trace.go index 50958bd..091d981 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -28,6 +28,7 @@ type Config struct { IPGeoSource ipgeo.Source RDns bool RealtimePrinter func(res *Result, ttl int) + AsyncPrinter func(res *Result) } type Method string @@ -73,8 +74,8 @@ func Traceroute(method Method, config Config) (*Result, error) { if config.DestIP.To4() != nil { tracer = &TCPTracer{Config: config} } else { - // tracer = &TCPTracerv6{Config: config} - return nil, errors.New("IPv6 TCP Traceroute is not supported") + tracer = &TCPTracerv6{Config: config} + //return nil, errors.New("IPv6 TCP Traceroute is not supported") } default: return &Result{}, ErrInvalidMethod diff --git a/util/util.go b/util/util.go index 8eb2c6b..4d743a9 100644 --- a/util/util.go +++ b/util/util.go @@ -1,14 +1,11 @@ package util import ( - "context" "fmt" + "github.com/fatih/color" "log" "net" "os" - "time" - - "github.com/fatih/color" ) // get the local ip and port based on our destination ip @@ -29,65 +26,44 @@ func LocalIPPort(dstip net.IP) (net.IP, int) { return nil, -1 } -func DomainLookUp(host string, customDNS string, ipv4Only bool, ipv6Only bool, auto bool) net.IP { - // 手动构造 Resolver 以定制化 DNS 服务器 IP 等参数 - r := &net.Resolver{ - // 尽管编译器已经禁用 Cgo,这里以防万一,保证无论何种编译环境下都能优先使用 Pure-Go,构造详见 lookup.go 源码 - PreferGo: true, +func LocalIPPortv6(dstip net.IP) (net.IP, int) { + serverAddr, err := net.ResolveUDPAddr("udp", "["+dstip.String()+"]:12345") + if err != nil { + log.Fatal(err) } - if customDNS != "" { - r.Dial = func(ctx context.Context, network, address string) (net.Conn, error) { - d := net.Dialer{ - Timeout: 5 * time.Second, - } - // 见文档 - Dial uses context.Background internally; to specify the context, use DialContext. - return d.DialContext(ctx, "udp", customDNS+":53") + // We don't actually connect to anything, but we can determine + // based on our destination ip what source ip we should use. + if con, err := net.DialUDP("udp", nil, serverAddr); err == nil { + defer con.Close() + if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok { + return udpaddr.IP, udpaddr.Port } } + return nil, -1 +} - ips, err := r.LookupHost(context.Background(), host) +func DomainLookUp(host string, ipv4Only bool) net.IP { + ips, err := net.LookupIP(host) if err != nil { fmt.Println("Domain " + host + " Lookup Fail.") os.Exit(1) } - var ipSlice = []net.IP{} var ipv6Flag = false - for _, ipStr := range ips { - ip := net.ParseIP(ipStr) - if ipv4Only { - // 仅返回ipv4的ip - if ip.To4() != nil { - ipSlice = append(ipSlice, ip) - } else { - ipv6Flag = true - } - } else if ipv6Only { - if ip.To4() == nil { - ipSlice = append(ipSlice, ip) - } - } else { - ipSlice = append(ipSlice, ip) - } - } - if ipv6Flag { - if !auto { - // fmt.Println("[Info] IPv6 TCP/UDP Traceroute is not supported right now.") - } - - if len(ipSlice) == 0 { + fmt.Println("[Info] IPv6 UDP Traceroute is not supported right now.") + if len(ips) == 0 { os.Exit(0) } } - if len(ipSlice) == 1 || auto { - return ipSlice[0] + if len(ips) == 1 { + return ips[0] } else { fmt.Println("Please Choose the IP You Want To TraceRoute") - for i, ip := range ipSlice { + for i, ip := range ips { fmt.Fprintf(color.Output, "%s %s\n", color.New(color.FgHiYellow, color.Bold).Sprintf("%d.", i), color.New(color.FgWhite, color.Bold).Sprintf("%s", ip), @@ -96,10 +72,10 @@ func DomainLookUp(host string, customDNS string, ipv4Only bool, ipv6Only bool, a var index int fmt.Printf("Your Option: ") fmt.Scanln(&index) - if index >= len(ipSlice) || index < 0 { + if index >= len(ips) || index < 0 { fmt.Println("Your Option is invalid") os.Exit(3) } - return ipSlice[index] + return ips[index] } }