diff options
-rw-r--r-- | include/libimobiledevice/bt_packet_logger.h | 162 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/bt_packet_logger.c | 235 | ||||
-rw-r--r-- | src/bt_packet_logger.h | 36 | ||||
-rw-r--r-- | tools/Makefile.am | 6 | ||||
-rw-r--r-- | tools/idevicebtlogger.c | 360 |
6 files changed, 801 insertions, 1 deletions
diff --git a/include/libimobiledevice/bt_packet_logger.h b/include/libimobiledevice/bt_packet_logger.h new file mode 100644 index 0000000..697e879 --- /dev/null +++ b/include/libimobiledevice/bt_packet_logger.h @@ -0,0 +1,162 @@ +/** + * @file libimobiledevice/bt_packet_logger.h + * @brief Capture the Bluetooth HCI trace from a device + * \internal + * + * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IBT_PACKET_LOGGER_H +#define IBT_PACKET_LOGGER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libimobiledevice/libimobiledevice.h> +#include <libimobiledevice/lockdown.h> + +#define BT_PACKETLOGGER_SERVICE_NAME "com.apple.bluetooth.BTPacketLogger" + +/** Error Codes */ +typedef enum { + BT_PACKET_LOGGER_E_SUCCESS = 0, + BT_PACKET_LOGGER_E_INVALID_ARG = -1, + BT_PACKET_LOGGER_E_MUX_ERROR = -2, + BT_PACKET_LOGGER_E_SSL_ERROR = -3, + BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA = -4, + BT_PACKET_LOGGER_E_TIMEOUT = -5, + BT_PACKET_LOGGER_E_UNKNOWN_ERROR = -256 +} bt_packet_logger_error_t; + +typedef struct bt_packet_logger_client_private bt_packet_logger_client_private; +typedef bt_packet_logger_client_private *bt_packet_logger_client_t; /**< The client handle. */ + +/** Receives each character received from the device. */ +typedef void (*bt_packet_logger_receive_cb_t)(uint8_t * data, uint16_t len, void *user_data); + +/* Interface */ + +/** + * Connects to the bt_packet_logger service on the specified device. + * + * @param device The device to connect to. + * @param service The service descriptor returned by lockdownd_start_service. + * @param client Pointer that will point to a newly allocated + * bt_packet_logger_client_t upon successful return. Must be freed using + * bt_packet_logger_client_free() after use. + * + * @return BT_PACKET_LOGGER_E_SUCCESS on success, BT_PACKET_LOGGER_E_INVALID_ARG when + * client is NULL, or an BT_PACKET_LOGGER_E_* error code otherwise. + */ +bt_packet_logger_error_t bt_packet_logger_client_new(idevice_t device, lockdownd_service_descriptor_t service, bt_packet_logger_client_t * client); + +/** + * Starts a new bt_packet_logger service on the specified device and connects to it. + * + * @param device The device to connect to. + * @param client Pointer that will point to a newly allocated + * bt_packet_logger_client_t upon successful return. Must be freed using + * bt_packet_logger_client_free() after use. + * @param label The label to use for communication. Usually the program name. + * Pass NULL to disable sending the label in requests to lockdownd. + * + * @return BT_PACKET_LOGGER_E_SUCCESS on success, or an BT_PACKET_LOGGER_E_* error + * code otherwise. + */ +bt_packet_logger_error_t bt_packet_logger_client_start_service(idevice_t device, bt_packet_logger_client_t * client, const char* label); + +/** + * Disconnects a bt_packet_logger client from the device and frees up the + * bt_packet_logger client data. + * + * @param client The bt_packet_logger client to disconnect and free. + * + * @return BT_PACKET_LOGGER_E_SUCCESS on success, BT_PACKET_LOGGER_E_INVALID_ARG when + * client is NULL, or an BT_PACKET_LOGGER_E_* error code otherwise. + */ +bt_packet_logger_error_t bt_packet_logger_client_free(bt_packet_logger_client_t client); + + +/** + * Starts capturing the syslog of the device using a callback. + * + * Use bt_packet_logger_stop_capture() to stop receiving the syslog. + * + * @param client The bt_packet_logger client to use + * @param callback Callback to receive each character from the syslog. + * @param user_data Custom pointer passed to the callback function. + * + * @return BT_PACKET_LOGGER_E_SUCCESS on success, + * BT_PACKET_LOGGER_E_INVALID_ARG when one or more parameters are + * invalid or BT_PACKET_LOGGER_E_UNKNOWN_ERROR when an unspecified + * error occurs or a syslog capture has already been started. + */ +bt_packet_logger_error_t bt_packet_logger_start_capture(bt_packet_logger_client_t client, bt_packet_logger_receive_cb_t callback, void* user_data); + +/** + * Stops capturing the syslog of the device. + * + * Use bt_packet_logger_start_capture() to start receiving the syslog. + * + * @param client The bt_packet_logger client to use + * + * @return BT_PACKET_LOGGER_E_SUCCESS on success, + * BT_PACKET_LOGGER_E_INVALID_ARG when one or more parameters are + * invalid or BT_PACKET_LOGGER_E_UNKNOWN_ERROR when an unspecified + * error occurs or a syslog capture has already been started. + */ +bt_packet_logger_error_t bt_packet_logger_stop_capture(bt_packet_logger_client_t client); + +/* Receiving */ + +/** + * Receives data using the given bt_packet_logger client with specified timeout. + * + * @param client The bt_packet_logger client to use for receiving + * @param data Buffer that will be filled with the data received + * @param size Number of bytes to receive + * @param received Number of bytes received (can be NULL to ignore) + * @param timeout Maximum time in milliseconds to wait for data. + * + * @return BT_PACKET_LOGGER_E_SUCCESS on success, + * BT_PACKET_LOGGER_E_INVALID_ARG when one or more parameters are + * invalid, BT_PACKET_LOGGER_E_MUX_ERROR when a communication error + * occurs, or BT_PACKET_LOGGER_E_UNKNOWN_ERROR when an unspecified + * error occurs. + */ +bt_packet_logger_error_t bt_packet_logger_receive_with_timeout(bt_packet_logger_client_t client, char *data, uint32_t size, uint32_t *received, unsigned int timeout); + +/** + * Receives data from the service. + * + * @param client The bt_packet_logger client + * @param data Buffer that will be filled with the data received + * @param size Number of bytes to receive + * @param received Number of bytes received (can be NULL to ignore) + * @param timeout Maximum time in milliseconds to wait for data. + * + * @return BT_PACKET_LOGGER_E_SUCCESS on success, + * BT_PACKET_LOGGER_E_INVALID_ARG when client or plist is NULL + */ +bt_packet_logger_error_t bt_packet_logger_receive(bt_packet_logger_client_t client, char *data, uint32_t size, uint32_t *received); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 13221b9..1c80ed6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -57,7 +57,8 @@ libimobiledevice_1_0_la_SOURCES = \ preboard.c preboard.h \ companion_proxy.c companion_proxy.h \ reverse_proxy.c reverse_proxy.h \ - syslog_relay.c syslog_relay.h + syslog_relay.c syslog_relay.h \ + bt_packet_logger.c bt_packet_logger.h if WIN32 libimobiledevice_1_0_la_LDFLAGS += -avoid-version -static-libgcc diff --git a/src/bt_packet_logger.c b/src/bt_packet_logger.c new file mode 100644 index 0000000..5391825 --- /dev/null +++ b/src/bt_packet_logger.c @@ -0,0 +1,235 @@ +/* + * bt_packet_logger.c + * com.apple.bt_packet_logger service implementation. + * + * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <string.h> +#include <stdlib.h> + +#include "bt_packet_logger.h" +#include "lockdown.h" +#include "common/debug.h" + +struct bt_packet_logger_worker_thread { + bt_packet_logger_client_t client; + bt_packet_logger_receive_cb_t cbfunc; + void *user_data; +}; + +/** + * Convert a service_error_t value to a bt_packet_logger_error_t value. + * Used internally to get correct error codes. + * + * @param err An service_error_t error code + * + * @return A matching bt_packet_logger_error_t error code, + * BT_PACKET_LOGGER_E_UNKNOWN_ERROR otherwise. + */ +static bt_packet_logger_error_t bt_packet_logger_error(service_error_t err) +{ + switch (err) { + case SERVICE_E_SUCCESS: + return BT_PACKET_LOGGER_E_SUCCESS; + case SERVICE_E_INVALID_ARG: + return BT_PACKET_LOGGER_E_INVALID_ARG; + case SERVICE_E_MUX_ERROR: + return BT_PACKET_LOGGER_E_MUX_ERROR; + case SERVICE_E_SSL_ERROR: + return BT_PACKET_LOGGER_E_SSL_ERROR; + case SERVICE_E_NOT_ENOUGH_DATA: + return BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA; + case SERVICE_E_TIMEOUT: + return BT_PACKET_LOGGER_E_TIMEOUT; + default: + break; + } + return BT_PACKET_LOGGER_E_UNKNOWN_ERROR; +} + +LIBIMOBILEDEVICE_API bt_packet_logger_error_t bt_packet_logger_client_new(idevice_t device, lockdownd_service_descriptor_t service, bt_packet_logger_client_t * client) +{ + *client = NULL; + + if (!device || !service || service->port == 0 || !client || *client) { + debug_info("Incorrect parameter passed to bt_packet_logger_client_new."); + return BT_PACKET_LOGGER_E_INVALID_ARG; + } + + debug_info("Creating bt_packet_logger_client, port = %d.", service->port); + + service_client_t parent = NULL; + bt_packet_logger_error_t ret = bt_packet_logger_error(service_client_new(device, service, &parent)); + if (ret != BT_PACKET_LOGGER_E_SUCCESS) { + debug_info("Creating base service client failed. Error: %i", ret); + return ret; + } + + bt_packet_logger_client_t client_loc = (bt_packet_logger_client_t) malloc(sizeof(struct bt_packet_logger_client_private)); + client_loc->parent = parent; + client_loc->worker = THREAD_T_NULL; + + *client = client_loc; + + debug_info("bt_packet_logger_client successfully created."); + return 0; +} + +LIBIMOBILEDEVICE_API bt_packet_logger_error_t bt_packet_logger_client_start_service(idevice_t device, bt_packet_logger_client_t * client, const char* label) +{ + bt_packet_logger_error_t err = BT_PACKET_LOGGER_E_UNKNOWN_ERROR; + service_client_factory_start_service(device, BT_PACKETLOGGER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(bt_packet_logger_client_new), &err); + return err; +} + +LIBIMOBILEDEVICE_API bt_packet_logger_error_t bt_packet_logger_client_free(bt_packet_logger_client_t client) +{ + if (!client) + return BT_PACKET_LOGGER_E_INVALID_ARG; + bt_packet_logger_stop_capture(client); + bt_packet_logger_error_t err = bt_packet_logger_error(service_client_free(client->parent)); + free(client); + + return err; +} + +LIBIMOBILEDEVICE_API bt_packet_logger_error_t bt_packet_logger_receive(bt_packet_logger_client_t client, char* data, uint32_t size, uint32_t *received) +{ + return bt_packet_logger_receive_with_timeout(client, data, size, received, 1000); +} + +LIBIMOBILEDEVICE_API bt_packet_logger_error_t bt_packet_logger_receive_with_timeout(bt_packet_logger_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) +{ + bt_packet_logger_error_t res = BT_PACKET_LOGGER_E_UNKNOWN_ERROR; + int bytes = 0; + + if (!client || !data || (size == 0)) { + return BT_PACKET_LOGGER_E_INVALID_ARG; + } + + res = bt_packet_logger_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout)); + if (res != BT_PACKET_LOGGER_E_SUCCESS && res != BT_PACKET_LOGGER_E_TIMEOUT && res != BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA) { + debug_info("Could not read data, error %d", res); + } + if (received) { + *received = (uint32_t)bytes; + } + + return res; +} + +void *bt_packet_logger_worker(void *arg) +{ + bt_packet_logger_error_t ret = BT_PACKET_LOGGER_E_UNKNOWN_ERROR; + struct bt_packet_logger_worker_thread *btwt = (struct bt_packet_logger_worker_thread*)arg; + + if (!btwt) + return NULL; + + debug_info("Running"); + + while (btwt->client->parent) { + uint32_t bytes = 0; + uint16_t len; + + ret = bt_packet_logger_receive_with_timeout(btwt->client, &len, 2, &bytes, 100); + + if (ret == BT_PACKET_LOGGER_E_TIMEOUT || ret == BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA || ((bytes == 0) && (ret == BT_PACKET_LOGGER_E_SUCCESS))) { + continue; + } else if (ret < 0) { + debug_info("Connection to bt packet logger interrupted"); + break; + } + + // todo remove magic and move "c" off stack + if(bytes > 0 && len > 12) { + char c[65535]; + debug_info("Reading %u bytes\n", len); + ret = bt_packet_logger_receive_with_timeout(btwt->client, c, len, &bytes, 500); + + if(len != bytes) { + debug_info("Failed Read Expected %u, Received %u\n", len, bytes); + continue; + } + + if (ret == BT_PACKET_LOGGER_E_TIMEOUT || ret == BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA || ((bytes == 0) && (ret == BT_PACKET_LOGGER_E_SUCCESS))) { + continue; + } else if (ret < 0) { + debug_info("Connection to bt packet logger interrupted"); + break; + } + + btwt->cbfunc(c, len, btwt->user_data); + } + } + + if (btwt) { + free(btwt); + } + + debug_info("Exiting"); + + return NULL; +} + +LIBIMOBILEDEVICE_API bt_packet_logger_error_t bt_packet_logger_start_capture(bt_packet_logger_client_t client, bt_packet_logger_receive_cb_t callback, void* user_data) +{ + if (!client || !callback) + return BT_PACKET_LOGGER_E_INVALID_ARG; + + bt_packet_logger_error_t res = BT_PACKET_LOGGER_E_UNKNOWN_ERROR; + + if (client->worker) { + debug_info("Another syslog capture thread appears to be running already."); + return res; + } + + /* start worker thread */ + struct bt_packet_logger_worker_thread *btwt = (struct bt_packet_logger_worker_thread*)malloc(sizeof(struct bt_packet_logger_worker_thread)); + if (btwt) { + btwt->client = client; + btwt->cbfunc = callback; + btwt->user_data = user_data; + + if (thread_new(&client->worker, bt_packet_logger_worker, btwt) == 0) { + res = BT_PACKET_LOGGER_E_SUCCESS; + } + } + + return res; +} + + +LIBIMOBILEDEVICE_API bt_packet_logger_error_t bt_packet_logger_stop_capture(bt_packet_logger_client_t client) +{ + if (client->worker) { + /* notify thread to finish */ + service_client_t parent = client->parent; + client->parent = NULL; + /* join thread to make it exit */ + thread_join(client->worker); + thread_free(client->worker); + client->worker = THREAD_T_NULL; + client->parent = parent; + } + + return BT_PACKET_LOGGER_E_SUCCESS; +} diff --git a/src/bt_packet_logger.h b/src/bt_packet_logger.h new file mode 100644 index 0000000..1ad906d --- /dev/null +++ b/src/bt_packet_logger.h @@ -0,0 +1,36 @@ +/* + * bt_packet_logger.h + * com.apple.bt_packet_logger service header file. + * + * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _SYSLOG_RELAY_H +#define _SYSLOG_RELAY_H + +#include "libimobiledevice/bt_packet_logger.h" +#include "service.h" +#include "common/thread.h" + +struct bt_packet_logger_client_private { + service_client_t parent; + THREAD_T worker; +}; + +void *bt_packet_logger_worker(void *arg); + +#endif diff --git a/tools/Makefile.am b/tools/Makefile.am index 4740330..d701bab 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -11,6 +11,7 @@ AM_LDFLAGS = \ $(libplist_LIBS) bin_PROGRAMS = \ + idevicebtlogger\ idevice_id \ ideviceinfo \ idevicename \ @@ -30,6 +31,11 @@ bin_PROGRAMS = \ idevicecrashreport \ idevicesetlocation +idevicebtlogger_SOURCES = idevicebtlogger.c +iidevicebtlogger_CFLAGS = $(AM_CFLAGS) +idevicebtlogger_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS) -lpcap +idevicebtlogger_LDADD = $(top_builddir)/src/libimobiledevice-1.0.la + ideviceinfo_SOURCES = ideviceinfo.c ideviceinfo_CFLAGS = $(AM_CFLAGS) $(limd_glue_CFLAGS) ideviceinfo_LDFLAGS = $(AM_LDFLAGS) $(limd_glue_LIBS) diff --git a/tools/idevicebtlogger.c b/tools/idevicebtlogger.c new file mode 100644 index 0000000..fc42290 --- /dev/null +++ b/tools/idevicebtlogger.c @@ -0,0 +1,360 @@ +/* + * idevicebt_packet_logger.c + * Capture bt HCI packet log to pcap + * + * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define TOOL_NAME "idevicebtlogger" + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> + +#ifdef WIN32 +#include <windows.h> +#define sleep(x) Sleep(x*1000) +#else +#include <arpa/inet.h> +#endif + + +#include <libimobiledevice/libimobiledevice.h> +#include <libimobiledevice/bt_packet_logger.h> +#include <pcap.h>// todo windows??? + +#define BT_MAX_PACKET_SIZE 65535 + +static int quit_flag = 0; +static int exit_on_disconnect = 0; + +static char* udid = NULL; +static idevice_t device = NULL; +static bt_packet_logger_client_t bt_packet_logger = NULL; +static int use_network = 0; +static char* out_filename = NULL; +static pcap_dumper_t * dump; + +typedef struct { + uint32_t length; + uint32_t ts_secs; + uint32_t ts_usecs; +} PacketHeaderType; + +typedef enum { + HCI_COMMAND = 0x00, + HCI_EVENT = 0x01, + SENT_ACL_DATA = 0x02, + RECV_ACL_DATA = 0x03 +} PacketLoggerPacketType; + +static void bt_packet_logger_callback(uint8_t * data, uint16_t len, void *user_data) +{ + PacketHeaderType * header = (PacketHeaderType *)data; + uint16_t offset = sizeof(PacketHeaderType); + + struct pcap_pkthdr pcap_header; + pcap_header.caplen = ntohl(header->length); + pcap_header.len = len - sizeof(PacketHeaderType); + pcap_header.ts.tv_sec = ntohl(header->ts_secs); + pcap_header.ts.tv_usec = ntohl(header->ts_usecs); + + // Sanity check incoming data and drop packet if its unreasonable. + if(pcap_header.len > BT_MAX_PACKET_SIZE || pcap_header.caplen > BT_MAX_PACKET_SIZE) { + fprintf(stderr, "WARNING: Packet length exceeded max size, corruption likely.\n "); + return; + } + + uint8_t packet_type = data[offset]; + uint8_t hci_h4_type = 0xff; + + switch(packet_type) { + case HCI_EVENT: + hci_h4_type = 0x04; + break; + + case HCI_COMMAND: + hci_h4_type = 0x01; + break; + + case SENT_ACL_DATA: + hci_h4_type = 0x02; + break; + + case RECV_ACL_DATA: + hci_h4_type = 0x02; + break; + + default: + // unknown packet logger type, just pass it on + hci_h4_type = packet_type; + break; + } + if(hci_h4_type != 0xff) { + data[offset] = hci_h4_type; + pcap_dump((unsigned char*)dump, &pcap_header, &data[offset]); + pcap_dump_flush(dump); + } + + // for(; offset < len; offset++) { + // if( (offset - sizeof(PacketHeaderType)) % 16 == 0) { + // printf("\n"); + // } + // printf("0x%02x, ", 0xff&data[offset]); + // } + // printf("\n------------------------------------------------------------------------------------------------\n"); +} + +static void stop_logging(void) +{ + fflush(NULL); + + if (bt_packet_logger) { + bt_packet_logger_client_free(bt_packet_logger); + bt_packet_logger = NULL; + } + + if (device) { + idevice_free(device); + device = NULL; + } +} + +static int start_logging(void) +{ + idevice_error_t ret = idevice_new_with_options(&device, udid, (use_network) ? IDEVICE_LOOKUP_NETWORK : IDEVICE_LOOKUP_USBMUX); + if (ret != IDEVICE_E_SUCCESS) { + fprintf(stderr, "Device with udid %s not found!?\n", udid); + return -1; + } + + lockdownd_client_t lockdown = NULL; + lockdownd_error_t lerr = lockdownd_client_new_with_handshake(device, &lockdown, TOOL_NAME); + if (lerr != LOCKDOWN_E_SUCCESS) { + fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr); + idevice_free(device); + device = NULL; + return -1; + } + + /* start bt_packet_logger service */ + lockdownd_service_descriptor_t svc = NULL; + lerr = lockdownd_start_service(lockdown, BT_PACKETLOGGER_SERVICE_NAME, &svc); + if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) { + fprintf(stderr, "*** Device is passcode protected, enter passcode on the device to continue ***\n"); + while (!quit_flag) { + lerr = lockdownd_start_service(lockdown, BT_PACKETLOGGER_SERVICE_NAME, &svc); + if (lerr != LOCKDOWN_E_PASSWORD_PROTECTED) { + break; + } + sleep(1); + } + } + if (lerr != LOCKDOWN_E_SUCCESS) { + fprintf(stderr, "ERROR: Could not connect to lockdownd: %d\n", lerr); + fprintf(stderr, "Please ensure the target device has a valid Bluetooth logging profile installed\n"); + idevice_free(device); + device = NULL; + return -1; + } + lockdownd_client_free(lockdown); + + /* connect to bt_packet_logger service */ + bt_packet_logger_error_t serr = BT_PACKET_LOGGER_E_UNKNOWN_ERROR; + serr = bt_packet_logger_client_new(device, svc, &bt_packet_logger); + lockdownd_service_descriptor_free(svc); + if (serr != BT_PACKET_LOGGER_E_SUCCESS) { + fprintf(stderr, "ERROR: Could not start service %s.\n", BT_PACKETLOGGER_SERVICE_NAME); + fprintf(stderr, "Please ensure the target device has a valid Bluetooth logging profile installed\n"); + idevice_free(device); + device = NULL; + return -1; + } + + /* start capturing bt_packet_logger */ + serr = bt_packet_logger_start_capture(bt_packet_logger, bt_packet_logger_callback, NULL); + if (serr != BT_PACKET_LOGGER_E_SUCCESS) { + fprintf(stderr, "ERROR: Unable to start capturing bt_packet_logger.\n"); + bt_packet_logger_client_free(bt_packet_logger); + bt_packet_logger = NULL; + idevice_free(device); + device = NULL; + return -1; + } + + fprintf(stdout, "[connected:%s]\n", udid); + fflush(stdout); + + return 0; +} + +static void device_event_cb(const idevice_event_t* event, void* userdata) +{ + if (use_network && event->conn_type != CONNECTION_NETWORK) { + return; + } else if (!use_network && event->conn_type != CONNECTION_USBMUXD) { + return; + } + if (event->event == IDEVICE_DEVICE_ADD) { + if (!bt_packet_logger) { + if (!udid) { + udid = strdup(event->udid); + } + if (strcmp(udid, event->udid) == 0) { + if (start_logging() != 0) { + fprintf(stderr, "Could not start logger for udid %s\n", udid); + } + } + } + } else if (event->event == IDEVICE_DEVICE_REMOVE) { + if (bt_packet_logger && (strcmp(udid, event->udid) == 0)) { + stop_logging(); + fprintf(stdout, "[disconnected:%s]\n", udid); + if (exit_on_disconnect) { + quit_flag++; + } + } + } +} + +/** + * signal handler function for cleaning up properly + */ +static void clean_exit(int sig) +{ + fprintf(stderr, "\nExiting...\n"); + quit_flag++; +} + +static void print_usage(int argc, char **argv, int is_error) +{ + char *name = NULL; + name = strrchr(argv[0], '/'); + fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] <FILE>\n", (name ? name + 1: argv[0])); + fprintf(is_error ? stderr : stdout, + "\n" \ + "Capture HCI packets from a connected device.\n" \ + "\n" \ + "OPTIONS:\n" \ + " -u, --udid UDID target specific device by UDID\n" \ + " -n, --network connect to network device\n" \ + " -x, --exit exit when device disconnects\n" \ + " -h, --help prints usage information\n" \ + " -d, --debug enable communication debugging\n" \ + " -v, --version prints version information\n" \ + "\n" \ + "Homepage: <" PACKAGE_URL ">\n" + "Bug Reports: <" PACKAGE_BUGREPORT ">\n" + ); +} + +int main(int argc, char *argv[]) +{ + int c = 0; + const struct option longopts[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "udid", required_argument, NULL, 'u' }, + { "network", no_argument, NULL, 'n' }, + { "exit", no_argument, NULL, 'x' }, + { "version", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0} + }; + + signal(SIGINT, clean_exit); + signal(SIGTERM, clean_exit); +#ifndef WIN32 + signal(SIGQUIT, clean_exit); + signal(SIGPIPE, SIG_IGN); +#endif + + while ((c = getopt_long(argc, argv, "dhu:nxv", longopts, NULL)) != -1) { + switch (c) { + case 'd': + idevice_set_debug_level(1); + break; + case 'u': + if (!*optarg) { + fprintf(stderr, "ERROR: UDID must not be empty!\n"); + print_usage(argc, argv, 1); + return 2; + } + free(udid); + udid = strdup(optarg); + break; + case 'n': + use_network = 1; + break; + case 'x': + exit_on_disconnect = 1; + break; + case 'h': + print_usage(argc, argv, 0); + return 0; + case 'v': + printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION); + return 0; + default: + print_usage(argc, argv, 1); + return 2; + } + } + + if (optind < argc) { + out_filename = argv[optind]; + printf("Output File: %s\n", out_filename); + } + else { + print_usage(argc, argv, 1); + return 2; + } + + int num = 0; + idevice_info_t *devices = NULL; + idevice_get_device_list_extended(&devices, &num); + idevice_device_list_extended_free(devices); + if (num == 0) { + if (!udid) { + fprintf(stderr, "No device found. Plug in a device or pass UDID with -u to wait for device to be available.\n"); + return -1; + } else { + fprintf(stderr, "Waiting for device with UDID %s to become available...\n", udid); + } + } + + dump = pcap_dump_open(pcap_open_dead(187, BT_MAX_PACKET_SIZE), out_filename); + idevice_event_subscribe(device_event_cb, NULL); + + while (!quit_flag) { + sleep(1); + } + + idevice_event_unsubscribe(); + stop_logging(); + + free(udid); + + return 0; +} |