traceroute.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package netutils
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "time"
  7. "golang.org/x/net/icmp"
  8. "golang.org/x/net/ipv4"
  9. )
  10. const (
  11. protocolICMP = 1
  12. )
  13. // liveTraceRoute return realtime tracing information to live response handler
  14. func liveTraceRoute(dst string, maxHops int, liveRespHandler func(string)) error {
  15. timeout := time.Second * 3
  16. // resolve the host name to an IP address
  17. ipAddr, err := net.ResolveIPAddr("ip4", dst)
  18. if err != nil {
  19. return fmt.Errorf("failed to resolve IP address for %s: %v", dst, err)
  20. }
  21. // create a socket to listen for incoming ICMP packets
  22. conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
  23. if err != nil {
  24. return fmt.Errorf("failed to create ICMP listener: %v", err)
  25. }
  26. defer conn.Close()
  27. id := os.Getpid() & 0xffff
  28. seq := 0
  29. loop_ttl:
  30. for ttl := 1; ttl <= maxHops; ttl++ {
  31. // set the TTL on the socket
  32. if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil {
  33. return fmt.Errorf("failed to set TTL: %v", err)
  34. }
  35. seq++
  36. // create an ICMP message
  37. msg := icmp.Message{
  38. Type: ipv4.ICMPTypeEcho,
  39. Code: 0,
  40. Body: &icmp.Echo{
  41. ID: id,
  42. Seq: seq,
  43. Data: []byte("zoraxy_trace"),
  44. },
  45. }
  46. // serialize the ICMP message
  47. msgBytes, err := msg.Marshal(nil)
  48. if err != nil {
  49. return fmt.Errorf("failed to serialize ICMP message: %v", err)
  50. }
  51. // send the ICMP message
  52. start := time.Now()
  53. if _, err := conn.WriteTo(msgBytes, ipAddr); err != nil {
  54. //log.Printf("%d: %v", ttl, err)
  55. liveRespHandler(fmt.Sprintf("%d: %v", ttl, err))
  56. continue loop_ttl
  57. }
  58. // listen for the reply
  59. replyBytes := make([]byte, 1500)
  60. if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
  61. return fmt.Errorf("failed to set read deadline: %v", err)
  62. }
  63. for i := 0; i < 3; i++ {
  64. n, peer, err := conn.ReadFrom(replyBytes)
  65. if err != nil {
  66. if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
  67. //fmt.Printf("%d: *\n", ttl)
  68. liveRespHandler(fmt.Sprintf("%d: *\n", ttl))
  69. continue loop_ttl
  70. } else {
  71. liveRespHandler(fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
  72. }
  73. continue
  74. }
  75. // parse the ICMP message
  76. replyMsg, err := icmp.ParseMessage(protocolICMP, replyBytes[:n])
  77. if err != nil {
  78. liveRespHandler(fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
  79. continue
  80. }
  81. // check if the reply is an echo reply
  82. if replyMsg.Type == ipv4.ICMPTypeEchoReply {
  83. echoReply, ok := msg.Body.(*icmp.Echo)
  84. if !ok || echoReply.ID != id || echoReply.Seq != seq {
  85. continue
  86. }
  87. liveRespHandler(fmt.Sprintf("%d: %v %v\n", ttl, peer, time.Since(start)))
  88. break loop_ttl
  89. }
  90. if replyMsg.Type == ipv4.ICMPTypeTimeExceeded {
  91. echoReply, ok := msg.Body.(*icmp.Echo)
  92. if !ok || echoReply.ID != id || echoReply.Seq != seq {
  93. continue
  94. }
  95. var raddr = peer.String()
  96. names, _ := net.LookupAddr(raddr)
  97. if len(names) > 0 {
  98. raddr = names[0] + " (" + raddr + ")"
  99. } else {
  100. raddr = raddr + " (" + raddr + ")"
  101. }
  102. liveRespHandler(fmt.Sprintf("%d: %v %v\n", ttl, raddr, time.Since(start)))
  103. continue loop_ttl
  104. }
  105. }
  106. }
  107. return nil
  108. }
  109. // Standard traceroute, return results after complete
  110. func traceroute(dst string, maxHops int) ([]string, error) {
  111. results := []string{}
  112. timeout := time.Second * 3
  113. // resolve the host name to an IP address
  114. ipAddr, err := net.ResolveIPAddr("ip4", dst)
  115. if err != nil {
  116. return results, fmt.Errorf("failed to resolve IP address for %s: %v", dst, err)
  117. }
  118. // create a socket to listen for incoming ICMP packets
  119. conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
  120. if err != nil {
  121. return results, fmt.Errorf("failed to create ICMP listener: %v", err)
  122. }
  123. defer conn.Close()
  124. id := os.Getpid() & 0xffff
  125. seq := 0
  126. loop_ttl:
  127. for ttl := 1; ttl <= maxHops; ttl++ {
  128. // set the TTL on the socket
  129. if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil {
  130. return results, fmt.Errorf("failed to set TTL: %v", err)
  131. }
  132. seq++
  133. // create an ICMP message
  134. msg := icmp.Message{
  135. Type: ipv4.ICMPTypeEcho,
  136. Code: 0,
  137. Body: &icmp.Echo{
  138. ID: id,
  139. Seq: seq,
  140. Data: []byte("zoraxy_trace"),
  141. },
  142. }
  143. // serialize the ICMP message
  144. msgBytes, err := msg.Marshal(nil)
  145. if err != nil {
  146. return results, fmt.Errorf("failed to serialize ICMP message: %v", err)
  147. }
  148. // send the ICMP message
  149. start := time.Now()
  150. if _, err := conn.WriteTo(msgBytes, ipAddr); err != nil {
  151. //log.Printf("%d: %v", ttl, err)
  152. results = append(results, fmt.Sprintf("%d: %v", ttl, err))
  153. continue loop_ttl
  154. }
  155. // listen for the reply
  156. replyBytes := make([]byte, 1500)
  157. if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
  158. return results, fmt.Errorf("failed to set read deadline: %v", err)
  159. }
  160. for i := 0; i < 3; i++ {
  161. n, peer, err := conn.ReadFrom(replyBytes)
  162. if err != nil {
  163. if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
  164. //fmt.Printf("%d: *\n", ttl)
  165. results = append(results, fmt.Sprintf("%d: *", ttl))
  166. continue loop_ttl
  167. } else {
  168. results = append(results, fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
  169. }
  170. continue
  171. }
  172. // parse the ICMP message
  173. replyMsg, err := icmp.ParseMessage(protocolICMP, replyBytes[:n])
  174. if err != nil {
  175. results = append(results, fmt.Sprintf("%d: Failed to parse ICMP message: %v", ttl, err))
  176. continue
  177. }
  178. // check if the reply is an echo reply
  179. if replyMsg.Type == ipv4.ICMPTypeEchoReply {
  180. echoReply, ok := msg.Body.(*icmp.Echo)
  181. if !ok || echoReply.ID != id || echoReply.Seq != seq {
  182. continue
  183. }
  184. results = append(results, fmt.Sprintf("%d: %v %v", ttl, peer, time.Since(start)))
  185. break loop_ttl
  186. }
  187. if replyMsg.Type == ipv4.ICMPTypeTimeExceeded {
  188. echoReply, ok := msg.Body.(*icmp.Echo)
  189. if !ok || echoReply.ID != id || echoReply.Seq != seq {
  190. continue
  191. }
  192. var raddr = peer.String()
  193. names, _ := net.LookupAddr(raddr)
  194. if len(names) > 0 {
  195. raddr = names[0] + " (" + raddr + ")"
  196. } else {
  197. raddr = raddr + " (" + raddr + ")"
  198. }
  199. results = append(results, fmt.Sprintf("%d: %v %v", ttl, raddr, time.Since(start)))
  200. continue loop_ttl
  201. }
  202. }
  203. }
  204. return results, nil
  205. }