mirror of
https://github.com/bettercap/bettercap
synced 2025-08-20 05:23:19 -07:00
Merge branch 'master' of github.com:evilsocket/bettercap-ng
This commit is contained in:
commit
8825ecb8db
25 changed files with 343 additions and 166 deletions
55
README.md
55
README.md
|
@ -278,6 +278,61 @@ function onResponse(req, res) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### caplets/airmon.cap
|
||||||
|
|
||||||
|
Put a wifi interface in monitor mode and listen for frames in order to detect WiF access points and clients.
|
||||||
|
|
||||||
|
```
|
||||||
|
set $ {by}{fw}{env.iface.name}{reset} {bold}» {reset}
|
||||||
|
set ticker.commands clear; wifi.show
|
||||||
|
|
||||||
|
# uncomment to disable channel hopping
|
||||||
|
# set wifi.recon.channel 1
|
||||||
|
|
||||||
|
wifi.recon on
|
||||||
|
ticker on
|
||||||
|
events.clear
|
||||||
|
clear
|
||||||
|
```
|
||||||
|
|
||||||
|
#### caplets/wpa\_handshake.cap
|
||||||
|
|
||||||
|
Use various modules to inject wifi frames performing a deauthentication attack, while a sniffer is waiting for WPA handshakes.
|
||||||
|
|
||||||
|
```
|
||||||
|
# swag prompt for wifi
|
||||||
|
set $ {by}{fw}{env.iface.name}{reset} {bold}» {reset}
|
||||||
|
|
||||||
|
# Sniff EAPOL frames ( WPA handshakes ) and save them to a pcap file.
|
||||||
|
set net.sniff.verbose true
|
||||||
|
set net.sniff.filter ether proto 0x888e
|
||||||
|
set net.sniff.output wpa.pcap
|
||||||
|
net.sniff on
|
||||||
|
|
||||||
|
# since we need to capture the handshake, we can't hop
|
||||||
|
# through channels but we need to stick to the one we're
|
||||||
|
# interested in otherwise the sniffer might lose packets.
|
||||||
|
set wifi.recon.channel 1
|
||||||
|
|
||||||
|
wifi.recon on
|
||||||
|
|
||||||
|
# uncomment to recon clients of a specific AP given its BSSID
|
||||||
|
# wifi.recon DE:AD:BE:EF:DE:AD
|
||||||
|
|
||||||
|
events.clear
|
||||||
|
clear
|
||||||
|
|
||||||
|
# now just deauth clients and wait ^_^
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# wifi.deauth AP-BSSID-HERE
|
||||||
|
#
|
||||||
|
# This will deauth every client for this specific access point,
|
||||||
|
# you can put it as ticker.commands to have the ticker module
|
||||||
|
# periodically deauth clients :D
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
`bettercap` and `bettercap-ng` are made with ♥ by [Simone Margaritelli](https://www.evilsocket.net/) and they're released under the GPL 3 license.
|
`bettercap` and `bettercap-ng` are made with ♥ by [Simone Margaritelli](https://www.evilsocket.net/) and they're released under the GPL 3 license.
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# let's add some api :D
|
||||||
|
include caplets/rest-api.cap
|
||||||
|
|
||||||
set $ {by}{fw}{env.iface.name}{reset} {bold}» {reset}
|
set $ {by}{fw}{env.iface.name}{reset} {bold}» {reset}
|
||||||
set ticker.commands clear; wifi.show
|
set ticker.commands clear; wifi.show
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,10 @@ net.sniff on
|
||||||
# interested in otherwise the sniffer might lose packets.
|
# interested in otherwise the sniffer might lose packets.
|
||||||
set wifi.recon.channel 1
|
set wifi.recon.channel 1
|
||||||
|
|
||||||
|
# this will enable the wifi recon
|
||||||
|
set ticker.commands clear; wifi.show
|
||||||
wifi.recon on
|
wifi.recon on
|
||||||
|
ticker on
|
||||||
|
|
||||||
# uncomment to recon clients of a specific AP given its BSSID
|
# uncomment to recon clients of a specific AP given its BSSID
|
||||||
# wifi.recon DE:AD:BE:EF:DE:AD
|
# wifi.recon DE:AD:BE:EF:DE:AD
|
||||||
|
@ -29,3 +32,7 @@ clear
|
||||||
# This will deauth every client for this specific access point,
|
# This will deauth every client for this specific access point,
|
||||||
# you can put it as ticker.commands to have the ticker module
|
# you can put it as ticker.commands to have the ticker module
|
||||||
# periodically deauth clients :D
|
# periodically deauth clients :D
|
||||||
|
#
|
||||||
|
# For more options `help wifi.recon`.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ type PfFirewall struct {
|
||||||
iface *network.Endpoint
|
iface *network.Endpoint
|
||||||
filename string
|
filename string
|
||||||
forwarding bool
|
forwarding bool
|
||||||
|
enabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Make(iface *network.Endpoint) FirewallManager {
|
func Make(iface *network.Endpoint) FirewallManager {
|
||||||
|
@ -29,6 +30,7 @@ func Make(iface *network.Endpoint) FirewallManager {
|
||||||
iface: iface,
|
iface: iface,
|
||||||
filename: pfFilePath,
|
filename: pfFilePath,
|
||||||
forwarding: false,
|
forwarding: false,
|
||||||
|
enabled: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
firewall.forwarding = firewall.IsForwardingEnabled()
|
firewall.forwarding = firewall.IsForwardingEnabled()
|
||||||
|
@ -108,7 +110,8 @@ func (f PfFirewall) generateRule(r *Redirection) string {
|
||||||
r.Interface, r.Protocol, src_a, r.SrcPort, dst_a, r.DstPort)
|
r.Interface, r.Protocol, src_a, r.SrcPort, dst_a, r.DstPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f PfFirewall) enable(enabled bool) {
|
func (f *PfFirewall) enable(enabled bool) {
|
||||||
|
f.enabled = enabled
|
||||||
if enabled {
|
if enabled {
|
||||||
core.Exec("pfctl", []string{"-e"})
|
core.Exec("pfctl", []string{"-e"})
|
||||||
} else {
|
} else {
|
||||||
|
@ -165,11 +168,9 @@ func (f PfFirewall) EnableRedirection(r *Redirection, enabled bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f PfFirewall) Restore() {
|
func (f PfFirewall) Restore() {
|
||||||
err := f.EnableForwarding(f.forwarding)
|
f.EnableForwarding(f.forwarding)
|
||||||
if err != nil {
|
if f.enabled {
|
||||||
fmt.Fprintf(os.Stderr, "%s", err)
|
f.enable(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.enable(false)
|
|
||||||
os.Remove(f.filename)
|
os.Remove(f.filename)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,6 @@ type APIResponse struct {
|
||||||
|
|
||||||
func SafeBind(c *gin.Context, obj interface{}) error {
|
func SafeBind(c *gin.Context, obj interface{}) error {
|
||||||
decoder := json.NewDecoder(io.LimitReader(c.Request.Body, 100*1024))
|
decoder := json.NewDecoder(io.LimitReader(c.Request.Body, 100*1024))
|
||||||
if binding.EnableDecoderUseNumber {
|
|
||||||
decoder.UseNumber()
|
|
||||||
}
|
|
||||||
if err := decoder.Decode(obj); err != nil {
|
if err := decoder.Decode(obj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,8 +154,8 @@ func (s *DHCP6Spoofer) dhcpAdvertise(pkt gopacket.Packet, solicit dhcp6.Packet,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip net.IP
|
var ip net.IP
|
||||||
if t, found := s.Session.Lan.Hosts[target.String()]; found == true {
|
if h, found := s.Session.Lan.Get(target.String()); found == true {
|
||||||
ip = t.IP
|
ip = h.IP
|
||||||
} else {
|
} else {
|
||||||
log.Warning("Address %s not known, using random identity association address.", target.String())
|
log.Warning("Address %s not known, using random identity association address.", target.String())
|
||||||
rand.Read(ip)
|
rand.Read(ip)
|
||||||
|
@ -312,8 +312,8 @@ func (s *DHCP6Spoofer) dhcpReply(toType string, pkt gopacket.Packet, req dhcp6.P
|
||||||
addr = net.IP(raw[0])
|
addr = net.IP(raw[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, found := s.Session.Lan.Hosts[target.String()]; found == true {
|
if h, found := s.Session.Lan.Get(target.String()); found == true {
|
||||||
log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), t)
|
log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), h)
|
||||||
} else {
|
} else {
|
||||||
log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), target)
|
log.Info("[%s] IPv6 address %s is now assigned to %s", core.Green("dhcp6"), addr.String(), target)
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (s *DNSSpoofer) dnsReply(pkt gopacket.Packet, peth *layers.Ethernet, pudp *
|
||||||
redir := fmt.Sprintf("(->%s)", s.Address)
|
redir := fmt.Sprintf("(->%s)", s.Address)
|
||||||
who := target.String()
|
who := target.String()
|
||||||
|
|
||||||
if t, found := s.Session.Lan.Hosts[target.String()]; found == true {
|
if t, found := s.Session.Lan.Get(target.String()); found == true {
|
||||||
who = t.String()
|
who = t.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,33 @@ func (s EventsStream) viewLogEvent(e session.Event) {
|
||||||
e.Data.(session.LogMessage).Message)
|
e.Data.(session.LogMessage).Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s EventsStream) viewStationEvent(e session.Event) {
|
||||||
|
st := e.Data.(*network.Station)
|
||||||
|
vend := ""
|
||||||
|
if st.Vendor != "" {
|
||||||
|
vend = fmt.Sprintf(" (%s)", st.Vendor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Tag == "wifi.station.new" {
|
||||||
|
fmt.Printf("[%s] WiFi station %s detected as %s%s.\n",
|
||||||
|
e.Time.Format(eventTimeFormat),
|
||||||
|
core.Bold(st.ESSID()),
|
||||||
|
core.Green(st.BSSID()),
|
||||||
|
vend)
|
||||||
|
} else if e.Tag == "wifi.station.lost" {
|
||||||
|
fmt.Printf("[%s] WiFi station %s (%s) lost.\n",
|
||||||
|
e.Time.Format(eventTimeFormat),
|
||||||
|
core.Red(st.ESSID()),
|
||||||
|
st.BSSID())
|
||||||
|
} else {
|
||||||
|
fmt.Printf("[%s] [%s] %s\n",
|
||||||
|
e.Time.Format(eventTimeFormat),
|
||||||
|
core.Green(e.Tag),
|
||||||
|
st)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (s EventsStream) viewEndpointEvent(e session.Event) {
|
func (s EventsStream) viewEndpointEvent(e session.Event) {
|
||||||
t := e.Data.(*network.Endpoint)
|
t := e.Data.(*network.Endpoint)
|
||||||
vend := ""
|
vend := ""
|
||||||
|
@ -74,6 +101,8 @@ func (s *EventsStream) View(e session.Event, refresh bool) {
|
||||||
s.viewLogEvent(e)
|
s.viewLogEvent(e)
|
||||||
} else if strings.HasPrefix(e.Tag, "endpoint.") {
|
} else if strings.HasPrefix(e.Tag, "endpoint.") {
|
||||||
s.viewEndpointEvent(e)
|
s.viewEndpointEvent(e)
|
||||||
|
} else if strings.HasPrefix(e.Tag, "wifi.station.") {
|
||||||
|
s.viewStationEvent(e)
|
||||||
} else if strings.HasPrefix(e.Tag, "mod.") {
|
} else if strings.HasPrefix(e.Tag, "mod.") {
|
||||||
s.viewModuleEvent(e)
|
s.viewModuleEvent(e)
|
||||||
} else if strings.HasPrefix(e.Tag, "net.sniff.") {
|
} else if strings.HasPrefix(e.Tag, "net.sniff.") {
|
||||||
|
|
|
@ -45,7 +45,7 @@ func LoadProxyScriptSource(path, source string, sess *session.Session) (err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// define session pointer
|
// define session pointer
|
||||||
err = s.VM.Set("env", sess.Env.Storage)
|
err = s.VM.Set("env", sess.Env.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error while defining environment: %s", err)
|
log.Error("Error while defining environment: %s", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/evilsocket/bettercap-ng/log"
|
"github.com/evilsocket/bettercap-ng/log"
|
||||||
|
"github.com/evilsocket/bettercap-ng/network"
|
||||||
"github.com/evilsocket/bettercap-ng/session"
|
"github.com/evilsocket/bettercap-ng/session"
|
||||||
|
|
||||||
"github.com/malfunkt/iprange"
|
"github.com/malfunkt/iprange"
|
||||||
|
@ -80,6 +81,11 @@ func (p *Prober) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.SetRunning(true, func() {
|
return p.SetRunning(true, func() {
|
||||||
|
if p.Session.Interface.IpAddress == network.MonitorModeAddress {
|
||||||
|
log.Info("Interface is in monitor mode, skipping net.probe")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
list, err := iprange.Parse(p.Session.Interface.CIDR())
|
list, err := iprange.Parse(p.Session.Interface.CIDR())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("%s", err)
|
log.Fatal("%s", err)
|
||||||
|
|
|
@ -71,11 +71,12 @@ func (d Discovery) Author() string {
|
||||||
func (d *Discovery) runDiff(cache network.ArpTable) {
|
func (d *Discovery) runDiff(cache network.ArpTable) {
|
||||||
// check for endpoints who disappeared
|
// check for endpoints who disappeared
|
||||||
var rem network.ArpTable = make(network.ArpTable)
|
var rem network.ArpTable = make(network.ArpTable)
|
||||||
for mac, t := range d.Session.Lan.Hosts {
|
|
||||||
|
d.Session.Lan.EachHost(func(mac string, e *network.Endpoint) {
|
||||||
if _, found := cache[mac]; found == false {
|
if _, found := cache[mac]; found == false {
|
||||||
rem[mac] = t.IpAddress
|
rem[mac] = e.IpAddress
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
for mac, ip := range rem {
|
for mac, ip := range rem {
|
||||||
d.Session.Lan.Remove(ip, mac)
|
d.Session.Lan.Remove(ip, mac)
|
||||||
|
|
28
modules/net_sniff_dot11.go
Normal file
28
modules/net_sniff_dot11.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dot11Parser(radiotap *layers.RadioTap, dot11 *layers.Dot11, pkt gopacket.Packet, verbose bool) {
|
||||||
|
NewSnifferEvent(
|
||||||
|
pkt.Metadata().Timestamp,
|
||||||
|
"802.11",
|
||||||
|
"-",
|
||||||
|
"-",
|
||||||
|
SniffData{
|
||||||
|
"Size": len(pkt.Data()),
|
||||||
|
},
|
||||||
|
"%s %s proto=%d a1=%s a2=%s a3=%s a4=%s seqn=%d frag=%d",
|
||||||
|
dot11.Type,
|
||||||
|
dot11.Flags,
|
||||||
|
dot11.Proto,
|
||||||
|
dot11.Address1,
|
||||||
|
dot11.Address2,
|
||||||
|
dot11.Address3,
|
||||||
|
dot11.Address4,
|
||||||
|
dot11.SequenceNumber,
|
||||||
|
dot11.FragmentNumber,
|
||||||
|
).Push()
|
||||||
|
}
|
|
@ -86,22 +86,6 @@ func unkParser(ip *layers.IPv4, pkt gopacket.Packet, verbose bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dot11Parser(radiotap *layers.RadioTap, dot11 *layers.Dot11, pkt gopacket.Packet, verbose bool) {
|
|
||||||
if verbose == true {
|
|
||||||
NewSnifferEvent(
|
|
||||||
pkt.Metadata().Timestamp,
|
|
||||||
"802.11",
|
|
||||||
"-",
|
|
||||||
"-",
|
|
||||||
SniffData{
|
|
||||||
"Size": len(pkt.Data()),
|
|
||||||
},
|
|
||||||
"%v",
|
|
||||||
dot11,
|
|
||||||
).Push()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mainParser(pkt gopacket.Packet, verbose bool) bool {
|
func mainParser(pkt gopacket.Packet, verbose bool) bool {
|
||||||
// simple networking sniffing mode?
|
// simple networking sniffing mode?
|
||||||
nlayer := pkt.NetworkLayer()
|
nlayer := pkt.NetworkLayer()
|
||||||
|
|
|
@ -23,8 +23,7 @@ func vIP(ip net.IP) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
address := ip.String()
|
address := ip.String()
|
||||||
host := session.I.Lan.Get(address)
|
host := session.I.Lan.GetByIp(address)
|
||||||
|
|
||||||
if host != nil {
|
if host != nil {
|
||||||
if host.Hostname != "" {
|
if host.Hostname != "" {
|
||||||
return host.Hostname
|
return host.Hostname
|
||||||
|
|
|
@ -322,7 +322,7 @@ func (w *WiFiRecon) startDeauth(apMac net.HardwareAddr, clMac net.HardwareAddr)
|
||||||
} else {
|
} else {
|
||||||
log.Info("Deauthing clients from AP %s ...", apMac.String())
|
log.Info("Deauthing clients from AP %s ...", apMac.String())
|
||||||
// deauth every authenticated client
|
// deauth every authenticated client
|
||||||
for _, station := range w.Session.WiFi.Stations {
|
for _, station := range w.Session.WiFi.List() {
|
||||||
if station.IsAP == false {
|
if station.IsAP == false {
|
||||||
w.sendDeauthPacket(apMac, station.HW)
|
w.sendDeauthPacket(apMac, station.HW)
|
||||||
}
|
}
|
||||||
|
|
85
network/aliases.go
Normal file
85
network/aliases.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/evilsocket/bettercap-ng/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fileName, _ = core.ExpandPath("~/bettercap.aliases")
|
||||||
|
|
||||||
|
type Aliases struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadAliases() (err error, aliases *Aliases) {
|
||||||
|
aliases = &Aliases{
|
||||||
|
data: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
if core.Exists(fileName) {
|
||||||
|
var file *os.File
|
||||||
|
|
||||||
|
file, err = os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
parts := strings.SplitN(line, " ", 2)
|
||||||
|
mac := core.Trim(parts[0])
|
||||||
|
alias := core.Trim(parts[1])
|
||||||
|
aliases.data[mac] = alias
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aliases) saveUnlocked() error {
|
||||||
|
data := ""
|
||||||
|
for mac, alias := range a.data {
|
||||||
|
data += fmt.Sprintf("%s %s\n", mac, alias)
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(fileName, []byte(data), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aliases) Save() error {
|
||||||
|
a.Lock()
|
||||||
|
defer a.Unlock()
|
||||||
|
|
||||||
|
return a.saveUnlocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aliases) Get(mac string) string {
|
||||||
|
a.Lock()
|
||||||
|
defer a.Unlock()
|
||||||
|
|
||||||
|
if alias, found := a.data[mac]; found == true {
|
||||||
|
return alias
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Aliases) Set(mac, alias string) error {
|
||||||
|
a.Lock()
|
||||||
|
defer a.Unlock()
|
||||||
|
|
||||||
|
if alias != "" {
|
||||||
|
a.data[mac] = alias
|
||||||
|
} else {
|
||||||
|
delete(a.data, mac)
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.saveUnlocked()
|
||||||
|
}
|
169
network/lan.go
169
network/lan.go
|
@ -1,18 +1,13 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/evilsocket/bettercap-ng/core"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const LANDefaultTTL = 10
|
const LANDefaultttl = 10
|
||||||
const LANAliasesFile = "~/bettercap.aliases"
|
const LANAliasesFile = "~/bettercap.aliases"
|
||||||
|
|
||||||
type EndpointNewCallback func(e *Endpoint)
|
type EndpointNewCallback func(e *Endpoint)
|
||||||
|
@ -21,36 +16,54 @@ type EndpointLostCallback func(e *Endpoint)
|
||||||
type LAN struct {
|
type LAN struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
Interface *Endpoint
|
Hosts map[string]*Endpoint `json:"hosts"`
|
||||||
Gateway *Endpoint
|
iface *Endpoint
|
||||||
Hosts map[string]*Endpoint
|
gateway *Endpoint
|
||||||
TTL map[string]uint
|
ttl map[string]uint
|
||||||
Aliases map[string]string
|
aliases *Aliases
|
||||||
|
|
||||||
newCb EndpointNewCallback
|
newCb EndpointNewCallback
|
||||||
lostCb EndpointLostCallback
|
lostCb EndpointLostCallback
|
||||||
aliasesFileName string
|
aliasesFileName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLAN(iface, gateway *Endpoint, newcb EndpointNewCallback, lostcb EndpointLostCallback) *LAN {
|
func NewLAN(iface, gateway *Endpoint, newcb EndpointNewCallback, lostcb EndpointLostCallback) *LAN {
|
||||||
lan := &LAN{
|
err, aliases := LoadAliases()
|
||||||
Interface: iface,
|
if err != nil {
|
||||||
Gateway: gateway,
|
fmt.Printf("%s\n", err)
|
||||||
Hosts: make(map[string]*Endpoint),
|
|
||||||
TTL: make(map[string]uint),
|
|
||||||
Aliases: make(map[string]string),
|
|
||||||
newCb: newcb,
|
|
||||||
lostCb: lostcb,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lan.aliasesFileName, _ = core.ExpandPath(LANAliasesFile)
|
return &LAN{
|
||||||
if core.Exists(lan.aliasesFileName) {
|
iface: iface,
|
||||||
if err := lan.loadAliases(); err != nil {
|
gateway: gateway,
|
||||||
fmt.Printf("%s\n", err)
|
Hosts: make(map[string]*Endpoint),
|
||||||
}
|
ttl: make(map[string]uint),
|
||||||
|
aliases: aliases,
|
||||||
|
newCb: newcb,
|
||||||
|
lostCb: lostcb,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lan
|
func (lan *LAN) SetAliasFor(mac, alias string) bool {
|
||||||
|
lan.Lock()
|
||||||
|
defer lan.Unlock()
|
||||||
|
|
||||||
|
mac = NormalizeMac(mac)
|
||||||
|
if e, found := lan.Hosts[mac]; found {
|
||||||
|
lan.aliases.Set(mac, alias)
|
||||||
|
e.Alias = alias
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lan *LAN) Get(mac string) (*Endpoint, bool) {
|
||||||
|
lan.Lock()
|
||||||
|
defer lan.Unlock()
|
||||||
|
|
||||||
|
if e, found := lan.Hosts[mac]; found == true {
|
||||||
|
return e, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lan *LAN) List() (list []*Endpoint) {
|
func (lan *LAN) List() (list []*Endpoint) {
|
||||||
|
@ -64,62 +77,16 @@ func (lan *LAN) List() (list []*Endpoint) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lan *LAN) loadAliases() error {
|
|
||||||
file, err := os.Open(lan.aliasesFileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
parts := strings.SplitN(line, " ", 2)
|
|
||||||
mac := core.Trim(parts[0])
|
|
||||||
alias := core.Trim(parts[1])
|
|
||||||
lan.Aliases[mac] = alias
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lan *LAN) saveAliases() {
|
|
||||||
data := ""
|
|
||||||
for mac, alias := range lan.Aliases {
|
|
||||||
data += fmt.Sprintf("%s %s\n", mac, alias)
|
|
||||||
}
|
|
||||||
ioutil.WriteFile(lan.aliasesFileName, []byte(data), 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lan *LAN) SetAliasFor(mac, alias string) bool {
|
|
||||||
lan.Lock()
|
|
||||||
defer lan.Unlock()
|
|
||||||
|
|
||||||
if t, found := lan.Hosts[mac]; found == true {
|
|
||||||
if alias != "" {
|
|
||||||
lan.Aliases[mac] = alias
|
|
||||||
} else {
|
|
||||||
delete(lan.Aliases, mac)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Alias = alias
|
|
||||||
lan.saveAliases()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lan *LAN) WasMissed(mac string) bool {
|
func (lan *LAN) WasMissed(mac string) bool {
|
||||||
if mac == lan.Interface.HwAddress || mac == lan.Gateway.HwAddress {
|
if mac == lan.iface.HwAddress || mac == lan.gateway.HwAddress {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
lan.Lock()
|
lan.Lock()
|
||||||
defer lan.Unlock()
|
defer lan.Unlock()
|
||||||
|
|
||||||
if ttl, found := lan.TTL[mac]; found == true {
|
if ttl, found := lan.ttl[mac]; found == true {
|
||||||
return ttl < LANDefaultTTL
|
return ttl < LANDefaultttl
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -129,33 +96,36 @@ func (lan *LAN) Remove(ip, mac string) {
|
||||||
defer lan.Unlock()
|
defer lan.Unlock()
|
||||||
|
|
||||||
if e, found := lan.Hosts[mac]; found {
|
if e, found := lan.Hosts[mac]; found {
|
||||||
lan.TTL[mac]--
|
lan.ttl[mac]--
|
||||||
if lan.TTL[mac] == 0 {
|
if lan.ttl[mac] == 0 {
|
||||||
delete(lan.Hosts, mac)
|
delete(lan.Hosts, mac)
|
||||||
delete(lan.TTL, mac)
|
delete(lan.ttl, mac)
|
||||||
|
|
||||||
lan.lostCb(e)
|
lan.lostCb(e)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lan *LAN) shouldIgnore(ip string) bool {
|
func (lan *LAN) shouldIgnore(ip, mac string) bool {
|
||||||
// skip our own address
|
// skip our own address
|
||||||
if ip == lan.Interface.IpAddress {
|
if ip == lan.iface.IpAddress {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// skip the gateway
|
// skip the gateway
|
||||||
if ip == lan.Gateway.IpAddress {
|
if ip == lan.gateway.IpAddress {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// skip broadcast addresses
|
// skip broadcast addresses
|
||||||
if strings.HasSuffix(ip, ".255") {
|
if strings.HasSuffix(ip, BroadcastSuffix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// skip broadcast macs
|
||||||
|
if strings.ToLower(mac) == BroadcastMac {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// skip everything which is not in our subnet (multicast noise)
|
// skip everything which is not in our subnet (multicast noise)
|
||||||
addr := net.ParseIP(ip)
|
addr := net.ParseIP(ip)
|
||||||
return lan.Interface.Net.Contains(addr) == false
|
return lan.iface.Net.Contains(addr) == false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lan *LAN) Has(ip string) bool {
|
func (lan *LAN) Has(ip string) bool {
|
||||||
|
@ -171,7 +141,16 @@ func (lan *LAN) Has(ip string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lan *LAN) Get(ip string) *Endpoint {
|
func (lan *LAN) EachHost(cb func(mac string, e *Endpoint)) {
|
||||||
|
lan.Lock()
|
||||||
|
defer lan.Unlock()
|
||||||
|
|
||||||
|
for m, h := range lan.Hosts {
|
||||||
|
cb(m, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lan *LAN) GetByIp(ip string) *Endpoint {
|
||||||
lan.Lock()
|
lan.Lock()
|
||||||
defer lan.Unlock()
|
defer lan.Unlock()
|
||||||
|
|
||||||
|
@ -188,25 +167,21 @@ func (lan *LAN) AddIfNew(ip, mac string) *Endpoint {
|
||||||
lan.Lock()
|
lan.Lock()
|
||||||
defer lan.Unlock()
|
defer lan.Unlock()
|
||||||
|
|
||||||
if lan.shouldIgnore(ip) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mac = NormalizeMac(mac)
|
mac = NormalizeMac(mac)
|
||||||
if t, found := lan.Hosts[mac]; found {
|
|
||||||
if lan.TTL[mac] < LANDefaultTTL {
|
if lan.shouldIgnore(ip, mac) {
|
||||||
lan.TTL[mac]++
|
return nil
|
||||||
|
} else if t, found := lan.Hosts[mac]; found {
|
||||||
|
if lan.ttl[mac] < LANDefaultttl {
|
||||||
|
lan.ttl[mac]++
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
e := NewEndpoint(ip, mac)
|
e := NewEndpointWithAlias(ip, mac, lan.aliases.Get(mac))
|
||||||
if alias, found := lan.Aliases[mac]; found {
|
|
||||||
e.Alias = alias
|
|
||||||
}
|
|
||||||
|
|
||||||
lan.Hosts[mac] = e
|
lan.Hosts[mac] = e
|
||||||
lan.TTL[mac] = LANDefaultTTL
|
lan.ttl[mac] = LANDefaultttl
|
||||||
|
|
||||||
lan.newCb(e)
|
lan.newCb(e)
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ func NewEndpoint(ip, mac string) *Endpoint {
|
||||||
|
|
||||||
// start resolver goroutine
|
// start resolver goroutine
|
||||||
go func() {
|
go func() {
|
||||||
if names, err := net.LookupAddr(e.IpAddress); err == nil {
|
if names, err := net.LookupAddr(e.IpAddress); err == nil && len(names) > 0 {
|
||||||
e.Hostname = names[0]
|
e.Hostname = names[0]
|
||||||
if e.ResolvedCallback != nil {
|
if e.ResolvedCallback != nil {
|
||||||
e.ResolvedCallback(e)
|
e.ResolvedCallback(e)
|
||||||
|
@ -80,6 +80,12 @@ func NewEndpoint(ip, mac string) *Endpoint {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewEndpointWithAlias(ip, mac, alias string) *Endpoint {
|
||||||
|
e := NewEndpoint(ip, mac)
|
||||||
|
e.Alias = alias
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Endpoint) Name() string {
|
func (t *Endpoint) Name() string {
|
||||||
return t.Hostname
|
return t.Hostname
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,14 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MonitorModeAddress = "0.0.0.0"
|
MonitorModeAddress = "0.0.0.0"
|
||||||
|
BroadcastSuffix = ".255"
|
||||||
|
BroadcastMac = "ff:ff:ff:ff:ff:ff"
|
||||||
IPv4MulticastStart = "01:00:5e:00:00:00"
|
IPv4MulticastStart = "01:00:5e:00:00:00"
|
||||||
IPv4MulticastEnd = "01:00:5e:7f:ff:ff"
|
IPv4MulticastEnd = "01:00:5e:7f:ff:ff"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BroadcastMac = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
BroadcastHw = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||||
IPv4Validator = regexp.MustCompile("^[0-9\\.]+/?\\d*$")
|
IPv4Validator = regexp.MustCompile("^[0-9\\.]+/?\\d*$")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,7 +62,9 @@ func FindInterface(name string) (*Endpoint, error) {
|
||||||
* if passed explicitly.
|
* if passed explicitly.
|
||||||
*/
|
*/
|
||||||
doCheck := false
|
doCheck := false
|
||||||
if name == "" && ifName != "lo" && ifName != "lo0" && nAddrs > 0 {
|
if name == mac {
|
||||||
|
doCheck = true
|
||||||
|
} else if name == "" && ifName != "lo" && ifName != "lo0" && nAddrs > 0 {
|
||||||
doCheck = true
|
doCheck = true
|
||||||
} else if ifName == name {
|
} else if ifName == name {
|
||||||
doCheck = true
|
doCheck = true
|
||||||
|
|
|
@ -12,19 +12,20 @@ var Channels5Ghz = [...]int{36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60,
|
||||||
|
|
||||||
type WiFi struct {
|
type WiFi struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
Interface *Endpoint
|
|
||||||
Stations map[string]*Station
|
|
||||||
|
|
||||||
|
Stations map[string]*Station
|
||||||
|
|
||||||
|
iface *Endpoint
|
||||||
newCb StationNewCallback
|
newCb StationNewCallback
|
||||||
lostCb StationLostCallback
|
lostCb StationLostCallback
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWiFi(iface *Endpoint, newcb StationNewCallback, lostcb StationLostCallback) *WiFi {
|
func NewWiFi(iface *Endpoint, newcb StationNewCallback, lostcb StationLostCallback) *WiFi {
|
||||||
return &WiFi{
|
return &WiFi{
|
||||||
Interface: iface,
|
Stations: make(map[string]*Station),
|
||||||
Stations: make(map[string]*Station),
|
iface: iface,
|
||||||
newCb: newcb,
|
newCb: newcb,
|
||||||
lostCb: lostcb,
|
lostCb: lostcb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ func DHCP6EncodeList(elements []string) (encoded []byte) {
|
||||||
encoded = make([]byte, 0)
|
encoded = make([]byte, 0)
|
||||||
|
|
||||||
for _, elem := range elements {
|
for _, elem := range elements {
|
||||||
|
// this would be worth fuzzing btw
|
||||||
encoded = append(encoded, byte(len(elem)&0xff))
|
encoded = append(encoded, byte(len(elem)&0xff))
|
||||||
encoded = append(encoded, []byte(elem)...)
|
encoded = append(encoded, []byte(elem)...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ type Environment struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
||||||
Padding int `json:"-"`
|
Padding int `json:"-"`
|
||||||
Storage map[string]string `json:"storage"`
|
Data map[string]string `json:"data"`
|
||||||
|
|
||||||
cbs map[string]SetCallback
|
cbs map[string]SetCallback
|
||||||
sess *Session
|
sess *Session
|
||||||
|
@ -24,7 +24,7 @@ type Environment struct {
|
||||||
func NewEnvironment(s *Session) *Environment {
|
func NewEnvironment(s *Session) *Environment {
|
||||||
env := &Environment{
|
env := &Environment{
|
||||||
Padding: 0,
|
Padding: 0,
|
||||||
Storage: make(map[string]string),
|
Data: make(map[string]string),
|
||||||
sess: s,
|
sess: s,
|
||||||
cbs: make(map[string]SetCallback),
|
cbs: make(map[string]SetCallback),
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ func (env *Environment) Has(name string) bool {
|
||||||
env.Lock()
|
env.Lock()
|
||||||
defer env.Unlock()
|
defer env.Unlock()
|
||||||
|
|
||||||
_, found := env.Storage[name]
|
_, found := env.Data[name]
|
||||||
|
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,8 @@ func (env *Environment) Set(name, value string) string {
|
||||||
env.Lock()
|
env.Lock()
|
||||||
defer env.Unlock()
|
defer env.Unlock()
|
||||||
|
|
||||||
old, _ := env.Storage[name]
|
old, _ := env.Data[name]
|
||||||
env.Storage[name] = value
|
env.Data[name] = value
|
||||||
|
|
||||||
if cb, hasCallback := env.cbs[name]; hasCallback == true {
|
if cb, hasCallback := env.cbs[name]; hasCallback == true {
|
||||||
cb(value)
|
cb(value)
|
||||||
|
@ -78,7 +78,7 @@ func (env *Environment) Get(name string) (bool, string) {
|
||||||
env.Lock()
|
env.Lock()
|
||||||
defer env.Unlock()
|
defer env.Unlock()
|
||||||
|
|
||||||
if value, found := env.Storage[name]; found == true {
|
if value, found := env.Data[name]; found == true {
|
||||||
return true, value
|
return true, value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ func (env *Environment) Sorted() []string {
|
||||||
defer env.Unlock()
|
defer env.Unlock()
|
||||||
|
|
||||||
var keys []string
|
var keys []string
|
||||||
for k := range env.Storage {
|
for k := range env.Data {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
|
|
@ -100,12 +100,6 @@ func (p *EventPool) Clear() {
|
||||||
p.events = make([]Event, 0)
|
p.events = make([]Event, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *EventPool) Events() []Event {
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
return p.events
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *EventPool) Sorted() []Event {
|
func (p *EventPool) Sorted() []Event {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
|
@ -56,7 +56,7 @@ var PromptCallbacks = map[string]func(s *Session) string{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var envRe = regexp.MustCompile("{env\\.(.+)}")
|
var envRe = regexp.MustCompile("{env\\.([^}]+)}")
|
||||||
|
|
||||||
type Prompt struct {
|
type Prompt struct {
|
||||||
}
|
}
|
||||||
|
@ -79,11 +79,11 @@ func (p Prompt) Render(s *Session) string {
|
||||||
prompt = strings.Replace(prompt, tok, cb(s), -1)
|
prompt = strings.Replace(prompt, tok, cb(s), -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := envRe.FindStringSubmatch(prompt)
|
m := envRe.FindAllString(prompt, -1)
|
||||||
if len(m) == 2 {
|
for _, match := range m {
|
||||||
name := m[1]
|
name := strings.Trim(strings.Replace(match, "env.", "", -1), "{}")
|
||||||
_, value := s.Env.Get(name)
|
_, value := s.Env.Get(name)
|
||||||
prompt = strings.Replace(prompt, m[0], value, -1)
|
prompt = strings.Replace(prompt, match, value, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure an user error does not screw all terminal
|
// make sure an user error does not screw all terminal
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/evilsocket/bettercap-ng/core"
|
"github.com/evilsocket/bettercap-ng/core"
|
||||||
|
"github.com/evilsocket/bettercap-ng/network"
|
||||||
|
|
||||||
"github.com/evilsocket/readline"
|
"github.com/evilsocket/readline"
|
||||||
)
|
)
|
||||||
|
@ -132,7 +133,7 @@ func (s *Session) getHandler(args []string, sess *Session) error {
|
||||||
prev_ns = ns
|
prev_ns = ns
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf(" %"+strconv.Itoa(s.Env.Padding)+"s: '%s'\n", k, s.Env.Storage[k])
|
fmt.Printf(" %"+strconv.Itoa(s.Env.Padding)+"s: '%s'\n", k, s.Env.Data[k])
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
} else if found, value := s.Env.Get(key); found == true {
|
} else if found, value := s.Env.Get(key); found == true {
|
||||||
|
@ -241,7 +242,7 @@ func (s *Session) registerCoreHandlers() {
|
||||||
readline.PcItem("get", readline.PcItemDynamic(func(prefix string) []string {
|
readline.PcItem("get", readline.PcItemDynamic(func(prefix string) []string {
|
||||||
prefix = core.Trim(prefix[3:])
|
prefix = core.Trim(prefix[3:])
|
||||||
varNames := []string{""}
|
varNames := []string{""}
|
||||||
for key := range s.Env.Storage {
|
for key := range s.Env.Data {
|
||||||
if prefix == "" || strings.HasPrefix(key, prefix) == true {
|
if prefix == "" || strings.HasPrefix(key, prefix) == true {
|
||||||
varNames = append(varNames, key)
|
varNames = append(varNames, key)
|
||||||
}
|
}
|
||||||
|
@ -256,7 +257,7 @@ func (s *Session) registerCoreHandlers() {
|
||||||
readline.PcItem("set", readline.PcItemDynamic(func(prefix string) []string {
|
readline.PcItem("set", readline.PcItemDynamic(func(prefix string) []string {
|
||||||
prefix = core.Trim(prefix[3:])
|
prefix = core.Trim(prefix[3:])
|
||||||
varNames := []string{""}
|
varNames := []string{""}
|
||||||
for key := range s.Env.Storage {
|
for key := range s.Env.Data {
|
||||||
if prefix == "" || strings.HasPrefix(key, prefix) == true {
|
if prefix == "" || strings.HasPrefix(key, prefix) == true {
|
||||||
varNames = append(varNames, key)
|
varNames = append(varNames, key)
|
||||||
}
|
}
|
||||||
|
@ -298,11 +299,11 @@ func (s *Session) registerCoreHandlers() {
|
||||||
readline.PcItem("alias", readline.PcItemDynamic(func(prefix string) []string {
|
readline.PcItem("alias", readline.PcItemDynamic(func(prefix string) []string {
|
||||||
prefix = core.Trim(prefix[5:])
|
prefix = core.Trim(prefix[5:])
|
||||||
macs := []string{""}
|
macs := []string{""}
|
||||||
for mac := range s.Lan.Hosts {
|
s.Lan.EachHost(func(mac string, e *network.Endpoint) {
|
||||||
if prefix == "" || strings.HasPrefix(mac, prefix) == true {
|
if prefix == "" || strings.HasPrefix(mac, prefix) == true {
|
||||||
macs = append(macs, mac)
|
macs = append(macs, mac)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
return macs
|
return macs
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue