mirror of
https://github.com/bettercap/bettercap
synced 2025-08-21 05:53:20 -07:00
refact: renamed package net to network to avoid collision with golang net package.
This commit is contained in:
parent
21236c257e
commit
48d27f274a
28 changed files with 298 additions and 298 deletions
79
network/arp.go
Normal file
79
network/arp.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/evilsocket/bettercap-ng/core"
|
||||
)
|
||||
|
||||
type ArpTable map[string]string
|
||||
|
||||
var (
|
||||
arpWasParsed = false
|
||||
arpLock = &sync.Mutex{}
|
||||
arpTable = make(ArpTable)
|
||||
)
|
||||
|
||||
func ArpUpdate(iface string) (ArpTable, error) {
|
||||
arpLock.Lock()
|
||||
defer arpLock.Unlock()
|
||||
|
||||
// Signal we parsed the ARP table at least once.
|
||||
arpWasParsed = true
|
||||
|
||||
// Run "arp -an" (darwin) or "ip neigh" (linux) and parse the output
|
||||
output, err := core.Exec(ArpCmd, ArpCmdOpts)
|
||||
if err != nil {
|
||||
return arpTable, err
|
||||
}
|
||||
|
||||
newTable := make(ArpTable)
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
m := ArpTableParser.FindStringSubmatch(line)
|
||||
if len(m) == ArpTableTokens {
|
||||
ipIndex := ArpTableTokenIndex[0]
|
||||
hwIndex := ArpTableTokenIndex[1]
|
||||
ifIndex := ArpTableTokenIndex[2]
|
||||
|
||||
address := m[ipIndex]
|
||||
mac := m[hwIndex]
|
||||
ifname := iface
|
||||
|
||||
if ifIndex != -1 {
|
||||
ifname = m[ifIndex]
|
||||
}
|
||||
|
||||
if ifname == iface {
|
||||
newTable[address] = mac
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arpTable = newTable
|
||||
|
||||
return arpTable, nil
|
||||
}
|
||||
|
||||
func ArpLookup(iface string, address string, refresh bool) (string, error) {
|
||||
// Refresh ARP table if first run or if a force refresh has been instructed.
|
||||
if ArpParsed() == false || refresh == true {
|
||||
if _, err := ArpUpdate(iface); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup the hardware address of this ip.
|
||||
if mac, found := arpTable[address]; found == true {
|
||||
return mac, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Could not find mac for %s", address)
|
||||
}
|
||||
|
||||
func ArpParsed() bool {
|
||||
arpLock.Lock()
|
||||
defer arpLock.Unlock()
|
||||
return arpWasParsed
|
||||
}
|
9
network/arp_parser_darwin.go
Normal file
9
network/arp_parser_darwin.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package network
|
||||
|
||||
import "regexp"
|
||||
|
||||
var ArpTableParser = regexp.MustCompile("^[^\\d\\.]+([\\d\\.]+).+\\s+([a-f0-9:]{11,17})\\s+on\\s+([^\\s]+)\\s+.+$")
|
||||
var ArpTableTokens = 4
|
||||
var ArpTableTokenIndex = []int{1, 2, 3}
|
||||
var ArpCmd = "arp"
|
||||
var ArpCmdOpts = []string{"-a", "-n"}
|
9
network/arp_parser_linux.go
Normal file
9
network/arp_parser_linux.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package network
|
||||
|
||||
import "regexp"
|
||||
|
||||
var ArpTableParser = regexp.MustCompile("^([\\d\\.]+)\\s+dev\\s+(\\w+)\\s+\\w+\\s+([a-f0-9:]{17})\\s+\\w+$")
|
||||
var ArpTableTokens = 4
|
||||
var ArpTableTokenIndex = []int{1, 3, 2}
|
||||
var ArpCmd = "ip"
|
||||
var ArpCmdOpts = []string{"neigh"}
|
9
network/arp_parser_windows.go
Normal file
9
network/arp_parser_windows.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package network
|
||||
|
||||
import "regexp"
|
||||
|
||||
var ArpTableParser = regexp.MustCompile("^[^\\d\\.]+([\\d\\.]+).+\\s+([a-f0-9\\-]{11,17})\\s+.+$")
|
||||
var ArpTableTokens = 3
|
||||
var ArpTableTokenIndex = []int{1, 2, -1}
|
||||
var ArpCmd = "arp"
|
||||
var ArpCmdOpts = []string{"-a"}
|
2
network/doc.go
Normal file
2
network/doc.go
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Package network contains network specific code ... lol.
|
||||
package network
|
106
network/endpoint.go
Normal file
106
network/endpoint.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/evilsocket/bettercap-ng/core"
|
||||
)
|
||||
|
||||
type OnHostResolvedCallback func(e *Endpoint)
|
||||
type Endpoint struct {
|
||||
Index int `json:"-"`
|
||||
IP net.IP `json:"-"`
|
||||
Net *net.IPNet `json:"-"`
|
||||
IPv6 net.IP `json:"-"`
|
||||
HW net.HardwareAddr `json:"-"`
|
||||
IpAddress string `json:"ipv4"`
|
||||
Ip6Address string `json:"ipv6"`
|
||||
SubnetBits uint32 `json:"-"`
|
||||
IpAddressUint32 uint32 `json:"-"`
|
||||
HwAddress string `json:"mac"`
|
||||
Hostname string `json:"hostname"`
|
||||
Alias string `json:"alias"`
|
||||
Vendor string `json:"vendor"`
|
||||
ResolvedCallback OnHostResolvedCallback `json:"-"`
|
||||
FirstSeen time.Time `json:"first_seen"`
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
}
|
||||
|
||||
func ip2int(ip net.IP) uint32 {
|
||||
if len(ip) == 16 {
|
||||
return binary.BigEndian.Uint32(ip[12:16])
|
||||
}
|
||||
return binary.BigEndian.Uint32(ip)
|
||||
}
|
||||
|
||||
func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
|
||||
addr := net.ParseIP(ip)
|
||||
mac = NormalizeMac(mac)
|
||||
hw, _ := net.ParseMAC(mac)
|
||||
now := time.Now()
|
||||
|
||||
e := &Endpoint{
|
||||
IP: addr,
|
||||
IpAddress: ip,
|
||||
IpAddressUint32: ip2int(addr),
|
||||
Net: nil,
|
||||
HW: hw,
|
||||
SubnetBits: bits,
|
||||
HwAddress: mac,
|
||||
Hostname: name,
|
||||
Vendor: OuiLookup(mac),
|
||||
ResolvedCallback: nil,
|
||||
FirstSeen: now,
|
||||
LastSeen: now,
|
||||
}
|
||||
|
||||
_, netw, _ := net.ParseCIDR(e.CIDR())
|
||||
e.Net = netw
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func NewEndpoint(ip, mac string) *Endpoint {
|
||||
e := NewEndpointNoResolve(ip, mac, "", 0)
|
||||
|
||||
// start resolver goroutine
|
||||
go func() {
|
||||
if names, err := net.LookupAddr(e.IpAddress); err == nil {
|
||||
e.Hostname = names[0]
|
||||
if e.ResolvedCallback != nil {
|
||||
e.ResolvedCallback(e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (t *Endpoint) Name() string {
|
||||
return t.Hostname
|
||||
}
|
||||
|
||||
func (t *Endpoint) CIDR() string {
|
||||
shift := 32 - t.SubnetBits
|
||||
address := t.IpAddressUint32
|
||||
ip := make(net.IP, 4)
|
||||
|
||||
binary.BigEndian.PutUint32(ip, (address>>shift)<<shift)
|
||||
|
||||
return fmt.Sprintf("%s/%d", ip.String(), t.SubnetBits)
|
||||
}
|
||||
|
||||
func (t *Endpoint) String() string {
|
||||
if t.HwAddress == "" {
|
||||
return t.IpAddress
|
||||
} else if t.Vendor == "" {
|
||||
return fmt.Sprintf("%s : %s", t.IpAddress, t.HwAddress)
|
||||
} else if t.Hostname == "" {
|
||||
return fmt.Sprintf("%s : %s ( %s )", t.IpAddress, t.HwAddress, t.Vendor)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s : %s ( %s ) - %s", t.IpAddress, t.HwAddress, t.Vendor, core.Bold(t.Hostname))
|
||||
}
|
149
network/net.go
Normal file
149
network/net.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/evilsocket/bettercap-ng/core"
|
||||
)
|
||||
|
||||
const MonitorModeAddress = "0.0.0.0"
|
||||
|
||||
var (
|
||||
IPv4Validator = regexp.MustCompile("^[0-9\\.]+/?\\d*$")
|
||||
)
|
||||
|
||||
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.Join(parts, ":")
|
||||
}
|
||||
|
||||
func FindInterface(name string) (*Endpoint, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
ifName := getInterfaceName(iface)
|
||||
mac := iface.HardwareAddr.String()
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
continue
|
||||
}
|
||||
nAddrs := len(addrs)
|
||||
|
||||
/*
|
||||
* If no interface has been specified, return the first active
|
||||
* one with at least an ip address, otherwise just the match
|
||||
* whatever it has, in order to also consider monitor interfaces
|
||||
* if passed explicitly.
|
||||
*/
|
||||
doCheck := false
|
||||
if name == "" && ifName != "lo" && ifName != "lo0" && nAddrs > 0 {
|
||||
doCheck = true
|
||||
} else if ifName == name {
|
||||
doCheck = true
|
||||
}
|
||||
|
||||
// Also search by ip if needed.
|
||||
if name != "" {
|
||||
for _, a := range addrs {
|
||||
if a.String() == name || strings.HasPrefix(a.String(), name) {
|
||||
doCheck = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if doCheck {
|
||||
var e *Endpoint = nil
|
||||
// interface is in monitor mode (or it's just down and the user is dumb)
|
||||
if nAddrs == 0 {
|
||||
e = NewEndpointNoResolve(MonitorModeAddress, mac, ifName, 0)
|
||||
} else {
|
||||
// For every address of the interface.
|
||||
for _, addr := range addrs {
|
||||
ip := addr.String()
|
||||
// Make sure this is an IPv4 address.
|
||||
if IPv4Validator.MatchString(ip) {
|
||||
if strings.IndexRune(ip, '/') == -1 {
|
||||
// plain ip
|
||||
e = NewEndpointNoResolve(ip, mac, ifName, 0)
|
||||
} else {
|
||||
// ip/bits
|
||||
parts := strings.Split(ip, "/")
|
||||
ip_part := parts[0]
|
||||
bits, err := strconv.Atoi(parts[1])
|
||||
if err == nil {
|
||||
e = NewEndpointNoResolve(ip_part, mac, ifName, uint32(bits))
|
||||
}
|
||||
}
|
||||
} else if e != nil {
|
||||
parts := strings.SplitN(ip, "/", 2)
|
||||
e.IPv6 = net.ParseIP(parts[0])
|
||||
if e.IPv6 != nil {
|
||||
e.Ip6Address = e.IPv6.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
if len(e.HW) == 0 {
|
||||
return nil, fmt.Errorf("Could not detect interface hardware address.")
|
||||
}
|
||||
e.Index = iface.Index
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("Could not find default network interface.")
|
||||
} else {
|
||||
return nil, fmt.Errorf("Could not find interface '%s'.", name)
|
||||
}
|
||||
}
|
||||
|
||||
func FindGateway(iface *Endpoint) (*Endpoint, error) {
|
||||
output, err := core.Exec(IPv4RouteCmd, IPv4RouteCmdOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
m := IPv4RouteParser.FindStringSubmatch(line)
|
||||
if len(m) == IPv4RouteTokens {
|
||||
return IPv4RouteIsGateway(iface.Name(), m, func(gateway string) (*Endpoint, error) {
|
||||
if gateway == iface.IpAddress {
|
||||
return iface, nil
|
||||
} else {
|
||||
// we have the address, now we need its mac
|
||||
mac, err := ArpLookup(iface.Name(), gateway, false)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
}
|
||||
return NewEndpoint(gateway, mac), nil
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Could not detect the gateway.")
|
||||
}
|
28
network/net_darwin.go
Normal file
28
network/net_darwin.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var IPv4RouteParser = regexp.MustCompile("^([a-z]+)+\\s+(\\d+\\.+\\d+.\\d.+\\d)+\\s+([a-zA-z]+)+\\s+(\\d+)+\\s+(\\d+)+\\s+([a-zA-Z]+\\d+)$")
|
||||
var IPv4RouteTokens = 7
|
||||
var IPv4RouteCmd = "netstat"
|
||||
var IPv4RouteCmdOpts = []string{"-n", "-r"}
|
||||
|
||||
func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) {
|
||||
ifname2 := tokens[6]
|
||||
flags := tokens[3]
|
||||
|
||||
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
|
||||
}
|
28
network/net_linux.go
Normal file
28
network/net_linux.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// only matches gateway lines
|
||||
var IPv4RouteParser = regexp.MustCompile("^(default|[0-9\\.]+)\\svia\\s([0-9\\.]+)\\sdev\\s(\\w+)\\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
|
||||
}
|
59
network/net_windows.go
Normal file
59
network/net_windows.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"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
|
||||
* had to do in Windows ... tnx Microsoft <3
|
||||
*
|
||||
* FIXME: Just to be clear *THIS IS SHIT*. Please someone test, find a more
|
||||
* elegant solution and refactor ... i'm seriously tired of this.
|
||||
*/
|
||||
|
||||
func areTheSame(iface net.Interface, piface pcap.Interface) bool {
|
||||
if addrs, err := iface.Addrs(); err == nil {
|
||||
for _, ia := range addrs {
|
||||
for _, ib := range piface.Addresses {
|
||||
if ia.String() == ib.IP.String() || strings.HasPrefix(ia.String(), ib.IP.String()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getInterfaceName(iface net.Interface) string {
|
||||
devs, err := pcap.FindAllDevs()
|
||||
if err != nil {
|
||||
return iface.Name
|
||||
}
|
||||
|
||||
for _, dev := range devs {
|
||||
if areTheSame(iface, dev) {
|
||||
return dev.Name
|
||||
}
|
||||
}
|
||||
|
||||
return iface.Name
|
||||
}
|
22930
network/oui.dat
Normal file
22930
network/oui.dat
Normal file
File diff suppressed because it is too large
Load diff
50
network/oui.go
Normal file
50
network/oui.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/evilsocket/bettercap-ng/core"
|
||||
)
|
||||
|
||||
var (
|
||||
oui = make(map[string]string)
|
||||
)
|
||||
|
||||
func OuiInit() {
|
||||
bytes, err := Asset("network/oui.dat")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
data := string(bytes)
|
||||
lines := strings.Split(data, "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
line = core.Trim(line)
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, " ", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := strings.ToLower(core.Trim(parts[0]))
|
||||
vendor := core.Trim(parts[1])
|
||||
|
||||
oui[prefix] = vendor
|
||||
}
|
||||
}
|
||||
|
||||
func OuiLookup(mac string) string {
|
||||
octects := strings.Split(mac, ":")
|
||||
if len(octects) > 3 {
|
||||
prefix := octects[0] + octects[1] + octects[2]
|
||||
|
||||
if vendor, found := oui[prefix]; found == true {
|
||||
return vendor
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
237
network/oui_compiled.go
Normal file
237
network/oui_compiled.go
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue