Init
This commit is contained in:
7
lib/go.mod
Normal file
7
lib/go.mod
Normal file
@ -0,0 +1,7 @@
|
||||
module traceroute
|
||||
|
||||
go 1.18
|
||||
|
||||
require golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
|
||||
|
||||
require golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
4
lib/go.sum
Normal file
4
lib/go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
288
lib/traceroute.go
Normal file
288
lib/traceroute.go
Normal file
@ -0,0 +1,288 @@
|
||||
// Package traceroute provides functions for executing a tracroute to a remote
|
||||
// host.
|
||||
package traceroute
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
const DEFAULT_PORT = 33434
|
||||
const DEFAULT_MAX_HOPS = 30
|
||||
const DEFAULT_FIRST_HOP = 1
|
||||
const DEFAULT_TIMEOUT_MS = 1000
|
||||
const DEFAULT_RETRIES = 3
|
||||
const DEFAULT_PACKET_SIZE = 60
|
||||
|
||||
func socketAddr(ip net.IP) (IPAddr [4]byte, err error) {
|
||||
routes, err := netlink.RouteGet(ip)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
copy(IPAddr[:], routes[0].Src.To4())
|
||||
err = errors.New("You do not appear to be connected to the Internet")
|
||||
return
|
||||
}
|
||||
|
||||
// Given a host name convert it to a 4 byte IP address.
|
||||
func destAddr(dest string) (destAddr [4]byte, IPAddr net.IPAddr, err error) {
|
||||
addrs, err := net.LookupHost(dest)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr := addrs[0]
|
||||
|
||||
ipAddr, err := net.ResolveIPAddr("ip", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
copy(destAddr[:], ipAddr.IP.To4())
|
||||
IPAddr.IP = ipAddr.IP
|
||||
return
|
||||
}
|
||||
|
||||
// TracrouteOptions type
|
||||
type TracerouteOptions struct {
|
||||
port int
|
||||
maxHops int
|
||||
firstHop int
|
||||
timeoutMs int
|
||||
retries int
|
||||
packetSize int
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) Port() int {
|
||||
if options.port == 0 {
|
||||
options.port = DEFAULT_PORT
|
||||
}
|
||||
return options.port
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) SetPort(port int) {
|
||||
options.port = port
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) MaxHops() int {
|
||||
if options.maxHops == 0 {
|
||||
options.maxHops = DEFAULT_MAX_HOPS
|
||||
}
|
||||
return options.maxHops
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) SetMaxHops(maxHops int) {
|
||||
options.maxHops = maxHops
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) FirstHop() int {
|
||||
if options.firstHop == 0 {
|
||||
options.firstHop = DEFAULT_FIRST_HOP
|
||||
}
|
||||
return options.firstHop
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) SetFirstHop(firstHop int) {
|
||||
options.firstHop = firstHop
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) TimeoutMs() int {
|
||||
if options.timeoutMs == 0 {
|
||||
options.timeoutMs = DEFAULT_TIMEOUT_MS
|
||||
}
|
||||
return options.timeoutMs
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) SetTimeoutMs(timeoutMs int) {
|
||||
options.timeoutMs = timeoutMs
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) Retries() int {
|
||||
if options.retries == 0 {
|
||||
options.retries = DEFAULT_RETRIES
|
||||
}
|
||||
return options.retries
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) SetRetries(retries int) {
|
||||
options.retries = retries
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) PacketSize() int {
|
||||
if options.packetSize == 0 {
|
||||
options.packetSize = DEFAULT_PACKET_SIZE
|
||||
}
|
||||
return options.packetSize
|
||||
}
|
||||
|
||||
func (options *TracerouteOptions) SetPacketSize(packetSize int) {
|
||||
options.packetSize = packetSize
|
||||
}
|
||||
|
||||
// TracerouteHop type
|
||||
type TracerouteHop struct {
|
||||
Success bool
|
||||
Address [4]byte
|
||||
Host string
|
||||
N int
|
||||
ElapsedTime time.Duration
|
||||
TTL int
|
||||
}
|
||||
|
||||
func (hop *TracerouteHop) AddressString() string {
|
||||
return fmt.Sprintf("%v.%v.%v.%v", hop.Address[0], hop.Address[1], hop.Address[2], hop.Address[3])
|
||||
}
|
||||
|
||||
func (hop *TracerouteHop) HostOrAddressString() string {
|
||||
hostOrAddr := hop.AddressString()
|
||||
if hop.Host != "" {
|
||||
hostOrAddr = hop.Host
|
||||
}
|
||||
return hostOrAddr
|
||||
}
|
||||
|
||||
// TracerouteResult type
|
||||
type TracerouteResult struct {
|
||||
DestinationAddress [4]byte
|
||||
Hops []TracerouteHop
|
||||
}
|
||||
|
||||
func notify(hop TracerouteHop, channels []chan TracerouteHop) {
|
||||
for _, c := range channels {
|
||||
c <- hop
|
||||
}
|
||||
}
|
||||
|
||||
func closeNotify(channels []chan TracerouteHop) {
|
||||
for _, c := range channels {
|
||||
close(c)
|
||||
}
|
||||
}
|
||||
|
||||
func Traceroute(dest string, options *TracerouteOptions, c ...chan TracerouteHop) (result TracerouteResult, err error) {
|
||||
result.Hops = []TracerouteHop{}
|
||||
destAddrBytes, destIPAddr, err := destAddr(dest)
|
||||
result.DestinationAddress = destAddrBytes
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
timeoutDuration, err := time.ParseDuration(fmt.Sprintf("%vs", options.TimeoutMs()))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ttl := options.FirstHop()
|
||||
retry := 0
|
||||
|
||||
socketAddr, err := socketAddr(destIPAddr.IP)
|
||||
|
||||
connection, err := net.ListenPacket("ip4:1", fmt.Sprintf("%v.%v.%v.%v", socketAddr[0], socketAddr[1], socketAddr[2], socketAddr[3]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer connection.Close()
|
||||
packet := ipv4.NewPacketConn(connection)
|
||||
|
||||
if err := packet.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true); err != nil {
|
||||
return TracerouteResult{}, err
|
||||
}
|
||||
|
||||
icmpMessage := icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: os.Getpid() & 0xffff,
|
||||
Data: []byte("HELLO-R-U-THERE"),
|
||||
},
|
||||
}
|
||||
|
||||
rb := make([]byte, 1500)
|
||||
|
||||
for {
|
||||
icmpMessage.Body.(*icmp.Echo).Seq = ttl
|
||||
|
||||
wb, err := icmpMessage.Marshal(nil)
|
||||
|
||||
if err != nil {
|
||||
return TracerouteResult{}, err
|
||||
}
|
||||
|
||||
if err := packet.SetTTL(ttl); err != nil {
|
||||
return TracerouteResult{}, err
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
if _, err := packet.WriteTo(wb, nil, &destIPAddr); err != nil {
|
||||
return TracerouteResult{}, err
|
||||
}
|
||||
|
||||
if err := packet.SetReadDeadline(start.Add(timeoutDuration)); err != nil {
|
||||
return TracerouteResult{}, err
|
||||
}
|
||||
|
||||
var n int
|
||||
var srcAddr net.Addr
|
||||
|
||||
n, _, srcAddr, err = packet.ReadFrom(rb)
|
||||
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
// means timeout here
|
||||
notify(TracerouteHop{Success: false, TTL: ttl}, c)
|
||||
retry += 1
|
||||
if retry > options.Retries() {
|
||||
ttl += 1
|
||||
retry = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
return TracerouteResult{}, err
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
rm, err := icmp.ParseMessage(1, rb[:n])
|
||||
|
||||
if err != nil {
|
||||
return TracerouteResult{}, err
|
||||
}
|
||||
|
||||
srcAddrBytes, _, _ := destAddr(srcAddr.String())
|
||||
|
||||
hop := TracerouteHop{
|
||||
Success: true,
|
||||
Address: srcAddrBytes,
|
||||
N: n,
|
||||
ElapsedTime: elapsed,
|
||||
TTL: ttl,
|
||||
}
|
||||
|
||||
currHost, err := net.LookupAddr(srcAddr.String())
|
||||
if err == nil {
|
||||
hop.Host = currHost[0]
|
||||
}
|
||||
|
||||
if rm.Type == ipv4.ICMPTypeEchoReply || rm.Type == ipv4.ICMPTypeTimeExceeded {
|
||||
notify(hop, c)
|
||||
result.Hops = append(result.Hops, hop)
|
||||
}
|
||||
|
||||
ttl += 1
|
||||
retry = 0
|
||||
|
||||
if ttl > options.MaxHops() || rm.Type == ipv4.ICMPTypeEchoReply {
|
||||
closeNotify(c)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user