123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- package netutils
- import (
- "fmt"
- "net"
- "os"
- "time"
- "golang.org/x/net/icmp"
- "golang.org/x/net/ipv4"
- )
- const (
- protocolICMP = 1
- )
- // liveTraceRoute return realtime tracing information to live response handler
- func liveTraceRoute(dst string, maxHops int, liveRespHandler func(string)) error {
- timeout := time.Second * 3
- // resolve the host name to an IP address
- ipAddr, err := net.ResolveIPAddr("ip4", dst)
- if err != nil {
- return fmt.Errorf("failed to resolve IP address for %s: %v", dst, err)
- }
- // create a socket to listen for incoming ICMP packets
- conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
- if err != nil {
- return fmt.Errorf("failed to create ICMP listener: %v", err)
- }
- defer conn.Close()
- id := os.Getpid() & 0xffff
- seq := 0
- loop_ttl:
- for ttl := 1; ttl <= maxHops; ttl++ {
- // set the TTL on the socket
- if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil {
- return fmt.Errorf("failed to set TTL: %v", err)
- }
- seq++
- // create an ICMP message
- msg := icmp.Message{
- Type: ipv4.ICMPTypeEcho,
- Code: 0,
- Body: &icmp.Echo{
- ID: id,
- Seq: seq,
- Data: []byte("zoraxy_trace"),
- },
- }
- // serialize the ICMP message
- msgBytes, err := msg.Marshal(nil)
- if err != nil {
- return fmt.Errorf("failed to serialize ICMP message: %v", err)
- }
- // send the ICMP message
- start := time.Now()
- if _, err := conn.WriteTo(msgBytes, ipAddr); err != nil {
- //log.Printf("%d: %v", ttl, err)
- liveRespHandler(fmt.Sprintf("%d: %v", ttl, err))
- continue loop_ttl
- }
- // listen for the reply
- replyBytes := make([]byte, 1500)
- if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
- return fmt.Errorf("failed to set read deadline: %v", err)
- }
- for i := 0; i < 3; i++ {
- n, peer, err := conn.ReadFrom(replyBytes)
- if err != nil {
- if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
- //fmt.Printf("%d: *\n", ttl)
- liveRespHandler(fmt.Sprintf("%d: *\n", ttl))
- continue loop_ttl
- } else {
- liveRespHandler(fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
- }
- continue
- }
- // parse the ICMP message
- replyMsg, err := icmp.ParseMessage(protocolICMP, replyBytes[:n])
- if err != nil {
- liveRespHandler(fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
- continue
- }
- // check if the reply is an echo reply
- if replyMsg.Type == ipv4.ICMPTypeEchoReply {
- echoReply, ok := msg.Body.(*icmp.Echo)
- if !ok || echoReply.ID != id || echoReply.Seq != seq {
- continue
- }
- liveRespHandler(fmt.Sprintf("%d: %v %v\n", ttl, peer, time.Since(start)))
- break loop_ttl
- }
- if replyMsg.Type == ipv4.ICMPTypeTimeExceeded {
- echoReply, ok := msg.Body.(*icmp.Echo)
- if !ok || echoReply.ID != id || echoReply.Seq != seq {
- continue
- }
- var raddr = peer.String()
- names, _ := net.LookupAddr(raddr)
- if len(names) > 0 {
- raddr = names[0] + " (" + raddr + ")"
- } else {
- raddr = raddr + " (" + raddr + ")"
- }
- liveRespHandler(fmt.Sprintf("%d: %v %v\n", ttl, raddr, time.Since(start)))
- continue loop_ttl
- }
- }
- }
- return nil
- }
- // Standard traceroute, return results after complete
- func traceroute(dst string, maxHops int) ([]string, error) {
- results := []string{}
- timeout := time.Second * 3
- // resolve the host name to an IP address
- ipAddr, err := net.ResolveIPAddr("ip4", dst)
- if err != nil {
- return results, fmt.Errorf("failed to resolve IP address for %s: %v", dst, err)
- }
- // create a socket to listen for incoming ICMP packets
- conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
- if err != nil {
- return results, fmt.Errorf("failed to create ICMP listener: %v", err)
- }
- defer conn.Close()
- id := os.Getpid() & 0xffff
- seq := 0
- loop_ttl:
- for ttl := 1; ttl <= maxHops; ttl++ {
- // set the TTL on the socket
- if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil {
- return results, fmt.Errorf("failed to set TTL: %v", err)
- }
- seq++
- // create an ICMP message
- msg := icmp.Message{
- Type: ipv4.ICMPTypeEcho,
- Code: 0,
- Body: &icmp.Echo{
- ID: id,
- Seq: seq,
- Data: []byte("zoraxy_trace"),
- },
- }
- // serialize the ICMP message
- msgBytes, err := msg.Marshal(nil)
- if err != nil {
- return results, fmt.Errorf("failed to serialize ICMP message: %v", err)
- }
- // send the ICMP message
- start := time.Now()
- if _, err := conn.WriteTo(msgBytes, ipAddr); err != nil {
- //log.Printf("%d: %v", ttl, err)
- results = append(results, fmt.Sprintf("%d: %v", ttl, err))
- continue loop_ttl
- }
- // listen for the reply
- replyBytes := make([]byte, 1500)
- if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
- return results, fmt.Errorf("failed to set read deadline: %v", err)
- }
- for i := 0; i < 3; i++ {
- n, peer, err := conn.ReadFrom(replyBytes)
- if err != nil {
- if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
- //fmt.Printf("%d: *\n", ttl)
- results = append(results, fmt.Sprintf("%d: *", ttl))
- continue loop_ttl
- } else {
- results = append(results, fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
- }
- continue
- }
- // parse the ICMP message
- replyMsg, err := icmp.ParseMessage(protocolICMP, replyBytes[:n])
- if err != nil {
- results = append(results, fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
- continue
- }
- // check if the reply is an echo reply
- if replyMsg.Type == ipv4.ICMPTypeEchoReply {
- echoReply, ok := msg.Body.(*icmp.Echo)
- if !ok || echoReply.ID != id || echoReply.Seq != seq {
- continue
- }
- results = append(results, fmt.Sprintf("%d: %v %v", ttl, peer, time.Since(start)))
- break loop_ttl
- }
- if replyMsg.Type == ipv4.ICMPTypeTimeExceeded {
- echoReply, ok := msg.Body.(*icmp.Echo)
- if !ok || echoReply.ID != id || echoReply.Seq != seq {
- continue
- }
- var raddr = peer.String()
- names, _ := net.LookupAddr(raddr)
- if len(names) > 0 {
- raddr = names[0] + " (" + raddr + ")"
- } else {
- raddr = raddr + " (" + raddr + ")"
- }
- results = append(results, fmt.Sprintf("%d: %v %v", ttl, raddr, time.Since(start)))
- continue loop_ttl
- }
- }
- }
- return results, nil
- }
|