diff --git a/dist/livetrace-linux-amd64 b/dist/livetrace-linux-amd64 index cbb4f31..5e57d6c 100755 Binary files a/dist/livetrace-linux-amd64 and b/dist/livetrace-linux-amd64 differ diff --git a/dist/livetrace-windows-amd64.exe b/dist/livetrace-windows-amd64.exe index 4a134ab..2197d1b 100755 Binary files a/dist/livetrace-windows-amd64.exe and b/dist/livetrace-windows-amd64.exe differ diff --git a/src/main.go b/src/main.go index 30a76c8..2efa7b4 100644 --- a/src/main.go +++ b/src/main.go @@ -444,7 +444,8 @@ func (t *Tracer) sendProbeUDP(ttl int) { t.hops[ttl-1].recordSent() t.sendMu.Lock() - _ = t.udpPC.SetTTL(ttl) + _ = setUDPSocketTTL(t.udpPC, t.udpSend, ttl) + _, err := t.udpSend.WriteTo(payload, &net.UDPAddr{IP: t.targetIP, Port: dstPort}) t.sendMu.Unlock() @@ -523,6 +524,8 @@ func (t *Tracer) receiveLoop() { } } + fmt.Fprintf(os.Stderr, "[debug] icmp recv %d bytes from %s\n", n, peer) + from := peerString(peer) now := time.Now() @@ -624,19 +627,22 @@ func extractOriginalICMP(data []byte) (seq, id int, ok bool) { // extractOriginalUDPPort reads the original UDP destination port from the // embedded original IP+UDP header in a Time Exceeded or Dest Unreachable reply. func extractOriginalUDPPort(data []byte) (dstPort int, ok bool) { - if len(data) < 28 { - return 0, false - } - ihl := int(data[0]&0x0f) * 4 - if ihl < 20 || len(data) < ihl+8 { - return 0, false - } - if data[9] != 17 { // protocol must be UDP (17) - return 0, false - } - // UDP: srcPort(2) dstPort(2) length(2) checksum(2) - dstPort = int(binary.BigEndian.Uint16(data[ihl+2 : ihl+4])) - return dstPort, true + if len(data) < 28 { + fmt.Fprintf(os.Stderr, "[debug] extractUDP: too short (%d bytes)\n", len(data)) + return 0, false + } + ihl := int(data[0]&0x0f) * 4 + if ihl < 20 || len(data) < ihl+8 { + fmt.Fprintf(os.Stderr, "[debug] extractUDP: bad IHL (%d)\n", ihl) + return 0, false + } + if data[9] != 17 { + fmt.Fprintf(os.Stderr, "[debug] extractUDP: proto=%d (not UDP)\n", data[9]) + return 0, false + } + dstPort = int(binary.BigEndian.Uint16(data[ihl+2 : ihl+4])) + fmt.Fprintf(os.Stderr, "[debug] extractUDP: dstPort=%d\n", dstPort) + return dstPort, true } // ─── Reply handler ──────────────────────────────────────────────────────────── diff --git a/src/winapi_other.go b/src/winapi_other.go index eeed34e..442c8c6 100644 --- a/src/winapi_other.go +++ b/src/winapi_other.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "time" + "golang.org/x/net/ipv4" ) func winAPIProbe(_ net.IP, _ int, _ time.Duration, _ []byte) (float64, string, uint32, error) { @@ -15,3 +16,7 @@ func winAPIProbe(_ net.IP, _ int, _ time.Duration, _ []byte) (float64, string, u func (t *Tracer) sendProbeWinAPI(_ int) {} func udpModeWarning() {} + +func setUDPSocketTTL(pc *ipv4.PacketConn, conn net.PacketConn, ttl int) error { + return pc.SetTTL(ttl) +} diff --git a/src/winapi_windows.go b/src/winapi_windows.go index c93da0b..93d5acd 100644 --- a/src/winapi_windows.go +++ b/src/winapi_windows.go @@ -9,6 +9,7 @@ import ( "time" "unsafe" "os" + "golang.org/x/net/ipv4" ) var ( @@ -142,3 +143,20 @@ func (t *Tracer) sendProbeWinAPI(ttl int) { } }() } + +func setUDPSocketTTL(pc *ipv4.PacketConn, conn net.PacketConn, ttl int) error { + udpConn, ok := conn.(*net.UDPConn) + if !ok { + return pc.SetTTL(ttl) + } + raw, err := udpConn.SyscallConn() + if err != nil { + return pc.SetTTL(ttl) + } + var sockErr error + raw.Control(func(fd uintptr) { + sockErr = syscall.SetsockoptInt(syscall.Handle(fd), + syscall.IPPROTO_IP, syscall.IP_TTL, ttl) + }) + return sockErr +}