ble_show.go 3.32 KB
Newer Older
Sophie Brun's avatar
Sophie Brun committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// +build !windows
// +build !darwin

package ble

import (
	"os"
	"sort"
	"time"

	"github.com/bettercap/bettercap/network"

	"github.com/evilsocket/islazy/ops"
	"github.com/evilsocket/islazy/tui"
)

var (
	bleAliveInterval   = time.Duration(5) * time.Second
	blePresentInterval = time.Duration(30) * time.Second
)

Sophie Brun's avatar
Sophie Brun committed
22
func (mod *BLERecon) getRow(dev *network.BLEDevice, withName bool) []string {
Sophie Brun's avatar
Sophie Brun committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36
	rssi := network.ColorRSSI(dev.RSSI)
	address := network.NormalizeMac(dev.Device.ID())
	vendor := tui.Dim(ops.Ternary(dev.Vendor == "", dev.Advertisement.Company, dev.Vendor).(string))
	isConnectable := ops.Ternary(dev.Advertisement.Connectable, tui.Green("✔"), tui.Red("✖")).(string)
	sinceSeen := time.Since(dev.LastSeen)
	lastSeen := dev.LastSeen.Format("15:04:05")

	if sinceSeen <= bleAliveInterval {
		lastSeen = tui.Bold(lastSeen)
	} else if sinceSeen > blePresentInterval {
		lastSeen = tui.Dim(lastSeen)
		address = tui.Dim(address)
	}

Sophie Brun's avatar
Sophie Brun committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
	if withName {
		return []string{
			rssi,
			address,
			dev.Name(),
			vendor,
			dev.Advertisement.Flags.String(),
			isConnectable,
			lastSeen,
		}
	} else {
		return []string{
			rssi,
			address,
			vendor,
			dev.Advertisement.Flags.String(),
			isConnectable,
			lastSeen,
		}
Sophie Brun's avatar
Sophie Brun committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
	}
}

func (mod *BLERecon) doFilter(dev *network.BLEDevice) bool {
	if mod.selector.Expression == nil {
		return true
	}
	return mod.selector.Expression.MatchString(dev.Device.ID()) ||
		mod.selector.Expression.MatchString(dev.Device.Name()) ||
		mod.selector.Expression.MatchString(dev.Vendor)
}

func (mod *BLERecon) doSelection() (err error, devices []*network.BLEDevice) {
	if err = mod.selector.Update(); err != nil {
		return
	}

	devices = mod.Session.BLE.Devices()
	filtered := []*network.BLEDevice{}
	for _, dev := range devices {
		if mod.doFilter(dev) {
			filtered = append(filtered, dev)
		}
	}
	devices = filtered

	switch mod.selector.SortField {
	case "mac":
		sort.Sort(ByBLEMacSorter(devices))
	case "seen":
		sort.Sort(ByBLESeenSorter(devices))
	default:
		sort.Sort(ByBLERSSISorter(devices))
	}

	// default is asc
	if mod.selector.Sort == "desc" {
		// from https://github.com/golang/go/wiki/SliceTricks
		for i := len(devices)/2 - 1; i >= 0; i-- {
			opp := len(devices) - 1 - i
			devices[i], devices[opp] = devices[opp], devices[i]
		}
	}

	if mod.selector.Limit > 0 {
		limit := mod.selector.Limit
		max := len(devices)
		if limit > max {
			limit = max
		}
		devices = devices[0:limit]
	}

	return
}

Sophie Brun's avatar
Sophie Brun committed
112
func (mod *BLERecon) colNames(withName bool) []string {
Sophie Brun's avatar
Sophie Brun committed
113
	colNames := []string{"RSSI", "MAC", "Vendor", "Flags", "Connect", "Seen"}
Sophie Brun's avatar
Sophie Brun committed
114 115 116 117 118
	seenIdx := 5
	if withName {
		colNames = []string{"RSSI", "MAC", "Name", "Vendor", "Flags", "Connect", "Seen"}
		seenIdx = 6
	}
Sophie Brun's avatar
Sophie Brun committed
119 120 121 122 123 124
	switch mod.selector.SortField {
	case "rssi":
		colNames[0] += " " + mod.selector.SortSymbol
	case "mac":
		colNames[1] += " " + mod.selector.SortSymbol
	case "seen":
Sophie Brun's avatar
Sophie Brun committed
125
		colNames[seenIdx] += " " + mod.selector.SortSymbol
Sophie Brun's avatar
Sophie Brun committed
126 127 128 129 130 131 132 133 134 135
	}
	return colNames
}

func (mod *BLERecon) Show() error {
	err, devices := mod.doSelection()
	if err != nil {
		return err
	}

Sophie Brun's avatar
Sophie Brun committed
136 137 138 139 140 141 142 143
	hasName := false
	for _, dev := range devices {
		if dev.Name() != "" {
			hasName = true
			break
		}
	}

Sophie Brun's avatar
Sophie Brun committed
144 145
	rows := make([][]string, 0)
	for _, dev := range devices {
Sophie Brun's avatar
Sophie Brun committed
146
		rows = append(rows, mod.getRow(dev, hasName))
Sophie Brun's avatar
Sophie Brun committed
147 148 149
	}

	if len(rows) > 0 {
Sophie Brun's avatar
Sophie Brun committed
150
		tui.Table(os.Stdout, mod.colNames(hasName), rows)
Sophie Brun's avatar
Sophie Brun committed
151 152 153 154 155
		mod.Session.Refresh()
	}

	return nil
}