bettercap/network/lan_endpoint.go
2024-09-22 15:03:06 +02:00

209 lines
4.7 KiB
Go

package network
import (
"encoding/binary"
"fmt"
"net"
"strconv"
"strings"
"time"
"github.com/evilsocket/islazy/tui"
)
type OnHostResolvedCallback func(e *Endpoint)
type Endpoint struct {
Index int `json:"-"`
IP net.IP `json:"-"`
Net *net.IPNet `json:"-"`
Net6 *net.IPNet `json:"-"`
IPv6 net.IP `json:"-"`
CIDR6 string `json:"-"`
HW net.HardwareAddr `json:"-"`
IpAddress string `json:"ipv4"`
Ip6Address string `json:"ipv6"`
SubnetBits uint32 `json:"-"`
SubnetBits6 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"`
Meta *Meta `json:"meta"`
}
func NewEndpointNoResolve(ip, mac, name string, bits uint32) *Endpoint {
mac = NormalizeMac(mac)
hw, _ := net.ParseMAC(mac)
now := time.Now()
e := &Endpoint{
IP: nil,
IpAddress: ip,
IpAddressUint32: 0,
Net: nil,
HW: hw,
SubnetBits: bits,
HwAddress: mac,
Hostname: name,
Vendor: ManufLookup(mac),
ResolvedCallback: nil,
FirstSeen: now,
LastSeen: now,
Meta: NewMeta(),
}
e.SetIP(ip)
e.SetBits(bits)
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 && len(names) > 0 {
e.Hostname = names[0]
if e.ResolvedCallback != nil {
e.ResolvedCallback(e)
}
}
}()
return e
}
func NewEndpointWithAlias(ip, mac, alias string) *Endpoint {
e := NewEndpoint(ip, mac)
e.Alias = alias
return e
}
func ip2int(ip net.IP) uint32 {
if len(ip) == 16 {
return binary.BigEndian.Uint32(ip[12:16])
}
return binary.BigEndian.Uint32(ip)
}
func (t *Endpoint) SetNetwork(netw string) {
parts := strings.Split(netw, "/")
address := parts[0]
bits, _ := strconv.Atoi(parts[1])
t.SetIP(address)
t.SetBits(uint32(bits))
}
func (t *Endpoint) SetIPv6(netw string) {
parts := strings.SplitN(netw, "/", 2)
address := parts[0]
if len(parts) > 1 {
bits6, _ := strconv.Atoi(parts[1])
t.SubnetBits6 = uint32(bits6)
t.CIDR6 = netw
_, netw, _ := net.ParseCIDR(netw)
t.Net6 = netw
}
t.IPv6 = net.ParseIP(address)
if t.IPv6 != nil {
t.Ip6Address = t.IPv6.String()
}
}
func (t *Endpoint) SetIP(ip string) {
addr := net.ParseIP(ip)
t.IP = addr
t.IpAddress = ip
t.IpAddressUint32 = ip2int(addr)
}
func (t *Endpoint) SetBits(bits uint32) {
t.SubnetBits = bits
_, netw, _ := net.ParseCIDR(t.CIDR())
t.Net = netw
}
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) IsMonitor() bool {
return t.IpAddress == MonitorModeAddress
}
func (t *Endpoint) String() string {
ipPart := fmt.Sprintf("%s : ", t.IpAddress)
if t.IpAddress == MonitorModeAddress {
ipPart = ""
}
if t.HwAddress == "" {
return t.IpAddress
} else if t.Vendor == "" {
return fmt.Sprintf("%s%s", ipPart, t.HwAddress)
} else if t.Hostname == "" && t.Alias == "" {
return fmt.Sprintf("%s%s (%s)", ipPart, t.HwAddress, t.Vendor)
}
name := t.Hostname
if t.Alias != "" {
name = t.Alias
}
return fmt.Sprintf("%s%s (%s) - %s", ipPart, t.HwAddress, t.Vendor, tui.Bold(name))
}
func (t *Endpoint) ShortString() string {
parts := []string{
t.IpAddress,
}
if t.Vendor != "" {
parts = append(parts, tui.Dim(fmt.Sprintf("(%s)", t.Vendor)))
}
name := t.Hostname
if t.Alias != "" {
name = t.Alias
}
if name != "" {
parts = append(parts, tui.Bold(name))
}
return strings.Join(parts, " ")
}
func (t *Endpoint) OnMeta(meta map[string]string) {
host := ""
for k, v := range meta {
// simple heuristics to get the longest candidate name
if strings.HasSuffix(k, ":hostname") && len(v) > len(host) {
host = v
} else if k == "mdns:md" && len(v) > len(host) {
host = v
}
t.Meta.Set(k, v)
}
if t.Hostname == "" {
t.Hostname = host
}
}