fix: fixed a bug in the gatt library which prevented ble.recon/ble.enum to work multiple times (fixes #471)

This commit is contained in:
evilsocket 2019-03-13 15:35:46 +01:00
commit 120db4db3d
No known key found for this signature in database
GPG key ID: 1564D7F30393A456
7 changed files with 75 additions and 13 deletions

4
Gopkg.lock generated
View file

@ -27,7 +27,7 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:c309b41787813f80ec393023471014b0be16346bba6e16bf5fa01ce1d310a4ea" digest = "1:a2c142e6c2aa1c71796c748bbe42d224e23d6638fd5b3ae153e70a4b08a8da4e"
name = "github.com/bettercap/gatt" name = "github.com/bettercap/gatt"
packages = [ packages = [
".", ".",
@ -40,7 +40,7 @@
"xpc", "xpc",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "1353e80bee488dc02d1f7e42759c1352492bf18b" revision = "277ee0d0ef94d26e3190252c59fa34dde0df4f26"
[[projects]] [[projects]]
branch = "master" branch = "master"

View file

@ -6,7 +6,6 @@ package ble
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io/ioutil"
golog "log" golog "log"
"time" "time"
@ -15,6 +14,8 @@ import (
"github.com/bettercap/bettercap/session" "github.com/bettercap/bettercap/session"
"github.com/bettercap/gatt" "github.com/bettercap/gatt"
"github.com/evilsocket/islazy/str"
) )
type BLERecon struct { type BLERecon struct {
@ -126,15 +127,25 @@ func (mod *BLERecon) isEnumerating() bool {
return mod.currDevice != nil return mod.currDevice != nil
} }
type dummyWriter struct {
mod *BLERecon
}
func (w dummyWriter) Write(p []byte) (n int, err error) {
w.mod.Debug("[gatt.log] %s", str.Trim(string(p)))
return len(p), nil
}
func (mod *BLERecon) Configure() (err error) { func (mod *BLERecon) Configure() (err error) {
if mod.Running() { if mod.Running() {
return session.ErrAlreadyStarted return session.ErrAlreadyStarted
} else if mod.gattDevice == nil { } else if mod.gattDevice == nil {
mod.Debug("initializing device ...") mod.Debug("initializing device ...")
// hey Paypal GATT library, could you please just STFU?! golog.SetFlags(0)
golog.SetOutput(ioutil.Discard) golog.SetOutput(dummyWriter{mod})
if mod.gattDevice, err = gatt.NewDevice(defaultBLEClientOptions...); err != nil { if mod.gattDevice, err = gatt.NewDevice(defaultBLEClientOptions...); err != nil {
mod.Debug("error while creating new gatt device: %v", err)
return err return err
} }
@ -162,7 +173,17 @@ func (mod *BLERecon) Start() error {
mod.Info("stopping scan ...") mod.Info("stopping scan ...")
mod.gattDevice.StopScanning() if mod.currDevice != nil && mod.currDevice.Device != nil && mod.gattDevice != nil {
mod.Debug("resetting connection with %v", mod.currDevice.Device)
mod.gattDevice.CancelConnection(mod.currDevice.Device)
}
mod.Debug("stopping device")
if err := mod.gattDevice.Stop(); err != nil {
mod.Warning("error while stopping device: %v", err)
} else {
mod.Debug("gatt device closed")
}
mod.done <- true mod.done <- true
}) })
@ -172,6 +193,9 @@ func (mod *BLERecon) Stop() error {
return mod.SetRunning(false, func() { return mod.SetRunning(false, func() {
mod.quit <- true mod.quit <- true
<-mod.done <-mod.done
mod.Debug("module stopped, cleaning state")
mod.gattDevice = nil
mod.setCurrentDevice(nil)
}) })
} }

View file

@ -15,8 +15,12 @@ func (mod *BLERecon) onStateChanged(dev gatt.Device, s gatt.State) {
if mod.currDevice == nil { if mod.currDevice == nil {
mod.Info("starting discovery ...") mod.Info("starting discovery ...")
dev.Scan([]gatt.UUID{}, true) dev.Scan([]gatt.UUID{}, true)
} else {
mod.Debug("current device was not cleaned: %v", mod.currDevice)
} }
case gatt.StatePoweredOff: case gatt.StatePoweredOff:
mod.Debug("resetting device instance")
mod.gattDevice.StopScanning()
mod.setCurrentDevice(nil) mod.setCurrentDevice(nil)
mod.gattDevice = nil mod.gattDevice = nil
@ -51,6 +55,7 @@ func (mod *BLERecon) onPeriphConnected(p gatt.Peripheral, err error) {
defer func(per gatt.Peripheral) { defer func(per gatt.Peripheral) {
mod.Info("disconnecting from %s ...", per.ID()) mod.Info("disconnecting from %s ...", per.ID())
per.Device().CancelConnection(per) per.Device().CancelConnection(per)
mod.setCurrentDevice(nil)
}(p) }(p)
mod.Session.Events.Add("ble.device.connected", mod.currDevice) mod.Session.Events.Add("ble.device.connected", mod.currDevice)

View file

@ -22,6 +22,7 @@ type device struct {
func newDevice(n int, chk bool) (*device, error) { func newDevice(n int, chk bool) (*device, error) {
fd, err := socket.Socket(socket.AF_BLUETOOTH, syscall.SOCK_RAW, socket.BTPROTO_HCI) fd, err := socket.Socket(socket.AF_BLUETOOTH, syscall.SOCK_RAW, socket.BTPROTO_HCI)
if err != nil { if err != nil {
log.Printf("could not create AF_BLUETOOTH raw socket")
return nil, err return nil, err
} }
if n != -1 { if n != -1 {
@ -30,13 +31,17 @@ func newDevice(n int, chk bool) (*device, error) {
req := devListRequest{devNum: hciMaxDevices} req := devListRequest{devNum: hciMaxDevices}
if err := gioctl.Ioctl(uintptr(fd), hciGetDeviceList, uintptr(unsafe.Pointer(&req))); err != nil { if err := gioctl.Ioctl(uintptr(fd), hciGetDeviceList, uintptr(unsafe.Pointer(&req))); err != nil {
log.Printf("hciGetDeviceList failed")
return nil, err return nil, err
} }
log.Printf("got %d devices", req.devNum)
for i := 0; i < int(req.devNum); i++ { for i := 0; i < int(req.devNum); i++ {
d, err := newSocket(fd, i, chk) d, err := newSocket(fd, i, chk)
if err == nil { if err == nil {
log.Printf("dev: %s opened", d.name) log.Printf("dev: %s opened", d.name)
return d, err return d, err
} else {
log.Printf("error while opening device %d: %v", i, err)
} }
} }
return nil, errors.New("no supported devices available") return nil, errors.New("no supported devices available")
@ -45,6 +50,7 @@ func newDevice(n int, chk bool) (*device, error) {
func newSocket(fd, n int, chk bool) (*device, error) { func newSocket(fd, n int, chk bool) (*device, error) {
i := hciDevInfo{id: uint16(n)} i := hciDevInfo{id: uint16(n)}
if err := gioctl.Ioctl(uintptr(fd), hciGetDeviceInfo, uintptr(unsafe.Pointer(&i))); err != nil { if err := gioctl.Ioctl(uintptr(fd), hciGetDeviceInfo, uintptr(unsafe.Pointer(&i))); err != nil {
log.Printf("hciGetDeviceInfo failed")
return nil, err return nil, err
} }
name := string(i.name[:]) name := string(i.name[:])
@ -61,6 +67,7 @@ func newSocket(fd, n int, chk bool) (*device, error) {
} }
log.Printf("dev: %s reset", name) log.Printf("dev: %s reset", name)
if err := gioctl.Ioctl(uintptr(fd), hciResetDevice, uintptr(n)); err != nil { if err := gioctl.Ioctl(uintptr(fd), hciResetDevice, uintptr(n)); err != nil {
log.Printf("hciResetDevice failed")
return nil, err return nil, err
} }
} }
@ -105,5 +112,6 @@ func (d device) Write(b []byte) (int, error) {
} }
func (d device) Close() error { func (d device) Close() error {
log.Printf("linux.device.Close()")
return syscall.Close(d.fd) return syscall.Close(d.fd)
} }

View file

@ -26,7 +26,8 @@ type HCI struct {
bufCnt chan struct{} bufCnt chan struct{}
bufSize int bufSize int
pool *util.BytePool pool *util.BytePool
loopDone chan bool
maxConn int maxConn int
connsmu *sync.Mutex connsmu *sync.Mutex
@ -68,7 +69,8 @@ func NewHCI(devID int, chk bool, maxConn int) (*HCI, error) {
bufCnt: make(chan struct{}, 15-1), bufCnt: make(chan struct{}, 15-1),
bufSize: 27, bufSize: 27,
pool: util.NewBytePool(4096, 16), pool: util.NewBytePool(4096, 16),
loopDone: make(chan bool),
maxConn: maxConn, maxConn: maxConn,
connsmu: &sync.Mutex{}, connsmu: &sync.Mutex{},
@ -89,9 +91,15 @@ func NewHCI(devID int, chk bool, maxConn int) (*HCI, error) {
} }
func (h *HCI) Close() error { func (h *HCI) Close() error {
log.Printf("hci.Close()")
h.pool.Put(nil)
<-h.loopDone
log.Printf("mainLoop exited")
for _, c := range h.conns { for _, c := range h.conns {
log.Printf("closing connection %v", c)
c.Close() c.Close()
} }
log.Printf("closing %v", h.d)
return h.d.Close() return h.d.Close()
} }
@ -151,7 +159,10 @@ func (h *HCI) Connect(pd *PlatData) error {
} }
func (h *HCI) CancelConnection(pd *PlatData) error { func (h *HCI) CancelConnection(pd *PlatData) error {
return pd.Conn.Close() if pd != nil && pd.Conn != nil {
return pd.Conn.Close()
}
return nil
} }
func (h *HCI) SendRawCommand(c cmd.CmdParam) ([]byte, error) { func (h *HCI) SendRawCommand(c cmd.CmdParam) ([]byte, error) {
@ -166,19 +177,33 @@ func btoi(b bool) uint8 {
} }
func (h *HCI) mainLoop() { func (h *HCI) mainLoop() {
log.Printf("hci.mainLoop started")
defer func() {
h.loopDone <- true
}()
for { for {
// log.Printf("hci.mainLoop pool.Get")
b := h.pool.Get() b := h.pool.Get()
if b == nil {
log.Printf("got nil buffer, breaking mainLoop")
break
}
// log.Printf("hci.mainLoop Read(%d)", len(b))
n, err := h.d.Read(b) n, err := h.d.Read(b)
if err != nil { if err != nil {
fmt.Sprintf("mainloop err: %v\n", err) log.Printf("mainloop err: %v", err)
return return
} }
if n == 0 { if n == 0 {
println("mainLoop failed to read") log.Printf("mainLoop failed to read")
return return
} }
// log.Printf("hci.mainLoop -> handlePacket")
h.handlePacket(b, n) h.handlePacket(b, n)
} }
log.Printf("hci.mainLoop stopped")
} }
func (h *HCI) handlePacket(buf []byte, n int) { func (h *HCI) handlePacket(buf []byte, n int) {

View file

@ -156,7 +156,7 @@ func (c *conn) Close() error {
defer h.connsmu.Unlock() defer h.connsmu.Unlock()
_, found := h.conns[hh] _, found := h.conns[hh]
if !found { if !found {
// log.Printf("l2conn: 0x%04x already disconnected", hh) log.Printf("l2conn: 0x%04x already disconnected", hh)
return nil return nil
} }
if err, _ := h.c.Send(cmd.Disconnect{ConnectionHandle: hh, Reason: 0x13}); err != nil { if err, _ := h.c.Send(cmd.Disconnect{ConnectionHandle: hh, Reason: 0x13}); err != nil {

View file

@ -407,7 +407,6 @@ func (p *peripheral) loop() {
if rspOp == attRspFor[reqOp] || (rspOp == attOpError && r[1] == reqOp) { if rspOp == attRspFor[reqOp] || (rspOp == attOpError && r[1] == reqOp) {
req.rspc <- r req.rspc <- r
break break
} }
log.Printf("Request 0x%02x got a mismatched response: 0x%02x", reqOp, rspOp) log.Printf("Request 0x%02x got a mismatched response: 0x%02x", reqOp, rspOp)
p.l2c.Write(attErrorRsp(rspOp, 0x0000, AttEcodeReqNotSupp)) p.l2c.Write(attErrorRsp(rspOp, 0x0000, AttEcodeReqNotSupp))
@ -434,6 +433,7 @@ func (p *peripheral) loop() {
copy(b, buf) copy(b, buf)
if (b[0] != attOpHandleNotify) && (b[0] != attOpHandleInd) { if (b[0] != attOpHandleNotify) && (b[0] != attOpHandleInd) {
log.Printf("response 0x%x", b[0])
rspc <- b rspc <- b
continue continue
} }