mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-22 19:43:21 +00:00
Refactor BLE error handling and time functions
Refactor BLE error handling to return standard error codes instead of PM3_* constants. Update time-related functions to use a consistent method for obtaining the current time. Signed-off-by: Niel Nielsen <nieldk@gmail.com>
This commit is contained in:
1 parent
c264251935
commit
284e4b385c
1 file changed
+44
-47
+44
-47
@@ -17,9 +17,11 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h> // strcasecmp
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
@@ -27,9 +29,8 @@
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_lib.h>
|
||||
|
||||
#include "ui.h" // PrintAndLogEx
|
||||
#include "pm3_cmd.h" // PM3_SUCCESS / PM3_E*
|
||||
#include "util_posix.h" // msclock, msleep
|
||||
#include "ui.h" // PrintAndLogEx
|
||||
#include "pm3_cmd.h" // PM3_* (only for messaging parity; returns are 0/neg here)
|
||||
|
||||
#define ATT_CID 4
|
||||
|
||||
@@ -192,6 +193,12 @@ static int att_subscribe(int fd, uint16_t cccd_handle) {
|
||||
#define BLE_ADV_NAME_SHORT 0x08
|
||||
#define BLE_ADV_NAME_FULL 0x09
|
||||
|
||||
static long ble_now_ms(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
|
||||
}
|
||||
|
||||
// Walk the AD structures of one advertising/scan-response report, looking for a
|
||||
// Local Name (complete or shortened) that equals `want` (case-insensitive).
|
||||
// Copies whatever name it finds into `found` for logging. Returns 1 on match.
|
||||
@@ -233,27 +240,25 @@ static void ble_print_caps_hint(void) {
|
||||
}
|
||||
|
||||
int ble_resolve_name(const char *name, char *out_mac, size_t out_mac_sz, int timeout_ms) {
|
||||
if (name == NULL || out_mac == NULL || out_mac_sz < 18) return PM3_EINVARG;
|
||||
if (name == NULL || out_mac == NULL || out_mac_sz < 18) return -1;
|
||||
out_mac[0] = 0;
|
||||
|
||||
int dev_id = hci_get_route(NULL);
|
||||
if (dev_id < 0) {
|
||||
PrintAndLogEx(ERR, "BLE: no local Bluetooth adapter found (%s)", strerror(errno));
|
||||
return PM3_EIO;
|
||||
return -1;
|
||||
}
|
||||
int dd = hci_open_dev(dev_id);
|
||||
if (dd < 0) {
|
||||
PrintAndLogEx(ERR, "BLE: cannot open hci%d (%s)", dev_id, strerror(errno));
|
||||
if (errno == EPERM || errno == EACCES)
|
||||
ble_print_caps_hint();
|
||||
return PM3_EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: despite the header naming its first arg `dev_id`, BlueZ's
|
||||
// hci_le_set_scan_* helpers expect the *open descriptor* (dd) and pass it
|
||||
// straight to hci_send_req(). Passing the raw device id here silently fails.
|
||||
// Active scan (0x01) so we also receive SCAN_RSP, which is where many
|
||||
// devices (incl. the BWM) put the complete local name.
|
||||
// BlueZ's hci_le_set_scan_* take the open descriptor (dd), not dev_id,
|
||||
// despite the arg name - passing dev_id silently fails.
|
||||
// Active scan (0x01) so SCAN_RSP (where the BWM puts its name) is seen.
|
||||
if (hci_le_set_scan_parameters(dd, 0x01, htobs(0x0010), htobs(0x0010), 0x00, 0x00, 1000) < 0)
|
||||
PrintAndLogEx(INFO, "BLE: set scan parameters failed (%s), continuing", strerror(errno));
|
||||
|
||||
@@ -264,7 +269,7 @@ int ble_resolve_name(const char *name, char *out_mac, size_t out_mac_sz, int tim
|
||||
if (errno == EPERM || errno == EACCES)
|
||||
ble_print_caps_hint();
|
||||
hci_close_dev(dd);
|
||||
return PM3_EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Receive only HCI event packets carrying LE meta events; save/restore the
|
||||
@@ -279,24 +284,22 @@ int ble_resolve_name(const char *name, char *out_mac, size_t out_mac_sz, int tim
|
||||
|
||||
PrintAndLogEx(INFO, "BLE: scanning up to " _YELLOW_("%d") " ms for " _YELLOW_("%s") " ...", timeout_ms, name);
|
||||
|
||||
int rc = PM3_ETIMEOUT;
|
||||
int rc = -1;
|
||||
char nm[64];
|
||||
uint64_t deadline = msclock() + (uint64_t)timeout_ms;
|
||||
long deadline = ble_now_ms() + timeout_ms;
|
||||
uint8_t buf[HCI_MAX_EVENT_SIZE];
|
||||
|
||||
while (msclock() < deadline) {
|
||||
int64_t rem = (int64_t)(deadline - msclock());
|
||||
while (ble_now_ms() < deadline) {
|
||||
long rem = deadline - ble_now_ms();
|
||||
if (rem <= 0) break;
|
||||
struct timeval tv = { .tv_sec = rem / 1000, .tv_usec = (rem % 1000) * 1000 };
|
||||
fd_set rs;
|
||||
FD_ZERO(&rs);
|
||||
FD_SET(dd, &rs);
|
||||
int s = select(dd + 1, &rs, NULL, NULL, &tv);
|
||||
if (s < 0) {
|
||||
if (errno == EINTR) continue; // Interrupted by signal, try again
|
||||
break; // Unrecoverable error, break loop
|
||||
}
|
||||
if (s == 0) break; // Timeout, handle gracefully
|
||||
if (s < 0) { if (errno == EINTR) break; break; } // Ctrl-C or error -> give up cleanly
|
||||
if (s == 0) break; // timeout
|
||||
|
||||
int n = read(dd, buf, sizeof(buf));
|
||||
if (n < (int)(1 + HCI_EVENT_HDR_SIZE + 1)) continue;
|
||||
uint8_t *pkt_end = buf + n;
|
||||
@@ -316,37 +319,31 @@ int ble_resolve_name(const char *name, char *out_mac, size_t out_mac_sz, int tim
|
||||
if (ble_ad_name_matches(name, info->data, info->length, nm, sizeof(nm))) {
|
||||
ba2str(&info->bdaddr, out_mac);
|
||||
PrintAndLogEx(SUCCESS, "BLE: found " _GREEN_("%s") " at " _GREEN_("%s"), nm, out_mac);
|
||||
rc = PM3_SUCCESS;
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
p = next;
|
||||
}
|
||||
if (rc == PM3_SUCCESS) break;
|
||||
if (rc == 0) break;
|
||||
}
|
||||
|
||||
hci_le_set_scan_enable(dd, 0x00, 0x00, 1000);
|
||||
if (have_of) setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
|
||||
hci_close_dev(dd);
|
||||
|
||||
if (rc != PM3_SUCCESS)
|
||||
if (rc != 0)
|
||||
PrintAndLogEx(ERR, "BLE: no advertising device named " _YELLOW_("%s") " seen within %d ms", name, timeout_ms);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// How hard to try the L2CAP connect before giving up. A create-connection
|
||||
// issued in the instant an LE scan tears down (the name path) can be refused by
|
||||
// the controller, and a peripheral may momentarily be between advertising
|
||||
// events; both clear within a few tens of ms. The connect-by-address path
|
||||
// normally succeeds on the first attempt, so this adds no delay there.
|
||||
// Retry the L2CAP connect briefly: a create-connection issued as an LE scan
|
||||
// tears down can be refused, and the peer may be between advertising events.
|
||||
#define BLE_CONNECT_TRIES 6
|
||||
#define BLE_CONNECT_BACKOFF_MS 150
|
||||
|
||||
// Open an L2CAP/ATT socket and connect to `mac`. Returns the fd (>= 0) on
|
||||
// success, -1 on a retryable connect failure (errno set to the connect
|
||||
// error), or -2 on a hard setup failure (socket/bind), which is already
|
||||
// logged. Private helper: intentionally NOT PM3_E*-coded since it returns a
|
||||
// real fd on success and PM3_SUCCESS (0) would collide with a valid fd 0;
|
||||
// ble_connect() below translates its result to PM3_E* for the public API.
|
||||
// Open an L2CAP/ATT socket and connect to `mac`. Returns the fd on success,
|
||||
// -1 on a retryable connect failure (errno set to the connect error), or
|
||||
// -2 on a hard setup failure (socket/bind), which is already logged.
|
||||
static int ble_l2cap_connect(const char *mac) {
|
||||
int fd = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
|
||||
if (fd < 0) {
|
||||
@@ -393,15 +390,15 @@ int ble_connect(const char *mac, uint16_t chr_uuid16, ble_conn_t *conn) {
|
||||
for (int attempt = 1; attempt <= BLE_CONNECT_TRIES; attempt++) {
|
||||
fd = ble_l2cap_connect(mac);
|
||||
if (fd >= 0) break;
|
||||
if (fd == -2) return PM3_EIO; // hard setup failure, already logged
|
||||
if (fd == -2) return -1; // hard setup failure, already logged
|
||||
last_errno = errno; // retryable connect failure
|
||||
if (attempt < BLE_CONNECT_TRIES) {
|
||||
msleep(BLE_CONNECT_BACKOFF_MS);
|
||||
usleep(BLE_CONNECT_BACKOFF_MS * 1000);
|
||||
}
|
||||
}
|
||||
if (fd < 0) {
|
||||
PrintAndLogEx(ERR, "BLE: cannot connect to " _YELLOW_("%s") " (%s)", mac, strerror(last_errno));
|
||||
return PM3_EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->fd = fd;
|
||||
@@ -428,16 +425,16 @@ int ble_connect(const char *mac, uint16_t chr_uuid16, ble_conn_t *conn) {
|
||||
|
||||
PrintAndLogEx(SUCCESS, "BLE connected, MTU " _GREEN_("%u") ", char handle " _GREEN_("0x%04X"),
|
||||
conn->mtu, conn->val_handle);
|
||||
return PM3_SUCCESS;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
conn->fd = -1;
|
||||
return PM3_EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ble_send(ble_conn_t *conn, const uint8_t *data, size_t len) {
|
||||
if (conn->fd < 0) return PM3_ENOTTY;
|
||||
if (conn->fd < 0) return -1;
|
||||
size_t chunk = (conn->mtu > 3) ? (size_t)(conn->mtu - 3) : 20;
|
||||
uint8_t pdu[3 + 517];
|
||||
for (size_t off = 0; off < len; off += chunk) {
|
||||
@@ -446,14 +443,14 @@ int ble_send(ble_conn_t *conn, const uint8_t *data, size_t len) {
|
||||
put16(&pdu[1], conn->val_handle);
|
||||
memcpy(&pdu[3], data + off, clen);
|
||||
if (att_write_pdu(conn->fd, pdu, 3 + clen) != 0) {
|
||||
return PM3_EIO;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ble_recv(ble_conn_t *conn, uint8_t *buf, size_t maxlen, size_t *out_len, int timeout_ms) {
|
||||
if (conn->fd < 0) return PM3_ENOTTY;
|
||||
if (conn->fd < 0) return -1;
|
||||
size_t got = 0;
|
||||
|
||||
// 1) drain any leftover payload from a previous oversized notification
|
||||
@@ -464,7 +461,7 @@ int ble_recv(ble_conn_t *conn, uint8_t *buf, size_t maxlen, size_t *out_len, int
|
||||
size_t rem = conn->leftover_len - take;
|
||||
if (rem) memmove(conn->leftover, conn->leftover + take, rem);
|
||||
conn->leftover_len = rem;
|
||||
if (got == maxlen) { *out_len = got; return PM3_SUCCESS; }
|
||||
if (got == maxlen) { *out_len = got; return 0; }
|
||||
}
|
||||
|
||||
// 2) pull notifications until buffer full or a real timeout.
|
||||
@@ -477,7 +474,7 @@ int ble_recv(ble_conn_t *conn, uint8_t *buf, size_t maxlen, size_t *out_len, int
|
||||
uint8_t pdu[3 + 517];
|
||||
for (;;) {
|
||||
int n = att_read_pdu(conn->fd, pdu, sizeof(pdu), timeout_ms);
|
||||
if (n < 0) { *out_len = got; return (got > 0) ? PM3_SUCCESS : PM3_EIO; }
|
||||
if (n < 0) { *out_len = got; return (got > 0) ? 0 : -1; }
|
||||
if (n == 0) break; // real timeout / no more
|
||||
if ((pdu[0] != ATT_OP_HANDLE_NOTIFY && pdu[0] != ATT_OP_HANDLE_INDICATE) || n < 3)
|
||||
continue; // ignore non-notifications
|
||||
@@ -498,7 +495,7 @@ int ble_recv(ble_conn_t *conn, uint8_t *buf, size_t maxlen, size_t *out_len, int
|
||||
}
|
||||
|
||||
*out_len = got;
|
||||
return PM3_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ble_close(ble_conn_t *conn) {
|
||||
|
||||
Reference in new issue
Block a user