diff --git a/network/net.go b/network/net.go index bb485fc7..e9b0625d 100644 --- a/network/net.go +++ b/network/net.go @@ -305,7 +305,6 @@ func SetInterfaceTxPower(name string, txpower int) error { } func GatewayProvidedByUser(iface *Endpoint, gateway string) (*Endpoint, error) { - Debug("GatewayProvidedByUser(%s) [cmd=%v opts=%v parser=%v]", gateway, IPv4RouteCmd, IPv4RouteCmdOpts, IPv4RouteParser) if IPv4Validator.MatchString(gateway) { Debug("valid gateway ip %s", gateway) // we have the address, now we need its mac diff --git a/network/net_darwin.go b/network/net_darwin.go index c5ec7712..27b598c3 100644 --- a/network/net_darwin.go +++ b/network/net_darwin.go @@ -13,23 +13,8 @@ import ( const airPortPath = "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport" -var IPv4RouteParser = regexp.MustCompile(`([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+).*`) -var IPv4RouteTokens = 5 -var IPv4RouteCmd = "netstat" -var IPv4RouteCmdOpts = []string{"-n", "-r"} var WiFiChannelParser = regexp.MustCompile(`(?m)^.*Supported Channels: (.*)$`) -func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) { - flags := tokens[3] - ifname2 := tokens[4] - if ifname == ifname2 && flags == "UGSc" { - gateway := tokens[2] - return f(gateway) - } - - return nil, nil -} - // see Windows version to understand why .... func getInterfaceName(iface net.Interface) string { return iface.Name diff --git a/network/net_gateway.go b/network/net_gateway.go index 1d7b6042..6255b288 100644 --- a/network/net_gateway.go +++ b/network/net_gateway.go @@ -3,46 +3,26 @@ package network import ( - "strings" - - "github.com/bettercap/bettercap/core" - - "github.com/evilsocket/islazy/str" + "github.com/bettercap/bettercap/routing" ) func FindGateway(iface *Endpoint) (*Endpoint, error) { - Debug("FindGateway(%s) [cmd=%v opts=%v parser=%v]", iface.Name(), IPv4RouteCmd, IPv4RouteCmdOpts, IPv4RouteParser) - - output, err := core.Exec(IPv4RouteCmd, IPv4RouteCmdOpts) + gateway, err := routing.Gateway(routing.IPv4, iface.Name()) if err != nil { - Debug("FindGateway(%s): core.Exec failed with %s", err) return nil, err } - Debug("FindGateway(%s) output:\n%s", iface.Name(), output) - - ifName := iface.Name() - for _, line := range strings.Split(output, "\n") { - if line = str.Trim(line); strings.Contains(line, ifName) { - m := IPv4RouteParser.FindStringSubmatch(line) - if len(m) >= IPv4RouteTokens { - Debug("FindGateway(%s) line '%s' matched with %v", iface.Name(), line, m) - return IPv4RouteIsGateway(ifName, m, func(gateway string) (*Endpoint, error) { - if gateway == iface.IpAddress { - Debug("gateway is the interface") - return iface, nil - } else { - // we have the address, now we need its mac - mac, err := ArpLookup(ifName, gateway, false) - if err != nil { - return nil, err - } - Debug("gateway is %s[%s]", gateway, mac) - return NewEndpoint(gateway, mac), nil - } - }) - } + if gateway == iface.IpAddress { + Debug("gateway is the interface") + return iface, nil + } else { + // we have the address, now we need its mac + mac, err := ArpLookup(iface.Name(), gateway, false) + if err != nil { + return nil, err } + Debug("gateway is %s[%s]", gateway, mac) + return NewEndpoint(gateway, mac), nil } Debug("FindGateway(%s): nothing found :/", iface.Name()) diff --git a/network/net_gateway_android.go b/network/net_gateway_android.go index ddfc8d1e..9e5caa0f 100644 --- a/network/net_gateway_android.go +++ b/network/net_gateway_android.go @@ -24,4 +24,4 @@ func FindGateway(iface *Endpoint) (*Endpoint, error) { } return nil, ErrNoGateway -} +} \ No newline at end of file diff --git a/network/net_linux.go b/network/net_linux.go index feca3d7b..dd0977ac 100644 --- a/network/net_linux.go +++ b/network/net_linux.go @@ -11,23 +11,6 @@ import ( "github.com/bettercap/bettercap/core" ) -// only matches gateway lines -var IPv4RouteParser = regexp.MustCompile(`^(default|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\svia\s([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\sdev\s(\S+).*$`) -var IPv4RouteTokens = 4 -var IPv4RouteCmd = "ip" -var IPv4RouteCmdOpts = []string{"route"} - -func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) { - ifname2 := tokens[3] - - if ifname == ifname2 { - gateway := tokens[2] - return f(gateway) - } - - return nil, nil -} - // see Windows version to understand why .... func getInterfaceName(iface net.Interface) string { return iface.Name diff --git a/network/net_windows.go b/network/net_windows.go index 5fe544c8..bf56e98f 100644 --- a/network/net_windows.go +++ b/network/net_windows.go @@ -3,25 +3,11 @@ package network import ( "fmt" "net" - "regexp" "strings" "github.com/google/gopacket/pcap" ) -// only matches gateway lines -var IPv4RouteParser = regexp.MustCompile("^.+\\s+.+\\s+\\d+\\s+([0-9\\.]+/\\d+)\\s+\\d+\\s+([0-9\\.]+).*$") -var IPv4RouteTokens = 3 -var IPv4RouteCmd = "netsh" -var IPv4RouteCmdOpts = []string{"interface", "ipv4", "show", "route"} - -func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) { - // TODO check if the subnet is the same as iface ? - // subnet := tokens[1] - gateway := tokens[2] - return f(gateway) -} - /* * net.Interface does not have the correct name on Windows and pcap.Interface * does not have the hardware address for some reason ... so this is what I diff --git a/routing/route.go b/routing/route.go new file mode 100644 index 00000000..e0f6bd39 --- /dev/null +++ b/routing/route.go @@ -0,0 +1,17 @@ +package routing + +type RouteType int + +const ( + IPv4 RouteType = 0 + IPv6 RouteType = 1 +) + +type Route struct { + Type RouteType + Default bool + Device string + Destination string + Gateway string + Flags string +} diff --git a/routing/tables.go b/routing/tables.go new file mode 100644 index 00000000..fcb9f043 --- /dev/null +++ b/routing/tables.go @@ -0,0 +1,39 @@ +package routing + +import "sync" + +var ( + lock = sync.RWMutex{} + table = make([]Route, 0) +) + +func Table() []Route { + lock.RLock() + defer lock.RUnlock() + return table +} + +func Update() ([]Route, error) { + lock.Lock() + defer lock.Unlock() + return update() +} + +func Gateway(ip RouteType, device string) (string, error) { + Update() + + lock.RLock() + defer lock.RUnlock() + + for _, r := range table { + if r.Type == ip { + if device == "" || r.Device == device || r.Device == "" /* windows case */ { + if r.Default { + return r.Gateway, nil + } + } + } + } + + return "", nil +} diff --git a/routing/update_darwin.go b/routing/update_darwin.go new file mode 100644 index 00000000..e5f507d8 --- /dev/null +++ b/routing/update_darwin.go @@ -0,0 +1,44 @@ +package routing + +import ( + "github.com/bettercap/bettercap/core" + "github.com/evilsocket/islazy/str" + "regexp" + "strings" +) + +var parser = regexp.MustCompile(`^([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+).*$`) + +func update() ([]Route, error) { + table = make([]Route, 0) + + output, err := core.Exec("netstat", []string{"-n", "-r"}) + if err != nil { + return nil, err + } + + for _, line := range strings.Split(output, "\n") { + if line = str.Trim(line); len(line) > 0 { + matches := parser.FindStringSubmatch(line) + if num := len(matches); num == 5 && matches[1] != "Destination" { + route := Route{ + Destination: matches[1], + Gateway: matches[2], + Flags: matches[3], + Device: matches[4], + Default: matches[1] == "default", + } + + if strings.ContainsRune(route.Destination, '.') || strings.ContainsRune(route.Gateway, '.') { + route.Type = IPv4 + } else { + route.Type = IPv6 + } + + table = append(table, route) + } + } + } + + return table, nil +} diff --git a/routing/update_linux.go b/routing/update_linux.go new file mode 100644 index 00000000..016fe8f4 --- /dev/null +++ b/routing/update_linux.go @@ -0,0 +1,45 @@ +package routing + +import ( + "github.com/bettercap/bettercap/core" + "github.com/evilsocket/islazy/str" + "regexp" + "strings" +) + +var parser = regexp.MustCompile(`^(.+)\sdev\s([^\s]+)\s(.+)$`) + +func update() ([]Route, error) { + table = make([]Route, 0) + + for ip, inet := range map[RouteType]string{IPv4: "inet", IPv6: "inet6"} { + output, err := core.Exec("ip", []string{"-f", inet, "route"}) + if err != nil { + return nil, err + } + + for _, line := range strings.Split(output, "\n") { + if line = str.Trim(line); len(line) > 0 { + matches := parser.FindStringSubmatch(line) + if num := len(matches); num == 4 { + route := Route{ + Type: ip, + Destination: matches[1], + Device: matches[2], + Flags: matches[3], + Default: strings.Index(matches[1], "default ") == 0, + } + + if idx := strings.Index(route.Destination, " via "); idx >= 0 { + route.Gateway = route.Destination[idx + len(" via "):] + route.Destination = route.Destination[:idx] + } + + table = append(table, route) + } + } + } + } + + return table, nil +} diff --git a/routing/update_windows.go b/routing/update_windows.go new file mode 100644 index 00000000..8ae28fea --- /dev/null +++ b/routing/update_windows.go @@ -0,0 +1,44 @@ +package routing + +import ( + "github.com/bettercap/bettercap/core" + "github.com/evilsocket/islazy/str" + "regexp" + "strings" +) + +var parser = regexp.MustCompile(`^.+\d+\s+([^\s]+)\s+\d+\s+(.+)$`) + +func update() ([]Route, error) { + table = make([]Route, 0) + + for ip, inet := range map[RouteType]string{IPv4: "ipv4", IPv6: "ipv6"} { + output, err := core.Exec("netsh", []string{"interface", inet, "show", "route"}) + if err != nil { + return nil, err + } + + for _, line := range strings.Split(output, "\n") { + if line = str.Trim(line); len(line) > 0 { + matches := parser.FindStringSubmatch(line) + if num := len(matches); num == 3 { + route := Route{ + Type: ip, + Destination: matches[1], + Device: matches[2], + } + + if route.Destination == "0.0.0.0/0" || route.Destination == "::/0" { + route.Default = true + route.Gateway = route.Device + route.Device = "" + } + + table = append(table, route) + } + } + } + } + + return table, nil +}