Advertisement
Guest User

Untitled

a guest
Apr 23rd, 2014
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.04 KB | None | 0 0
  1. // Package traceroute provides functions for executing a tracroute to a remote
  2. // host.
  3. package traceroute
  4.  
  5. import (
  6. "errors"
  7. "fmt"
  8. "net"
  9. "syscall"
  10. "time"
  11. )
  12.  
  13. const DEFAULT_PORT = 33434
  14. const DEFAULT_MAX_HOPS = 64
  15. const DEFAULT_TIMEOUT_MS = 500
  16. const DEFAULT_RETRIES = 3
  17. const DEFAULT_PACKET_SIZE = 52
  18.  
  19. // Return the first non-loopback address as a 4 byte IP address. This address
  20. // is used for sending packets out.
  21. func socketAddr() (addr [4]byte, err error) {
  22. addrs, err := net.InterfaceAddrs()
  23. if err != nil {
  24. return
  25. }
  26.  
  27. for _, a := range addrs {
  28. if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
  29. if len(ipnet.IP.To4()) == net.IPv4len {
  30. copy(addr[:], ipnet.IP.To4())
  31. return
  32. }
  33. }
  34. }
  35. err = errors.New("You do not appear to be connected to the Internet")
  36. return
  37. }
  38.  
  39. // Given a host name convert it to a 4 byte IP address.
  40. func destAddr(dest string) (destAddr [4]byte, err error) {
  41. addrs, err := net.LookupHost(dest)
  42. if err != nil {
  43. return
  44. }
  45. addr := addrs[0]
  46.  
  47. ipAddr, err := net.ResolveIPAddr("ip", addr)
  48. if err != nil {
  49. return
  50. }
  51. copy(destAddr[:], ipAddr.IP.To4())
  52. return
  53. }
  54.  
  55. // TracrouteOptions type
  56. type TracerouteOptions struct {
  57. port int
  58. maxHops int
  59. timeoutMs int
  60. retries int
  61. packetSize int
  62. }
  63.  
  64. func (options *TracerouteOptions) Port() int {
  65. if options.port == 0 {
  66. options.port = DEFAULT_PORT
  67. }
  68. return options.port
  69. }
  70.  
  71. func (options *TracerouteOptions) SetPort(port int) {
  72. options.port = port
  73. }
  74.  
  75. func (options *TracerouteOptions) MaxHops() int {
  76. if options.maxHops == 0 {
  77. options.maxHops = DEFAULT_MAX_HOPS
  78. }
  79. return options.maxHops
  80. }
  81.  
  82. func (options *TracerouteOptions) SetMaxHops(maxHops int) {
  83. options.maxHops = maxHops
  84. }
  85.  
  86. func (options *TracerouteOptions) TimeoutMs() int {
  87. if options.timeoutMs == 0 {
  88. options.timeoutMs = DEFAULT_TIMEOUT_MS
  89. }
  90. return options.timeoutMs
  91. }
  92.  
  93. func (options *TracerouteOptions) SetTimeoutMs(timeoutMs int) {
  94. options.timeoutMs = timeoutMs
  95. }
  96.  
  97. func (options *TracerouteOptions) Retries() int {
  98. if options.retries == 0 {
  99. options.retries = DEFAULT_RETRIES
  100. }
  101. return options.retries
  102. }
  103.  
  104. func (options *TracerouteOptions) SetRetries(retries int) {
  105. options.retries = retries
  106. }
  107.  
  108. func (options *TracerouteOptions) PacketSize() int {
  109. if options.packetSize == 0 {
  110. options.packetSize = DEFAULT_PACKET_SIZE
  111. }
  112. return options.packetSize
  113. }
  114.  
  115. func (options *TracerouteOptions) SetPacketSize(packetSize int) {
  116. options.packetSize = packetSize
  117. }
  118.  
  119. // TracerouteHop type
  120. type TracerouteHop struct {
  121. Success bool
  122. Address [4]byte
  123. Host string
  124. N int
  125. ElapsedTime time.Duration
  126. TTL int
  127. }
  128.  
  129. func (hop *TracerouteHop) AddressString() string {
  130. return fmt.Sprintf("%v.%v.%v.%v", hop.Address[0], hop.Address[1], hop.Address[2], hop.Address[3])
  131. }
  132.  
  133. func (hop *TracerouteHop) HostOrAddressString() string {
  134. hostOrAddr := hop.AddressString()
  135. if hop.Host != "" {
  136. hostOrAddr = hop.Host
  137. }
  138. return hostOrAddr
  139. }
  140.  
  141. // TracerouteResult type
  142. type TracerouteResult struct {
  143. DestinationAddress [4]byte
  144. Hops []TracerouteHop
  145. }
  146.  
  147. func notify(hop TracerouteHop, channels []chan TracerouteHop) {
  148. for _, c := range channels {
  149. c <- hop
  150. }
  151. }
  152.  
  153. func closeNotify(channels []chan TracerouteHop) {
  154. for _, c := range channels {
  155. close(c)
  156. }
  157. }
  158.  
  159. // Traceroute uses the given dest (hostname) and options to execute a traceroute
  160. // from your machine to the remote host.
  161. //
  162. // Outbound packets are UDP packets and inbound packets are ICMP.
  163. //
  164. // Returns a TracerouteResult which contains an array of hops. Each hop includes
  165. // the elapsed time and its IP address.
  166. func Traceroute(dest string, options *TracerouteOptions, c ...chan TracerouteHop) (result TracerouteResult, err error) {
  167. result.Hops = []TracerouteHop{}
  168. destAddr, err := destAddr(dest)
  169. result.DestinationAddress = destAddr
  170. socketAddr, err := socketAddr()
  171. if err != nil {
  172. return
  173. }
  174.  
  175. timeoutMs := (int64)(options.TimeoutMs())
  176. tv := syscall.NsecToTimeval(1000 * 1000 * timeoutMs)
  177. ttl := 1
  178. retry := 0
  179.  
  180. // Set up the socket to receive inbound packets
  181. recvSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
  182. if err != nil {
  183. return result, err
  184. }
  185.  
  186. // Set up the socket to send packets out.
  187. sendSocket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
  188. if err != nil {
  189. return result, err
  190. }
  191. // This sets the current hop TTL
  192. syscall.SetsockoptInt(sendSocket, 0x0, syscall.IP_TTL, ttl)
  193. // This sets the timeout to wait for a response from the remote host
  194. syscall.SetsockoptTimeval(recvSocket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &tv)
  195.  
  196. defer syscall.Close(recvSocket)
  197. defer syscall.Close(sendSocket)
  198.  
  199. // Bind to the local socket to listen for ICMP packets
  200. syscall.Bind(recvSocket, &syscall.SockaddrInet4{Port: options.Port(), Addr: socketAddr})
  201.  
  202. for {
  203. //log.Println("TTL: ", ttl)
  204. start := time.Now()
  205.  
  206. // Send a single null byte UDP packet
  207. syscall.Sendto(sendSocket, []byte{0x0}, 0, &syscall.SockaddrInet4{Port: options.Port(), Addr: destAddr})
  208.  
  209. var p = make([]byte, options.PacketSize())
  210. n, from, err := syscall.Recvfrom(recvSocket, p, 0)
  211. elapsed := time.Since(start)
  212. if err == nil {
  213. currAddr := from.(*syscall.SockaddrInet4).Addr
  214.  
  215. hop := TracerouteHop{Success: true, Address: currAddr, N: n, ElapsedTime: elapsed, TTL: ttl}
  216.  
  217. // TODO: this reverse lookup appears to have some standard timeout that is relatively
  218. // high. Consider switching to something where there is greater control.
  219. currHost, err := net.LookupAddr(hop.AddressString())
  220. if err == nil {
  221. hop.Host = currHost[0]
  222. }
  223.  
  224. notify(hop, c)
  225.  
  226. result.Hops = append(result.Hops, hop)
  227.  
  228. ttl += 1
  229. retry = 0
  230.  
  231. if ttl > options.MaxHops() || currAddr == destAddr {
  232. closeNotify(c)
  233. return result, nil
  234. }
  235. } else {
  236. retry += 1
  237. if retry > options.Retries() {
  238. notify(TracerouteHop{Success: false, TTL: ttl}, c)
  239. ttl += 1
  240. retry = 0
  241. }
  242. }
  243.  
  244. }
  245. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement