diff --git a/arp/arp.go b/arp/arp.go index 0de8f7a..e368334 100644 --- a/arp/arp.go +++ b/arp/arp.go @@ -2,7 +2,6 @@ package arp import ( - "encoding/binary" "net" "sync" @@ -11,7 +10,7 @@ import ( ) var ( - table map[uint32]net.HardwareAddr + table = make(map[string]net.HardwareAddr) tableMu sync.RWMutex ) @@ -20,26 +19,32 @@ var defaultSerializeOpts = gopacket.SerializeOptions{ ComputeChecksums: true, } -func init() { - table = make(map[uint32]net.HardwareAddr) +// Add adds a IP-MAC map to a runtime ARP table. +func internalLookup(ip net.IP) (*net.HardwareAddr, bool) { + tableMu.Lock() + defer tableMu.Unlock() + if hwaddr, ok := table[ip.To4().String()]; ok { + return &hwaddr, true + } + return nil, false } // Add adds a IP-MAC map to a runtime ARP table. func Add(ip net.IP, hwaddr net.HardwareAddr) { tableMu.Lock() defer tableMu.Unlock() - table[binary.BigEndian.Uint32(ip)] = hwaddr + table[ip.To4().String()] = hwaddr } // Delete removes an IP from the runtime ARP table. func Delete(ip net.IP) { tableMu.Lock() defer tableMu.Unlock() - delete(table, binary.BigEndian.Uint32(ip)) + delete(table, ip.To4().String()) } // List returns the current runtime ARP table. -func List() map[uint32]net.HardwareAddr { +func List() map[string]net.HardwareAddr { tableMu.RLock() defer tableMu.RUnlock() return table @@ -54,16 +59,19 @@ type Address struct { // Lookup returns the Address given an IP, if the IP is not found within the // table, a lookup is attempted. -func Lookup(ip uint32) (*Address, error) { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, ip) - if hwaddr, ok := table[ip]; ok { +func Lookup(ip net.IP) (*Address, error) { + if hwaddr, ok := internalLookup(ip); ok { return &Address{ - IP: net.IP(b), - HardwareAddr: hwaddr, + IP: ip, + HardwareAddr: *hwaddr, }, nil } - return doARPLookup(net.IP(b).To4().String()) + + addr, err := doARPLookup(ip.To4().String()) + if err == nil { + Add(addr.IP, addr.HardwareAddr) + } + return addr, err } // NewARPRequest creates a bew ARP packet of type "request. diff --git a/arp/arp_darwin.go b/arp/arp_darwin.go index 03aa225..9e658cb 100644 --- a/arp/arp_darwin.go +++ b/arp/arp_darwin.go @@ -39,8 +39,13 @@ func hexToInt(h string) uint8 { func doARPLookup(ip string) (*Address, error) { ping := exec.Command("ping", "-c1", "-t1", ip) - ping.Run() // TODO: manually inject arp who has packet. - ping.Wait() + if err := ping.Start(); err != nil { + return nil, err + } + + if err := ping.Wait(); err != nil { + return nil, err + } cmd := exec.Command("arp", "-n", ip) out, err := cmd.Output() diff --git a/arp/arp_freebsd.go b/arp/arp_freebsd.go index 08c4b82..c945789 100644 --- a/arp/arp_freebsd.go +++ b/arp/arp_freebsd.go @@ -20,8 +20,13 @@ func hexToInt(h string) uint8 { func doARPLookup(ip string) (*Address, error) { ping := exec.Command("ping", "-c1", "-t1", ip) - ping.Run() // TODO: manually inject arp who has packet. - ping.Wait() + if err := ping.Start(); err != nil { + return nil, err + } + + if err := ping.Wait(); err != nil { + return nil, err + } cmd := exec.Command("arp", "-n", ip) out, err := cmd.Output() diff --git a/arp/arp_linux.go b/arp/arp_linux.go index e5fac87..a8f7774 100644 --- a/arp/arp_linux.go +++ b/arp/arp_linux.go @@ -15,7 +15,7 @@ var lineMatch = regexp.MustCompile(`([0-9\.]+)\s+dev\s+([^\s]+)\s+lladdr\s+([0-9 func doARPLookup(ip string) (*Address, error) { ping := exec.Command("ping", "-c1", "-t1", ip) - if err := ping.Run(); err != nil { // TODO: manually inject arp who has packet. + if err := ping.Start(); err != nil { return nil, err } @@ -50,6 +50,7 @@ func doARPLookup(ip string) (*Address, error) { HardwareAddr: macAddr, Interface: *iface, } + return &localAddr, nil } return nil, errors.New("Lookup failed.") diff --git a/arp/arp_test.go b/arp/arp_test.go index 1391cd0..5b668d1 100644 --- a/arp/arp_test.go +++ b/arp/arp_test.go @@ -1,14 +1,13 @@ package arp import ( - "encoding/binary" "net" "testing" ) func SkipTestArp(t *testing.T) { ip := net.IP{10, 0, 0, 1} - addr, err := Lookup(binary.BigEndian.Uint32(ip)) + addr, err := Lookup(ip) if err != nil { t.Fatal(err) } diff --git a/arp/arp_windows.go b/arp/arp_windows.go index 23f2379..0695f39 100644 --- a/arp/arp_windows.go +++ b/arp/arp_windows.go @@ -47,7 +47,13 @@ func winTable() map[string]string { func doARPLookup(ip string) (*Address, error) { ping := exec.Command("cmd", "/C", "ping -n 1", ip) - ping.Run() + if err := ping.Start(); err != nil { + return nil, err + } + + if err := ping.Wait(); err != nil { + return nil, err + } arpAllTable := winTable() if hwaddr, ok := arpAllTable[ip]; ok { diff --git a/go.mod b/go.mod index 9d13c14..6188448 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,10 @@ module github.com/malfunkt/arpfox -go 1.12 +go 1.14 require ( github.com/google/gopacket v1.1.17 github.com/malfunkt/iprange v0.9.0 - github.com/pkg/errors v0.8.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/stretchr/testify v1.5.1 // indirect ) diff --git a/go.sum b/go.sum index cdc0447..f7a416e 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,23 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/malfunkt/iprange v0.9.0 h1:VCs0PKLUPotNVQTpVNszsut4lP7OCGNBwX+lOYBrnVQ= github.com/malfunkt/iprange v0.9.0/go.mod h1:TRGqO/f95gh3LOndUGTL46+W0GXA91WTqyZ0Quwvt4U= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 11d1ee8..572b1cc 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,6 @@ package main import ( "bytes" - "encoding/binary" "flag" "fmt" "log" @@ -57,7 +56,7 @@ var ( flagInterface = flag.String("i", defaultInterface(), `Network interface.`) flagTarget = flag.String("t", "", `Target host(s). Provide a single IP: "1.2.3.4", a CIDR block "1.2.3.0/24", an IP range: "1.2.3-7.4-12", an IP with a wildcard: "1.2.3.*", or a list with any combination: "1.2.3.4, 1.2.3.0/24, ..."`) flagListInterfaces = flag.Bool("l", false, `List available interfaces and exit.`) - flagWaitInterval = flag.Float64("w", 5.0, `Wait seconds between every broadcast, must be a value greater than 0.1.`) + flagWaitInterval = flag.Float64("w", 2, `Wait seconds between every broadcast, must be a value greater than 0.1.`) flagHelp = flag.Bool("h", false, `Print usage instructions and exit.`) ) @@ -174,7 +173,7 @@ func main() { go readARP(handler, stop, iface) // Get original source - origSrc, err := arp.Lookup(binary.BigEndian.Uint32(hostIP)) + origSrc, err := arp.Lookup(hostIP) if err != nil { log.Fatalf("Unable to lookup hw address for %s: %v", hostIP, err) } @@ -206,6 +205,7 @@ func cleanUpAndReARP(handler *pcap.Handle, targetAddrs []net.IP, src *arp.Addres func writeARP(handler *pcap.Handle, stop chan struct{}, targetAddrs []net.IP, src *arp.Address, waitInterval time.Duration) chan struct{} { stoppedWriting := make(chan struct{}) + go func(stoppedWriting chan struct{}) { t := time.NewTicker(waitInterval) for { @@ -216,7 +216,7 @@ func writeARP(handler *pcap.Handle, stop chan struct{}, targetAddrs []net.IP, sr default: <-t.C for _, ip := range targetAddrs { - arpAddr, err := arp.Lookup(binary.BigEndian.Uint32(ip)) + arpAddr, err := arp.Lookup(ip) if err != nil { log.Printf("Could not retrieve %v's MAC address: %v", ip, err) continue @@ -225,6 +225,7 @@ func writeARP(handler *pcap.Handle, stop chan struct{}, targetAddrs []net.IP, sr IP: ip, HardwareAddr: arpAddr.HardwareAddr, } + buf, err := arp.NewARPRequest(src, dst) if err != nil { log.Print("NewARPRequest: ", err) @@ -237,6 +238,7 @@ func writeARP(handler *pcap.Handle, stop chan struct{}, targetAddrs []net.IP, sr } } }(stoppedWriting) + return stoppedWriting }