From f5a7387a54ae08c9cd1d83a415393e0e909dc6e6 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Sat, 13 Oct 2018 04:23:20 +0200 Subject: Add proper support for USB and network (WiFi sync) devices reported by usbmuxd This commit extends the interface with a new function usbmuxd_get_device() that allows to look up USB *and* network devices, while the 'old' interface usbmuxd_get_device_by_udid() only targets USB devices. The usbmuxd_device_info_t structure now has new members 'conn_type' and 'conn_data' so that the returned device info allows to figure out if a device is available via USB or network. Check the comments in include/usbmuxd.h for more details. --- configure.ac | 2 +- include/usbmuxd.h | 64 ++++++++++-- src/libusbmuxd.c | 302 ++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 257 insertions(+), 111 deletions(-) diff --git a/configure.ac b/configure.ac index b719b96..ab056b6 100644 --- a/configure.ac +++ b/configure.ac @@ -66,7 +66,7 @@ AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_REALLOC -AC_CHECK_FUNCS([strcasecmp strdup strerror strndup sleep]) +AC_CHECK_FUNCS([strcasecmp strdup strerror stpncpy sleep]) # Check for operating system AC_MSG_CHECKING([whether we need platform-specific build settings]) diff --git a/include/usbmuxd.h b/include/usbmuxd.h index 0efebed..0cb7cab 100644 --- a/include/usbmuxd.h +++ b/include/usbmuxd.h @@ -1,8 +1,8 @@ /* * usbmuxd.h - A client library to talk to the usbmuxd daemon. * + * Copyright (C) 2009-2018 Nikias Bassen * Copyright (C) 2014 Martin Szulecki - * Copyright (C) 2009 Nikias Bassen * Copyright (C) 2009 Paul Sladen * * This library is free software; you can redistribute it and/or modify @@ -28,6 +28,19 @@ extern "C" { #endif +/** Device lookup options for usbmuxd_get_device. */ +enum usbmux_lookup_options { + DEVICE_LOOKUP_USBMUX = 1 << 1, /** include USBMUX devices during lookup */ + DEVICE_LOOKUP_NETWORK = 1 << 2, /** include network devices during lookup */ + DEVICE_LOOKUP_PREFER_NETWORK = 1 << 3 /** prefer network connection if device is available via USBMUX *and* network */ +}; + +/** Type of connection a device is available on */ +enum usbmux_connection_type { + CONNECTION_TYPE_USB = 1, + CONNECTION_TYPE_NETWORK +}; + /** * Device information structure holding data to identify the device. * The relevant 'handle' should be passed to 'usbmuxd_connect()', to @@ -36,8 +49,10 @@ extern "C" { */ typedef struct { uint32_t handle; - int product_id; - char udid[41]; + uint32_t product_id; + char udid[44]; + enum usbmux_connection_type conn_type; + char conn_data[200]; } usbmuxd_device_info_t; /** @@ -104,7 +119,12 @@ int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list); int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list); /** - * Gets device information for the device specified by udid. + * Looks up the device specified by UDID and returns device information. + * + * @note This function only considers devices connected through USB. To + * query devices available via network, use usbmuxd_get_device(). + * + * @see usbmuxd_get_device * * @param udid A device UDID of the device to look for. If udid is NULL, * This function will return the first device found. @@ -117,21 +137,47 @@ int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list); int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device); /** - * Request proxy connect to + * Looks up the device specified by UDID with given options and returns + * device information. + * + * @param udid A device UDID of the device to look for. If udid is NULL, + * this function will return the first device found. + * @param device Pointer to a previously allocated (or static) + * usbmuxd_device_info_t that will be filled with the device info. + * @param options Specifying what device connection types should be + * considered during lookup. Accepts bitwise or'ed values of + * usbmux_lookup_options. + * If 0 (no option) is specified it will default to DEVICE_LOOKUP_USBMUX. + * To lookup both USB and network-connected devices, pass + * DEVICE_LOOKUP_USBMUX | DEVICE_LOOKUP_NETWORK. If a device is available + * both via USBMUX *and* network, it will select the USB connection. + * This behavior can be changed by adding DEVICE_LOOKUP_PREFER_NETWORK + * to the options in which case it will select the network connection. + * + * @see enum usbmux_lookup_options + * + * @return 0 if no matching device is connected, 1 if the device was found, + * or a negative value on error. + */ +int usbmuxd_get_device(const char *udid, usbmuxd_device_info_t *device, enum usbmux_lookup_options options); + +/** + * Request proxy connection to the specified device and port. * - * @param handle returned by 'usbmuxd_scan()' + * @param handle returned in the usbmux_device_info_t structure via + * usbmuxd_get_device() or usbmuxd_get_device_list(). * * @param tcp_port TCP port number on device, in range 0-65535. * common values are 62078 for lockdown, and 22 for SSH. * - * @return file descriptor socket of the connection, or -1 on error + * @return socket file descriptor of the connection, or -1 on error */ -int usbmuxd_connect(const int handle, const unsigned short tcp_port); +int usbmuxd_connect(const uint32_t handle, const unsigned short tcp_port); /** * Disconnect. For now, this just closes the socket file descriptor. * - * @param sfd socker file descriptor returned by usbmuxd_connect() + * @param sfd socket file descriptor returned by usbmuxd_connect() * * @return 0 on success, -1 on error. */ diff --git a/src/libusbmuxd.c b/src/libusbmuxd.c index 379a948..0aaae24 100644 --- a/src/libusbmuxd.c +++ b/src/libusbmuxd.c @@ -1,8 +1,8 @@ /* * libusbmuxd.c * + * Copyright (C) 2009-2018 Nikias Bassen * Copyright (C) 2009-2014 Martin Szulecki - * Copyright (C) 2009-2014 Nikias Bassen * Copyright (C) 2009 Paul Sladen * * This library is free software; you can redistribute it and/or @@ -76,6 +76,16 @@ extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize); #define USBMUXD_SOCKET_NAME "usbmuxd" #endif /* HAVE_INOTIFY */ +#ifndef HAVE_STPNCPY +static char* stpncpy(char *dst, const char *src, size_t len) +{ + size_t n = strlen(src); + if (n > len) + n = len; + return strncpy(dst, src, len) + n; +} +#endif + #include #define PLIST_CLIENT_VERSION_STRING PACKAGE_STRING #define PLIST_LIBUSBMUX_VERSION 3 @@ -93,7 +103,11 @@ static char *prog_name = NULL; #include "collection.h" static int libusbmuxd_debug = 0; -#define DEBUG(x, y, ...) if (x <= libusbmuxd_debug) fprintf(stderr, (y), __VA_ARGS__); fflush(stderr); +#ifndef PACKAGE +#define PACKAGE "libusbmuxd" +#endif +#define DEBUG(level, format, ...) if (level <= libusbmuxd_debug) fprintf(stderr, ("[" PACKAGE "] " format), __VA_ARGS__); fflush(stderr); +#define ERROR(format, ...) DEBUG(0, format, __VA_ARGS__) static struct collection devices; static usbmuxd_event_cb_t event_cb = NULL; @@ -136,45 +150,114 @@ static int connect_usbmuxd_socket() #endif } -static struct usbmuxd_device_record* device_record_from_plist(plist_t props) +static usbmuxd_device_info_t *device_info_from_plist(plist_t props) { - struct usbmuxd_device_record* dev = NULL; + usbmuxd_device_info_t* devinfo = NULL; plist_t n = NULL; uint64_t val = 0; char *strval = NULL; - dev = (struct usbmuxd_device_record*)malloc(sizeof(struct usbmuxd_device_record)); - if (!dev) + devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); + if (!devinfo) return NULL; - memset(dev, 0, sizeof(struct usbmuxd_device_record)); + memset(devinfo, 0, sizeof(usbmuxd_device_info_t)); n = plist_dict_get_item(props, "DeviceID"); if (n && plist_get_node_type(n) == PLIST_UINT) { plist_get_uint_val(n, &val); - dev->device_id = (uint32_t)val; + devinfo->handle = (uint32_t)val; } n = plist_dict_get_item(props, "ProductID"); if (n && plist_get_node_type(n) == PLIST_UINT) { plist_get_uint_val(n, &val); - dev->product_id = (uint32_t)val; + devinfo->product_id = (uint32_t)val; } n = plist_dict_get_item(props, "SerialNumber"); if (n && plist_get_node_type(n) == PLIST_STRING) { plist_get_string_val(n, &strval); if (strval) { - strncpy(dev->serial_number, strval, 255); + char *t = stpncpy(devinfo->udid, strval, sizeof(devinfo->udid)-1); + *t = '\0'; + if (strlen(devinfo->udid) == 24) { + memmove(&devinfo->udid[9], &devinfo->udid[8], 17); + devinfo->udid[8] = '-'; + } + if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { + sprintf(devinfo->udid + 32, "%08x", devinfo->handle); + } free(strval); } } - n = plist_dict_get_item(props, "LocationID"); - if (n && plist_get_node_type(n) == PLIST_UINT) { - plist_get_uint_val(n, &val); - dev->location = (uint32_t)val; + + n = plist_dict_get_item(props, "ConnectionType"); + if (n && plist_get_node_type(n) == PLIST_STRING) { + plist_get_string_val(n, &strval); + if (strval) { + if (strcmp(strval, "USB") == 0) { + devinfo->conn_type = CONNECTION_TYPE_USB; + } else if (strcmp(strval, "Network") == 0) { + devinfo->conn_type = CONNECTION_TYPE_NETWORK; + n = plist_dict_get_item(props, "NetworkAddress"); + if (n && plist_get_node_type(n) == PLIST_DATA) { + char *netaddr = NULL; + uint64_t addr_len = 0; + plist_get_data_val(n, &netaddr, &addr_len); + if (netaddr && addr_len > 0 && addr_len < sizeof(devinfo->conn_data)) { + memcpy(devinfo->conn_data, netaddr, addr_len); + } + free(netaddr); + } + } else { + ERROR("%s: Unexpected ConnectionType '%s'\n", __func__, strval); + } + free(strval); + } + } + + if (!devinfo->udid[0]) { + ERROR("%s: Failed to get SerialNumber (UDID)!\n", __func__); + free(devinfo); + devinfo = NULL; + } + if (!devinfo->conn_type) { + ERROR("%s: Failed to get ConnectionType!\n", __func__); + free(devinfo); + devinfo = NULL; + } else if (devinfo->conn_type == CONNECTION_TYPE_NETWORK && !devinfo->conn_data[0]) { + ERROR("%s: Failed to get EscapedFullServiceName!\n", __func__); + free(devinfo); + devinfo = NULL; } - return dev; + return devinfo; +} + +static usbmuxd_device_info_t *device_info_from_device_record(struct usbmuxd_device_record *dev) +{ + if (!dev) { + return NULL; + } + usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); + if (!devinfo) { + ERROR("%s: Out of memory while allocating device info object\n", __func__); + return NULL; + } + + devinfo->handle = dev->device_id; + devinfo->product_id = dev->product_id; + char *t = stpncpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)-2); + *t = '\0'; + if (strlen(devinfo->udid) == 24) { + memmove(&devinfo->udid[9], &devinfo->udid[8], 17); + devinfo->udid[8] = '-'; + } + if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { + sprintf(devinfo->udid + 32, "%08x", devinfo->handle); + } + + return devinfo; } static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload, int timeout) @@ -249,7 +332,7 @@ static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload hdr.message = MESSAGE_RESULT; } else if (strcmp(message, "Attached") == 0) { /* device add message */ - struct usbmuxd_device_record *dev = NULL; + usbmuxd_device_info_t *devinfo = NULL; plist_t props = plist_dict_get_item(plist, "Properties"); if (!props) { DEBUG(1, "%s: Could not get properties for message '%s' from plist!\n", __func__, message); @@ -258,15 +341,15 @@ static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload return -EBADMSG; } - dev = device_record_from_plist(props); - if (!dev) { - DEBUG(1, "%s: Could not create device record object from properties!\n", __func__); + devinfo = device_info_from_plist(props); + if (!devinfo) { + DEBUG(1, "%s: Could not create device info object from properties!\n", __func__); free(message); plist_free(plist); return -EBADMSG; } - *payload = (void*)dev; - hdr.length = sizeof(hdr) + sizeof(struct usbmuxd_device_record); + *payload = (void*)devinfo; + hdr.length = sizeof(hdr) + sizeof(usbmuxd_device_info_t); hdr.message = MESSAGE_DEVICE_ADD; } else if (strcmp(message, "Detached") == 0) { /* device remove message */ @@ -305,6 +388,10 @@ static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload free(message); } plist_free(plist); + } else if (hdr.message == MESSAGE_DEVICE_ADD) { + usbmuxd_device_info_t *devinfo = device_info_from_device_record((struct usbmuxd_device_record*)payload_loc); + free(payload_loc); + *payload = devinfo; } else { *payload = payload_loc; } @@ -535,11 +622,11 @@ static void get_prog_name() size_t r = fread(tmpbuf, 1, 512, f); if (r > 0) { char *p = tmpbuf; - while ((p-tmpbuf < (ssize_t)r) && (*p != '(') && (*p != '\0')) p++; + while ((p-tmpbuf < r) && (*p != '(') && (*p != '\0')) p++; if (*p == '(') { p++; char *pname = p; - while ((p-tmpbuf < (ssize_t)r) && (*p != ')') && (*p != '\0')) p++; + while ((p-tmpbuf < r) && (*p != ')') && (*p != '\0')) p++; if (*p == ')') { *p = '\0'; prog_name = strdup(pname); @@ -841,30 +928,10 @@ static int get_next_event(int sfd, usbmuxd_event_cb_t callback, void *user_data) } if (hdr.message == MESSAGE_DEVICE_ADD) { - struct usbmuxd_device_record *dev = payload; - usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); - if (!devinfo) { - DEBUG(1, "%s: Out of memory!\n", __func__); - free(payload); - return -1; - } - - devinfo->handle = dev->device_id; - devinfo->product_id = dev->product_id; - memset(devinfo->udid, '\0', sizeof(devinfo->udid)); - memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)); - - if (strlen(devinfo->udid) == 24) { - memmove(&devinfo->udid[9], &devinfo->udid[8], 17); - devinfo->udid[8] = '-'; - } - - if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { - sprintf(devinfo->udid + 32, "%08x", devinfo->handle); - } - + usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)payload; collection_add(&devices, devinfo); generate_event(callback, devinfo, UE_DEVICE_ADD, user_data); + payload = NULL; } else if (hdr.message == MESSAGE_DEVICE_REMOVE) { uint32_t handle; usbmuxd_device_info_t *devinfo; @@ -1000,34 +1067,6 @@ USBMUXD_API int usbmuxd_unsubscribe() return 0; } -static usbmuxd_device_info_t *device_info_from_device_record(struct usbmuxd_device_record *dev) -{ - if (!dev) { - return NULL; - } - usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); - if (!devinfo) { - DEBUG(1, "%s: Out of memory!\n", __func__); - return NULL; - } - - devinfo->handle = dev->device_id; - devinfo->product_id = dev->product_id; - memset(devinfo->udid, '\0', sizeof(devinfo->udid)); - memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)); - - if (strlen(devinfo->udid) == 24) { - memmove(&devinfo->udid[9], &devinfo->udid[8], 17); - devinfo->udid[8] = '-'; - } - - if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { - sprintf(devinfo->udid + 32, "%08x", devinfo->handle); - } - - return devinfo; -} - USBMUXD_API int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list) { int sfd; @@ -1037,7 +1076,6 @@ USBMUXD_API int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list) struct collection tmpdevs; usbmuxd_device_info_t *newlist = NULL; struct usbmuxd_header hdr; - struct usbmuxd_device_record *dev; int dev_cnt = 0; void *payload = NULL; @@ -1063,12 +1101,10 @@ retry: for (i = 0; i < numdevs; i++) { plist_t pdev = plist_array_get_item(devlist, i); plist_t props = plist_dict_get_item(pdev, "Properties"); - dev = device_record_from_plist(props); - usbmuxd_device_info_t *devinfo = device_info_from_device_record(dev); - free(dev); + usbmuxd_device_info_t *devinfo = device_info_from_plist(props); if (!devinfo) { socket_close(sfd); - DEBUG(1, "%s: can't create device info object\n", __func__); + DEBUG(1, "%s: Could not create device info object from properties!\n", __func__); plist_free(list); return -1; } @@ -1119,17 +1155,9 @@ retry: while (1) { if (receive_packet(sfd, &hdr, &payload, 100) > 0) { if (hdr.message == MESSAGE_DEVICE_ADD) { - dev = payload; - - usbmuxd_device_info_t *devinfo = device_info_from_device_record(dev); - if (!devinfo) { - socket_close(sfd); - DEBUG(1, "%s: can't create device info object\n", __func__); - free(payload); - return -1; - } + usbmuxd_device_info_t *devinfo = payload; collection_add(&tmpdevs, devinfo); - + payload = NULL; } else if (hdr.message == MESSAGE_DEVICE_REMOVE) { uint32_t handle; usbmuxd_device_info_t *devinfo = NULL; @@ -1192,6 +1220,9 @@ USBMUXD_API int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list) USBMUXD_API int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device) { usbmuxd_device_info_t *dev_list = NULL; + usbmuxd_device_info_t *dev = NULL; + int result = 0; + int i; if (!device) { return -EINVAL; @@ -1200,31 +1231,100 @@ USBMUXD_API int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info return -ENODEV; } - int i; - int result = 0; for (i = 0; dev_list[i].handle > 0; i++) { if (!udid) { - device->handle = dev_list[i].handle; - device->product_id = dev_list[i].product_id; - strcpy(device->udid, dev_list[i].udid); - result = 1; - break; + if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { + dev = &dev_list[i]; + break; + } + } else if (!strcmp(udid, dev_list[i].udid)) { + if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { + dev = &dev_list[i]; + break; + } } - if (!strcmp(udid, dev_list[i].udid)) { - device->handle = dev_list[i].handle; - device->product_id = dev_list[i].product_id; - strcpy(device->udid, dev_list[i].udid); - result = 1; + } + + if (dev) { + device->handle = dev->handle; + device->product_id = dev->product_id; + char *t = stpncpy(device->udid, dev->udid, sizeof(device->udid)-1); + *t = '\0'; + device->conn_type = dev->conn_type; + memcpy(device->conn_data, dev->conn_data, sizeof(device->conn_data)); + result = 1; + } + + usbmuxd_device_list_free(&dev_list); + + return result; +} + +USBMUXD_API int usbmuxd_get_device(const char *udid, usbmuxd_device_info_t *device, enum usbmux_lookup_options options) +{ + usbmuxd_device_info_t *dev_list = NULL; + usbmuxd_device_info_t *dev_network = NULL; + usbmuxd_device_info_t *dev_usbmuxd = NULL; + usbmuxd_device_info_t *dev = NULL; + int result = 0; + int i; + + if (!device) { + return -EINVAL; + } + if (usbmuxd_get_device_list(&dev_list) < 0) { + return -ENODEV; + } + + if (options == 0) { + options = DEVICE_LOOKUP_USBMUX; + } + + for (i = 0; dev_list[i].handle > 0; i++) { + if (!udid) { + if ((options & DEVICE_LOOKUP_USBMUX) && (dev_list[i].conn_type == CONNECTION_TYPE_USB)) { + dev_usbmuxd = &dev_list[i]; + break; + } else if ((options & DEVICE_LOOKUP_NETWORK) && (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK)) { + dev_network = &dev_list[i]; + break; + } + } else if (!strcmp(udid, dev_list[i].udid)) { + if ((options & DEVICE_LOOKUP_USBMUX) && (dev_list[i].conn_type == CONNECTION_TYPE_USB)) { + dev_usbmuxd = &dev_list[i]; + } else if ((options & DEVICE_LOOKUP_NETWORK) && (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK)) { + dev_network = &dev_list[i]; + } + } + if (dev_usbmuxd && dev_network) { break; } } + if (dev_network && dev_usbmuxd) { + dev = (options & DEVICE_LOOKUP_PREFER_NETWORK) ? dev_network : dev_usbmuxd; + } else if (dev_network) { + dev = dev_network; + } else if (dev_usbmuxd) { + dev = dev_usbmuxd; + } + + if (dev) { + device->handle = dev->handle; + device->product_id = dev->product_id; + char *t = stpncpy(device->udid, dev->udid, sizeof(device->udid)-1); + *t = '\0'; + device->conn_type = dev->conn_type; + memcpy(device->conn_data, dev->conn_data, sizeof(device->conn_data)); + result = 1; + } + free(dev_list); return result; } -USBMUXD_API int usbmuxd_connect(const int handle, const unsigned short port) +USBMUXD_API int usbmuxd_connect(const uint32_t handle, const unsigned short port) { int sfd; int tag; -- cgit v1.1-32-gdbae