package network import ( "errors" "fmt" "net" "regexp" "strings" "github.com/bettercap/bettercap/core" "github.com/evilsocket/islazy/data" "github.com/evilsocket/islazy/str" "github.com/evilsocket/islazy/tui" "github.com/malfunkt/iprange" ) var ErrNoIfaces = errors.New("No active interfaces found.") var ErrNoGateway = errors.New("Could not detect gateway.") const ( MonitorModeAddress = "0.0.0.0" BroadcastSuffix = ".255" BroadcastMac = "ff:ff:ff:ff:ff:ff" IPv4MulticastStart = "01:00:5e:00:00:00" IPv4MulticastEnd = "01:00:5e:7f:ff:ff" ) var ( BroadcastHw = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} IPv4Validator = regexp.MustCompile(`^[0-9\.]+/?\d*$`) IPv4RangeValidator = regexp.MustCompile(`^[0-9\.\-]+/?\d*$`) MACValidator = regexp.MustCompile(`(?i)^[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}$`) // lulz this sounds like a hamburger macParser = regexp.MustCompile(`(?i)([a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2}:[a-f0-9]{1,2})`) aliasParser = regexp.MustCompile(`(?i)([a-z_][a-z_0-9]+)`) ) func IsZeroMac(mac net.HardwareAddr) bool { for _, b := range mac { if b != 0x00 { return false } } return true } func IsBroadcastMac(mac net.HardwareAddr) bool { for _, b := range mac { if b != 0xff { return false } } return true } func NormalizeMac(mac string) string { var parts []string if strings.ContainsRune(mac, '-') { parts = strings.Split(mac, "-") } else { parts = strings.Split(mac, ":") } for i, p := range parts { if len(p) < 2 { parts[i] = "0" + p } } return strings.ToLower(strings.Join(parts, ":")) } func ParseMACs(targets string) (macs []net.HardwareAddr, err error) { macs = make([]net.HardwareAddr, 0) if targets = str.Trim(targets); targets == "" { return } for _, mac := range macParser.FindAllString(targets, -1) { mac = NormalizeMac(mac) hw, err := net.ParseMAC(mac) if err != nil { return nil, fmt.Errorf("error while parsing MAC '%s': %s", mac, err) } macs = append(macs, hw) targets = strings.Replace(targets, mac, "", -1) } return } func ParseTargets(targets string, aliasMap *data.UnsortedKV) (ips []net.IP, macs []net.HardwareAddr, err error) { ips = make([]net.IP, 0) macs = make([]net.HardwareAddr, 0) if targets = str.Trim(targets); targets == "" { return } // first isolate MACs and parse them for _, mac := range macParser.FindAllString(targets, -1) { normalizedMac := NormalizeMac(mac) hw, err := net.ParseMAC(normalizedMac) if err != nil { return nil, nil, fmt.Errorf("error while parsing MAC '%s': %s", normalizedMac, err) } macs = append(macs, hw) targets = strings.Replace(targets, mac, "", -1) } targets = strings.Trim(targets, ", ") // fmt.Printf("targets=%s macs=%#v\n", targets, macs) // check and resolve aliases for _, targetAlias := range aliasParser.FindAllString(targets, -1) { found := false aliasMap.Each(func(mac, alias string) bool { if alias == targetAlias { if hw, err := net.ParseMAC(mac); err == nil { macs = append(macs, hw) targets = strings.Replace(targets, targetAlias, "", -1) found = true return true } } return false }) if !found { return nil, nil, fmt.Errorf("could not resolve alias %s", targetAlias) } } targets = strings.Trim(targets, ", ") // parse what's left if targets != "" { list, err := iprange.ParseList(targets) if err != nil { return nil, nil, fmt.Errorf("error while parsing address list '%s': %s", targets, err) } ips = list.Expand() } return } func ParseEndpoints(targets string, lan *LAN) ([]*Endpoint, error) { ips, macs, err := ParseTargets(targets, lan.Aliases()) if err != nil { return nil, err } tmp := make(map[string]*Endpoint) for _, ip := range ips { if e := lan.GetByIp(ip.String()); e != nil { tmp[e.HW.String()] = e } } for _, mac := range macs { if e, found := lan.Get(mac.String()); found { tmp[e.HW.String()] = e } } ret := make([]*Endpoint, 0) for _, e := range tmp { ret = append(ret, e) } return ret, nil } func buildEndpointFromInterface(iface net.Interface) (*Endpoint, error) { addrs, err := iface.Addrs() if err != nil { return nil, err } ifName := getInterfaceName(iface) e := NewEndpointNoResolve(MonitorModeAddress, iface.HardwareAddr.String(), ifName, 0) e.Index = iface.Index for _, a := range addrs { address := a.String() if IPv4Validator.MatchString(address) { if !strings.ContainsRune(address, '/') { // plain ip e.SetIP(address) } else { // ip/bits e.SetNetwork(address) } } else { // ipv6/xxx e.SetIPv6(address) } } return e, nil } func matchByAddress(iface net.Interface, name string) bool { ifMac := iface.HardwareAddr.String() if NormalizeMac(ifMac) == NormalizeMac(name) { return true } addrs, err := iface.Addrs() if err == nil { for _, addr := range addrs { ip := addr.String() if ip == name || strings.HasPrefix(ip, name) { return true } } } return false } func findInterfaceByName(name string, ifaces []net.Interface) (*Endpoint, error) { for _, iface := range ifaces { ifName := getInterfaceName(iface) if ifName == name || matchByAddress(iface, name) { return buildEndpointFromInterface(iface) } } return nil, fmt.Errorf("no interface matching '%s' found.", name) } func FindInterface(name string) (*Endpoint, error) { ifaces, err := net.Interfaces() if err != nil { return nil, err } name = str.Trim(name) if name != "" { return findInterfaceByName(name, ifaces) } // user did not provide an interface name, // return the first one with a valid ipv4 // address for _, iface := range ifaces { addrs, err := iface.Addrs() if err != nil { fmt.Printf("wtf of the day: %s", err) continue } for _, address := range addrs { ip := address.String() if !strings.Contains(ip, "127.0.0.1") && IPv4Validator.MatchString(ip) { return buildEndpointFromInterface(iface) } } } return nil, ErrNoIfaces } func SetWiFiRegion(region string) error { if core.HasBinary("iw") { if out, err := core.Exec("iw", []string{"reg", "set", region}); err != nil { return err } else if out != "" { return fmt.Errorf("unexpected output while setting WiFi region %s: %s", region, out) } } return nil } func ActivateInterface(name string) error { if out, err := core.Exec("ifconfig", []string{name, "up"}); err != nil { return err } else if out != "" { return fmt.Errorf("unexpected output while activating interface %s: %s", name, out) } return nil } func SetInterfaceTxPower(name string, txpower int) error { if core.HasBinary("iw") { Debug("SetInterfaceTxPower(%s, %d) iw based", name, txpower) if _, err := core.Exec("iw", []string{"dev", name, "set", "txpower", "fixed", fmt.Sprintf("%d", txpower)}); err != nil { return err } } else if core.HasBinary("iwconfig") { Debug("SetInterfaceTxPower(%s, %d) iwconfig based", name, txpower) if out, err := core.Exec("iwconfig", []string{name, "txpower", fmt.Sprintf("%d", txpower)}); err != nil { return err } else if out != "" { return fmt.Errorf("unexpected output while setting txpower to %d for interface %s: %s", txpower, name, out) } } return nil } func GatewayProvidedByUser(iface *Endpoint, gateway string) (*Endpoint, error) { if IPv4Validator.MatchString(gateway) { Debug("valid gateway ip %s", gateway) // we have the address, now we need its mac if mac, err := ArpLookup(iface.Name(), gateway, false); err != nil { return nil, err } else { Debug("gateway is %s[%s]", gateway, mac) return NewEndpoint(gateway, mac), nil } } return nil, fmt.Errorf("Provided gateway %s not a valid IPv4 address! Revert to find default gateway.", gateway) } func ColorRSSI(n int) string { // ref. https://www.metageek.com/training/resources/understanding-rssi-2.html rssi := fmt.Sprintf("%d dBm", n) if n >= -67 { rssi = tui.Green(rssi) } else if n >= -70 { rssi = tui.Dim(tui.Green(rssi)) } else if n >= -80 { rssi = tui.Yellow(rssi) } else { rssi = tui.Dim(tui.Red(rssi)) } return rssi }