diff options
Diffstat (limited to 'src')
59 files changed, 5381 insertions, 1169 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index fcde8ae..58cf07c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,39 +1,68 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir) +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/3rd_party/libsrp6a-sha512 \ + -I$(top_srcdir)/3rd_party/ed25519 \ + -I$(top_srcdir) -AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusbmuxd_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libplist_CFLAGS) $(LFS_CFLAGS) $(openssl_CFLAGS) $(PTHREAD_CFLAGS) -AM_LDFLAGS = $(libgnutls_LIBS) $(libtasn1_LIBS) $(libplist_LIBS) $(libusbmuxd_LIBS) $(libgcrypt_LIBS) $(openssl_LIBS) $(PTHREAD_LIBS) +AM_CFLAGS = \ + $(GLOBAL_CFLAGS) \ + $(ssl_lib_CFLAGS) \ + $(LFS_CFLAGS) \ + $(PTHREAD_CFLAGS) \ + $(libusbmuxd_CFLAGS) \ + $(libplist_CFLAGS) \ + $(limd_glue_CFLAGS) -lib_LTLIBRARIES = libimobiledevice.la -libimobiledevice_la_LIBADD = $(top_builddir)/common/libinternalcommon.la -libimobiledevice_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_SO_VERSION) -no-undefined -libimobiledevice_la_SOURCES = idevice.c idevice.h \ - service.c service.h\ - property_list_service.c property_list_service.h\ - device_link_service.c device_link_service.h\ - lockdown.c lockdown.h\ - afc.c afc.h\ - file_relay.c file_relay.h\ - notification_proxy.c notification_proxy.h\ - installation_proxy.c installation_proxy.h\ - sbservices.c sbservices.h\ - mobile_image_mounter.c mobile_image_mounter.h\ - screenshotr.c screenshotr.h\ - mobilesync.c mobilesync.h\ - mobilebackup.c mobilebackup.h\ - house_arrest.c house_arrest.h\ - mobilebackup2.c mobilebackup2.h\ - misagent.c misagent.h\ - restore.c restore.h\ - diagnostics_relay.c diagnostics_relay.h\ - heartbeat.c heartbeat.h\ - debugserver.c debugserver.h\ - webinspector.c webinspector.h\ - mobileactivation.c mobileactivation.h\ - syslog_relay.c syslog_relay.h +AM_LDFLAGS = \ + $(ssl_lib_LIBS) \ + $(PTHREAD_LIBS) \ + $(libusbmuxd_LIBS) \ + $(libplist_LIBS) \ + $(limd_glue_LIBS) + +lib_LTLIBRARIES = libimobiledevice-1.0.la +libimobiledevice_1_0_la_LIBADD = $(top_builddir)/common/libinternalcommon.la +if HAVE_WIRELESS_PAIRING +libimobiledevice_1_0_la_LIBADD += $(top_builddir)/3rd_party/ed25519/libed25519.la $(top_builddir)/3rd_party/libsrp6a-sha512/libsrp6a-sha512.la +endif +libimobiledevice_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_SO_VERSION) -no-undefined +if DARWIN +libimobiledevice_1_0_la_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration +endif +libimobiledevice_1_0_la_SOURCES = \ + idevice.c idevice.h \ + service.c service.h \ + property_list_service.c property_list_service.h \ + device_link_service.c device_link_service.h \ + lockdown.c lockdown.h \ + lockdown-cu.c \ + afc.c afc.h \ + file_relay.c file_relay.h \ + notification_proxy.c notification_proxy.h \ + installation_proxy.c installation_proxy.h \ + sbservices.c sbservices.h \ + mobile_image_mounter.c mobile_image_mounter.h \ + screenshotr.c screenshotr.h \ + mobilesync.c mobilesync.h \ + mobilebackup.c mobilebackup.h \ + house_arrest.c house_arrest.h \ + mobilebackup2.c mobilebackup2.h \ + misagent.c misagent.h \ + restore.c restore.h \ + diagnostics_relay.c diagnostics_relay.h \ + heartbeat.c heartbeat.h \ + debugserver.c debugserver.h \ + webinspector.c webinspector.h \ + mobileactivation.c mobileactivation.h \ + preboard.c preboard.h \ + companion_proxy.c companion_proxy.h \ + reverse_proxy.c reverse_proxy.h \ + syslog_relay.c syslog_relay.h \ + bt_packet_logger.c bt_packet_logger.h if WIN32 -libimobiledevice_la_LDFLAGS += -avoid-version -libimobiledevice_la_LIBADD += -lole32 -lws2_32 -lgdi32 +libimobiledevice_1_0_la_LDFLAGS += -avoid-version -static-libgcc +libimobiledevice_1_0_la_LIBADD += -lole32 -lws2_32 -lgdi32 endif pkgconfigdir = $(libdir)/pkgconfig @@ -29,8 +29,8 @@ #include <unistd.h> #include <string.h> -#include "afc.h" #include "idevice.h" +#include "afc.h" #include "common/debug.h" #include "endianness.h" @@ -68,7 +68,7 @@ static void afc_unlock(afc_client_t client) * invalid, or AFC_E_NO_MEM if there is a memory allocation problem. */ -LIBIMOBILEDEVICE_API afc_error_t afc_client_new_with_service_client(service_client_t service_client, afc_client_t *client) +afc_error_t afc_client_new_with_service_client(service_client_t service_client, afc_client_t *client) { if (!service_client) return AFC_E_INVALID_ARG; @@ -78,25 +78,23 @@ LIBIMOBILEDEVICE_API afc_error_t afc_client_new_with_service_client(service_clie client_loc->free_parent = 0; /* allocate a packet */ - client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); + client_loc->packet_extra = 1024; + client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket) + client_loc->packet_extra); if (!client_loc->afc_packet) { free(client_loc); return AFC_E_NO_MEM; } - client_loc->afc_packet->packet_num = 0; client_loc->afc_packet->entire_length = 0; client_loc->afc_packet->this_length = 0; memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN); - client_loc->file_handle = 0; - client_loc->lock = 0; mutex_init(&client_loc->mutex); *client = client_loc; return AFC_E_SUCCESS; } -LIBIMOBILEDEVICE_API afc_error_t afc_client_new(idevice_t device, lockdownd_service_descriptor_t service, afc_client_t * client) +afc_error_t afc_client_new(idevice_t device, lockdownd_service_descriptor_t service, afc_client_t * client) { if (!device || !service || service->port == 0) return AFC_E_INVALID_ARG; @@ -115,14 +113,14 @@ LIBIMOBILEDEVICE_API afc_error_t afc_client_new(idevice_t device, lockdownd_serv return err; } -LIBIMOBILEDEVICE_API afc_error_t afc_client_start_service(idevice_t device, afc_client_t * client, const char* label) +afc_error_t afc_client_start_service(idevice_t device, afc_client_t * client, const char* label) { afc_error_t err = AFC_E_UNKNOWN_ERROR; service_client_factory_start_service(device, AFC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(afc_client_new), &err); return err; } -LIBIMOBILEDEVICE_API afc_error_t afc_client_free(afc_client_t client) +afc_error_t afc_client_free(afc_client_t client) { if (!client || !client->afc_packet) return AFC_E_INVALID_ARG; @@ -150,7 +148,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_client_free(afc_client_t client) * * @return AFC_E_SUCCESS on success or an AFC_E_* error value. */ -static afc_error_t afc_dispatch_packet(afc_client_t client, uint64_t operation, const char *data, uint32_t data_length, const char* payload, uint32_t payload_length, uint32_t *bytes_sent) +static afc_error_t afc_dispatch_packet(afc_client_t client, uint64_t operation, uint32_t data_length, const char* payload, uint32_t payload_length, uint32_t *bytes_sent) { uint32_t sent = 0; @@ -159,8 +157,6 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, uint64_t operation, *bytes_sent = 0; - if (!data || !data_length) - data_length = 0; if (!payload || !payload_length) payload_length = 0; @@ -171,34 +167,26 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, uint64_t operation, debug_info("packet length = %i", client->afc_packet->this_length); - debug_buffer((char*)client->afc_packet, sizeof(AFCPacket)); - - /* send AFC packet header */ + /* send AFC packet header and data */ AFCPacket_to_LE(client->afc_packet); + debug_buffer((char*)client->afc_packet, sizeof(AFCPacket) + data_length); sent = 0; - service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket), &sent); + service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket) + data_length, &sent); AFCPacket_from_LE(client->afc_packet); *bytes_sent += sent; - if (sent < sizeof(AFCPacket)) { - return AFC_E_SUCCESS; - } - - /* send AFC packet data (if there's data to send) */ - sent = 0; - if (data_length > 0) { - debug_info("packet data follows"); - debug_buffer(data, data_length); - service_send(client->parent, data, data_length, &sent); - } - *bytes_sent += sent; - if (sent < data_length) { + if (sent < sizeof(AFCPacket) + data_length) { return AFC_E_SUCCESS; } sent = 0; if (payload_length > 0) { - debug_info("packet payload follows"); - debug_buffer(payload, payload_length); + if (payload_length > 256) { + debug_info("packet payload follows (256/%u)", payload_length); + debug_buffer(payload, 256); + } else { + debug_info("packet payload follows"); + debug_buffer(payload, payload_length); + } service_send(client->parent, payload, payload_length, &sent); } *bytes_sent += sent; @@ -225,7 +213,8 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t uint32_t this_len = 0; uint32_t current_count = 0; uint64_t param1 = -1; - char* dump_here = NULL; + char *buf = NULL; + uint32_t recv_len = 0; if (bytes_recv) { *bytes_recv = 0; @@ -235,18 +224,20 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t } /* first, read the AFC header */ - service_receive(client->parent, (char*)&header, sizeof(AFCPacket), bytes_recv); + service_receive(client->parent, (char*)&header, sizeof(AFCPacket), &recv_len); AFCPacket_from_LE(&header); - if (*bytes_recv == 0) { + if (recv_len == 0) { debug_info("Just didn't get enough."); return AFC_E_MUX_ERROR; - } else if (*bytes_recv < sizeof(AFCPacket)) { + } + + if (recv_len < sizeof(AFCPacket)) { debug_info("Did not even get the AFCPacket header"); return AFC_E_MUX_ERROR; } /* check if it's a valid AFC header */ - if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN)) { + if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN) != 0) { debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!"); } @@ -261,15 +252,14 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t if (header.this_length < sizeof(AFCPacket)) { debug_info("Invalid AFCPacket header received!"); return AFC_E_OP_HEADER_INVALID; - } else if ((header.this_length == header.entire_length) - && header.entire_length == sizeof(AFCPacket)) { + } + if ((header.this_length == header.entire_length) + && header.entire_length == sizeof(AFCPacket)) { debug_info("Empty AFCPacket received!"); - *bytes_recv = 0; if (header.operation == AFC_OP_DATA) { return AFC_E_SUCCESS; - } else { - return AFC_E_IO_ERROR; } + return AFC_E_IO_ERROR; } debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation); @@ -277,15 +267,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); this_len = (uint32_t)header.this_length - sizeof(AFCPacket); - dump_here = (char*)malloc(entire_len); + buf = (char*)malloc(entire_len); if (this_len > 0) { - service_receive(client->parent, dump_here, this_len, bytes_recv); - if (*bytes_recv <= 0) { - free(dump_here); + recv_len = 0; + service_receive(client->parent, buf, this_len, &recv_len); + if (recv_len <= 0) { + free(buf); debug_info("Did not get packet contents!"); return AFC_E_NOT_ENOUGH_DATA; - } else if (*bytes_recv < this_len) { - free(dump_here); + } + if (recv_len < this_len) { + free(buf); debug_info("Could not receive this_len=%d bytes", this_len); return AFC_E_NOT_ENOUGH_DATA; } @@ -295,12 +287,13 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t if (entire_len > this_len) { while (current_count < entire_len) { - service_receive(client->parent, dump_here+current_count, entire_len - current_count, bytes_recv); - if (*bytes_recv <= 0) { - debug_info("Error receiving data (recv returned %d)", *bytes_recv); + recv_len = 0; + service_receive(client->parent, buf+current_count, entire_len - current_count, &recv_len); + if (recv_len <= 0) { + debug_info("Error receiving data (recv returned %d)", recv_len); break; } - current_count += *bytes_recv; + current_count += recv_len; } if (current_count < entire_len) { debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len); @@ -308,12 +301,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t } if (current_count >= sizeof(uint64_t)) { - param1 = le64toh(*(uint64_t*)(dump_here)); + param1 = le64toh(*(uint64_t*)(buf)); } debug_info("packet data size = %i", current_count); - debug_info("packet data follows"); - debug_buffer(dump_here, current_count); + if (current_count > 256) { + debug_info("packet data follows (256/%u)", current_count); + debug_buffer(buf, 256); + } else { + debug_info("packet data follows"); + debug_buffer(buf, current_count); + } /* check operation types */ if (header.operation == AFC_OP_STATUS) { @@ -323,7 +321,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t if (param1 != AFC_E_SUCCESS) { /* error status */ /* free buffer */ - free(dump_here); + free(buf); return (afc_error_t)param1; } } else if (header.operation == AFC_OP_DATA) { @@ -337,8 +335,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t debug_info("got a tell response, position=%lld", param1); } else { /* unknown operation code received */ - free(dump_here); - *bytes_recv = 0; + free(buf); debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); #ifndef WIN32 @@ -349,9 +346,9 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t } if (bytes) { - *bytes = dump_here; + *bytes = buf; } else { - free(dump_here); + free(buf); } *bytes_recv = current_count; @@ -361,7 +358,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t /** * Returns counts of null characters within a string. */ -static uint32_t count_nullspaces(char *string, uint32_t number) +static uint32_t count_nullspaces(const char *string, uint32_t number) { uint32_t i = 0, nulls = 0; @@ -402,7 +399,22 @@ static char **make_strings_list(char *tokens, uint32_t length) return list; } -LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const char *path, char ***directory_information) +static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len) +{ + if (data_len > client->packet_extra) { + client->packet_extra = (data_len & ~8) + 8; + AFCPacket* newpkt = (AFCPacket*)realloc(client->afc_packet, sizeof(AFCPacket) + client->packet_extra); + if (!newpkt) { + return -1; + } + client->afc_packet = newpkt; + } + return 0; +} + +#define AFC_PACKET_DATA_PTR ((char*)client->afc_packet + sizeof(AFCPacket)) + +afc_error_t afc_read_directory(afc_client_t client, const char *path, char ***directory_information) { uint32_t bytes = 0; char *data = NULL, **list_loc = NULL; @@ -413,8 +425,16 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c afc_lock(client); + uint32_t data_len = (uint32_t)strlen(path)+1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send the command */ - ret = afc_dispatch_packet(client, AFC_OP_READ_DIR, path, strlen(path)+1, NULL, 0, &bytes); + memcpy(AFC_PACKET_DATA_PTR, path, data_len); + ret = afc_dispatch_packet(client, AFC_OP_READ_DIR, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -438,7 +458,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const c return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info(afc_client_t client, char ***device_information) +afc_error_t afc_get_device_info(afc_client_t client, char ***device_information) { uint32_t bytes = 0; char *data = NULL, **list = NULL; @@ -450,7 +470,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info(afc_client_t client, char * afc_lock(client); /* Send the command */ - ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, NULL, 0, NULL, 0, &bytes); + ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -475,7 +495,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info(afc_client_t client, char * return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) +afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) { afc_error_t ret = AFC_E_INTERNAL_ERROR; char **kvps, **ptr; @@ -502,7 +522,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_get_device_info_key(afc_client_t client, co return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_remove_path(afc_client_t client, const char *path) +afc_error_t afc_remove_path(afc_client_t client, const char *path) { uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; @@ -512,8 +532,16 @@ LIBIMOBILEDEVICE_API afc_error_t afc_remove_path(afc_client_t client, const char afc_lock(client); + uint32_t data_len = (uint32_t)strlen(path)+1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send command */ - ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH, path, strlen(path)+1, NULL, 0, &bytes); + memcpy(AFC_PACKET_DATA_PTR, path, data_len); + ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -530,23 +558,30 @@ LIBIMOBILEDEVICE_API afc_error_t afc_remove_path(afc_client_t client, const char return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to) +afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to) { if (!client || !from || !to || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; - char *buffer = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t))); uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; + size_t from_len = strlen(from); + size_t to_len = strlen(to); + afc_lock(client); - /* Send command */ - memcpy(buffer, from, strlen(from) + 1); - memcpy(buffer + strlen(from) + 1, to, strlen(to) + 1); - ret = afc_dispatch_packet(client, AFC_OP_RENAME_PATH, buffer, strlen(to)+1 + strlen(from)+1, NULL, 0, &bytes); - free(buffer); + uint32_t data_len = (uint32_t)(from_len+1 + to_len+1); + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send command */ + memcpy(AFC_PACKET_DATA_PTR, from, from_len+1); + memcpy(AFC_PACKET_DATA_PTR + from_len+1, to, to_len+1); + ret = afc_dispatch_packet(client, AFC_OP_RENAME_PATH, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -559,7 +594,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_rename_path(afc_client_t client, const char return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_make_directory(afc_client_t client, const char *path) +afc_error_t afc_make_directory(afc_client_t client, const char *path) { uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; @@ -569,8 +604,16 @@ LIBIMOBILEDEVICE_API afc_error_t afc_make_directory(afc_client_t client, const c afc_lock(client); + uint32_t data_len = (uint32_t)strlen(path)+1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send command */ - ret = afc_dispatch_packet(client, AFC_OP_MAKE_DIR, path, strlen(path)+1, NULL, 0, &bytes); + memcpy(AFC_PACKET_DATA_PTR, path, data_len); + ret = afc_dispatch_packet(client, AFC_OP_MAKE_DIR, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -583,7 +626,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_make_directory(afc_client_t client, const c return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information) +afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information) { char *received = NULL; uint32_t bytes = 0; @@ -594,8 +637,18 @@ LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const ch afc_lock(client); + uint32_t data_len = (uint32_t)strlen(path)+1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + + debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR); + /* Send command */ - ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, path, strlen(path)+1, NULL, 0, &bytes); + memcpy(AFC_PACKET_DATA_PTR, path, data_len); + ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -613,14 +666,13 @@ LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const ch return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) +afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) { if (!client || !client->parent || !client->afc_packet) return AFC_E_INVALID_ARG; - uint64_t file_mode_loc = htole64(file_mode); + //uint64_t file_mode_loc = htole64(file_mode); uint32_t bytes = 0; - char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1)); afc_error_t ret = AFC_E_UNKNOWN_ERROR; /* set handle to 0 so in case an error occurs, the handle is invalid */ @@ -628,20 +680,25 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_open(afc_client_t client, const char * afc_lock(client); - /* Send command */ - memcpy(data, &file_mode_loc, 8); - memcpy(data + 8, filename, strlen(filename)); - data[8 + strlen(filename)] = '\0'; - ret = afc_dispatch_packet(client, AFC_OP_FILE_OPEN, data, 8 + strlen(filename) + 1, NULL, 0, &bytes); - free(data); + uint32_t data_len = (uint32_t)(strlen(filename)+1 + 8); + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send command */ + //memcpy(AFC_PACKET_DATA_PTR, &file_mode_loc, 8); + *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(file_mode); + memcpy(AFC_PACKET_DATA_PTR + 8, filename, data_len-8); + ret = afc_dispatch_packet(client, AFC_OP_FILE_OPEN, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { debug_info("Didn't receive a response to the command"); afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; } /* Receive the data */ - data = NULL; + char* data = NULL; ret = afc_receive_data(client, &data, &bytes); if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { afc_unlock(client); @@ -661,27 +718,29 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_open(afc_client_t client, const char * return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read) +afc_error_t afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read) { char *input = NULL; uint32_t current_count = 0, bytes_loc = 0; + struct readinfo { + uint64_t handle; + uint64_t size; + }; afc_error_t ret = AFC_E_SUCCESS; if (!client || !client->afc_packet || !client->parent || handle == 0) return AFC_E_INVALID_ARG; debug_info("called for length %i", length); + //uint32_t data_len = 8 + 8; + afc_lock(client); /* Send the read command */ - struct { - uint64_t handle; - uint64_t size; - } readinfo; - readinfo.handle = handle; - readinfo.size = htole64(length); - ret = afc_dispatch_packet(client, AFC_OP_FILE_READ, (const char*)&readinfo, sizeof(readinfo), NULL, 0, &bytes_loc); - + struct readinfo* readinfo = (struct readinfo*)(AFC_PACKET_DATA_PTR); + readinfo->handle = handle; + readinfo->size = htole64(length); + ret = afc_dispatch_packet(client, AFC_OP_FILE_READ, sizeof(struct readinfo), NULL, 0, &bytes_loc); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -693,28 +752,29 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_read(afc_client_t client, uint64_t han if (ret != AFC_E_SUCCESS) { afc_unlock(client); return ret; - } else if (bytes_loc == 0) { + } + if (bytes_loc == 0) { if (input) free(input); afc_unlock(client); *bytes_read = current_count; /* FIXME: check that's actually a success */ return ret; - } else { - if (input) { - debug_info("%d", bytes_loc); - memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); - free(input); - input = NULL; - current_count += (bytes_loc > length) ? length : bytes_loc; - } } + if (input) { + debug_info("%d", bytes_loc); + memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); + free(input); + input = NULL; + current_count += (bytes_loc > length) ? length : bytes_loc; + } + afc_unlock(client); *bytes_read = current_count; return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written) +afc_error_t afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written) { uint32_t current_count = 0; uint32_t bytes_loc = 0; @@ -723,11 +783,14 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_write(afc_client_t client, uint64_t ha if (!client || !client->afc_packet || !client->parent || !bytes_written || (handle == 0)) return AFC_E_INVALID_ARG; + uint32_t data_len = 8; + afc_lock(client); debug_info("Write length: %i", length); - ret = afc_dispatch_packet(client, AFC_OP_FILE_WRITE, (const char*)&handle, 8, data, length, &bytes_loc); + *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; + ret = afc_dispatch_packet(client, AFC_OP_FILE_WRITE, data_len, data, length, &bytes_loc); current_count += bytes_loc - (sizeof(AFCPacket) + 8); @@ -740,13 +803,13 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_write(afc_client_t client, uint64_t ha ret = afc_receive_data(client, NULL, &bytes_loc); afc_unlock(client); if (ret != AFC_E_SUCCESS) { - debug_info("uh oh?"); + debug_info("Failed to receive reply (%d)", ret); } *bytes_written = current_count; return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_close(afc_client_t client, uint64_t handle) +afc_error_t afc_file_close(afc_client_t client, uint64_t handle) { uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; @@ -754,12 +817,15 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_close(afc_client_t client, uint64_t ha if (!client || (handle == 0)) return AFC_E_INVALID_ARG; + uint32_t data_len = 8; + afc_lock(client); debug_info("File handle %i", handle); /* Send command */ - ret = afc_dispatch_packet(client, AFC_OP_FILE_CLOSE, (const char*)&handle, 8, NULL, 0, &bytes); + *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; + ret = afc_dispatch_packet(client, AFC_OP_FILE_CLOSE, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); @@ -774,13 +840,13 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_close(afc_client_t client, uint64_t ha return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) +afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) { uint32_t bytes = 0; - struct { + struct lockinfo { uint64_t handle; uint64_t op; - } lockinfo; + }; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || (handle == 0)) @@ -791,10 +857,10 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_lock(afc_client_t client, uint64_t han debug_info("file handle %i", handle); /* Send command */ - lockinfo.handle = handle; - lockinfo.op = htole64(operation); - ret = afc_dispatch_packet(client, AFC_OP_FILE_LOCK, (const char*)&lockinfo, sizeof(lockinfo), NULL, 0, &bytes); - + struct lockinfo* lockinfo = (struct lockinfo*)(AFC_PACKET_DATA_PTR); + lockinfo->handle = handle; + lockinfo->op = htole64(operation); + ret = afc_dispatch_packet(client, AFC_OP_FILE_LOCK, sizeof(struct lockinfo), NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); debug_info("could not send lock command"); @@ -808,14 +874,14 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_lock(afc_client_t client, uint64_t han return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) +afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) { uint32_t bytes = 0; - struct { + struct seekinfo { uint64_t handle; uint64_t whence; int64_t offset; - } seekinfo; + }; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || (handle == 0)) @@ -824,10 +890,11 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_seek(afc_client_t client, uint64_t han afc_lock(client); /* Send the command */ - seekinfo.handle = handle; - seekinfo.whence = htole64(whence); - seekinfo.offset = (int64_t)htole64(offset); - ret = afc_dispatch_packet(client, AFC_OP_FILE_SEEK, (const char*)&seekinfo, sizeof(seekinfo), NULL, 0, &bytes); + struct seekinfo* seekinfo = (struct seekinfo*)(AFC_PACKET_DATA_PTR); + seekinfo->handle = handle; + seekinfo->whence = htole64(whence); + seekinfo->offset = (int64_t)htole64(offset); + ret = afc_dispatch_packet(client, AFC_OP_FILE_SEEK, sizeof(struct seekinfo), NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); @@ -841,7 +908,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_seek(afc_client_t client, uint64_t han return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position) +afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position) { char *buffer = NULL; uint32_t bytes = 0; @@ -850,11 +917,13 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_tell(afc_client_t client, uint64_t han if (!client || (handle == 0)) return AFC_E_INVALID_ARG; + uint32_t data_len = 8; + afc_lock(client); /* Send the command */ - ret = afc_dispatch_packet(client, AFC_OP_FILE_TELL, (const char*)&handle, 8, NULL, 0, &bytes); - + *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; + ret = afc_dispatch_packet(client, AFC_OP_FILE_TELL, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -874,13 +943,13 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_tell(afc_client_t client, uint64_t han return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize) +afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize) { uint32_t bytes = 0; - struct { + struct truncinfo { uint64_t handle; uint64_t newsize; - } truncinfo; + }; afc_error_t ret = AFC_E_UNKNOWN_ERROR; if (!client || (handle == 0)) @@ -889,9 +958,10 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_truncate(afc_client_t client, uint64_t afc_lock(client); /* Send command */ - truncinfo.handle = handle; - truncinfo.newsize = htole64(newsize); - ret = afc_dispatch_packet(client, AFC_OP_FILE_SET_SIZE, (const char*)&truncinfo, sizeof(truncinfo), NULL, 0, &bytes); + struct truncinfo* truncinfo = (struct truncinfo*)(AFC_PACKET_DATA_PTR); + truncinfo->handle = handle; + truncinfo->newsize = htole64(newsize); + ret = afc_dispatch_packet(client, AFC_OP_FILE_SET_SIZE, sizeof(struct truncinfo), NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); @@ -905,24 +975,27 @@ LIBIMOBILEDEVICE_API afc_error_t afc_file_truncate(afc_client_t client, uint64_t return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize) +afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize) { if (!client || !path || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; - char *buffer = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); uint32_t bytes = 0; - uint64_t size_requested = htole64(newsize); afc_error_t ret = AFC_E_UNKNOWN_ERROR; afc_lock(client); - /* Send command */ - memcpy(buffer, &size_requested, 8); - memcpy(buffer + 8, path, strlen(path) + 1); - ret = afc_dispatch_packet(client, AFC_OP_TRUNCATE, buffer, 8 + strlen(path) + 1, NULL, 0, &bytes); - free(buffer); + uint32_t data_len = 8 + (uint32_t)(strlen(path)+1); + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send command */ + *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(newsize); + memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8); + ret = afc_dispatch_packet(client, AFC_OP_TRUNCATE, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -935,28 +1008,35 @@ LIBIMOBILEDEVICE_API afc_error_t afc_truncate(afc_client_t client, const char *p return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) +afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) { if (!client || !target || !linkname || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; - char *buffer = (char *) malloc(sizeof(char) * (strlen(target)+1 + strlen(linkname)+1 + 8)); uint32_t bytes = 0; - uint64_t type = htole64(linktype); afc_error_t ret = AFC_E_UNKNOWN_ERROR; + size_t target_len = strlen(target); + size_t link_len = strlen(linkname); + afc_lock(client); - debug_info("link type: %lld", type); - debug_info("target: %s, length:%d", target, strlen(target)); - debug_info("linkname: %s, length:%d", linkname, strlen(linkname)); + uint32_t data_len = 8 + target_len + 1 + link_len + 1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + + debug_info("link type: %lld", htole64(linktype)); + debug_info("target: %s, length:%d", target, target_len); + debug_info("linkname: %s, length:%d", linkname, link_len); /* Send command */ - memcpy(buffer, &type, 8); - memcpy(buffer + 8, target, strlen(target) + 1); - memcpy(buffer + 8 + strlen(target) + 1, linkname, strlen(linkname) + 1); - ret = afc_dispatch_packet(client, AFC_OP_MAKE_LINK, buffer, 8 + strlen(linkname) + 1 + strlen(target) + 1, NULL, 0, &bytes); - free(buffer); + *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(linktype); + memcpy(AFC_PACKET_DATA_PTR + 8, target, target_len + 1); + memcpy(AFC_PACKET_DATA_PTR + 8 + target_len + 1, linkname, link_len + 1); + ret = afc_dispatch_packet(client, AFC_OP_MAKE_LINK, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -969,23 +1049,27 @@ LIBIMOBILEDEVICE_API afc_error_t afc_make_link(afc_client_t client, afc_link_typ return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime) +afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime) { if (!client || !path || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; - char *buffer = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); uint32_t bytes = 0; - uint64_t mtime_loc = htole64(mtime); afc_error_t ret = AFC_E_UNKNOWN_ERROR; afc_lock(client); + uint32_t data_len = 8 + strlen(path) + 1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send command */ - memcpy(buffer, &mtime_loc, 8); - memcpy(buffer + 8, path, strlen(path) + 1); - ret = afc_dispatch_packet(client, AFC_OP_SET_FILE_MOD_TIME, buffer, 8 + strlen(path) + 1, NULL, 0, &bytes); - free(buffer); + *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(mtime); + memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8); + ret = afc_dispatch_packet(client, AFC_OP_SET_FILE_MOD_TIME, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -998,7 +1082,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_set_file_time(afc_client_t client, const ch return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_remove_path_and_contents(afc_client_t client, const char *path) +afc_error_t afc_remove_path_and_contents(afc_client_t client, const char *path) { uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; @@ -1008,8 +1092,16 @@ LIBIMOBILEDEVICE_API afc_error_t afc_remove_path_and_contents(afc_client_t clien afc_lock(client); + uint32_t data_len = strlen(path) + 1; + if (_afc_check_packet_buffer(client, data_len) < 0) { + afc_unlock(client); + debug_info("Failed to realloc packet buffer"); + return AFC_E_NO_MEM; + } + /* Send command */ - ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH_AND_CONTENTS, path, strlen(path)+1, NULL, 0, &bytes); + memcpy(AFC_PACKET_DATA_PTR, path, data_len); + ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH_AND_CONTENTS, data_len, NULL, 0, &bytes); if (ret != AFC_E_SUCCESS) { afc_unlock(client); return AFC_E_NOT_ENOUGH_DATA; @@ -1022,7 +1114,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_remove_path_and_contents(afc_client_t clien return ret; } -LIBIMOBILEDEVICE_API afc_error_t afc_dictionary_free(char **dictionary) +afc_error_t afc_dictionary_free(char **dictionary) { int i = 0; @@ -1036,3 +1128,70 @@ LIBIMOBILEDEVICE_API afc_error_t afc_dictionary_free(char **dictionary) return AFC_E_SUCCESS; } + +const char* afc_strerror(afc_error_t err) +{ + switch (err) { + case AFC_E_SUCCESS: + return "Success"; + case AFC_E_UNKNOWN_ERROR: + return "Unknown Error"; + case AFC_E_OP_HEADER_INVALID: + return "Operation header invalid"; + case AFC_E_NO_RESOURCES: + return "No resources"; + case AFC_E_READ_ERROR: + return "Read error"; + case AFC_E_WRITE_ERROR: + return "Write error"; + case AFC_E_UNKNOWN_PACKET_TYPE: + return "Unknown packet type"; + case AFC_E_INVALID_ARG: + return "Invalid argument"; + case AFC_E_OBJECT_NOT_FOUND: + return "Not found"; + case AFC_E_OBJECT_IS_DIR: + return "Object is a directory"; + case AFC_E_PERM_DENIED: + return "Permission denied"; + case AFC_E_SERVICE_NOT_CONNECTED: + return "Service not connected"; + case AFC_E_OP_TIMEOUT: + return "Timeout"; + case AFC_E_TOO_MUCH_DATA: + return "Too much data"; + case AFC_E_END_OF_DATA: + return "End of data"; + case AFC_E_OP_NOT_SUPPORTED: + return "Operation not supported"; + case AFC_E_OBJECT_EXISTS: + return "Object exists"; + case AFC_E_OBJECT_BUSY: + return "Object busy"; + case AFC_E_NO_SPACE_LEFT: + return "No space left on device"; + case AFC_E_OP_WOULD_BLOCK: + return "Operation would block"; + case AFC_E_IO_ERROR: + return "I/O error"; + case AFC_E_OP_INTERRUPTED: + return "Operation interrupted"; + case AFC_E_OP_IN_PROGRESS: + return "Operation on progress"; + case AFC_E_INTERNAL_ERROR: + return "Internal error"; + case AFC_E_MUX_ERROR: + return "MUX error"; + case AFC_E_NO_MEM: + return "Out of memory"; + case AFC_E_NOT_ENOUGH_DATA: + return "Not enough data"; + case AFC_E_DIR_NOT_EMPTY: + return "Directory not empty"; + case AFC_E_FORCE_SIGNED_TYPE: + return "Force signed type"; + default: + break; + } + return "Unknown Error"; +} @@ -28,7 +28,7 @@ #include "libimobiledevice/afc.h" #include "service.h" #include "endianness.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h> #define AFC_MAGIC "CFA6LPAA" #define AFC_MAGIC_LEN (8) @@ -53,8 +53,7 @@ typedef struct { struct afc_client_private { service_client_t parent; AFCPacket *afc_packet; - int file_handle; - int lock; + uint32_t packet_extra; mutex_t mutex; int free_parent; }; diff --git a/src/bt_packet_logger.c b/src/bt_packet_logger.c new file mode 100644 index 0000000..937747c --- /dev/null +++ b/src/bt_packet_logger.c @@ -0,0 +1,231 @@ +/* + * bt_packet_logger.c + * com.apple.bluetooth.BTPacketLogger 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; + uint8_t rxbuff[BT_MAX_PACKET_SIZE]; +}; + +#define SZ_READ_TIMEOUT 100 +#define PAYLOAD_READ_TIMEOUT 500 + +/** + * 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; +} + +bt_packet_logger_error_t bt_packet_logger_client_new(idevice_t device, lockdownd_service_descriptor_t service, bt_packet_logger_client_t * client) +{ + 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; +} + +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; +} + +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; +} + +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, (char*)&len, 2, &bytes, SZ_READ_TIMEOUT); + + 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; + } + + // sanity check received length + if(bytes > 0 && len > sizeof(bt_packet_logger_header_t)) { + debug_info("Reading %u bytes\n", len); + ret = bt_packet_logger_receive_with_timeout(btwt->client, (char *)btwt->rxbuff, len, &bytes, PAYLOAD_READ_TIMEOUT); + + 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(btwt->rxbuff, len, btwt->user_data); + } + } + + // null check performed above + free(btwt); + + debug_info("Exiting"); + + return NULL; +} + +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; +} + + +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..620555e --- /dev/null +++ b/src/bt_packet_logger.h @@ -0,0 +1,37 @@ +/* + * bt_packet_logger.h + * com.apple.bluetooth.BTPacketLogger 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 _BR_PACKET_LOGGER_H +#define _BR_PACKET_LOGGER_H + +#include "idevice.h" +#include "libimobiledevice/bt_packet_logger.h" +#include "service.h" +#include <libimobiledevice-glue/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/src/companion_proxy.c b/src/companion_proxy.c new file mode 100644 index 0000000..421fa9a --- /dev/null +++ b/src/companion_proxy.c @@ -0,0 +1,380 @@ +/* + * companion_proxy.c + * com.apple.companion_proxy service implementation. + * + * Copyright (c) 2019-2020 Nikias Bassen, 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 <plist/plist.h> + +#include "companion_proxy.h" +#include "lockdown.h" +#include "common/debug.h" + +/** + * Convert a property_list_service_error_t value to a companion_proxy_error_t value. + * Used internally to get correct error codes. + * + * @param err An property_list_service_error_t error code + * + * @return A matching companion_proxy_error_t error code, + * COMPANION_PROXY_E_UNKNOWN_ERROR otherwise. + */ +static companion_proxy_error_t companion_proxy_error(property_list_service_error_t err) +{ + switch (err) { + case PROPERTY_LIST_SERVICE_E_SUCCESS: + return COMPANION_PROXY_E_SUCCESS; + case PROPERTY_LIST_SERVICE_E_INVALID_ARG: + return COMPANION_PROXY_E_INVALID_ARG; + case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: + return COMPANION_PROXY_E_PLIST_ERROR; + case PROPERTY_LIST_SERVICE_E_MUX_ERROR: + return COMPANION_PROXY_E_MUX_ERROR; + case PROPERTY_LIST_SERVICE_E_SSL_ERROR: + return COMPANION_PROXY_E_SSL_ERROR; + case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA: + return COMPANION_PROXY_E_NOT_ENOUGH_DATA; + case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: + return COMPANION_PROXY_E_TIMEOUT; + default: + break; + } + return COMPANION_PROXY_E_UNKNOWN_ERROR; +} + +companion_proxy_error_t companion_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, companion_proxy_client_t * client) +{ + *client = NULL; + + if (!device || !service || service->port == 0 || !client || *client) { + debug_info("Incorrect parameter passed to companion_proxy_client_new."); + return COMPANION_PROXY_E_INVALID_ARG; + } + + debug_info("Creating companion_proxy_client, port = %d.", service->port); + + property_list_service_client_t plclient = NULL; + companion_proxy_error_t ret = companion_proxy_error(property_list_service_client_new(device, service, &plclient)); + if (ret != COMPANION_PROXY_E_SUCCESS) { + debug_info("Creating a property list client failed. Error: %i", ret); + return ret; + } + + companion_proxy_client_t client_loc = (companion_proxy_client_t) malloc(sizeof(struct companion_proxy_client_private)); + client_loc->parent = plclient; + client_loc->event_thread = THREAD_T_NULL; + + *client = client_loc; + + debug_info("Created companion_proxy_client successfully."); + return COMPANION_PROXY_E_SUCCESS; +} + +companion_proxy_error_t companion_proxy_client_start_service(idevice_t device, companion_proxy_client_t * client, const char* label) +{ + companion_proxy_error_t err = COMPANION_PROXY_E_UNKNOWN_ERROR; + service_client_factory_start_service(device, COMPANION_PROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(companion_proxy_client_new), &err); + return err; +} + +companion_proxy_error_t companion_proxy_client_free(companion_proxy_client_t client) +{ + if (!client) + return COMPANION_PROXY_E_INVALID_ARG; + + property_list_service_client_t parent = client->parent; + client->parent = NULL; + if (client->event_thread) { + debug_info("joining event thread"); + thread_join(client->event_thread); + thread_free(client->event_thread); + client->event_thread = THREAD_T_NULL; + } + companion_proxy_error_t err = companion_proxy_error(property_list_service_client_free(parent)); + free(client); + + return err; +} + +companion_proxy_error_t companion_proxy_send(companion_proxy_client_t client, plist_t plist) +{ + companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR; + + res = companion_proxy_error(property_list_service_send_binary_plist(client->parent, plist)); + if (res != COMPANION_PROXY_E_SUCCESS) { + debug_info("Sending plist failed with error %d", res); + return res; + } + + return res; +} + +companion_proxy_error_t companion_proxy_receive(companion_proxy_client_t client, plist_t * plist) +{ + companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR; + plist_t outplist = NULL; + res = companion_proxy_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, 10000)); + if (res != COMPANION_PROXY_E_SUCCESS && res != COMPANION_PROXY_E_TIMEOUT) { + debug_info("Could not receive plist, error %d", res); + plist_free(outplist); + } else if (res == COMPANION_PROXY_E_SUCCESS) { + *plist = outplist; + } + return res; +} + +companion_proxy_error_t companion_proxy_get_device_registry(companion_proxy_client_t client, plist_t* paired_devices) +{ + if (!client || !paired_devices) { + return COMPANION_PROXY_E_INVALID_ARG; + } + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string("GetDeviceRegistry")); + + companion_proxy_error_t res = companion_proxy_send(client, dict); + plist_free(dict); + dict = NULL; + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + + res = companion_proxy_receive(client, &dict); + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + if (!dict || !PLIST_IS_DICT(dict)) { + return COMPANION_PROXY_E_PLIST_ERROR; + } + plist_t val = plist_dict_get_item(dict, "PairedDevicesArray"); + if (val) { + *paired_devices = plist_copy(val); + res = COMPANION_PROXY_E_SUCCESS; + } else { + res = COMPANION_PROXY_E_UNKNOWN_ERROR; + val = plist_dict_get_item(dict, "Error"); + if (val) { + if (plist_string_val_compare(val, "NoPairedWatches")) { + res = COMPANION_PROXY_E_NO_DEVICES; + } + } + } + plist_free(dict); + return res; +} + +struct companion_proxy_cb_data { + companion_proxy_client_t client; + companion_proxy_device_event_cb_t cbfunc; + void* user_data; +}; + +static void* companion_proxy_event_thread(void* arg) +{ + struct companion_proxy_cb_data* data = (struct companion_proxy_cb_data*)arg; + companion_proxy_client_t client = data->client; + companion_proxy_error_t res; + + plist_t command = plist_new_dict(); + plist_dict_set_item(command, "Command", plist_new_string("StartListeningForDevices")); + res = companion_proxy_send(client, command); + plist_free(command); + + if (res != COMPANION_PROXY_E_SUCCESS) { + free(data); + client->event_thread = THREAD_T_NULL; + return NULL; + } + + while (client && client->parent) { + plist_t node = NULL; + res = companion_proxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000)); + if (res != COMPANION_PROXY_E_SUCCESS && res != COMPANION_PROXY_E_TIMEOUT) { + debug_info("could not receive plist, error %d", res); + break; + } + + if (node) { + data->cbfunc(node, data->user_data); + } + plist_free(node); + } + + client->event_thread = THREAD_T_NULL; + free(data); + + return NULL; +} + +companion_proxy_error_t companion_proxy_start_listening_for_devices(companion_proxy_client_t client, companion_proxy_device_event_cb_t callback, void* userdata) +{ + if (!client || !client->parent || !callback) { + return COMPANION_PROXY_E_INVALID_ARG; + } + + if (client->event_thread) { + return COMPANION_PROXY_E_OP_IN_PROGRESS; + } + + companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR; + struct companion_proxy_cb_data *data = (struct companion_proxy_cb_data*)malloc(sizeof(struct companion_proxy_cb_data)); + if (data) { + data->client = client; + data->cbfunc = callback; + data->user_data = userdata; + + if (thread_new(&client->event_thread, companion_proxy_event_thread, data) == 0) { + res = COMPANION_PROXY_E_SUCCESS; + } else { + free(data); + } + } + return res; +} + +companion_proxy_error_t companion_proxy_stop_listening_for_devices(companion_proxy_client_t client) +{ + property_list_service_client_t parent = client->parent; + client->parent = NULL; + if (client->event_thread) { + debug_info("joining event thread"); + thread_join(client->event_thread); + thread_free(client->event_thread); + client->event_thread = THREAD_T_NULL; + } + client->parent = parent; + return COMPANION_PROXY_E_SUCCESS; +} + +companion_proxy_error_t companion_proxy_get_value_from_registry(companion_proxy_client_t client, const char* companion_udid, const char* key, plist_t* value) +{ + if (!client || !companion_udid || !key || !value) { + return COMPANION_PROXY_E_INVALID_ARG; + } + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string("GetValueFromRegistry")); + plist_dict_set_item(dict, "GetValueGizmoUDIDKey", plist_new_string(companion_udid)); + plist_dict_set_item(dict, "GetValueKeyKey", plist_new_string(key)); + + companion_proxy_error_t res = companion_proxy_send(client, dict); + plist_free(dict); + dict = NULL; + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + + res = companion_proxy_receive(client, &dict); + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + if (!dict || !PLIST_IS_DICT(dict)) { + return COMPANION_PROXY_E_PLIST_ERROR; + } + plist_t val = plist_dict_get_item(dict, "RetrievedValueDictionary"); + if (val) { + *value = plist_copy(val); + res = COMPANION_PROXY_E_SUCCESS; + } else { + res = COMPANION_PROXY_E_UNKNOWN_ERROR; + val = plist_dict_get_item(dict, "Error"); + if (val) { + if (!plist_string_val_compare(val, "UnsupportedWatchKey")) { + res = COMPANION_PROXY_E_UNSUPPORTED_KEY; + } else if (plist_string_val_compare(val, "TimeoutReply")) { + res = COMPANION_PROXY_E_TIMEOUT_REPLY; + } + } + } + plist_free(dict); + return res; +} + +companion_proxy_error_t companion_proxy_start_forwarding_service_port(companion_proxy_client_t client, uint16_t remote_port, const char* service_name, uint16_t* forward_port, plist_t options) +{ + if (!client) { + return COMPANION_PROXY_E_INVALID_ARG; + } + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string("StartForwardingServicePort")); + plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port)); + if (service_name) { + plist_dict_set_item(dict, "ForwardedServiceName", plist_new_string(service_name)); + } + plist_dict_set_item(dict, "IsServiceLowPriority", plist_new_bool(0)); + plist_dict_set_item(dict, "PreferWifi", plist_new_bool(0)); + if (options) { + plist_dict_merge(&dict, options); + } + + companion_proxy_error_t res = companion_proxy_send(client, dict); + plist_free(dict); + dict = NULL; + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + + res = companion_proxy_receive(client, &dict); + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + plist_t val = plist_dict_get_item(dict, "CompanionProxyServicePort"); + if (val) { + uint64_t u64val = 0; + plist_get_uint_val(val, &u64val); + *forward_port = (uint16_t)u64val; + res = COMPANION_PROXY_E_SUCCESS; + } else { + res = COMPANION_PROXY_E_UNKNOWN_ERROR; + } + plist_free(dict); + + return res; +} + +companion_proxy_error_t companion_proxy_stop_forwarding_service_port(companion_proxy_client_t client, uint16_t remote_port) +{ + if (!client) { + return COMPANION_PROXY_E_INVALID_ARG; + } + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string("StopForwardingServicePort")); + plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port)); + + companion_proxy_error_t res = companion_proxy_send(client, dict); + plist_free(dict); + dict = NULL; + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + + res = companion_proxy_receive(client, &dict); + if (res != COMPANION_PROXY_E_SUCCESS) { + return res; + } + plist_free(dict); + + return res; +} diff --git a/src/companion_proxy.h b/src/companion_proxy.h new file mode 100644 index 0000000..e36932a --- /dev/null +++ b/src/companion_proxy.h @@ -0,0 +1,35 @@ +/* + * companion_proxy.h + * com.apple.companion_proxy service header file. + * + * Copyright (c) 2019-2020 Nikias Bassen, 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 __COMPANION_PROXY_H +#define __COMPANION_PROXY_H + +#include "idevice.h" +#include "libimobiledevice/companion_proxy.h" +#include "property_list_service.h" +#include <libimobiledevice-glue/thread.h> + +struct companion_proxy_client_private { + property_list_service_client_t parent; + THREAD_T event_thread; +}; + +#endif diff --git a/src/debugserver.c b/src/debugserver.c index 41546dd..74ade8a 100644 --- a/src/debugserver.c +++ b/src/debugserver.c @@ -2,7 +2,8 @@ * debugserver.c * com.apple.debugserver service implementation. * - * Copyright (c) 2014 Martin Szulecki All Rights Reserved. + * Copyright (c) 2019 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2014-2015 Martin Szulecki 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 @@ -28,10 +29,11 @@ #define __USE_GNU 1 #include <stdio.h> +#include <libimobiledevice-glue/utils.h> + #include "debugserver.h" #include "lockdown.h" #include "common/debug.h" -#include "common/utils.h" #include "asprintf.h" /** @@ -54,13 +56,15 @@ static debugserver_error_t debugserver_error(service_error_t err) return DEBUGSERVER_E_MUX_ERROR; case SERVICE_E_SSL_ERROR: return DEBUGSERVER_E_SSL_ERROR; + case SERVICE_E_TIMEOUT: + return DEBUGSERVER_E_TIMEOUT; default: break; } return DEBUGSERVER_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device, lockdownd_service_descriptor_t service, debugserver_client_t* client) +debugserver_error_t debugserver_client_new(idevice_t device, lockdownd_service_descriptor_t service, debugserver_client_t* client) { *client = NULL; @@ -78,9 +82,15 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device return ret; } + if (service->identifier && (strcmp(service->identifier, DEBUGSERVER_SECURE_SERVICE_NAME) != 0)) { + service_disable_bypass_ssl(parent, 1); + } + debugserver_client_t client_loc = (debugserver_client_t) malloc(sizeof(struct debugserver_client_private)); client_loc->parent = parent; client_loc->noack_mode = 0; + client_loc->cancel_receive = NULL; + client_loc->receive_loop_timeout = 1000; *client = client_loc; @@ -88,14 +98,18 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device return DEBUGSERVER_E_SUCCESS; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_start_service(idevice_t device, debugserver_client_t * client, const char* label) +debugserver_error_t debugserver_client_start_service(idevice_t device, debugserver_client_t * client, const char* label) { debugserver_error_t err = DEBUGSERVER_E_UNKNOWN_ERROR; - service_client_factory_start_service(device, DEBUGSERVER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); + service_client_factory_start_service(device, DEBUGSERVER_SECURE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); + if (err != DEBUGSERVER_E_SUCCESS) { + err = DEBUGSERVER_E_UNKNOWN_ERROR; + service_client_factory_start_service(device, DEBUGSERVER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); + } return err; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_free(debugserver_client_t client) +debugserver_error_t debugserver_client_free(debugserver_client_t client) { if (!client) return DEBUGSERVER_E_INVALID_ARG; @@ -107,7 +121,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_free(debugserver_cli return err; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send(debugserver_client_t client, const char* data, uint32_t size, uint32_t *sent) +debugserver_error_t debugserver_client_send(debugserver_client_t client, const char* data, uint32_t size, uint32_t *sent) { debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; int bytes = 0; @@ -128,7 +142,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send(debugserver_cli return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) +debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) { debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; int bytes = 0; @@ -138,22 +152,27 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_with_timeout } res = debugserver_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout)); - if (bytes <= 0) { + if (bytes <= 0 && res != DEBUGSERVER_E_TIMEOUT) { debug_info("Could not read data, error %d", res); } if (received) { *received = (uint32_t)bytes; } - return res; + return (bytes > 0) ? DEBUGSERVER_E_SUCCESS : res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received) +debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received) { - return debugserver_client_receive_with_timeout(client, data, size, received, 1000); + debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; + do { + /* Is this allowed to return DEBUGSERVER_E_TIMEOUT and also set data and received? */ + res = debugserver_client_receive_with_timeout(client, data, size, received, client->receive_loop_timeout); + } while (res == DEBUGSERVER_E_TIMEOUT && client->cancel_receive != NULL && !client->cancel_receive()); + return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_command_new(const char* name, int argc, char* argv[], debugserver_command_t* command) +debugserver_error_t debugserver_command_new(const char* name, int argc, char* argv[], debugserver_command_t* command) { int i; debugserver_command_t tmp = (debugserver_command_t) malloc(sizeof(struct debugserver_command_private)); @@ -178,7 +197,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_command_new(const char* nam return DEBUGSERVER_E_SUCCESS; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_command_free(debugserver_command_t command) +debugserver_error_t debugserver_command_free(debugserver_command_t command) { int i; debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; @@ -220,10 +239,10 @@ static char debugserver_int2hex(int x) return hexchars[x]; } -#define DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(byte) debugserver_int2hex((byte >> 0x4) & 0xf) -#define DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(byte) debugserver_int2hex(byte & 0xf) -#define DEBUGSERVER_HEX_DECODE_FIRST_BYTE(byte) ((byte >> 0x4) & 0xf) -#define DEBUGSERVER_HEX_DECODE_SECOND_BYTE(byte) (byte & 0xf) +#define DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(byte) debugserver_int2hex(((byte) >> 0x4) & 0xf) +#define DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(byte) debugserver_int2hex((byte) & 0xf) +#define DEBUGSERVER_HEX_DECODE_FIRST_BYTE(byte) (((byte) >> 0x4) & 0xf) +#define DEBUGSERVER_HEX_DECODE_SECOND_BYTE(byte) ((byte) & 0xf) static uint32_t debugserver_get_checksum_for_buffer(const char* buffer, uint32_t size) { @@ -256,7 +275,7 @@ static int debugserver_response_is_checksum_valid(const char* response, uint32_t return 1; } -LIBIMOBILEDEVICE_API void debugserver_encode_string(const char* buffer, char** encoded_buffer, uint32_t* encoded_length) +void debugserver_encode_string(const char* buffer, char** encoded_buffer, uint32_t* encoded_length) { uint32_t position; uint32_t index; @@ -272,7 +291,7 @@ LIBIMOBILEDEVICE_API void debugserver_encode_string(const char* buffer, char** e } } -LIBIMOBILEDEVICE_API void debugserver_decode_string(const char *encoded_buffer, size_t encoded_length, char** buffer) +void debugserver_decode_string(const char *encoded_buffer, size_t encoded_length, char** buffer) { *buffer = malloc(sizeof(char) * ((encoded_length / 2)+1)); char* t = *buffer; @@ -331,7 +350,7 @@ static debugserver_error_t debugserver_client_send_noack(debugserver_client_t cl return debugserver_client_send(client, "-", sizeof(char), NULL); } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled) +debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled) { if (!client) return DEBUGSERVER_E_INVALID_ARG; @@ -343,119 +362,137 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_ack_mode(debugse return DEBUGSERVER_E_SUCCESS; } -static int debugserver_client_receive_internal_check(debugserver_client_t client, char* received_char) +debugserver_error_t debugserver_client_set_receive_params(debugserver_client_t client, int (*cancel_receive)(), int receive_loop_timeout) +{ + if (!client) + return DEBUGSERVER_E_INVALID_ARG; + + client->cancel_receive = cancel_receive; + client->receive_loop_timeout = receive_loop_timeout; + + debug_info("receive params: cancel_receive %s, receive_loop_timeout %dms", (client->cancel_receive == NULL ? "unset": "set"), client->receive_loop_timeout); + + return DEBUGSERVER_E_SUCCESS; +} + +static debugserver_error_t debugserver_client_receive_internal_char(debugserver_client_t client, char* received_char) { debugserver_error_t res = DEBUGSERVER_E_SUCCESS; - int did_receive_char = 0; - char buffer = 0; uint32_t bytes = 0; /* we loop here as we expect an answer */ - res = debugserver_client_receive_with_timeout(client, &buffer, sizeof(char), &bytes, 1000); - if (res == DEBUGSERVER_E_SUCCESS && received_char[0] != 0) { - if (memcmp(&buffer, received_char, sizeof(char)) == 0) { - did_receive_char = 1; - } - } else { - did_receive_char = 0; + res = debugserver_client_receive(client, received_char, sizeof(char), &bytes); + if (res != DEBUGSERVER_E_SUCCESS) { + return res; } - - if (!did_receive_char) { - memcpy(received_char, &buffer, sizeof(char)); + if (bytes != 1) { + debug_info("received %d bytes when asking for %d!", bytes, sizeof(char)); + return DEBUGSERVER_E_UNKNOWN_ERROR; } - - return did_receive_char; + return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_response(debugserver_client_t client, char** response) +debugserver_error_t debugserver_client_receive_response(debugserver_client_t client, char** response, size_t* response_size) { debugserver_error_t res = DEBUGSERVER_E_SUCCESS; - int should_receive = 1; + char data = '\0'; int skip_prefix = 0; - char* command_prefix = strdup("$"); - char* buffer = NULL; + char* buffer = malloc(1024); uint32_t buffer_size = 0; + uint32_t buffer_capacity = 1024; if (response) *response = NULL; if (!client->noack_mode) { - char ack[2] = {'+', '\0'}; - debug_info("attempting to receive ACK %c", *ack); - should_receive = debugserver_client_receive_internal_check(client, ack); - debug_info("received char: %c", *ack); - if (strncmp(ack, command_prefix, sizeof(char)) == 0) { - should_receive = 1; + debug_info("attempting to receive ACK (+)"); + res = debugserver_client_receive_internal_char(client, &data); + if (res != DEBUGSERVER_E_SUCCESS) { + goto cleanup; + } + if (data == '+') { + debug_info("received ACK (+)"); + } else if (data == '$') { + debug_info("received prefix ($)"); + buffer[0] = '$'; + buffer_size = 1; skip_prefix = 1; - buffer = strdup(command_prefix); - buffer_size += sizeof(char); - debug_info("received ACK"); + } else { + debug_info("unrecognized response when looking for ACK: %c", data); + goto cleanup; } } - debug_info("should_receive: %d, skip_prefix: %d", should_receive, skip_prefix); + debug_info("skip_prefix: %d", skip_prefix); - if (should_receive && !skip_prefix) { - debug_info("attempting to receive prefix"); - should_receive = debugserver_client_receive_internal_check(client, command_prefix); - debug_info("received command_prefix: %c", *command_prefix); - if (should_receive) { - if (buffer) { - memcpy(buffer, command_prefix, sizeof(char)); - } else { - buffer = strdup(command_prefix); - buffer_size += sizeof(char); - } + if (!skip_prefix) { + debug_info("attempting to receive prefix ($)"); + res = debugserver_client_receive_internal_char(client, &data); + if (res != DEBUGSERVER_E_SUCCESS) { + goto cleanup; + } + if (data == '$') { + debug_info("received prefix ($)"); + buffer[0] = '$'; + buffer_size = 1; + } else { + debug_info("unrecognized response when looking for prefix: %c", data); + goto cleanup; } } - debug_info("buffer: %*s, should_receive: %d, skip_prefix: %d", buffer_size, buffer, should_receive, skip_prefix); + uint32_t checksum_length = DEBUGSERVER_CHECKSUM_HASH_LENGTH; + int receiving_checksum_response = 0; + debug_info("attempting to read up response until checksum"); - if (should_receive) { - uint32_t checksum_length = DEBUGSERVER_CHECKSUM_HASH_LENGTH; - int receiving_checksum_response = 0; - debug_info("attempting to read up response until checksum"); - while ((checksum_length > 0)) { - char data[2] = {'#', '\0'}; - if (debugserver_client_receive_internal_check(client, data)) { - receiving_checksum_response = 1; - } - if (receiving_checksum_response) { - checksum_length--; + while ((checksum_length > 0)) { + res = debugserver_client_receive_internal_char(client, &data); + if (res != DEBUGSERVER_E_SUCCESS) { + goto cleanup; + } + if (data == '#') { + receiving_checksum_response = 1; + } + if (receiving_checksum_response) { + checksum_length--; + } + if (buffer_size + 1 >= buffer_capacity) { + char* newbuffer = realloc(buffer, buffer_capacity+1024); + if (!newbuffer) { + return DEBUGSERVER_E_UNKNOWN_ERROR; } - char* newbuffer = string_concat(buffer, data, NULL); - buffer_size += sizeof(char); - free(buffer); - buffer = NULL; buffer = newbuffer; - newbuffer = NULL; + buffer_capacity += 1024; } - debug_info("validating response checksum..."); - int valid_response = debugserver_response_is_checksum_valid(buffer, buffer_size); - if (valid_response) { - if (response) { - /* assemble response string */ - uint32_t response_size = sizeof(char) * (buffer_size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1); - *response = (char*)malloc(response_size + 1); - memcpy(*response, buffer + 1, response_size); - (*response)[response_size] = '\0'; - } - if (!client->noack_mode) { - /* confirm valid command */ - debugserver_client_send_ack(client); - } - } else { - /* response was invalid */ - res = DEBUGSERVER_E_RESPONSE_ERROR; - if (!client->noack_mode) { - /* report invalid command */ - debugserver_client_send_noack(client); - } + buffer[buffer_size] = data; + buffer_size += sizeof(char); + } + debug_info("validating response checksum..."); + if (client->noack_mode || debugserver_response_is_checksum_valid(buffer, buffer_size)) { + if (response) { + /* assemble response string */ + uint32_t resp_size = sizeof(char) * (buffer_size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1); + *response = (char*)malloc(resp_size + 1); + memcpy(*response, buffer + 1, resp_size); + (*response)[resp_size] = '\0'; + if (response_size) *response_size = resp_size; + } + if (!client->noack_mode) { + /* confirm valid command */ + debugserver_client_send_ack(client); + } + } else { + /* response was invalid */ + res = DEBUGSERVER_E_RESPONSE_ERROR; + if (!client->noack_mode) { + /* report invalid command */ + debugserver_client_send_noack(client); } } +cleanup: if (response) { debug_info("response: %s", *response); } @@ -463,13 +500,10 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_response(deb if (buffer) free(buffer); - if (command_prefix) - free(command_prefix); - return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send_command(debugserver_client_t client, debugserver_command_t command, char** response) +debugserver_error_t debugserver_client_send_command(debugserver_client_t client, debugserver_command_t command, char** response, size_t* response_size) { debugserver_error_t res = DEBUGSERVER_E_SUCCESS; int i; @@ -481,25 +515,15 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send_command(debugse char* command_arguments = NULL; /* concat all arguments */ - char* tmp = NULL; - char* newtmp = NULL; for (i = 0; i < command->argc; i++) { debug_info("argv[%d]: %s", i, command->argv[i]); - if (!tmp) { - tmp = strdup(command->argv[i]); - } else { - newtmp = string_concat(tmp, command->argv[i], NULL); - free(tmp); - tmp = newtmp; - } + command_arguments = string_append(command_arguments, command->argv[i], NULL); } - command_arguments = tmp; - tmp = NULL; debug_info("command_arguments(%d): %s", command->argc, command_arguments); /* encode command arguments, add checksum if required and assemble entire command */ - debugserver_format_command("$", command->name, command_arguments, !client->noack_mode, &send_buffer, &send_buffer_size); + debugserver_format_command("$", command->name, command_arguments, 1, &send_buffer, &send_buffer_size); debug_info("sending encoded command: %s", send_buffer); @@ -510,7 +534,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send_command(debugse } /* receive response */ - res = debugserver_client_receive_response(client, response); + res = debugserver_client_receive_response(client, response, response_size); debug_info("response result: %d", res); if (res != DEBUGSERVER_E_SUCCESS) { goto cleanup; @@ -535,7 +559,7 @@ cleanup: return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_environment_hex_encoded(debugserver_client_t client, const char* env, char** response) +debugserver_error_t debugserver_client_set_environment_hex_encoded(debugserver_client_t client, const char* env, char** response) { if (!client || !env) return DEBUGSERVER_E_INVALID_ARG; @@ -546,7 +570,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_environment_hex_ debugserver_command_t command = NULL; debugserver_command_new("QEnvironmentHexEncoded:", 1, env_arg, &command); - result = debugserver_client_send_command(client, command, response); + result = debugserver_client_send_command(client, command, response, NULL); debugserver_command_free(command); free(env_tmp); @@ -554,21 +578,25 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_environment_hex_ return result; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_argv(debugserver_client_t client, int argc, char* argv[], char** response) +debugserver_error_t debugserver_client_set_argv(debugserver_client_t client, int argc, char* argv[], char** response) { if (!client || !argc) return DEBUGSERVER_E_INVALID_ARG; debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR; char *pkt = NULL; - int pkt_len = 0; + size_t pkt_len = 0; int i = 0; /* calculate total length */ while (i < argc && argv && argv[i]) { char *prefix = NULL; - asprintf(&prefix, ",%d,%d,", (int)strlen(argv[i]) * 2, i); - pkt_len += (int)strlen(prefix) + (int)strlen(argv[i]) * 2; + int ret = asprintf(&prefix, ",%zu,%d,", strlen(argv[i]) * 2, i); + if (ret < 0 || prefix == NULL) { + debug_info("asprintf failed, out of memory?"); + return DEBUGSERVER_E_UNKNOWN_ERROR; + } + pkt_len += strlen(prefix) + strlen(argv[i]) * 2; free(prefix); i++; } @@ -585,17 +613,21 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_argv(debugserver char *prefix = NULL; char *m = NULL; - int arg_len = strlen(argv[i]); - int arg_hexlen = arg_len * 2; + size_t arg_len = strlen(argv[i]); + size_t arg_hexlen = arg_len * 2; - asprintf(&prefix, ",%d,%d,", arg_hexlen, i); + int ret = asprintf(&prefix, ",%zu,%d,", arg_hexlen, i); + if (ret < 0 || prefix == NULL) { + debug_info("asprintf failed, out of memory?"); + return DEBUGSERVER_E_UNKNOWN_ERROR; + } m = (char *) malloc(arg_hexlen); char *p = m; char *q = (char*)argv[i]; while (*q) { - *p++ = debugserver_int2hex(*q >> 4); - *p++ = debugserver_int2hex(*q & 0xf); + *p++ = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(*q); + *p++ = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(*q); q++; } @@ -615,7 +647,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_argv(debugserver debugserver_command_t command = NULL; debugserver_command_new(pkt, 0, NULL, &command); - result = debugserver_client_send_command(client, command, response); + result = debugserver_client_send_command(client, command, response, NULL); debugserver_command_free(command); if (pkt) diff --git a/src/debugserver.h b/src/debugserver.h index 05cd97b..ce9c255 100644 --- a/src/debugserver.h +++ b/src/debugserver.h @@ -22,6 +22,7 @@ #ifndef _DEBUGSERVER_H #define _DEBUGSERVER_H +#include "idevice.h" #include "libimobiledevice/debugserver.h" #include "service.h" @@ -30,6 +31,8 @@ struct debugserver_client_private { service_client_t parent; int noack_mode; + int (*cancel_receive)(); + int receive_loop_timeout; }; struct debugserver_command_private { diff --git a/src/device_link_service.c b/src/device_link_service.c index 007223e..66c2461 100644 --- a/src/device_link_service.c +++ b/src/device_link_service.c @@ -2,7 +2,7 @@ * device_link_service.c * DeviceLink service implementation. * - * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2010-2019 Nikias Bassen, 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 @@ -18,12 +18,37 @@ * 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 "device_link_service.h" #include "property_list_service.h" #include "common/debug.h" +static device_link_service_error_t device_link_error(property_list_service_error_t err) +{ + switch (err) { + case PROPERTY_LIST_SERVICE_E_SUCCESS: + return DEVICE_LINK_SERVICE_E_SUCCESS; + case PROPERTY_LIST_SERVICE_E_INVALID_ARG: + return DEVICE_LINK_SERVICE_E_INVALID_ARG; + case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: + return DEVICE_LINK_SERVICE_E_PLIST_ERROR; + case PROPERTY_LIST_SERVICE_E_MUX_ERROR: + return DEVICE_LINK_SERVICE_E_MUX_ERROR; + case PROPERTY_LIST_SERVICE_E_SSL_ERROR: + return DEVICE_LINK_SERVICE_E_SSL_ERROR; + case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: + return DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT; + default: + break; + } + return DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR; +} + /** * Internally used function to extract the message string from a DL* message * plist. @@ -58,7 +83,7 @@ static int device_link_service_get_message(plist_t dl_msg, char **message) return 0; } - if ((strlen(cmd_str) < 9) || (strncmp(cmd_str, "DL", 2))) { + if ((strlen(cmd_str) < 9) || (strncmp(cmd_str, "DL", 2) != 0)) { free(cmd_str); return 0; } @@ -89,18 +114,15 @@ device_link_service_error_t device_link_service_client_new(idevice_t device, loc } property_list_service_client_t plistclient = NULL; - if (property_list_service_client_new(device, service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - return DEVICE_LINK_SERVICE_E_MUX_ERROR; + device_link_service_error_t err = device_link_error(property_list_service_client_new(device, service, &plistclient)); + if (err != DEVICE_LINK_SERVICE_E_SUCCESS) { + return err; } /* create client object */ device_link_service_client_t client_loc = (device_link_service_client_t) malloc(sizeof(struct device_link_service_client_private)); client_loc->parent = plistclient; - /* enable SSL if requested */ - if (service->ssl_enabled) - property_list_service_enable_ssl(client_loc->parent); - /* all done, return success */ *client = client_loc; return DEVICE_LINK_SERVICE_E_SUCCESS; @@ -121,11 +143,10 @@ device_link_service_error_t device_link_service_client_free(device_link_service_ if (!client) return DEVICE_LINK_SERVICE_E_INVALID_ARG; - if (property_list_service_client_free(client->parent) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - return DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR; - } + device_link_service_error_t err = device_link_error(property_list_service_client_free(client->parent)); free(client); - return DEVICE_LINK_SERVICE_E_SUCCESS; + + return err; } /** @@ -157,13 +178,13 @@ device_link_service_error_t device_link_service_version_exchange(device_link_ser char *msg = NULL; /* receive DLMessageVersionExchange from device */ - if (property_list_service_receive_plist(client->parent, &array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { + err = device_link_error(property_list_service_receive_plist(client->parent, &array)); + if (err != DEVICE_LINK_SERVICE_E_SUCCESS) { debug_info("Did not receive initial message from device!"); - err = DEVICE_LINK_SERVICE_E_MUX_ERROR; goto leave; } device_link_service_get_message(array, &msg); - if (!msg || strcmp(msg, "DLMessageVersionExchange")) { + if (!msg || strcmp(msg, "DLMessageVersionExchange") != 0) { debug_info("Did not receive DLMessageVersionExchange from device!"); err = DEVICE_LINK_SERVICE_E_PLIST_ERROR; goto leave; @@ -203,22 +224,22 @@ device_link_service_error_t device_link_service_version_exchange(device_link_ser plist_array_append_item(array, plist_new_string("DLMessageVersionExchange")); plist_array_append_item(array, plist_new_string("DLVersionsOk")); plist_array_append_item(array, plist_new_uint(version_major)); - if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { + err = device_link_error(property_list_service_send_binary_plist(client->parent, array)); + if (err != DEVICE_LINK_SERVICE_E_SUCCESS) { debug_info("Error when sending DLVersionsOk"); - err = DEVICE_LINK_SERVICE_E_MUX_ERROR; goto leave; } plist_free(array); /* receive DeviceReady message */ array = NULL; - if (property_list_service_receive_plist(client->parent, &array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { + err = device_link_error(property_list_service_receive_plist(client->parent, &array)); + if (err != DEVICE_LINK_SERVICE_E_SUCCESS) { debug_info("Error when receiving DLMessageDeviceReady!"); - err = DEVICE_LINK_SERVICE_E_MUX_ERROR; goto leave; } device_link_service_get_message(array, &msg); - if (!msg || strcmp(msg, "DLMessageDeviceReady")) { + if (!msg || strcmp(msg, "DLMessageDeviceReady") != 0) { debug_info("Did not get DLMessageDeviceReady!"); err = DEVICE_LINK_SERVICE_E_PLIST_ERROR; goto leave; @@ -258,11 +279,9 @@ device_link_service_error_t device_link_service_disconnect(device_link_service_c else plist_array_append_item(array, plist_new_string("___EmptyParameterString___")); - device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS; - if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - err = DEVICE_LINK_SERVICE_E_MUX_ERROR; - } + device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array)); plist_free(array); + return err; } @@ -286,11 +305,9 @@ device_link_service_error_t device_link_service_send_ping(device_link_service_cl plist_array_append_item(array, plist_new_string("DLMessagePing")); plist_array_append_item(array, plist_new_string(message)); - device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS; - if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - err = DEVICE_LINK_SERVICE_E_MUX_ERROR; - } + device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array)); plist_free(array); + return err; } @@ -317,11 +334,9 @@ device_link_service_error_t device_link_service_send_process_message(device_link plist_array_append_item(array, plist_new_string("DLMessageProcessMessage")); plist_array_append_item(array, plist_copy(message)); - device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS; - if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - err = DEVICE_LINK_SERVICE_E_MUX_ERROR; - } + device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array)); plist_free(array); + return err; } @@ -348,8 +363,9 @@ device_link_service_error_t device_link_service_receive_message(device_link_serv return DEVICE_LINK_SERVICE_E_INVALID_ARG; *msg_plist = NULL; - if (property_list_service_receive_plist(client->parent, msg_plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - return DEVICE_LINK_SERVICE_E_MUX_ERROR; + device_link_service_error_t err = device_link_error(property_list_service_receive_plist(client->parent, msg_plist)); + if (err != DEVICE_LINK_SERVICE_E_SUCCESS) { + return err; } if (!device_link_service_get_message(*msg_plist, dlmessage)) { @@ -378,15 +394,16 @@ device_link_service_error_t device_link_service_receive_process_message(device_l return DEVICE_LINK_SERVICE_E_INVALID_ARG; plist_t pmsg = NULL; - if (property_list_service_receive_plist(client->parent, &pmsg) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - return DEVICE_LINK_SERVICE_E_MUX_ERROR; + device_link_service_error_t err = device_link_error(property_list_service_receive_plist(client->parent, &pmsg)); + if (err != DEVICE_LINK_SERVICE_E_SUCCESS) { + return err; } - device_link_service_error_t err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR; + err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR; char *msg = NULL; device_link_service_get_message(pmsg, &msg); - if (!msg || strcmp(msg, "DLMessageProcessMessage")) { + if (!msg || strcmp(msg, "DLMessageProcessMessage") != 0) { debug_info("Did not receive DLMessageProcessMessage as expected!"); err = DEVICE_LINK_SERVICE_E_PLIST_ERROR; goto leave; @@ -432,10 +449,7 @@ device_link_service_error_t device_link_service_send(device_link_service_client_ if (!client || !plist) { return DEVICE_LINK_SERVICE_E_INVALID_ARG; } - if (property_list_service_send_binary_plist(client->parent, plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - return DEVICE_LINK_SERVICE_E_MUX_ERROR; - } - return DEVICE_LINK_SERVICE_E_SUCCESS; + return device_link_error(property_list_service_send_binary_plist(client->parent, plist)); } /* Generic device link service receive function. @@ -455,9 +469,6 @@ device_link_service_error_t device_link_service_receive(device_link_service_clie return DEVICE_LINK_SERVICE_E_INVALID_ARG; } - if (property_list_service_receive_plist(client->parent, plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) { - return DEVICE_LINK_SERVICE_E_MUX_ERROR; - } - return DEVICE_LINK_SERVICE_E_SUCCESS; + return device_link_error(property_list_service_receive_plist(client->parent, plist)); } diff --git a/src/device_link_service.h b/src/device_link_service.h index a0c8390..0255b21 100644 --- a/src/device_link_service.h +++ b/src/device_link_service.h @@ -2,7 +2,7 @@ * device_link_service.h * Definitions for the DeviceLink service * - * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2010-2019 Nikias Bassen, 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 @@ -22,19 +22,20 @@ #ifndef __DEVICE_LINK_SERVICE_H #define __DEVICE_LINK_SERVICE_H +#include "idevice.h" #include "property_list_service.h" /* Error Codes */ -#define DEVICE_LINK_SERVICE_E_SUCCESS 0 -#define DEVICE_LINK_SERVICE_E_INVALID_ARG -1 -#define DEVICE_LINK_SERVICE_E_PLIST_ERROR -2 -#define DEVICE_LINK_SERVICE_E_MUX_ERROR -3 -#define DEVICE_LINK_SERVICE_E_BAD_VERSION -4 - -#define DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR -256 - -/** Represents an error code. */ -typedef int16_t device_link_service_error_t; +typedef enum { + DEVICE_LINK_SERVICE_E_SUCCESS = 0, + DEVICE_LINK_SERVICE_E_INVALID_ARG = -1, + DEVICE_LINK_SERVICE_E_PLIST_ERROR = -2, + DEVICE_LINK_SERVICE_E_MUX_ERROR = -3, + DEVICE_LINK_SERVICE_E_SSL_ERROR = -4, + DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT = -5, + DEVICE_LINK_SERVICE_E_BAD_VERSION = -6, + DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR = -256 +} device_link_service_error_t; struct device_link_service_client_private { property_list_service_client_t parent; diff --git a/src/diagnostics_relay.c b/src/diagnostics_relay.c index 79e041e..6ee3150 100644 --- a/src/diagnostics_relay.c +++ b/src/diagnostics_relay.c @@ -18,6 +18,10 @@ * 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 "diagnostics_relay.h" @@ -36,7 +40,7 @@ * * @return RESULT_SUCCESS when the result is 'Success', * RESULT_FAILURE when the result is 'Failure', - * or a negative value if an error occured during evaluation. + * or a negative value if an error occurred during evaluation. */ static int diagnostics_relay_check_result(plist_t dict) { @@ -69,7 +73,7 @@ static int diagnostics_relay_check_result(plist_t dict) return ret; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, diagnostics_relay_client_t *client) +diagnostics_relay_error_t diagnostics_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, diagnostics_relay_client_t *client) { if (!device || !service || service->port == 0 || !client || *client) { return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -89,14 +93,14 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_client_new(idev return DIAGNOSTICS_RELAY_E_SUCCESS; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_client_start_service(idevice_t device, diagnostics_relay_client_t * client, const char* label) +diagnostics_relay_error_t diagnostics_relay_client_start_service(idevice_t device, diagnostics_relay_client_t * client, const char* label) { diagnostics_relay_error_t err = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR; service_client_factory_start_service(device, DIAGNOSTICS_RELAY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(diagnostics_relay_client_new), &err); return err; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_client_free(diagnostics_relay_client_t client) +diagnostics_relay_error_t diagnostics_relay_client_free(diagnostics_relay_client_t client) { if (!client) return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -163,7 +167,7 @@ static diagnostics_relay_error_t diagnostics_relay_send(diagnostics_relay_client return ret; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_goodbye(diagnostics_relay_client_t client) +diagnostics_relay_error_t diagnostics_relay_goodbye(diagnostics_relay_client_t client) { if (!client) return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -197,7 +201,7 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_goodbye(diagnos return ret; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_sleep(diagnostics_relay_client_t client) +diagnostics_relay_error_t diagnostics_relay_sleep(diagnostics_relay_client_t client) { if (!client) return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -229,7 +233,7 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_sleep(diagnosti return ret; } -static diagnostics_relay_error_t internal_diagnostics_relay_action(diagnostics_relay_client_t client, const char* name, int flags) +static diagnostics_relay_error_t internal_diagnostics_relay_action(diagnostics_relay_client_t client, const char* name, diagnostics_relay_action_t flags) { if (!client) return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -273,17 +277,17 @@ static diagnostics_relay_error_t internal_diagnostics_relay_action(diagnostics_r return ret; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_restart(diagnostics_relay_client_t client, int flags) +diagnostics_relay_error_t diagnostics_relay_restart(diagnostics_relay_client_t client, diagnostics_relay_action_t flags) { return internal_diagnostics_relay_action(client, "Restart", flags); } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_shutdown(diagnostics_relay_client_t client, int flags) +diagnostics_relay_error_t diagnostics_relay_shutdown(diagnostics_relay_client_t client, diagnostics_relay_action_t flags) { return internal_diagnostics_relay_action(client, "Shutdown", flags); } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_request_diagnostics(diagnostics_relay_client_t client, const char* type, plist_t* diagnostics) +diagnostics_relay_error_t diagnostics_relay_request_diagnostics(diagnostics_relay_client_t client, const char* type, plist_t* diagnostics) { if (!client || diagnostics == NULL) return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -295,6 +299,9 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_request_diagnos ret = diagnostics_relay_send(client, dict); plist_free(dict); dict = NULL; + if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) { + return ret; + } ret = diagnostics_relay_receive(client, &dict); if (!dict) { @@ -324,7 +331,7 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_request_diagnos return ret; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_query_mobilegestalt(diagnostics_relay_client_t client, plist_t keys, plist_t* result) +diagnostics_relay_error_t diagnostics_relay_query_mobilegestalt(diagnostics_relay_client_t client, plist_t keys, plist_t* result) { if (!client || plist_get_node_type(keys) != PLIST_ARRAY || result == NULL) return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -337,6 +344,9 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_query_mobileges ret = diagnostics_relay_send(client, dict); plist_free(dict); dict = NULL; + if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) { + return ret; + } ret = diagnostics_relay_receive(client, &dict); if (!dict) { @@ -366,22 +376,25 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_query_mobileges return ret; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_query_ioregistry_entry(diagnostics_relay_client_t client, const char* name, const char* class, plist_t* result) +diagnostics_relay_error_t diagnostics_relay_query_ioregistry_entry(diagnostics_relay_client_t client, const char* entry_name, const char* entry_class, plist_t* result) { - if (!client || (name == NULL && class == NULL) || result == NULL) + if (!client || (entry_name == NULL && entry_class == NULL) || result == NULL) return DIAGNOSTICS_RELAY_E_INVALID_ARG; diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR; plist_t dict = plist_new_dict(); - if (name) - plist_dict_set_item(dict,"EntryName", plist_new_string(name)); - if (class) - plist_dict_set_item(dict,"EntryClass", plist_new_string(class)); + if (entry_name) + plist_dict_set_item(dict,"EntryName", plist_new_string(entry_name)); + if (entry_class) + plist_dict_set_item(dict,"EntryClass", plist_new_string(entry_class)); plist_dict_set_item(dict,"Request", plist_new_string("IORegistry")); ret = diagnostics_relay_send(client, dict); plist_free(dict); dict = NULL; + if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) { + return ret; + } ret = diagnostics_relay_receive(client, &dict); if (!dict) { @@ -411,7 +424,7 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_query_ioregistr return ret; } -LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_query_ioregistry_plane(diagnostics_relay_client_t client, const char* plane, plist_t* result) +diagnostics_relay_error_t diagnostics_relay_query_ioregistry_plane(diagnostics_relay_client_t client, const char* plane, plist_t* result) { if (!client || plane == NULL || result == NULL) return DIAGNOSTICS_RELAY_E_INVALID_ARG; diff --git a/src/diagnostics_relay.h b/src/diagnostics_relay.h index 6d11ea1..3bb543a 100644 --- a/src/diagnostics_relay.h +++ b/src/diagnostics_relay.h @@ -22,6 +22,7 @@ #ifndef __DIAGNOSTICS_RELAY_H #define __DIAGNOSTICS_RELAY_H +#include "idevice.h" #include "libimobiledevice/diagnostics_relay.h" #include "property_list_service.h" diff --git a/src/file_relay.c b/src/file_relay.c index 455855b..fbe7cbf 100644 --- a/src/file_relay.c +++ b/src/file_relay.c @@ -18,13 +18,17 @@ * 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 "file_relay.h" #include "property_list_service.h" #include "common/debug.h" -LIBIMOBILEDEVICE_API file_relay_error_t file_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, file_relay_client_t *client) +file_relay_error_t file_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, file_relay_client_t *client) { if (!device || !service || service->port == 0 || !client || *client) { return FILE_RELAY_E_INVALID_ARG; @@ -44,14 +48,14 @@ LIBIMOBILEDEVICE_API file_relay_error_t file_relay_client_new(idevice_t device, return FILE_RELAY_E_SUCCESS; } -LIBIMOBILEDEVICE_API file_relay_error_t file_relay_client_start_service(idevice_t device, file_relay_client_t * client, const char* label) +file_relay_error_t file_relay_client_start_service(idevice_t device, file_relay_client_t * client, const char* label) { file_relay_error_t err = FILE_RELAY_E_UNKNOWN_ERROR; service_client_factory_start_service(device, FILE_RELAY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(file_relay_client_new), &err); return err; } -LIBIMOBILEDEVICE_API file_relay_error_t file_relay_client_free(file_relay_client_t client) +file_relay_error_t file_relay_client_free(file_relay_client_t client) { if (!client) return FILE_RELAY_E_INVALID_ARG; @@ -63,7 +67,7 @@ LIBIMOBILEDEVICE_API file_relay_error_t file_relay_client_free(file_relay_client return FILE_RELAY_E_SUCCESS; } -LIBIMOBILEDEVICE_API file_relay_error_t file_relay_request_sources_timeout(file_relay_client_t client, const char **sources, idevice_connection_t *connection, unsigned int timeout) +file_relay_error_t file_relay_request_sources_timeout(file_relay_client_t client, const char **sources, idevice_connection_t *connection, unsigned int timeout) { if (!client || !client->parent || !sources || !sources[0]) { return FILE_RELAY_E_INVALID_ARG; @@ -139,7 +143,7 @@ LIBIMOBILEDEVICE_API file_relay_error_t file_relay_request_sources_timeout(file_ goto leave; } - if (strcmp(ack, "Acknowledged")) { + if (strcmp(ack, "Acknowledged") != 0) { debug_info("ERROR: Did not receive 'Acknowledged' but '%s'", ack); goto leave; } @@ -155,7 +159,7 @@ leave: return err; } -LIBIMOBILEDEVICE_API file_relay_error_t file_relay_request_sources(file_relay_client_t client, const char **sources, idevice_connection_t *connection) +file_relay_error_t file_relay_request_sources(file_relay_client_t client, const char **sources, idevice_connection_t *connection) { return file_relay_request_sources_timeout(client, sources, connection, 60000); } diff --git a/src/file_relay.h b/src/file_relay.h index 626fab8..65bf460 100644 --- a/src/file_relay.h +++ b/src/file_relay.h @@ -22,6 +22,7 @@ #ifndef __FILE_RELAY_H #define __FILE_RELAY_H +#include "idevice.h" #include "libimobiledevice/file_relay.h" #include "property_list_service.h" diff --git a/src/heartbeat.c b/src/heartbeat.c index fe7e63a..3945d73 100644 --- a/src/heartbeat.c +++ b/src/heartbeat.c @@ -52,13 +52,17 @@ static heartbeat_error_t heartbeat_error(property_list_service_error_t err) return HEARTBEAT_E_MUX_ERROR; case PROPERTY_LIST_SERVICE_E_SSL_ERROR: return HEARTBEAT_E_SSL_ERROR; + case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA: + return HEARTBEAT_E_NOT_ENOUGH_DATA; + case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: + return HEARTBEAT_E_TIMEOUT; default: break; } return HEARTBEAT_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_client_new(idevice_t device, lockdownd_service_descriptor_t service, heartbeat_client_t * client) +heartbeat_error_t heartbeat_client_new(idevice_t device, lockdownd_service_descriptor_t service, heartbeat_client_t * client) { *client = NULL; @@ -85,14 +89,14 @@ LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_client_new(idevice_t device, lo return 0; } -LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_client_start_service(idevice_t device, heartbeat_client_t * client, const char* label) +heartbeat_error_t heartbeat_client_start_service(idevice_t device, heartbeat_client_t * client, const char* label) { heartbeat_error_t err = HEARTBEAT_E_UNKNOWN_ERROR; service_client_factory_start_service(device, HEARTBEAT_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(heartbeat_client_new), &err); return err; } -LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_client_free(heartbeat_client_t client) +heartbeat_error_t heartbeat_client_free(heartbeat_client_t client) { if (!client) return HEARTBEAT_E_INVALID_ARG; @@ -103,7 +107,7 @@ LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_client_free(heartbeat_client_t return err; } -LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_send(heartbeat_client_t client, plist_t plist) +heartbeat_error_t heartbeat_send(heartbeat_client_t client, plist_t plist) { heartbeat_error_t res = HEARTBEAT_E_UNKNOWN_ERROR; @@ -118,12 +122,12 @@ LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_send(heartbeat_client_t client, return res; } -LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_receive(heartbeat_client_t client, plist_t * plist) +heartbeat_error_t heartbeat_receive(heartbeat_client_t client, plist_t * plist) { return heartbeat_receive_with_timeout(client, plist, 1000); } -LIBIMOBILEDEVICE_API heartbeat_error_t heartbeat_receive_with_timeout(heartbeat_client_t client, plist_t * plist, uint32_t timeout_ms) +heartbeat_error_t heartbeat_receive_with_timeout(heartbeat_client_t client, plist_t * plist, uint32_t timeout_ms) { heartbeat_error_t res = HEARTBEAT_E_UNKNOWN_ERROR; plist_t outplist = NULL; diff --git a/src/heartbeat.h b/src/heartbeat.h index f648681..379ecc1 100644 --- a/src/heartbeat.h +++ b/src/heartbeat.h @@ -22,6 +22,7 @@ #ifndef __HEARTBEAT_H #define __HEARTBEAT_H +#include "idevice.h" #include "libimobiledevice/heartbeat.h" #include "property_list_service.h" diff --git a/src/house_arrest.c b/src/house_arrest.c index 135fc01..caad731 100644 --- a/src/house_arrest.c +++ b/src/house_arrest.c @@ -19,6 +19,9 @@ * 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 <unistd.h> @@ -55,7 +58,7 @@ static house_arrest_error_t house_arrest_error(property_list_service_error_t err return HOUSE_ARREST_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_client_new(idevice_t device, lockdownd_service_descriptor_t service, house_arrest_client_t *client) +house_arrest_error_t house_arrest_client_new(idevice_t device, lockdownd_service_descriptor_t service, house_arrest_client_t *client) { property_list_service_client_t plistclient = NULL; house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, service, &plistclient)); @@ -71,14 +74,14 @@ LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_client_new(idevice_t devi return HOUSE_ARREST_E_SUCCESS; } -LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_client_start_service(idevice_t device, house_arrest_client_t * client, const char* label) +house_arrest_error_t house_arrest_client_start_service(idevice_t device, house_arrest_client_t * client, const char* label) { house_arrest_error_t err = HOUSE_ARREST_E_UNKNOWN_ERROR; service_client_factory_start_service(device, HOUSE_ARREST_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(house_arrest_client_new), &err); return err; } -LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) +house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) { if (!client) return HOUSE_ARREST_E_INVALID_ARG; @@ -93,7 +96,7 @@ LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_client_free(house_arrest_ return err; } -LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict) +house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict) { if (!client || !client->parent || !dict) return HOUSE_ARREST_E_INVALID_ARG; @@ -109,7 +112,7 @@ LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_send_request(house_arrest return res; } -LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid) +house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid) { if (!client || !client->parent || !command || !appid) return HOUSE_ARREST_E_INVALID_ARG; @@ -129,7 +132,7 @@ LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_send_command(house_arrest return res; } -LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict) +house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict) { if (!client || !client->parent) return HOUSE_ARREST_E_INVALID_ARG; @@ -147,7 +150,7 @@ LIBIMOBILEDEVICE_API house_arrest_error_t house_arrest_get_result(house_arrest_c return res; } -LIBIMOBILEDEVICE_API afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client) +afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client) { if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { return AFC_E_INVALID_ARG; diff --git a/src/house_arrest.h b/src/house_arrest.h index 387594f..5612a29 100644 --- a/src/house_arrest.h +++ b/src/house_arrest.h @@ -22,6 +22,7 @@ #ifndef __HOUSE_ARREST_H #define __HOUSE_ARREST_H +#include "idevice.h" #include "libimobiledevice/house_arrest.h" #include "property_list_service.h" diff --git a/src/idevice.c b/src/idevice.c index cb9bb5c..b9bbb1f 100644 --- a/src/idevice.c +++ b/src/idevice.c @@ -2,8 +2,8 @@ * idevice.c * Device discovery and communication interface. * + * Copyright (c) 2009-2021 Nikias Bassen. All Rights Reserved. * Copyright (c) 2014 Martin Szulecki All Rights Reserved. - * Copyright (c) 2009-2014 Nikias Bassen. All Rights Reserved. * Copyright (c) 2008 Zach C. All Rights Reserved. * * This library is free software; you can redistribute it and/or @@ -28,28 +28,59 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <time.h> #ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> #include <windows.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> #endif #include <usbmuxd.h> -#ifdef HAVE_OPENSSL + +#if defined(HAVE_OPENSSL) #include <openssl/err.h> +#include <openssl/rsa.h> #include <openssl/ssl.h> - -#else +#elif defined(HAVE_GNUTLS) #include <gnutls/gnutls.h> +#elif defined(HAVE_MBEDTLS) +#include <mbedtls/rsa.h> +#include <mbedtls/ssl.h> +#include <mbedtls/entropy.h> +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/debug.h> +#else +#error No supported TLS/SSL library enabled #endif +#include <libimobiledevice-glue/socket.h> +#include <libimobiledevice-glue/thread.h> + #include "idevice.h" +#include "lockdown.h" #include "common/userpref.h" -#include "common/thread.h" #include "common/debug.h" +#ifndef ECONNREFUSED +#define ECONNREFUSED 107 +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT 138 +#endif + + #ifdef HAVE_OPENSSL -#if OPENSSL_VERSION_NUMBER < 0x10002000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20020000L)) +#define TLS_method TLSv1_method +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) static void SSL_COMP_free_compression_methods(void) { sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); @@ -60,7 +91,7 @@ static void openssl_remove_thread_state(void) { /* ERR_remove_thread_state() is available since OpenSSL 1.0.0-beta1, but * deprecated in OpenSSL 1.1.0 */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #if OPENSSL_VERSION_NUMBER >= 0x10000001L ERR_remove_thread_state(NULL); #else @@ -69,7 +100,7 @@ static void openssl_remove_thread_state(void) #endif } -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) static mutex_t *mutex_buf = NULL; static void locking_function(int mode, int n, const char* file, int line) { @@ -79,17 +110,24 @@ static void locking_function(int mode, int n, const char* file, int line) mutex_unlock(&mutex_buf[n]); } +#if OPENSSL_VERSION_NUMBER < 0x10000000L static unsigned long id_function(void) { return ((unsigned long)THREAD_ID); } +#else +static void id_function(CRYPTO_THREADID *thread) +{ + CRYPTO_THREADID_set_numeric(thread, (unsigned long)THREAD_ID); +} #endif #endif +#endif /* HAVE_OPENSSL */ static void internal_idevice_init(void) { -#ifdef HAVE_OPENSSL -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if defined(HAVE_OPENSSL) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) int i; SSL_library_init(); @@ -99,21 +137,31 @@ static void internal_idevice_init(void) for (i = 0; i < CRYPTO_num_locks(); i++) mutex_init(&mutex_buf[i]); +#if OPENSSL_VERSION_NUMBER < 0x10000000L CRYPTO_set_id_callback(id_function); +#else + CRYPTO_THREADID_set_callback(id_function); +#endif CRYPTO_set_locking_callback(locking_function); #endif -#else +#elif defined(HAVE_GNUTLS) gnutls_global_init(); +#elif defined(HAVE_MBEDTLS) + // NO-OP #endif } static void internal_idevice_deinit(void) { -#ifdef HAVE_OPENSSL -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if defined(HAVE_OPENSSL) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) int i; if (mutex_buf) { +#if OPENSSL_VERSION_NUMBER < 0x10000000L CRYPTO_set_id_callback(NULL); +#else + CRYPTO_THREADID_set_callback(NULL); +#endif CRYPTO_set_locking_callback(NULL); for (i = 0; i < CRYPTO_num_locks(); i++) mutex_destroy(&mutex_buf[i]); @@ -126,15 +174,33 @@ static void internal_idevice_deinit(void) SSL_COMP_free_compression_methods(); openssl_remove_thread_state(); #endif -#else +#elif defined(HAVE_GNUTLS) gnutls_global_deinit(); +#elif defined(HAVE_MBEDTLS) + // NO-OP #endif } static thread_once_t init_once = THREAD_ONCE_INIT; static thread_once_t deinit_once = THREAD_ONCE_INIT; -#ifdef WIN32 +#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR + #if defined(__llvm__) || defined(__GNUC__) + #define HAVE_ATTRIBUTE_CONSTRUCTOR + #endif +#endif + +#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR +static void __attribute__((constructor)) libimobiledevice_initialize(void) +{ + thread_once(&init_once, internal_idevice_init); +} + +static void __attribute__((destructor)) libimobiledevice_deinitialize(void) +{ + thread_once(&deinit_once, internal_idevice_deinit); +} +#elif defined(WIN32) BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) { @@ -150,56 +216,103 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) return 1; } #else -static void __attribute__((constructor)) libimobiledevice_initialize(void) -{ - thread_once(&init_once, internal_idevice_init); -} +#warning No compiler support for constructor/destructor attributes, some features might not be available. +#endif -static void __attribute__((destructor)) libimobiledevice_deinitialize(void) +const char* libimobiledevice_version() { - thread_once(&deinit_once, internal_idevice_deinit); -} +#ifndef PACKAGE_VERSION +#error PACKAGE_VERSION is not defined! #endif + return PACKAGE_VERSION; +} + +struct idevice_subscription_context { + idevice_event_cb_t callback; + void *user_data; + usbmuxd_subscription_context_t ctx; +}; -static idevice_event_cb_t event_cb = NULL; +static idevice_subscription_context_t event_ctx = NULL; static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) { + idevice_subscription_context_t context = (idevice_subscription_context_t)user_data; idevice_event_t ev; ev.event = event->event; ev.udid = event->device.udid; - ev.conn_type = CONNECTION_USBMUXD; + ev.conn_type = 0; + if (event->device.conn_type == CONNECTION_TYPE_USB) { + ev.conn_type = CONNECTION_USBMUXD; + } else if (event->device.conn_type == CONNECTION_TYPE_NETWORK) { + ev.conn_type = CONNECTION_NETWORK; + } else { + debug_info("Unknown connection type %d", event->device.conn_type); + } - if (event_cb) { - event_cb(&ev, user_data); + if (context->callback) { + context->callback(&ev, context->user_data); } } -LIBIMOBILEDEVICE_API idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) +idevice_error_t idevice_events_subscribe(idevice_subscription_context_t *context, idevice_event_cb_t callback, void *user_data) { - event_cb = callback; - int res = usbmuxd_subscribe(usbmux_event_cb, user_data); + if (!context || !callback) { + return IDEVICE_E_INVALID_ARG; + } + *context = malloc(sizeof(struct idevice_subscription_context)); + if (!*context) { + debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__); + return IDEVICE_E_UNKNOWN_ERROR; + } + (*context)->callback = callback; + (*context)->user_data = user_data; + int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context); if (res != 0) { - event_cb = NULL; + free(*context); + *context = NULL; debug_info("ERROR: usbmuxd_subscribe() returned %d!", res); return IDEVICE_E_UNKNOWN_ERROR; } return IDEVICE_E_SUCCESS; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_event_unsubscribe(void) +idevice_error_t idevice_events_unsubscribe(idevice_subscription_context_t context) { - event_cb = NULL; - int res = usbmuxd_unsubscribe(); + if (!context) { + return IDEVICE_E_INVALID_ARG; + } + int res = usbmuxd_events_unsubscribe(context->ctx); if (res != 0) { debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res); return IDEVICE_E_UNKNOWN_ERROR; } + if (context == event_ctx) { + event_ctx = NULL; + } + free(context); return IDEVICE_E_SUCCESS; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_get_device_list(char ***devices, int *count) +idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) +{ + if (event_ctx) { + idevice_events_unsubscribe(event_ctx); + } + return idevice_events_subscribe(&event_ctx, callback, user_data); +} + +idevice_error_t idevice_event_unsubscribe(void) +{ + if (!event_ctx) { + return IDEVICE_E_SUCCESS; + } + event_ctx->callback = NULL; + return idevice_events_unsubscribe(event_ctx); +} + +idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count) { usbmuxd_device_info_t *dev_list; @@ -211,17 +324,89 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_device_list(char ***devices, in return IDEVICE_E_NO_DEVICE; } - char **newlist = NULL; + idevice_info_t *newlist = NULL; int i, newcount = 0; for (i = 0; dev_list[i].handle > 0; i++) { - newlist = realloc(*devices, sizeof(char*) * (newcount+1)); - newlist[newcount++] = strdup(dev_list[i].udid); + newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); + newlist[newcount] = malloc(sizeof(struct idevice_info)); + newlist[newcount]->udid = strdup(dev_list[i].udid); + if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { + newlist[newcount]->conn_type = CONNECTION_USBMUXD; + newlist[newcount]->conn_data = NULL; + } else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) { + newlist[newcount]->conn_type = CONNECTION_NETWORK; + struct sockaddr* saddr = (struct sockaddr*)(dev_list[i].conn_data); + size_t addrlen = 0; + switch (saddr->sa_family) { + case AF_INET: + addrlen = sizeof(struct sockaddr_in); + break; +#ifdef AF_INET6 + case AF_INET6: + addrlen = sizeof(struct sockaddr_in6); + break; +#endif + default: + debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); + continue; + } + newlist[newcount]->conn_data = malloc(addrlen); + memcpy(newlist[newcount]->conn_data, dev_list[i].conn_data, addrlen); + } + newcount++; *devices = newlist; } usbmuxd_device_list_free(&dev_list); *count = newcount; + newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); + newlist[newcount] = NULL; + *devices = newlist; + + return IDEVICE_E_SUCCESS; +} + +idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices) +{ + if (devices) { + int i = 0; + while (devices[i]) { + free(devices[i]->udid); + free(devices[i]->conn_data); + free(devices[i]); + i++; + } + free(devices); + } + return IDEVICE_E_SUCCESS; +} + +idevice_error_t idevice_get_device_list(char ***devices, int *count) +{ + usbmuxd_device_info_t *dev_list; + + *devices = NULL; + *count = 0; + + if (usbmuxd_get_device_list(&dev_list) < 0) { + debug_info("ERROR: usbmuxd is not running!", __func__); + return IDEVICE_E_NO_DEVICE; + } + + char **newlist = NULL; + int i, newcount = 0; + + for (i = 0; dev_list[i].handle > 0; i++) { + if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { + newlist = realloc(*devices, sizeof(char*) * (newcount+1)); + newlist[newcount++] = strdup(dev_list[i].udid); + *devices = newlist; + } + } + usbmuxd_device_list_free(&dev_list); + + *count = newcount; newlist = realloc(*devices, sizeof(char*) * (newcount+1)); newlist[newcount] = NULL; *devices = newlist; @@ -229,7 +414,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_device_list(char ***devices, in return IDEVICE_E_SUCCESS; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_device_list_free(char **devices) +idevice_error_t idevice_device_list_free(char **devices) { if (devices) { int i = 0; @@ -242,31 +427,89 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_device_list_free(char **devices) return IDEVICE_E_SUCCESS; } -LIBIMOBILEDEVICE_API void idevice_set_debug_level(int level) +void idevice_set_debug_level(int level) { internal_set_debug_level(level); } -LIBIMOBILEDEVICE_API idevice_error_t idevice_new(idevice_t * device, const char *udid) +static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev) +{ + if (!muxdev) + return NULL; + + idevice_t device = (idevice_t)malloc(sizeof(struct idevice_private)); + if (!device) + return NULL; + + device->udid = strdup(muxdev->udid); + device->mux_id = muxdev->handle; + device->version = 0; + device->device_class = 0; + switch (muxdev->conn_type) { + case CONNECTION_TYPE_USB: + device->conn_type = CONNECTION_USBMUXD; + device->conn_data = NULL; + break; + case CONNECTION_TYPE_NETWORK: + device->conn_type = CONNECTION_NETWORK; + struct sockaddr* saddr = (struct sockaddr*)(muxdev->conn_data); + size_t addrlen = 0; + switch (saddr->sa_family) { + case AF_INET: + addrlen = sizeof(struct sockaddr_in); + break; +#ifdef AF_INET6 + case AF_INET6: + addrlen = sizeof(struct sockaddr_in6); + break; +#endif + default: + debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); + free(device->udid); + free(device); + return NULL; + } + device->conn_data = malloc(addrlen); + memcpy(device->conn_data, muxdev->conn_data, addrlen); + break; + default: + device->conn_type = 0; + device->conn_data = NULL; + break; + } + return device; +} + +idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options) { usbmuxd_device_info_t muxdev; - int res = usbmuxd_get_device_by_udid(udid, &muxdev); + int usbmux_options = 0; + if (options & IDEVICE_LOOKUP_USBMUX) { + usbmux_options |= DEVICE_LOOKUP_USBMUX; + } + if (options & IDEVICE_LOOKUP_NETWORK) { + usbmux_options |= DEVICE_LOOKUP_NETWORK; + } + if (options & IDEVICE_LOOKUP_PREFER_NETWORK) { + usbmux_options |= DEVICE_LOOKUP_PREFER_NETWORK; + } + int res = usbmuxd_get_device(udid, &muxdev, usbmux_options); if (res > 0) { - idevice_t dev = (idevice_t) malloc(sizeof(struct idevice_private)); - dev->udid = strdup(muxdev.udid); - dev->mux_id = muxdev.handle; - dev->conn_type = CONNECTION_USBMUXD; - dev->conn_data = NULL; - dev->version = 0; - *device = dev; + *device = idevice_from_mux_device(&muxdev); + if (!*device) { + return IDEVICE_E_UNKNOWN_ERROR; + } return IDEVICE_E_SUCCESS; } - /* other connection types could follow here */ - return IDEVICE_E_NO_DEVICE; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_free(idevice_t device) +idevice_error_t idevice_new(idevice_t * device, const char *udid) +{ + return idevice_new_with_options(device, udid, 0); +} + +idevice_error_t idevice_free(idevice_t device) { if (!device) return IDEVICE_E_INVALID_ARG; @@ -283,7 +526,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_free(idevice_t device) return ret; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) +idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) { if (!device) { return IDEVICE_E_INVALID_ARG; @@ -292,24 +535,79 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connect(idevice_t device, uint16_t if (device->conn_type == CONNECTION_USBMUXD) { int sfd = usbmuxd_connect(device->mux_id, port); if (sfd < 0) { - debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); + debug_info("ERROR: Connecting to usbmux device failed: %d (%s)", sfd, strerror(-sfd)); + switch (-sfd) { + case ECONNREFUSED: + return IDEVICE_E_CONNREFUSED; + case ENODEV: + return IDEVICE_E_NO_DEVICE; + default: + break; + } return IDEVICE_E_UNKNOWN_ERROR; } idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); new_connection->type = CONNECTION_USBMUXD; new_connection->data = (void*)(long)sfd; new_connection->ssl_data = NULL; - idevice_get_udid(device, &new_connection->udid); + new_connection->device = device; + new_connection->ssl_recv_timeout = (unsigned int)-1; + new_connection->status = IDEVICE_E_SUCCESS; *connection = new_connection; return IDEVICE_E_SUCCESS; - } else { - debug_info("Unknown connection type %d", device->conn_type); + } + if (device->conn_type == CONNECTION_NETWORK) { + struct sockaddr* saddr = (struct sockaddr*)(device->conn_data); + switch (saddr->sa_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + break; + default: + debug_info("Unsupported address family 0x%02x", saddr->sa_family); + return IDEVICE_E_UNKNOWN_ERROR; + } + + char addrtxt[48]; + addrtxt[0] = '\0'; + + if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) { + debug_info("Failed to convert network address: %d (%s)", errno, strerror(errno)); + } + + debug_info("Connecting to %s port %d...", addrtxt, port); + + int sfd = socket_connect_addr(saddr, port); + if (sfd < 0) { + int result = errno; + debug_info("ERROR: Connecting to network device failed: %d (%s)", result, strerror(result)); + switch (result) { + case ECONNREFUSED: + return IDEVICE_E_CONNREFUSED; + default: + break; + } + return IDEVICE_E_NO_DEVICE; + } + + idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); + new_connection->type = CONNECTION_NETWORK; + new_connection->data = (void*)(long)sfd; + new_connection->ssl_data = NULL; + new_connection->device = device; + new_connection->ssl_recv_timeout = (unsigned int)-1; + + *connection = new_connection; + + return IDEVICE_E_SUCCESS; } + debug_info("Unknown connection type %d", device->conn_type); return IDEVICE_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_disconnect(idevice_connection_t connection) +idevice_error_t idevice_disconnect(idevice_connection_t connection) { if (!connection) { return IDEVICE_E_INVALID_ARG; @@ -323,13 +621,14 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_disconnect(idevice_connection_t con usbmuxd_disconnect((int)(long)connection->data); connection->data = NULL; result = IDEVICE_E_SUCCESS; + } else if (connection->type == CONNECTION_NETWORK) { + socket_close((int)(long)connection->data); + connection->data = NULL; + result = IDEVICE_E_SUCCESS; } else { debug_info("Unknown connection type %d", connection->type); } - if (connection->udid) - free(connection->udid); - free(connection); connection = NULL; @@ -346,40 +645,111 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection, } if (connection->type == CONNECTION_USBMUXD) { - int res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); + int res; + do { + res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); + } while (res == -EAGAIN); if (res < 0) { debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); return IDEVICE_E_UNKNOWN_ERROR; } return IDEVICE_E_SUCCESS; - } else { - debug_info("Unknown connection type %d", connection->type); } + if (connection->type == CONNECTION_NETWORK) { + int s = socket_send((int)(long)connection->data, (void*)data, len); + if (s < 0) { + *sent_bytes = 0; + return IDEVICE_E_UNKNOWN_ERROR; + } + *sent_bytes = s; + return IDEVICE_E_SUCCESS; + } + + debug_info("Unknown connection type %d", connection->type); return IDEVICE_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) +idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) { - if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { + if (!connection || !data +#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) + || (connection->ssl_data && !connection->ssl_data->session) +#endif + ) { return IDEVICE_E_INVALID_ARG; } if (connection->ssl_data) { -#ifdef HAVE_OPENSSL - int sent = SSL_write(connection->ssl_data->session, (const void*)data, (int)len); - debug_info("SSL_write %d, sent %d", len, sent); -#else - ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); + connection->status = IDEVICE_E_SUCCESS; + uint32_t sent = 0; + while (sent < len) { +#if defined(HAVE_OPENSSL) + int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent)); + if (s <= 0) { + int sslerr = SSL_get_error(connection->ssl_data->session, s); + if (sslerr == SSL_ERROR_WANT_WRITE) { + continue; + } + break; + } +#elif defined(HAVE_GNUTLS) + ssize_t s = gnutls_record_send(connection->ssl_data->session, (void*)(data+sent), (size_t)(len-sent)); +#elif defined(HAVE_MBEDTLS) + int s = mbedtls_ssl_write(&connection->ssl_data->ctx, (const unsigned char*)(data+sent), (size_t)(len-sent)); #endif - if ((uint32_t)sent == (uint32_t)len) { - *sent_bytes = sent; - return IDEVICE_E_SUCCESS; + if (s < 0) { + break; + } + sent += s; + } + debug_info("SSL_write %d, sent %d", len, sent); + if (sent < len) { + *sent_bytes = 0; + return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; + } + *sent_bytes = sent; + return IDEVICE_E_SUCCESS; + } + uint32_t sent = 0; + while (sent < len) { + uint32_t bytes = 0; + int s = internal_connection_send(connection, data+sent, len-sent, &bytes); + if (s < 0) { + break; + } + sent += bytes; + } + debug_info("internal_connection_send %d, sent %d", len, sent); + if (sent < len) { + *sent_bytes = sent; + if (sent == 0) { + return IDEVICE_E_UNKNOWN_ERROR; + } + return IDEVICE_E_NOT_ENOUGH_DATA; + } + *sent_bytes = sent; + return IDEVICE_E_SUCCESS; +} + +static inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received) +{ + if (conn_error < 0) { + switch (conn_error) { + case -EAGAIN: + if (len) { + debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error)); + } else { + debug_info("ERROR: received partial data (%s)", strerror(-conn_error)); + } + return IDEVICE_E_NOT_ENOUGH_DATA; + case -ETIMEDOUT: + return IDEVICE_E_TIMEOUT; + default: + return IDEVICE_E_UNKNOWN_ERROR; } - *sent_bytes = 0; - return IDEVICE_E_SSL_ERROR; } - return internal_connection_send(connection, data, len, sent_bytes); + return IDEVICE_E_SUCCESS; } /** @@ -393,45 +763,92 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t } if (connection->type == CONNECTION_USBMUXD) { - int res = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); - if (res < 0) { - debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(errno)); - return (res == -EAGAIN ? IDEVICE_E_NOT_ENOUGH_DATA : IDEVICE_E_UNKNOWN_ERROR); + int conn_error = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); + idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes); + if (error == IDEVICE_E_UNKNOWN_ERROR) { + debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error)); } - return IDEVICE_E_SUCCESS; - } else { - debug_info("Unknown connection type %d", connection->type); + return error; } + if (connection->type == CONNECTION_NETWORK) { + int res = socket_receive_timeout((int)(long)connection->data, data, len, 0, timeout); + idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0); + if (error == IDEVICE_E_SUCCESS) { + *recv_bytes = (uint32_t)res; + } else if (error == IDEVICE_E_UNKNOWN_ERROR) { + debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res)); + } + return error; + } + + debug_info("Unknown connection type %d", connection->type); return IDEVICE_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) +idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) { - if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { + if (!connection +#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) + || (connection->ssl_data && !connection->ssl_data->session) +#endif + || len == 0 + ) { return IDEVICE_E_INVALID_ARG; } if (connection->ssl_data) { uint32_t received = 0; + + if (connection->ssl_recv_timeout != (unsigned int)-1) { + debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); + } + + // this should be reset after the SSL_read call on all codepaths, as + // the supplied timeout should only apply to the current read. + connection->ssl_recv_timeout = timeout; + connection->status = IDEVICE_E_SUCCESS; while (received < len) { -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received); -#else + if (r > 0) { + received += r; + } else { + int sslerr = SSL_get_error(connection->ssl_data->session, r); + if (sslerr == SSL_ERROR_WANT_READ) { + continue; + } else if (sslerr == SSL_ERROR_ZERO_RETURN) { + if (connection->status == IDEVICE_E_TIMEOUT) { + SSL_set_shutdown(connection->ssl_data->session, 0); + } + } + break; + } +#elif defined(HAVE_GNUTLS) ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received); -#endif if (r > 0) { received += r; } else { break; } +#elif defined(HAVE_MBEDTLS) + int r = mbedtls_ssl_read(&connection->ssl_data->ctx, (void*)(data+received), (size_t)len-received); + if (r > 0) { + received += r; + } else { + break; + } +#endif } + connection->ssl_recv_timeout = (unsigned int)-1; + debug_info("SSL_read %d, received %d", len, received); - if (received > 0) { + if (received < len) { *recv_bytes = received; - return IDEVICE_E_SUCCESS; + return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; } - *recv_bytes = 0; - return IDEVICE_E_SSL_ERROR; + + *recv_bytes = received; + return IDEVICE_E_SUCCESS; } return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); } @@ -451,26 +868,44 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); return IDEVICE_E_UNKNOWN_ERROR; } - return IDEVICE_E_SUCCESS; - } else { - debug_info("Unknown connection type %d", connection->type); } + if (connection->type == CONNECTION_NETWORK) { + int res = socket_receive((int)(long)connection->data, data, len); + if (res < 0) { + debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res)); + return IDEVICE_E_UNKNOWN_ERROR; + } + *recv_bytes = (uint32_t)res; + return IDEVICE_E_SUCCESS; + } + + debug_info("Unknown connection type %d", connection->type); return IDEVICE_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) +idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) { - if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { + if (!connection +#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) + || (connection->ssl_data && !connection->ssl_data->session) +#endif + ) { return IDEVICE_E_INVALID_ARG; } if (connection->ssl_data) { -#ifdef HAVE_OPENSSL + if (connection->ssl_recv_timeout != (unsigned int)-1) { + debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); + connection->ssl_recv_timeout = (unsigned int)-1; + } +#if defined(HAVE_OPENSSL) int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len); debug_info("SSL_read %d, received %d", len, received); -#else +#elif defined(HAVE_GNUTLS) ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); +#elif defined(HAVE_MBEDTLS) + int received = mbedtls_ssl_read(&connection->ssl_data->ctx, (unsigned char*)data, (size_t)len); #endif if (received > 0) { *recv_bytes = received; @@ -482,23 +917,26 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive(idevice_connecti return internal_connection_receive(connection, data, len, recv_bytes); } -LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *fd) +idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *fd) { if (!connection || !fd) { return IDEVICE_E_INVALID_ARG; } - idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; if (connection->type == CONNECTION_USBMUXD) { *fd = (int)(long)connection->data; - result = IDEVICE_E_SUCCESS; - } else { - debug_info("Unknown connection type %d", connection->type); + return IDEVICE_E_SUCCESS; } - return result; + if (connection->type == CONNECTION_NETWORK) { + *fd = (int)(long)connection->data; + return IDEVICE_E_SUCCESS; + } + + debug_info("Unknown connection type %d", connection->type); + return IDEVICE_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) +idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) { if (!device || !handle) return IDEVICE_E_INVALID_ARG; @@ -507,78 +945,80 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_handle(idevice_t device, uint32 return IDEVICE_E_SUCCESS; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_get_udid(idevice_t device, char **udid) +idevice_error_t idevice_get_udid(idevice_t device, char **udid) { if (!device || !udid) return IDEVICE_E_INVALID_ARG; - *udid = strdup(device->udid); + if (device->udid) { + *udid = strdup(device->udid); + } return IDEVICE_E_SUCCESS; } -#ifndef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) +typedef ssize_t ssl_cb_ret_type_t; +#elif defined(HAVE_MBEDTLS) +typedef int ssl_cb_ret_type_t; +#endif + /** - * Internally used gnutls callback function for receiving encrypted data. + * Internally used SSL callback function for receiving encrypted data. */ -static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) +static ssl_cb_ret_type_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length) { - int bytes = 0, pos_start_fill = 0; - size_t tbytes = 0; - int this_len = length; + uint32_t bytes = 0; + uint32_t pos = 0; idevice_error_t res; - idevice_connection_t connection = (idevice_connection_t)transport; - char *recv_buffer; - - debug_info("pre-read client wants %zi bytes", length); + unsigned int timeout = connection->ssl_recv_timeout; - recv_buffer = (char *)malloc(sizeof(char) * this_len); + debug_info("pre-read length = %zi bytes", length); /* repeat until we have the full data or an error occurs */ do { - if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { - debug_info("ERROR: idevice_connection_receive returned %d", res); - return res; + bytes = 0; + if (timeout == (unsigned int)-1) { + res = internal_connection_receive(connection, buffer + pos, (uint32_t)length - pos, &bytes); + } else { + res = internal_connection_receive_timeout(connection, buffer + pos, (uint32_t)length - pos, &bytes, (unsigned int)timeout); } - debug_info("post-read we got %i bytes", bytes); + if (res != IDEVICE_E_SUCCESS) { + if (res != IDEVICE_E_TIMEOUT) { + debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res); + } + connection->status = res; + return -1; + } + debug_info("read %i bytes", bytes); /* increase read count */ - tbytes += bytes; - - /* fill the buffer with what we got right now */ - memcpy(buffer + pos_start_fill, recv_buffer, bytes); - pos_start_fill += bytes; - - if (tbytes >= length) { - break; + pos += bytes; + if (pos < (uint32_t)length) { + debug_info("re-read trying to read missing %i bytes", (uint32_t)length - pos); } + } while (pos < (uint32_t)length); - this_len = length - tbytes; - debug_info("re-read trying to read missing %i bytes", this_len); - } while (tbytes < length); + debug_info("post-read received %i bytes", pos); - if (recv_buffer) { - free(recv_buffer); - } - return tbytes; + return pos; } /** - * Internally used gnutls callback function for sending encrypted data. + * Internally used SSL callback function for sending encrypted data. */ -static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) +static ssl_cb_ret_type_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length) { uint32_t bytes = 0; idevice_error_t res; - idevice_connection_t connection = (idevice_connection_t)transport; - debug_info("pre-send length = %zi", length); + debug_info("pre-send length = %zi bytes", length); if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) { debug_info("ERROR: internal_connection_send returned %d", res); + connection->status = res; return -1; } debug_info("post-send sent %i bytes", bytes); return bytes; } -#endif /** * Internally used function for cleaning up SSL stuff. @@ -588,14 +1028,14 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) if (!ssl_data) return; -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) if (ssl_data->session) { SSL_free(ssl_data->session); } if (ssl_data->ctx) { SSL_CTX_free(ssl_data->ctx); } -#else +#elif defined(HAVE_GNUTLS) if (ssl_data->session) { gnutls_deinit(ssl_data->session); } @@ -614,10 +1054,62 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) if (ssl_data->host_privkey) { gnutls_x509_privkey_deinit(ssl_data->host_privkey); } +#elif defined(HAVE_MBEDTLS) + mbedtls_pk_free(&ssl_data->root_privkey); + mbedtls_x509_crt_free(&ssl_data->certificate); + mbedtls_entropy_free(&ssl_data->entropy); + mbedtls_ctr_drbg_free(&ssl_data->ctr_drbg); + mbedtls_ssl_config_free(&ssl_data->config); + mbedtls_ssl_free(&ssl_data->ctx); #endif } #ifdef HAVE_OPENSSL +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, size_t len, int argi, long argl, int retvalue, size_t *processed) +#else +static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue) +#endif +{ + ssize_t bytes = 0; + idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + size_t len = (size_t)argi; + size_t *processed = (size_t*)&bytes; +#endif + switch (oper) { + case (BIO_CB_READ|BIO_CB_RETURN): + if (argp) { + bytes = internal_ssl_read(conn, (char *)argp, len); + *processed = bytes; + return (long)bytes; + } + return 0; + case (BIO_CB_PUTS|BIO_CB_RETURN): + len = strlen(argp); + // fallthrough + case (BIO_CB_WRITE|BIO_CB_RETURN): + bytes = internal_ssl_write(conn, argp, len); + *processed = bytes; + return (long)bytes; + default: + return retvalue; + } +} + +static BIO *ssl_idevice_bio_new(idevice_connection_t conn) +{ + BIO *b = BIO_new(BIO_s_null()); + if (!b) return NULL; + BIO_set_callback_arg(b, (char *)conn); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + BIO_set_callback_ex(b, ssl_idevice_bio_callback); +#else + BIO_set_callback(b, ssl_idevice_bio_callback); +#endif + return b; +} + static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx) { return 1; @@ -630,7 +1122,7 @@ static const char *ssl_error_to_string(int e) case SSL_ERROR_NONE: return "SSL_ERROR_NONE"; case SSL_ERROR_SSL: - return "SSL_ERROR_SSL"; + return ERR_error_string(ERR_get_error(), NULL); case SSL_ERROR_WANT_READ: return "SSL_ERROR_WANT_READ"; case SSL_ERROR_WANT_WRITE: @@ -652,7 +1144,7 @@ static const char *ssl_error_to_string(int e) #endif #endif -#ifndef HAVE_OPENSSL +#if defined(HAVE_GNUTLS) /** * Internally used gnutls callback function that gets called during handshake. */ @@ -683,28 +1175,40 @@ static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t } return res; } +#elif defined(HAVE_MBEDTLS) +static void _mbedtls_log_cb(void* ctx, int level, const char* filename, int line, const char* message) +{ + fprintf(stderr, "[mbedtls][%d] %s:%d => %s", level, filename, line, message); +} + +static int cert_verify_cb(void* ctx, mbedtls_x509_crt* cert, int depth, uint32_t *flags) +{ + *flags = 0; + return 0; +} + +static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len) +{ + memset(buf, 4, len); + return 0; +} #endif -LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) +idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) { if (!connection || connection->ssl_data) return IDEVICE_E_INVALID_ARG; idevice_error_t ret = IDEVICE_E_SSL_ERROR; -#ifdef HAVE_OPENSSL - uint32_t return_me = 0; -#else - int return_me = 0; -#endif plist_t pair_record = NULL; - userpref_read_pair_record(connection->udid, &pair_record); - if (!pair_record) { - debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s.", connection->udid); + userpref_error_t uerr = userpref_read_pair_record(connection->device->udid, &pair_record); + if (uerr != USERPREF_E_SUCCESS) { + debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s (%d)", connection->device->udid, uerr); return ret; } -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) key_data_t root_cert = { NULL, 0 }; key_data_t root_privkey = { NULL, 0 }; @@ -714,20 +1218,76 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne if (pair_record) plist_free(pair_record); - BIO *ssl_bio = BIO_new(BIO_s_socket()); + BIO *ssl_bio = ssl_idevice_bio_new(connection); if (!ssl_bio) { debug_info("ERROR: Could not create SSL bio."); return ret; } - BIO_set_fd(ssl_bio, (int)(long)connection->data, BIO_NOCLOSE); - SSL_CTX *ssl_ctx = SSL_CTX_new(TLSv1_method()); + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); if (ssl_ctx == NULL) { debug_info("ERROR: Could not create SSL context."); BIO_free(ssl_bio); return ret; } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || \ + (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3060000fL)) + SSL_CTX_set_security_level(ssl_ctx, 0); +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100002L || \ + (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL)) + /* force use of TLSv1 for older devices */ + if (connection->device->version < DEVICE_VERSION(10,0,0)) { +#ifdef SSL_OP_NO_TLSv1_1 + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); +#endif +#ifdef SSL_OP_NO_TLSv1_2 + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); +#endif +#ifdef SSL_OP_NO_TLSv1_3 + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); +#endif + } +#else + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); + if (connection->device->version < DEVICE_VERSION(10,0,0)) { + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION); + if (connection->device->version == 0) { + /* + iOS 1 doesn't understand TLS1_VERSION, it can only speak SSL3_VERSION. + However, modern OpenSSL is usually compiled without SSLv3 support. + So if we set min_proto_version to SSL3_VERSION on an OpenSSL instance which doesn't support it, + it will just ignore min_proto_version altogether and fall back to an even higher version. + To avoid accidentally breaking iOS 2.0+, we set min version to 0 instead. + Here is what documentation says: + Setting the minimum or maximum version to 0, + will enable protocol versions down to the lowest version, + or up to the highest version supported by the library, respectively. + */ + SSL_CTX_set_min_proto_version(ssl_ctx, 0); + } + } +#endif +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#if defined(SSL_OP_IGNORE_UNEXPECTED_EOF) + /* + * For OpenSSL 3 and later, mark close_notify alerts as optional. + * For prior versions of OpenSSL we check for SSL_ERROR_SYSCALL when + * reading instead (this error changes to SSL_ERROR_SSL in OpenSSL 3). + */ + SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); +#endif +#if defined(SSL_OP_LEGACY_SERVER_CONNECT) + /* + * Without setting SSL_OP_LEGACY_SERVER_CONNECT, OpenSSL 3 fails with + * error "unsafe legacy renegotiation disabled" when talking to iOS 5 + */ + SSL_CTX_set_options(ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT); +#endif +#endif + BIO* membp; X509* rootCert = NULL; membp = BIO_new_mem_buf(root_cert.data, root_cert.size); @@ -739,6 +1299,16 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne X509_free(rootCert); free(root_cert.data); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_PKEY* rootPrivKey = NULL; + membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); + PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL); + BIO_free(membp); + if (SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey) != 1) { + debug_info("WARNING: Could not load RootPrivateKey"); + } + EVP_PKEY_free(rootPrivKey); +#else RSA* rootPrivKey = NULL; membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); PEM_read_bio_RSAPrivateKey(membp, &rootPrivKey, NULL, NULL); @@ -747,6 +1317,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne debug_info("WARNING: Could not load RootPrivateKey"); } RSA_free(rootPrivKey); +#endif free(root_privkey.data); SSL *ssl = SSL_new(ssl_ctx); @@ -760,9 +1331,22 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne SSL_set_verify(ssl, 0, ssl_verify_callback); SSL_set_bio(ssl, ssl_bio, ssl_bio); - return_me = SSL_do_handshake(ssl); - if (return_me != 1) { - debug_info("ERROR in SSL_do_handshake: %s", ssl_error_to_string(SSL_get_error(ssl, return_me))); + debug_info("Performing SSL handshake"); + int ssl_error = 0; + do { + ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl)); + if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) { + break; + } +#ifdef WIN32 + Sleep(100); +#else + struct timespec ts = { 0, 100000000 }; + nanosleep(&ts, NULL); +#endif + } while (1); + if (ssl_error != 0) { + debug_info("ERROR during SSL handshake: %s", ssl_error_to_string(ssl_error)); SSL_free(ssl); SSL_CTX_free(ssl_ctx); } else { @@ -771,11 +1355,11 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne ssl_data_loc->ctx = ssl_ctx; connection->ssl_data = ssl_data_loc; ret = IDEVICE_E_SUCCESS; - debug_info("SSL mode enabled, cipher: %s", SSL_get_cipher(ssl)); + debug_info("SSL mode enabled, %s, cipher: %s", SSL_get_version(ssl), SSL_get_cipher(ssl)); } /* required for proper multi-thread clean up to prevent leaks */ openssl_remove_thread_state(); -#else +#elif defined(HAVE_GNUTLS) ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); /* Set up GnuTLS... */ @@ -816,6 +1400,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne debug_info("WARNING: errno says %s before handshake!", strerror(errno)); } + int return_me = 0; do { return_me = gnutls_handshake(ssl_data_loc->session); } while(return_me == GNUTLS_E_AGAIN || return_me == GNUTLS_E_INTERRUPTED); @@ -832,11 +1417,92 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne ret = IDEVICE_E_SUCCESS; debug_info("SSL mode enabled"); } +#elif defined(HAVE_MBEDTLS) + key_data_t root_cert = { NULL, 0 }; + key_data_t root_privkey = { NULL, 0 }; + + pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); + pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); + + plist_free(pair_record); + + ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); + + mbedtls_ssl_init(&ssl_data_loc->ctx); + mbedtls_ssl_config_init(&ssl_data_loc->config); + mbedtls_entropy_init(&ssl_data_loc->entropy); + mbedtls_ctr_drbg_init(&ssl_data_loc->ctr_drbg); + + int r = mbedtls_ctr_drbg_seed(&ssl_data_loc->ctr_drbg, mbedtls_entropy_func, &ssl_data_loc->entropy, NULL, 0); + if (r != 0) { + debug_info("ERROR: [mbedtls] mbedtls_ctr_drbg_seed failed: %d", r); + return ret; + } + + if (mbedtls_ssl_config_defaults(&ssl_data_loc->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { + debug_info("ERROR: [mbedtls] Failed to set config defaults"); + return ret; + } + + mbedtls_ssl_conf_rng(&ssl_data_loc->config, mbedtls_ctr_drbg_random, &ssl_data_loc->ctr_drbg); + + mbedtls_ssl_conf_dbg(&ssl_data_loc->config, _mbedtls_log_cb, NULL); + + mbedtls_ssl_conf_verify(&ssl_data_loc->config, cert_verify_cb, NULL); + + mbedtls_ssl_setup(&ssl_data_loc->ctx, &ssl_data_loc->config); + + mbedtls_ssl_set_bio(&ssl_data_loc->ctx, connection, (mbedtls_ssl_send_t*)&internal_ssl_write, (mbedtls_ssl_recv_t*)&internal_ssl_read, NULL); + + mbedtls_x509_crt_init(&ssl_data_loc->certificate); + + int crterr = mbedtls_x509_crt_parse(&ssl_data_loc->certificate, root_cert.data, root_cert.size); + if (crterr < 0) { + debug_info("ERROR: [mbedtls] parsing root cert failed: %d", crterr); + return ret; + } + + mbedtls_ssl_conf_ca_chain(&ssl_data_loc->config, &ssl_data_loc->certificate, NULL); + + mbedtls_pk_init(&ssl_data_loc->root_privkey); + +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0, &_mbedtls_f_rng, NULL); +#else + int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0); +#endif + if (pkerr < 0) { + debug_info("ERROR: [mbedtls] parsing private key failed: %d (size=%d)", pkerr, root_privkey.size); + return ret; + } + + mbedtls_ssl_conf_own_cert(&ssl_data_loc->config, &ssl_data_loc->certificate, &ssl_data_loc->root_privkey); + + int return_me = 0; + do { + return_me = mbedtls_ssl_handshake(&ssl_data_loc->ctx); + } while (return_me == MBEDTLS_ERR_SSL_WANT_READ || return_me == MBEDTLS_ERR_SSL_WANT_WRITE); + + if (return_me != 0) { + debug_info("ERROR during SSL handshake: %d", return_me); + internal_ssl_cleanup(ssl_data_loc); + free(ssl_data_loc); + } else { + connection->ssl_data = ssl_data_loc; + ret = IDEVICE_E_SUCCESS; + debug_info("SSL mode enabled, %s, cipher: %s", mbedtls_ssl_get_version(&ssl_data_loc->ctx), mbedtls_ssl_get_ciphersuite(&ssl_data_loc->ctx)); + debug_info("SSL mode enabled"); + } #endif return ret; } -LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) +idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) +{ + return idevice_connection_disable_bypass_ssl(connection, 0); +} + +idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass) { if (!connection) return IDEVICE_E_INVALID_ARG; @@ -845,18 +1511,31 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_disable_ssl(idevice_conn return IDEVICE_E_SUCCESS; } -#ifdef HAVE_OPENSSL - if (connection->ssl_data->session) { - /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */ - if (SSL_shutdown(connection->ssl_data->session) == 0) { - SSL_shutdown(connection->ssl_data->session); + // some services require plain text communication after SSL handshake + // sending out SSL_shutdown will cause bytes + if (!sslBypass) { +#if defined(HAVE_OPENSSL) + if (connection->ssl_data->session) { + /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */ + if (SSL_shutdown(connection->ssl_data->session) == 0) { + /* Only try bidirectional shutdown if we know it can complete */ + int ssl_error; + if ((ssl_error = SSL_get_error(connection->ssl_data->session, 0)) == SSL_ERROR_NONE) { + SSL_shutdown(connection->ssl_data->session); + } else { + debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error); + } + } } - } -#else - if (connection->ssl_data->session) { - gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); - } +#elif defined(HAVE_GNUTLS) + if (connection->ssl_data->session) { + gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); + } +#elif defined(HAVE_MBEDTLS) + mbedtls_ssl_close_notify(&connection->ssl_data->ctx); #endif + } + internal_ssl_cleanup(connection->ssl_data); free(connection->ssl_data); connection->ssl_data = NULL; diff --git a/src/idevice.h b/src/idevice.h index 94e828b..dd72f9d 100644 --- a/src/idevice.h +++ b/src/idevice.h @@ -26,58 +26,79 @@ #include <config.h> #endif -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) #include <openssl/ssl.h> -#else +#elif defined(HAVE_GNUTLS) #include <gnutls/gnutls.h> #include <gnutls/x509.h> +#elif defined(HAVE_MBEDTLS) +#include <mbedtls/ssl.h> +#include <mbedtls/entropy.h> +#include <mbedtls/ctr_drbg.h> #endif -#ifdef WIN32 -#define LIBIMOBILEDEVICE_API __declspec( dllexport ) -#else -#ifdef HAVE_FVISIBILITY -#define LIBIMOBILEDEVICE_API __attribute__((visibility("default"))) +#ifdef LIBIMOBILEDEVICE_STATIC + #define LIBIMOBILEDEVICE_API +#elif defined(_WIN32) + #define LIBIMOBILEDEVICE_API __declspec( dllexport ) #else -#define LIBIMOBILEDEVICE_API -#endif + #if __GNUC__ >= 4 + #define LIBIMOBILEDEVICE_API __attribute__((visibility("default"))) + #else + #define LIBIMOBILEDEVICE_API + #endif #endif #include "common/userpref.h" #include "libimobiledevice/libimobiledevice.h" -enum connection_type { - CONNECTION_USBMUXD = 1 -}; +#define DEVICE_VERSION(maj, min, patch) (((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF)) + +#define DEVICE_CLASS_IPHONE 1 +#define DEVICE_CLASS_IPAD 2 +#define DEVICE_CLASS_IPOD 3 +#define DEVICE_CLASS_APPLETV 4 +#define DEVICE_CLASS_WATCH 5 +#define DEVICE_CLASS_UNKNOWN 255 struct ssl_data_private { -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) SSL *session; SSL_CTX *ctx; -#else +#elif defined(HAVE_GNUTLS) gnutls_certificate_credentials_t certificate; gnutls_session_t session; gnutls_x509_privkey_t root_privkey; gnutls_x509_crt_t root_cert; gnutls_x509_privkey_t host_privkey; gnutls_x509_crt_t host_cert; +#elif defined(HAVE_MBEDTLS) + mbedtls_ssl_context ctx; + mbedtls_ssl_config config; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_x509_crt certificate; + mbedtls_pk_context root_privkey; #endif }; typedef struct ssl_data_private *ssl_data_t; struct idevice_connection_private { - char *udid; - enum connection_type type; + idevice_t device; + enum idevice_connection_type type; void *data; ssl_data_t ssl_data; + unsigned int ssl_recv_timeout; + idevice_error_t status; }; struct idevice_private { char *udid; uint32_t mux_id; - enum connection_type conn_type; + enum idevice_connection_type conn_type; void *conn_data; int version; + int device_class; }; #endif diff --git a/src/installation_proxy.c b/src/installation_proxy.c index f82eecc..ec19da0 100644 --- a/src/installation_proxy.c +++ b/src/installation_proxy.c @@ -20,6 +20,9 @@ * 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 <inttypes.h> @@ -229,7 +232,7 @@ static instproxy_error_t instproxy_error(property_list_service_error_t err) return INSTPROXY_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, instproxy_client_t *client) +instproxy_error_t instproxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, instproxy_client_t *client) { property_list_service_client_t plistclient = NULL; instproxy_error_t err = instproxy_error(property_list_service_client_new(device, service, &plistclient)); @@ -240,32 +243,33 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_new(idevice_t device, lo instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); client_loc->parent = plistclient; mutex_init(&client_loc->mutex); - client_loc->receive_status_thread = (thread_t)NULL; + client_loc->receive_status_thread = THREAD_T_NULL; *client = client_loc; return INSTPROXY_E_SUCCESS; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_start_service(idevice_t device, instproxy_client_t * client, const char* label) +instproxy_error_t instproxy_client_start_service(idevice_t device, instproxy_client_t * client, const char* label) { instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR; service_client_factory_start_service(device, INSTPROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(instproxy_client_new), &err); return err; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_free(instproxy_client_t client) +instproxy_error_t instproxy_client_free(instproxy_client_t client) { if (!client) return INSTPROXY_E_INVALID_ARG; - property_list_service_client_free(client->parent); + property_list_service_client_t parent = client->parent; client->parent = NULL; if (client->receive_status_thread) { debug_info("joining receive_status_thread"); thread_join(client->receive_status_thread); thread_free(client->receive_status_thread); - client->receive_status_thread = (thread_t)NULL; + client->receive_status_thread = THREAD_T_NULL; } + property_list_service_client_free(parent); mutex_destroy(&client->mutex); free(client); @@ -278,12 +282,9 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_free(instproxy_client_t * * @param client The connected installation_proxy client. * @param command The command to execute. Required. - * @param client_options The client options to use, as PLIST_DICT, or NULL. - * @param appid The ApplicationIdentifier to add or NULL if not required. - * @param package_path The installation package path or NULL if not required. * * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if - * an error occured. + * an error occurred. */ static instproxy_error_t instproxy_send_command(instproxy_client_t client, plist_t command) { @@ -343,7 +344,7 @@ static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client /* parse status response */ if (node) { - /* check status for possible error to allow reporting it and aborting it gracefully */ + /* check status for possible error to allow reporting it and aborting it gracefully */ res = instproxy_status_get_error(node, &error_name, &error_description, &error_code); if (res != INSTPROXY_E_SUCCESS) { debug_info("command: %s, error %d, code 0x%08"PRIx64", name: %s, description: \"%s\"", command_name, res, error_code, error_name, error_description ? error_description: "N/A"); @@ -363,17 +364,14 @@ static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client /* check status from response */ instproxy_status_get_name(node, &status_name); if (!status_name) { - debug_info("failed to retrieve name from status response with error %d.", res); - complete = 1; - } - - if (status_name) { + debug_info("ignoring message without Status key:"); + debug_plist(node); + } else { if (!strcmp(status_name, "Complete")) { complete = 1; } else { res = INSTPROXY_E_OP_IN_PROGRESS; } - #ifndef STRIP_DEBUG_CODE percent_complete = -1; instproxy_status_get_percent_complete(node, &percent_complete); @@ -431,7 +429,7 @@ static void* instproxy_receive_status_loop_thread(void* arg) if (data->client->receive_status_thread) { thread_free(data->client->receive_status_thread); - data->client->receive_status_thread = (thread_t)NULL; + data->client->receive_status_thread = THREAD_T_NULL; } instproxy_unlock(data->client); @@ -457,7 +455,7 @@ static void* instproxy_receive_status_loop_thread(void* arg) * * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or * when the command completed successfully (sync). - * An INSTPROXY_E_* error value is returned if an error occured. + * An INSTPROXY_E_* error value is returned if an error occurred. */ static instproxy_error_t instproxy_receive_status_loop_with_callback(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data) { @@ -502,7 +500,7 @@ static instproxy_error_t instproxy_receive_status_loop_with_callback(instproxy_c * @param user_data Callback data passed to status_cb. * * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if - * an error occured. + * an error occurred. */ static instproxy_error_t instproxy_perform_command(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data) { @@ -527,7 +525,7 @@ static instproxy_error_t instproxy_perform_command(instproxy_client_t client, pl return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_browse_with_callback(instproxy_client_t client, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) +instproxy_error_t instproxy_browse_with_callback(instproxy_client_t client, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) { if (!client || !client->parent || !status_cb) return INSTPROXY_E_INVALID_ARG; @@ -568,7 +566,7 @@ static void instproxy_append_current_list_to_result_cb(plist_t command, plist_t plist_free(current_list); } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result) +instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result) { if (!client || !client->parent || !result) return INSTPROXY_E_INVALID_ARG; @@ -605,7 +603,7 @@ static void instproxy_copy_lookup_result_cb(plist_t command, plist_t status, voi } } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup(instproxy_client_t client, const char** appids, plist_t client_options, plist_t *result) +instproxy_error_t instproxy_lookup(instproxy_client_t client, const char** appids, plist_t client_options, plist_t *result) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; int i = 0; @@ -652,7 +650,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup(instproxy_client_t clien return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) +instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -662,14 +660,14 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_install(instproxy_client_t clie plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path)); - res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); + res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); plist_free(command); return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) +instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -679,14 +677,14 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_upgrade(instproxy_client_t clie plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path)); - res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); + res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); plist_free(command); return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) +instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -696,14 +694,14 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_uninstall(instproxy_client_t cl plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); - res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); + res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); plist_free(command); return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) +instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -719,7 +717,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_lookup_archives(instproxy_clien return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) +instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -729,14 +727,14 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_archive(instproxy_client_t clie plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); - res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); + res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); plist_free(command); return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) +instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -746,14 +744,14 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_restore(instproxy_client_t clie plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); - res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); + res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); plist_free(command); return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) +instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -763,14 +761,14 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_remove_archive(instproxy_client plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); - res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); + res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); plist_free(command); return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, const char** capabilities, plist_t client_options, plist_t *result) +instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, const char** capabilities, plist_t client_options, plist_t *result) { if (!client || !capabilities || !result) return INSTPROXY_E_INVALID_ARG; @@ -807,7 +805,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_check_capabilities_match(instpr return res; } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_status_get_error(plist_t status, char **name, char** description, uint64_t* code) +instproxy_error_t instproxy_status_get_error(plist_t status, char **name, char** description, uint64_t* code) { instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; @@ -845,7 +843,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_status_get_error(plist_t status return res; } -LIBIMOBILEDEVICE_API void instproxy_status_get_name(plist_t status, char **name) +void instproxy_status_get_name(plist_t status, char **name) { if (name) { plist_t node = plist_dict_get_item(status, "Status"); @@ -857,7 +855,7 @@ LIBIMOBILEDEVICE_API void instproxy_status_get_name(plist_t status, char **name) } } -LIBIMOBILEDEVICE_API void instproxy_status_get_percent_complete(plist_t status, int *percent) +void instproxy_status_get_percent_complete(plist_t status, int *percent) { uint64_t val = 0; if (percent) { @@ -869,7 +867,7 @@ LIBIMOBILEDEVICE_API void instproxy_status_get_percent_complete(plist_t status, } } -LIBIMOBILEDEVICE_API void instproxy_status_get_current_list(plist_t status, uint64_t* total, uint64_t* current_index, uint64_t* current_amount, plist_t* list) +void instproxy_status_get_current_list(plist_t status, uint64_t* total, uint64_t* current_index, uint64_t* current_amount, plist_t* list) { plist_t node = NULL; @@ -906,7 +904,7 @@ LIBIMOBILEDEVICE_API void instproxy_status_get_current_list(plist_t status, uint } } -LIBIMOBILEDEVICE_API void instproxy_command_get_name(plist_t command, char** name) +void instproxy_command_get_name(plist_t command, char** name) { if (name) { plist_t node = plist_dict_get_item(command, "Command"); @@ -918,12 +916,12 @@ LIBIMOBILEDEVICE_API void instproxy_command_get_name(plist_t command, char** nam } } -LIBIMOBILEDEVICE_API plist_t instproxy_client_options_new(void) +plist_t instproxy_client_options_new(void) { return plist_new_dict(); } -LIBIMOBILEDEVICE_API void instproxy_client_options_add(plist_t client_options, ...) +void instproxy_client_options_add(plist_t client_options, ...) { if (!client_options) return; @@ -936,7 +934,7 @@ LIBIMOBILEDEVICE_API void instproxy_client_options_add(plist_t client_options, . if (!strcmp(key, "SkipUninstall")) { int intval = va_arg(args, int); plist_dict_set_item(client_options, key, plist_new_bool(intval)); - } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata") || !strcmp(key, "ReturnAttributes")) { + } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata") || !strcmp(key, "ReturnAttributes") || !strcmp(key, "BundleIDs")) { plist_t plistval = va_arg(args, plist_t); if (!plistval) { free(key); @@ -957,7 +955,7 @@ LIBIMOBILEDEVICE_API void instproxy_client_options_add(plist_t client_options, . va_end(args); } -LIBIMOBILEDEVICE_API void instproxy_client_options_set_return_attributes(plist_t client_options, ...) +void instproxy_client_options_set_return_attributes(plist_t client_options, ...) { if (!client_options) return; @@ -978,16 +976,16 @@ LIBIMOBILEDEVICE_API void instproxy_client_options_set_return_attributes(plist_t plist_dict_set_item(client_options, "ReturnAttributes", return_attributes); } -LIBIMOBILEDEVICE_API void instproxy_client_options_free(plist_t client_options) +void instproxy_client_options_free(plist_t client_options) { if (client_options) { plist_free(client_options); } } -LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_get_path_for_bundle_identifier(instproxy_client_t client, const char* appid, char** path) +instproxy_error_t instproxy_client_get_path_for_bundle_identifier(instproxy_client_t client, const char* bundle_id, char** path) { - if (!client || !client->parent || !appid) + if (!client || !client->parent || !bundle_id) return INSTPROXY_E_INVALID_ARG; plist_t apps = NULL; @@ -1000,7 +998,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_get_path_for_bundle_iden instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "CFBundleExecutable", "Path", NULL); // only query for specific appid - const char* appids[] = {appid, NULL}; + const char* appids[] = {bundle_id, NULL}; // query device for list of apps instproxy_error_t ierr = instproxy_lookup(client, appids, client_opts, &apps); @@ -1011,7 +1009,7 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_get_path_for_bundle_iden return ierr; } - plist_t app_found = plist_access_path(apps, 1, appid); + plist_t app_found = plist_access_path(apps, 1, bundle_id); if (!app_found) { if (apps) plist_free(apps); diff --git a/src/installation_proxy.h b/src/installation_proxy.h index bbc14ce..5bdbb71 100644 --- a/src/installation_proxy.h +++ b/src/installation_proxy.h @@ -23,14 +23,15 @@ #ifndef __INSTALLATION_PROXY_H #define __INSTALLATION_PROXY_H +#include "idevice.h" #include "libimobiledevice/installation_proxy.h" #include "property_list_service.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h> struct instproxy_client_private { property_list_service_client_t parent; mutex_t mutex; - thread_t receive_status_thread; + THREAD_T receive_status_thread; }; #endif diff --git a/src/libimobiledevice-1.0.pc.in b/src/libimobiledevice-1.0.pc.in index dfcdd8a..f00c392 100644 --- a/src/libimobiledevice-1.0.pc.in +++ b/src/libimobiledevice-1.0.pc.in @@ -6,7 +6,7 @@ includedir=@includedir@ Name: @PACKAGE_NAME@ Description: A library to communicate with services running on Apple iOS devices. Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -limobiledevice +Libs: -L${libdir} -limobiledevice-1.0 Cflags: -I${includedir} -Requires: libplist >= @LIBPLIST_VERSION@ -Requires.private: libusbmuxd >= @LIBUSBMUXD_VERSION@ @ssl_requires@
\ No newline at end of file +Requires: libplist-2.0 >= @LIBPLIST_VERSION@ +Requires.private: libusbmuxd-2.0 >= @LIBUSBMUXD_VERSION@ libimobiledevice-glue-1.0 >= @LIMD_GLUE_VERSION@ @ssl_requires@ diff --git a/src/lockdown-cu.c b/src/lockdown-cu.c new file mode 100644 index 0000000..1afc2c5 --- /dev/null +++ b/src/lockdown-cu.c @@ -0,0 +1,1193 @@ +/* + * lockdown-cu.c + * com.apple.mobile.lockdownd service CU additions + * + * Copyright (c) 2021 Nikias Bassen, 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> +#define _GNU_SOURCE 1 +#define __USE_GNU 1 +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <plist/plist.h> + +#include "idevice.h" +#include "lockdown.h" +#include "common/debug.h" + +#ifdef HAVE_WIRELESS_PAIRING + +#include <libimobiledevice-glue/utils.h> +#include <libimobiledevice-glue/socket.h> +#include <libimobiledevice-glue/opack.h> +#include <libimobiledevice-glue/tlv.h> + +#if defined(HAVE_OPENSSL) +#include <openssl/hmac.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) +#include <openssl/chacha.h> +#include <openssl/poly1305.h> +#endif +#elif defined(HAVE_GCRYPT) +#include <gcrypt.h> +#elif defined(HAVE_MBEDTLS) +#include <mbedtls/md.h> +#include <mbedtls/chachapoly.h> +#endif + +#ifdef __APPLE__ +#include <sys/sysctl.h> +#include <SystemConfiguration/SystemConfiguration.h> +#include <CoreFoundation/CoreFoundation.h> +#include <TargetConditionals.h> +#endif + +#include "property_list_service.h" +#include "common/userpref.h" + +#include "endianness.h" + +#include "srp.h" +#include "ed25519.h" + +/* {{{ SRP6a parameters */ +static const unsigned char kSRPModulus3072[384] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, + 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, + 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, + 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, + 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, + 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, + 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, + 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, + 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, + 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, + 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, + 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char kSRPGenerator5 = 5; +/* }}} */ + +/* {{{ HKDF */ +#if defined(HAVE_OPENSSL) +#define MD_ALGO_SHA512 EVP_sha512() +typedef const EVP_MD* MD_ALGO_TYPE_T; +#define MD_ALGO_DIGEST_SIZE EVP_MD_size +#define MD_MAX_DIGEST_SIZE EVP_MAX_MD_SIZE + +#elif defined(HAVE_GCRYPT) +#define MD_ALGO_SHA512 GCRY_MD_SHA512 +typedef int MD_ALGO_TYPE_T; +#define MD_ALGO_DIGEST_SIZE gcry_md_get_algo_dlen +#define MD_MAX_DIGEST_SIZE 64 + +static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len) +{ + gcry_md_hd_t hd; + if (gcry_md_open(&hd, md, GCRY_MD_FLAG_HMAC)) { + debug_info("gcry_md_open() failed"); + return; + } + if (gcry_md_setkey(hd, key, key_len)) { + gcry_md_close (hd); + debug_info("gcry_md_setkey() failed"); + return; + } + gcry_md_write(hd, data, data_len); + + unsigned char* digest = gcry_md_read(hd, md); + if (!digest) { + gcry_md_close(hd); + debug_info("gcry_md_read() failed"); + return; + } + + *out_len = gcry_md_get_algo_dlen(md); + memcpy(out, digest, *out_len); + gcry_md_close(hd); +} +#elif defined(HAVE_MBEDTLS) +#define MD_ALGO_SHA512 MBEDTLS_MD_SHA512 +typedef mbedtls_md_type_t MD_ALGO_TYPE_T; +#define MD_ALGO_DIGEST_SIZE(x) mbedtls_md_get_size(mbedtls_md_info_from_type(x)) +#define MD_MAX_DIGEST_SIZE MBEDTLS_MD_MAX_SIZE + +static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len) +{ + mbedtls_md_context_t mdctx; + mbedtls_md_init(&mdctx); + int mr = mbedtls_md_setup(&mdctx, mbedtls_md_info_from_type(md), 1); + if (mr != 0) { + debug_info("mbedtls_md_setup() failed: %d", mr); + return; + } + + mr = mbedtls_md_hmac_starts(&mdctx, key, key_len); + if (mr != 0) { + mbedtls_md_free(&mdctx); + debug_info("mbedtls_md_hmac_starts() failed: %d", mr); + return; + } + + mbedtls_md_hmac_update(&mdctx, data, data_len); + + mr = mbedtls_md_hmac_finish(&mdctx, out); + if (mr == 0) { + *out_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md)); + } else { + debug_info("mbedtls_md_hmac_finish() failed: %d", mr); + } + mbedtls_md_free(&mdctx); +} +#endif + +static void hkdf_md_extract(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* input_key_material, unsigned int input_key_material_len, unsigned char* out, unsigned int* out_len) +{ + unsigned char empty_salt[MD_MAX_DIGEST_SIZE]; + if (!md || !out || !out_len || !*out_len) return; + if (salt_len == 0) { + salt_len = MD_ALGO_DIGEST_SIZE(md); + salt = (unsigned char*)empty_salt; + } + HMAC(md, salt, salt_len, input_key_material, input_key_material_len, out, out_len); +} + +static void hkdf_md_expand(MD_ALGO_TYPE_T md, unsigned char* prk, unsigned int prk_len, unsigned char* info, unsigned int info_len, unsigned char* out, unsigned int* out_len) +{ + if (!md || !out || !out_len || !*out_len) return; + unsigned int md_size = MD_ALGO_DIGEST_SIZE(md); + if (*out_len > 255 * md_size) { + *out_len = 0; + return; + } + int blocks_needed = (*out_len) / md_size; + if (((*out_len) % md_size) != 0) blocks_needed++; + unsigned int okm_len = 0; + unsigned char okm_block[MD_MAX_DIGEST_SIZE]; + unsigned int okm_block_len = 0; + int i; + for (i = 0; i < blocks_needed; i++) { + unsigned int output_block_len = okm_block_len + info_len + 1; + unsigned char* output_block = malloc(output_block_len); + if (okm_block_len > 0) { + memcpy(output_block, okm_block, okm_block_len); + } + memcpy(output_block + okm_block_len, info, info_len); + output_block[okm_block_len + info_len] = (uint8_t)(i+1); + + HMAC(md, prk, prk_len, output_block, output_block_len, okm_block, &okm_block_len); + if (okm_len < *out_len) { + memcpy(out + okm_len, okm_block, (okm_len + okm_block_len > *out_len) ? *out_len - okm_len : okm_block_len); + } + okm_len += okm_block_len; + free(output_block); + } +} + +static void hkdf_md(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* info, unsigned int info_len, unsigned char* initial_key_material, unsigned int initial_key_material_size, unsigned char* out, unsigned int *out_len) +{ + if (!md || !initial_key_material || !out || !out_len || !*out_len) return; + + unsigned char prk[MD_MAX_DIGEST_SIZE]; + unsigned int prk_len = MD_ALGO_DIGEST_SIZE(md); + + hkdf_md_extract(md, salt, salt_len, initial_key_material, initial_key_material_size, prk, &prk_len); + if (prk_len > 0) { + hkdf_md_expand(md, prk, prk_len, info, info_len, out, out_len); + } else { + *out_len = 0; + } +} +/* }}} */ + +/* {{{ chacha20 poly1305 encryption/decryption */ +#if defined(HAVE_OPENSSL) && defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) +/* {{{ From: OpenBSD's e_chacha20poly1305.c */ +/* + * Copyright (c) 2015 Reyk Floter <reyk@openbsd.org> + * Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +static void +poly1305_update_with_length(poly1305_state *poly1305, + const unsigned char *data, size_t data_len) +{ + size_t j = data_len; + unsigned char length_bytes[8]; + unsigned i; + + for (i = 0; i < sizeof(length_bytes); i++) { + length_bytes[i] = j; + j >>= 8; + } + + if (data != NULL) + CRYPTO_poly1305_update(poly1305, data, data_len); + CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes)); +} + +static void +poly1305_update_with_pad16(poly1305_state *poly1305, + const unsigned char *data, size_t data_len) +{ + static const unsigned char zero_pad16[16]; + size_t pad_len; + + CRYPTO_poly1305_update(poly1305, data, data_len); + + /* pad16() is defined in RFC 7539 2.8.1. */ + if ((pad_len = data_len % 16) == 0) + return; + + CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len); +} +/* }}} */ +#endif + +static void chacha20_poly1305_encrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) +{ +#if defined(HAVE_OPENSSL) +#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) +#if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) + const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); + EVP_AEAD_CTX ctx; + EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); + EVP_AEAD_CTX_seal(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); +#else + unsigned char poly1305_key[32]; + poly1305_state poly1305; + uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; + const unsigned char* iv = nonce + 4; + + memset(poly1305_key, 0, sizeof(poly1305_key)); + CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); + + CRYPTO_poly1305_init(&poly1305, poly1305_key); + poly1305_update_with_pad16(&poly1305, ad, ad_len); + CRYPTO_chacha_20(out, in, in_len, key, iv, ctr + 1); + poly1305_update_with_pad16(&poly1305, out, in_len); + poly1305_update_with_length(&poly1305, NULL, ad_len); + poly1305_update_with_length(&poly1305, NULL, in_len); + + CRYPTO_poly1305_finish(&poly1305, out + in_len); + + *out_len = in_len + 16; +#endif +#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) + int outl = 0; + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); + EVP_EncryptUpdate(ctx, out, &outl, in, in_len); + *out_len = outl; + outl = 0; + EVP_EncryptFinal_ex(ctx, out + *out_len, &outl); + *out_len += outl; + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, out + *out_len); + EVP_CIPHER_CTX_free(ctx); + *out_len += 16; +#else +#error Please use a newer version of OpenSSL (>= 1.1.0) +#endif +#elif defined(HAVE_GCRYPT) +#if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) + gcry_cipher_hd_t hd; + if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { + debug_info("gcry_cipher_open() failed"); + return; + } + gcry_cipher_setkey(hd, key, 32); + gcry_cipher_setiv(hd, nonce, 12); + gcry_cipher_authenticate(hd, ad, ad_len); + *out_len = in_len + 16; + if (gcry_cipher_encrypt(hd, out, *out_len, in, in_len)) { + *out_len = 0; + } + gcry_cipher_gettag(hd, out+in_len, 16); + gcry_cipher_close(hd); +#else +#error Please use a newer version of libgcrypt (>= 1.7.0) +#endif +#elif defined (HAVE_MBEDTLS) + mbedtls_chachapoly_context ctx; + mbedtls_chachapoly_init(&ctx); + mbedtls_chachapoly_setkey(&ctx, key); + if (mbedtls_chachapoly_encrypt_and_tag(&ctx, in_len, nonce, ad, ad_len, in, out, out+in_len) != 0) { + *out_len = 0; + } + mbedtls_chachapoly_free(&ctx); +#else +#error chacha20_poly1305_encrypt_96 is not implemented +#endif +} + +static void chacha20_poly1305_encrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) +{ + unsigned char _nonce[12]; + *(uint32_t*)(&_nonce[0]) = 0; + memcpy(&_nonce[4], nonce, 8); + chacha20_poly1305_encrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); +} + +static void chacha20_poly1305_decrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) +{ +#if defined(HAVE_OPENSSL) +#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) +#if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) + const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); + EVP_AEAD_CTX ctx; + EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); + EVP_AEAD_CTX_open(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); +#else + unsigned char mac[16]; + unsigned char poly1305_key[32]; + poly1305_state poly1305; + size_t plaintext_len = in_len - 16; + uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; + const unsigned char *iv = nonce + 4; + + memset(poly1305_key, 0, sizeof(poly1305_key)); + CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); + + CRYPTO_poly1305_init(&poly1305, poly1305_key); + poly1305_update_with_pad16(&poly1305, ad, ad_len); + poly1305_update_with_pad16(&poly1305, in, plaintext_len); + poly1305_update_with_length(&poly1305, NULL, ad_len); + poly1305_update_with_length(&poly1305, NULL, plaintext_len); + + CRYPTO_poly1305_finish(&poly1305, mac); + + if (memcmp(mac, in + plaintext_len, 16) != 0) { + *out_len = 0; + return; + } + + CRYPTO_chacha_20(out, in, plaintext_len, key, iv, ctr + 1); + *out_len = plaintext_len; +#endif +#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) + int outl = 0; + size_t plaintext_len = in_len - 16; + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, in + plaintext_len); + EVP_DecryptUpdate(ctx, out, &outl, in, plaintext_len); + *out_len = outl; + outl = 0; + if (EVP_DecryptFinal_ex(ctx, out + *out_len, &outl) == 1) { + *out_len += outl; + } else { + *out_len = 0; + } + EVP_CIPHER_CTX_free(ctx); +#else +#error Please use a newer version of OpenSSL (>= 1.1.0) +#endif +#elif defined(HAVE_GCRYPT) +#if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) + gcry_cipher_hd_t hd; + if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { + debug_info("gcry_cipher_open() failed"); + return; + } + gcry_cipher_setkey(hd, key, 32); + gcry_cipher_setiv(hd, nonce, 12); + gcry_cipher_authenticate(hd, ad, ad_len); + unsigned int plaintext_len = in_len - 16; + gcry_cipher_decrypt(hd, out, *out_len, in, plaintext_len); + if (gcry_cipher_checktag(hd, in + plaintext_len, 16) == 0) { + *out_len = plaintext_len; + } else { + *out_len = 0; + } + gcry_cipher_close(hd); +#else +#error Please use a newer version of libgcrypt (>= 1.7.0) +#endif +#elif defined(HAVE_MBEDTLS) + mbedtls_chachapoly_context ctx; + mbedtls_chachapoly_init(&ctx); + mbedtls_chachapoly_setkey(&ctx, key); + unsigned int plaintext_len = in_len - 16; + if (mbedtls_chachapoly_auth_decrypt(&ctx, plaintext_len, nonce, ad, ad_len, in + plaintext_len, in, out) == 0) { + *out_len = plaintext_len; + } else { + *out_len = 0; + } + mbedtls_chachapoly_free(&ctx); +#else +#error chacha20_poly1305_decrypt_96 is not implemented +#endif +} + +static void chacha20_poly1305_decrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) +{ + unsigned char _nonce[12]; + *(uint32_t*)(&_nonce[0]) = 0; + memcpy(&_nonce[4], nonce, 8); + chacha20_poly1305_decrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); +} +/* }}} */ + +#define PAIRING_ERROR(x) \ + debug_info(x); \ + if (pairing_callback) { \ + pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, (char*)x, NULL); \ + } + +#define PAIRING_ERROR_FMT(...) \ + sprintf(tmp, __VA_ARGS__); \ + debug_info(tmp); \ + if (pairing_callback) { \ + pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, tmp, NULL); \ + } + +#endif /* HAVE_WIRELESS_PAIRING */ + +lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdownd_cu_pairing_cb_t pairing_callback, void* cb_user_data, plist_t host_info, plist_t acl) +{ +#ifdef HAVE_WIRELESS_PAIRING + if (!client || !pairing_callback || (host_info && plist_get_node_type(host_info) != PLIST_DICT) || (acl && plist_get_node_type(acl) != PLIST_DICT)) + return LOCKDOWN_E_INVALID_ARG; + + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; + + if (client->device && client->device->version == 0) { + plist_t p_version = NULL; + if (lockdownd_get_value(client, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { + int vers[3] = {0, 0, 0}; + char *s_version = NULL; + plist_get_string_val(p_version, &s_version); + if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { + client->device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); + } + free(s_version); + } + plist_free(p_version); + } + + char* pairing_uuid = NULL; + if (host_info) { + plist_t accountid = plist_dict_get_item(host_info, "accountID"); + if (accountid && plist_get_node_type(accountid) == PLIST_STRING) { + plist_get_string_val(accountid, &pairing_uuid); + } + } + if (!pairing_uuid) { + userpref_read_system_buid(&pairing_uuid); + } + if (!pairing_uuid) { + pairing_uuid = generate_uuid(); + } + unsigned int pairing_uuid_len = strlen(pairing_uuid); + + SRP_initialize_library(); + + SRP* srp = SRP_new(SRP6a_sha512_client_method()); + if (!srp) { + PAIRING_ERROR("Failed to initialize SRP") + return LOCKDOWN_E_UNKNOWN_ERROR; + } + + char tmp[256]; + plist_t dict = NULL; + uint8_t current_state = 0; + uint8_t final_state = 6; + + unsigned char* salt = NULL; + unsigned int salt_size = 0; + unsigned char* pubkey = NULL; + unsigned int pubkey_size = 0; + + unsigned char setup_encryption_key[32]; + + cstr *thekey = NULL; + + do { + current_state++; + + dict = plist_new_dict(); + plist_dict_set_item(dict, "Request", plist_new_string("CUPairingCreate")); + if (current_state == 1) { + plist_dict_set_item(dict, "Flags", plist_new_uint(1)); + } else { + plist_dict_set_item(dict, "Flags", plist_new_uint(0)); + } + + tlv_buf_t tlv = tlv_buf_new(); + + if (current_state == 1) { + /* send method */ + tlv_buf_append(tlv, 0x00, 1, (void*)"\x00"); // 0x00 (Method), 1 bytes, 00 + } else if (current_state == 3) { + /* generate public key */ + cstr* own_pub = NULL; + SRP_gen_pub(srp, &own_pub); + + if (!own_pub) { + PAIRING_ERROR("[SRP] Failed to generate public key") + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + /* compute key from remote's public key */ + if (SRP_compute_key(srp, &thekey, pubkey, pubkey_size) != 0) { + cstr_free(own_pub); + PAIRING_ERROR("[SRP] Failed to compute key") + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + /* compute response */ + cstr *response = NULL; + SRP_respond(srp, &response); + + /* send our public key + response */ + tlv_buf_append(tlv, 0x03, own_pub->length, own_pub->data); + tlv_buf_append(tlv, 0x04, response->length, response->data); + cstr_free(response); + cstr_free(own_pub); + } else if (current_state == 5) { + /* send encrypted info */ + + static const char PAIR_SETUP_ENCRYPT_SALT[] = "Pair-Setup-Encrypt-Salt"; + static const char PAIR_SETUP_ENCRYPT_INFO[] = "Pair-Setup-Encrypt-Info"; + static const char PAIR_SETUP_CONTROLLER_SIGN_SALT[] = "Pair-Setup-Controller-Sign-Salt"; + static const char PAIR_SETUP_CONTROLLER_SIGN_INFO[] = "Pair-Setup-Controller-Sign-Info"; + + // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Encrypt-Salt + Pair-Setup-Encrypt-Info + // result used as key for chacha20-poly1305 + unsigned int setup_encryption_key_len = sizeof(setup_encryption_key); + hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_ENCRYPT_SALT, sizeof(PAIR_SETUP_ENCRYPT_SALT)-1, (unsigned char*)PAIR_SETUP_ENCRYPT_INFO, sizeof(PAIR_SETUP_ENCRYPT_INFO)-1, (unsigned char*)thekey->data, thekey->length, setup_encryption_key, &setup_encryption_key_len); + + unsigned char ed25519_pubkey[32]; + unsigned char ed25519_privkey[64]; + unsigned char ed25519seed[32]; + ed25519_create_seed(ed25519seed); + + ed25519_create_keypair(ed25519_pubkey, ed25519_privkey, ed25519seed); + + unsigned int signbuf_len = pairing_uuid_len + 64; + unsigned char* signbuf = malloc(signbuf_len); + unsigned int hkdf_len = 32; + // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Controller-Sign-Salt + Pair-Setup-Controller-Sign-Info + hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_SALT, sizeof(PAIR_SETUP_CONTROLLER_SIGN_SALT)-1, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_INFO, sizeof(PAIR_SETUP_CONTROLLER_SIGN_INFO)-1, (unsigned char*)thekey->data, thekey->length, signbuf, &hkdf_len); + + memcpy(signbuf + 32, pairing_uuid, pairing_uuid_len); + memcpy(signbuf + 32 + pairing_uuid_len, ed25519_pubkey, 32); + + unsigned char ed_sig[64]; + ed25519_sign(ed_sig, signbuf, 0x64, ed25519_pubkey, ed25519_privkey); + + tlv_buf_t tlvbuf = tlv_buf_new(); + tlv_buf_append(tlvbuf, 0x01, pairing_uuid_len, (void*)pairing_uuid); + tlv_buf_append(tlvbuf, 0x03, sizeof(ed25519_pubkey), ed25519_pubkey); + tlv_buf_append(tlvbuf, 0x0a, sizeof(ed_sig), ed_sig); + + /* ACL */ + unsigned char* odata = NULL; + unsigned int olen = 0; + if (acl) { + opack_encode_from_plist(acl, &odata, &olen); + } else { + /* defaut ACL */ + plist_t acl_plist = plist_new_dict(); + plist_dict_set_item(acl_plist, "com.apple.ScreenCapture", plist_new_bool(1)); + plist_dict_set_item(acl_plist, "com.apple.developer", plist_new_bool(1)); + opack_encode_from_plist(acl_plist, &odata, &olen); + plist_free(acl_plist); + } + tlv_buf_append(tlvbuf, 0x12, olen, odata); + free(odata); + + /* HOST INFORMATION */ + char hostname[256]; +#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE) + CFStringRef cname = SCDynamicStoreCopyComputerName(NULL, NULL); + CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8); + CFRelease(cname); +#else +#ifdef WIN32 + DWORD hostname_len = sizeof(hostname); + GetComputerName(hostname, &hostname_len); +#else + gethostname(hostname, sizeof(hostname)); +#endif +#endif + + char modelname[256]; + modelname[0] = '\0'; +#ifdef __APPLE__ + size_t len = sizeof(modelname); + sysctlbyname("hw.model", &modelname, &len, NULL, 0); +#endif + if (strlen(modelname) == 0) { + strcpy(modelname, "HackbookPro13,37"); + } + + unsigned char primary_mac_addr[6] = { 0, 0, 0, 0, 0, 0 }; + if (get_primary_mac_address(primary_mac_addr) != 0) { + debug_info("Failed to get primary mac address"); + } + debug_info("Primary mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", primary_mac_addr[0], primary_mac_addr[1], primary_mac_addr[2], primary_mac_addr[3], primary_mac_addr[4], primary_mac_addr[5]); + + // "OPACK" encoded device info + plist_t info_plist = plist_new_dict(); + //plist_dict_set_item(info_plist, "altIRK", plist_new_data((char*)altIRK, 16)); + plist_dict_set_item(info_plist, "accountID", plist_new_string(pairing_uuid)); + plist_dict_set_item(info_plist, "model", plist_new_string(modelname)); + plist_dict_set_item(info_plist, "name", plist_new_string(hostname)); + plist_dict_set_item(info_plist, "mac", plist_new_data((char*)primary_mac_addr, 6)); + if (host_info) { + plist_dict_merge(&info_plist, host_info); + } + opack_encode_from_plist(info_plist, &odata, &olen); + plist_free(info_plist); + tlv_buf_append(tlvbuf, 0x11, olen, odata); + free(odata); + + size_t encrypted_len = tlvbuf->length + 16; + unsigned char* encrypted_buf = (unsigned char*)malloc(encrypted_len); + + chacha20_poly1305_encrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg05", NULL, 0, tlvbuf->data, tlvbuf->length, encrypted_buf, &encrypted_len); + + tlv_buf_free(tlvbuf); + + tlv_buf_append(tlv, 0x05, encrypted_len, encrypted_buf); + free(encrypted_buf); + } else { + tlv_buf_free(tlv); + PAIRING_ERROR("[SRP] Invalid state"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + tlv_buf_append(tlv, 0x06, 1, ¤t_state); + plist_dict_set_item(dict, "Payload", plist_new_data((char*)tlv->data, tlv->length)); + tlv_buf_free(tlv); + + plist_dict_set_item(dict, "Label", plist_new_string(client->label)); + plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); + + ret = lockdownd_send(client, dict); + plist_free(dict); + dict = NULL; + + if (ret != LOCKDOWN_E_SUCCESS) { + break; + } + + current_state++; + + ret = lockdownd_receive(client, &dict); + if (ret != LOCKDOWN_E_SUCCESS) { + break; + } + ret = lockdown_check_result(dict, "CUPairingCreate"); + if (ret != LOCKDOWN_E_SUCCESS) { + break; + } + + plist_t extresp = plist_dict_get_item(dict, "ExtendedResponse"); + if (!extresp) { + ret = LOCKDOWN_E_PLIST_ERROR; + break; + } + plist_t blob = plist_dict_get_item(extresp, "Payload"); + if (!blob) { + ret = LOCKDOWN_E_PLIST_ERROR; + break; + } + uint64_t data_len = 0; + const char* data = plist_get_data_ptr(blob, &data_len); + + uint8_t state = 0; + if (!tlv_data_get_uint8(data, data_len, 0x06, &state)) { + PAIRING_ERROR("[SRP] ERROR: Could not find state in response"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + if (state != current_state) { + PAIRING_ERROR_FMT("[SRP] ERROR: Unexpected state %d, expected %d", state, current_state); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + unsigned int errval = 0; + uint64_t u64val = 0; + tlv_data_get_uint(data, data_len, 0x07, &u64val); +debug_buffer(data, data_len); + errval = (unsigned int)u64val; + if (errval > 0) { + if (errval == 3) { + u64val = 0; + tlv_data_get_uint(data, data_len, 0x08, &u64val); + if (u64val > 0) { + uint32_t retry_delay = (uint32_t)u64val; + PAIRING_ERROR_FMT("[SRP] Pairing is blocked for another %u seconds", retry_delay) + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + } else if (errval == 2 && state == 4) { + PAIRING_ERROR_FMT("[SRP] Invalid PIN") + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } else { + PAIRING_ERROR_FMT("[SRP] Received error %u in state %d.", errval, state); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + } + + if (state == 2) { + /* receive salt and public key */ + if (!tlv_data_copy_data(data, data_len, 0x02, (void**)&salt, &salt_size)) { + PAIRING_ERROR("[SRP] ERROR: Could not find salt in response"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + if (!tlv_data_copy_data(data, data_len, 0x03, (void**)&pubkey, &pubkey_size)) { + PAIRING_ERROR("[SRP] ERROR: Could not find public key in response"); + + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + const char PAIR_SETUP[] = "Pair-Setup"; + if (SRP_set_user_raw(srp, (const unsigned char*)PAIR_SETUP, sizeof(PAIR_SETUP)-1) != 0) { + PAIRING_ERROR("[SRP] Failed to set SRP user"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + /* kSRPParameters_3072_SHA512 */ + if (SRP_set_params(srp, kSRPModulus3072, sizeof(kSRPModulus3072), &kSRPGenerator5, 1, salt, salt_size) != 0) { + PAIRING_ERROR("[SRP] Failed to set SRP parameters"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + + } + + if (pairing_callback) { + char pin[64]; + unsigned int pin_len = sizeof(pin); + pairing_callback(LOCKDOWN_CU_PAIRING_PIN_REQUESTED, cb_user_data, pin, &pin_len); + + SRP_set_auth_password_raw(srp, (const unsigned char*)pin, pin_len); + } + } else if (state == 4) { + /* receive proof */ + unsigned char* proof = NULL; + unsigned int proof_len = 0; + + if (!tlv_data_copy_data(data, data_len, 0x04, (void**)&proof, &proof_len)) { + PAIRING_ERROR("[SRP] ERROR: Could not find proof data in response"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + /* verify */ + int vrfy_result = SRP_verify(srp, proof, proof_len); + free(proof); + + if (vrfy_result == 0) { + debug_info("[SRP] PIN verified successfully"); + } else { + PAIRING_ERROR("[SRP] PIN verification failure"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + } else if (state == 6) { + int srp_pair_success = 0; + plist_t node = plist_dict_get_item(extresp, "doSRPPair"); + if (node) { + const char* strv = plist_get_string_ptr(node, NULL); + if (strcmp(strv, "succeed") == 0) { + srp_pair_success = 1; + } + } + if (!srp_pair_success) { + PAIRING_ERROR("SRP Pairing failed"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + /* receive encrypted info */ + unsigned char* encrypted_buf = NULL; + unsigned int enc_len = 0; + if (!tlv_data_copy_data(data, data_len, 0x05, (void**)&encrypted_buf, &enc_len)) { + PAIRING_ERROR("[SRP] ERROR: Could not find encrypted data in response"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + size_t plain_len = enc_len-16; + unsigned char* plain_buf = malloc(plain_len); + chacha20_poly1305_decrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg06", NULL, 0, encrypted_buf, enc_len, plain_buf, &plain_len); + free(encrypted_buf); + + unsigned char* dev_info = NULL; + unsigned int dev_info_len = 0; + int res = tlv_data_copy_data(plain_buf, plain_len, 0x11, (void**)&dev_info, &dev_info_len); + free(plain_buf); + if (!res) { + PAIRING_ERROR("[SRP] ERROR: Failed to locate device info in response"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + plist_t device_info = NULL; + opack_decode_to_plist(dev_info, dev_info_len, &device_info); + free(dev_info); + + if (!device_info) { + PAIRING_ERROR("[SRP] ERROR: Failed to parse device info"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + + if (pairing_callback) { + pairing_callback(LOCKDOWN_CU_PAIRING_DEVICE_INFO, cb_user_data, device_info, NULL); + } + plist_free(device_info); + } else { + PAIRING_ERROR("[SRP] ERROR: Invalid state"); + ret = LOCKDOWN_E_PAIRING_FAILED; + break; + } + plist_free(dict); + dict = NULL; + + } while (current_state != final_state); + + plist_free(dict); + + free(salt); + free(pubkey); + + SRP_free(srp); + srp = NULL; + + if (ret != LOCKDOWN_E_SUCCESS) { + if (thekey) { + cstr_free(thekey); + } + return ret; + } + + free(client->cu_key); + client->cu_key = malloc(thekey->length); + memcpy(client->cu_key, thekey->data, thekey->length); + client->cu_key_len = thekey->length; + cstr_free(thekey); + + return LOCKDOWN_E_SUCCESS; +#else + debug_info("not supported"); + return LOCKDOWN_E_UNKNOWN_ERROR; +#endif +} + +lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t client, const char* request, plist_t request_payload, plist_t* reply) +{ +#ifdef HAVE_WIRELESS_PAIRING + if (!client || !request) + return LOCKDOWN_E_INVALID_ARG; + + if (!client->cu_key) + return LOCKDOWN_E_NO_RUNNING_SESSION; + + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; + + /* derive keys */ + unsigned char cu_write_key[32]; + unsigned int cu_write_key_len = sizeof(cu_write_key); + static const char WRITE_KEY_SALT_MDLD[] = "WriteKeySaltMDLD"; + static const char WRITE_KEY_INFO_MDLD[] = "WriteKeyInfoMDLD"; + hkdf_md(MD_ALGO_SHA512, (unsigned char*)WRITE_KEY_SALT_MDLD, sizeof(WRITE_KEY_SALT_MDLD)-1, (unsigned char*)WRITE_KEY_INFO_MDLD, sizeof(WRITE_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_write_key, &cu_write_key_len); + + unsigned char cu_read_key[32]; + unsigned int cu_read_key_len = sizeof(cu_write_key); + static const char READ_KEY_SALT_MDLD[] = "ReadKeySaltMDLD"; + static const char READ_KEY_INFO_MDLD[] = "ReadKeyInfoMDLD"; + hkdf_md(MD_ALGO_SHA512, (unsigned char*)READ_KEY_SALT_MDLD, sizeof(READ_KEY_SALT_MDLD)-1, (unsigned char*)READ_KEY_INFO_MDLD, sizeof(READ_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_read_key, &cu_read_key_len); + + // Starting with iOS/tvOS 11.2 and WatchOS 4.2, this nonce is random and sent along with the request. Before, the request doesn't have a nonce and it uses hardcoded nonce "sendone01234". + unsigned char cu_nonce[12] = "sendone01234"; // guaranteed to be random by fair dice troll + if (client->device->version >= DEVICE_VERSION(11,2,0)) { +#if defined(HAVE_OPENSSL) + RAND_bytes(cu_nonce, sizeof(cu_nonce)); +#elif defined(HAVE_GCRYPT) + gcry_create_nonce(cu_nonce, sizeof(cu_nonce)); +#endif + } + + debug_plist(request_payload); + + /* convert request payload to binary */ + uint32_t bin_len = 0; + char* bin = NULL; + plist_to_bin(request_payload, &bin, &bin_len); + + /* encrypt request */ + size_t encrypted_len = bin_len + 16; + unsigned char* encrypted_buf = malloc(encrypted_len); + chacha20_poly1305_encrypt_96(cu_write_key, cu_nonce, NULL, 0, (unsigned char*)bin, bin_len, encrypted_buf, &encrypted_len); + free(bin); + bin = NULL; + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict,"Request", plist_new_string(request)); + plist_dict_set_item(dict, "Payload", plist_new_data((char*)encrypted_buf, encrypted_len)); + free(encrypted_buf); + plist_dict_set_item(dict, "Nonce", plist_new_data((char*)cu_nonce, sizeof(cu_nonce))); + plist_dict_set_item(dict, "Label", plist_new_string(client->label)); + plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); + + /* send to device */ + ret = lockdownd_send(client, dict); + plist_free(dict); + dict = NULL; + + if (ret != LOCKDOWN_E_SUCCESS) + return ret; + + /* Now get device's answer */ + ret = lockdownd_receive(client, &dict); + if (ret != LOCKDOWN_E_SUCCESS) + return ret; + + ret = lockdown_check_result(dict, request); + if (ret != LOCKDOWN_E_SUCCESS) { + plist_free(dict); + return ret; + } + + /* get payload */ + plist_t blob = plist_dict_get_item(dict, "Payload"); + if (!blob) { + plist_free(dict); + return LOCKDOWN_E_DICT_ERROR; + } + + uint64_t dl = 0; + const char* dt = plist_get_data_ptr(blob, &dl); + + /* see if we have a nonce */ + blob = plist_dict_get_item(dict, "Nonce"); + const unsigned char* rnonce = (unsigned char*)"receiveone01"; + if (blob) { + uint64_t rl = 0; + rnonce = (const unsigned char*)plist_get_data_ptr(blob, &rl); + } + + /* decrypt payload */ + size_t decrypted_len = dl-16; + unsigned char* decrypted = malloc(decrypted_len); + chacha20_poly1305_decrypt_96(cu_read_key, (unsigned char*)rnonce, NULL, 0, (unsigned char*)dt, dl, decrypted, &decrypted_len); + plist_free(dict); + dict = NULL; + + plist_from_memory((const char*)decrypted, decrypted_len, &dict, NULL); + if (!dict) { + ret = LOCKDOWN_E_PLIST_ERROR; + debug_info("Failed to parse PLIST from decrypted payload:"); + debug_buffer((const char*)decrypted, decrypted_len); + free(decrypted); + return ret; + } + free(decrypted); + + debug_plist(dict); + + if (reply) { + *reply = dict; + } else { + plist_free(dict); + } + + return LOCKDOWN_E_SUCCESS; +#else + debug_info("not supported"); + return LOCKDOWN_E_UNKNOWN_ERROR; +#endif +} + +lockdownd_error_t lockdownd_get_value_cu(lockdownd_client_t client, const char* domain, const char* key, plist_t* value) +{ +#ifdef HAVE_WIRELESS_PAIRING + if (!client) + return LOCKDOWN_E_INVALID_ARG; + + if (!client->cu_key) + return LOCKDOWN_E_NO_RUNNING_SESSION; + + lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; + + plist_t request = plist_new_dict(); + if (domain) { + plist_dict_set_item(request, "Domain", plist_new_string(domain)); + } + if (key) { + plist_dict_set_item(request, "Key", plist_new_string(key)); + } + + plist_t reply = NULL; + ret = lockdownd_cu_send_request_and_get_reply(client, "GetValueCU", request, &reply); + plist_free(request); + if (ret != LOCKDOWN_E_SUCCESS) { + return ret; + } + + plist_t value_node = plist_dict_get_item(reply, "Value"); + if (value_node) { + debug_info("has a value"); + *value = plist_copy(value_node); + } + plist_free(reply); + + return ret; +#else + debug_info("not supported"); + return LOCKDOWN_E_UNKNOWN_ERROR; +#endif +} + +lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client) +{ +#ifdef HAVE_WIRELESS_PAIRING + if (!client) + return LOCKDOWN_E_INVALID_ARG; + + if (!client->cu_key) + return LOCKDOWN_E_NO_RUNNING_SESSION; + + lockdownd_error_t ret; + + plist_t wifi_mac = NULL; + ret = lockdownd_get_value_cu(client, NULL, "WiFiAddress", &wifi_mac); + if (ret != LOCKDOWN_E_SUCCESS) { + return ret; + } + + plist_t pubkey = NULL; + ret = lockdownd_get_value_cu(client, NULL, "DevicePublicKey", &pubkey); + if (ret != LOCKDOWN_E_SUCCESS) { + plist_free(wifi_mac); + return ret; + } + + key_data_t public_key = { NULL, 0 }; + uint64_t data_len = 0; + plist_get_data_val(pubkey, (char**)&public_key.data, &data_len); + public_key.size = (unsigned int)data_len; + plist_free(pubkey); + + plist_t pair_record_plist = plist_new_dict(); + pair_record_generate_keys_and_certs(pair_record_plist, public_key); + + char* host_id = NULL; + char* system_buid = NULL; + + /* set SystemBUID */ + userpref_read_system_buid(&system_buid); + if (system_buid) { + plist_dict_set_item(pair_record_plist, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); + free(system_buid); + } + + /* set HostID */ + host_id = generate_uuid(); + pair_record_set_host_id(pair_record_plist, host_id); + free(host_id); + + plist_t request_pair_record = plist_copy(pair_record_plist); + /* remove stuff that is private */ + plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); + plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); + + plist_t request = plist_new_dict(); + plist_dict_set_item(request, "PairRecord", request_pair_record); + plist_t pairing_opts = plist_new_dict(); + plist_dict_set_item(pairing_opts, "ExtendedPairingErrors", plist_new_bool(1)); + plist_dict_set_item(request, "PairingOptions", pairing_opts); + + plist_t reply = NULL; + ret = lockdownd_cu_send_request_and_get_reply(client, "PairCU", request, &reply); + plist_free(request); + if (ret != LOCKDOWN_E_SUCCESS) { + plist_free(wifi_mac); + return ret; + } + + char *s_udid = NULL; + plist_t p_udid = plist_dict_get_item(reply, "UDID"); + if (p_udid) { + plist_get_string_val(p_udid, &s_udid); + } + plist_t ebag = plist_dict_get_item(reply, "EscrowBag"); + if (ebag) { + plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(ebag)); + } + plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, wifi_mac); + plist_free(reply); + + if (userpref_save_pair_record(s_udid, 0, pair_record_plist) != 0) { + printf("Failed to save pair record for UDID %s\n", s_udid); + } + free(s_udid); + s_udid = NULL; + plist_free(pair_record_plist); + + ret = LOCKDOWN_E_SUCCESS; + + return ret; +#else + debug_info("not supported"); + return LOCKDOWN_E_UNKNOWN_ERROR; +#endif +} diff --git a/src/lockdown.c b/src/lockdown.c index 1a162ed..256bff0 100644 --- a/src/lockdown.c +++ b/src/lockdown.c @@ -33,23 +33,14 @@ #include <stdio.h> #include <ctype.h> #include <unistd.h> -#ifdef HAVE_OPENSSL -#include <openssl/pem.h> -#include <openssl/x509.h> -#include <openssl/x509v3.h> -#else -#include <libtasn1.h> -#include <gnutls/x509.h> -#include <gnutls/crypto.h> -#endif #include <plist/plist.h> +#include <libimobiledevice-glue/utils.h> #include "property_list_service.h" #include "lockdown.h" #include "idevice.h" #include "common/debug.h" #include "common/userpref.h" -#include "common/utils.h" #include "asprintf.h" #ifdef WIN32 @@ -57,6 +48,46 @@ #define sleep(x) Sleep(x*1000) #endif +struct st_lockdownd_error_str_map { + const char *lockdown_errstr; + const char *errstr; + lockdownd_error_t errcode; +}; + +static struct st_lockdownd_error_str_map lockdownd_error_str_map[] = { + { "InvalidResponse", "Invalid response", LOCKDOWN_E_INVALID_RESPONSE }, + { "MissingKey", "Missing key", LOCKDOWN_E_MISSING_KEY }, + { "MissingValue", "Missing value", LOCKDOWN_E_MISSING_VALUE }, + { "GetProhibited", "Get value prohibited", LOCKDOWN_E_GET_PROHIBITED }, + { "SetProhibited", "Set value prohibited", LOCKDOWN_E_SET_PROHIBITED }, + { "RemoveProhibited", "Remove value prohibited", LOCKDOWN_E_REMOVE_PROHIBITED }, + { "ImmutableValue", "Immutable value", LOCKDOWN_E_IMMUTABLE_VALUE }, + { "PasswordProtected", "Password protected", LOCKDOWN_E_PASSWORD_PROTECTED }, + { "UserDeniedPairing", "User denied pairing", LOCKDOWN_E_USER_DENIED_PAIRING }, + { "PairingDialogResponsePending", "Pairing dialog response pending", LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING }, + { "MissingHostID", "Missing HostID", LOCKDOWN_E_MISSING_HOST_ID }, + { "InvalidHostID", "Invalid HostID", LOCKDOWN_E_INVALID_HOST_ID }, + { "SessionActive", "Session active", LOCKDOWN_E_SESSION_ACTIVE }, + { "SessionInactive", "Session inactive", LOCKDOWN_E_SESSION_INACTIVE }, + { "MissingSessionID", "Missing session ID", LOCKDOWN_E_MISSING_SESSION_ID }, + { "InvalidSessionID", "Invalid session ID", LOCKDOWN_E_INVALID_SESSION_ID }, + { "MissingService", "Missing service", LOCKDOWN_E_MISSING_SERVICE }, + { "InvalidService", "Invalid service", LOCKDOWN_E_INVALID_SERVICE }, + { "ServiceLimit", "Service limit reached", LOCKDOWN_E_SERVICE_LIMIT }, + { "MissingPairRecord", "Missing pair record", LOCKDOWN_E_MISSING_PAIR_RECORD }, + { "SavePairRecordFailed", "Saving pair record failed", LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED }, + { "InvalidPairRecord", "Invalid pair record", LOCKDOWN_E_INVALID_PAIR_RECORD }, + { "InvalidActivationRecord", "Invalid activation record", LOCKDOWN_E_INVALID_ACTIVATION_RECORD }, + { "MissingActivationRecord", "Missing activation record", LOCKDOWN_E_MISSING_ACTIVATION_RECORD }, + { "ServiceProhibited", "Service prohibited", LOCKDOWN_E_SERVICE_PROHIBITED }, + { "EscrowLocked", "Escrow lockded", LOCKDOWN_E_ESCROW_LOCKED }, + { "PairingProhibitedOverThisConnection", "Pairing prohibited over this connection", LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION }, + { "FMiPProtected", "Find My iPhone/iPod/iPad protected", LOCKDOWN_E_FMIP_PROTECTED }, + { "MCProtected", "MC protected" , LOCKDOWN_E_MC_PROTECTED }, + { "MCChallengeRequired", "MC challenge required", LOCKDOWN_E_MC_CHALLENGE_REQUIRED }, + { NULL, NULL, 0 } +}; + /** * Convert an error string identifier to a lockdownd_error_t value. * Used internally to get correct error codes from a response. @@ -69,69 +100,13 @@ static lockdownd_error_t lockdownd_strtoerr(const char* name) { lockdownd_error_t err = LOCKDOWN_E_UNKNOWN_ERROR; - - if (strcmp(name, "InvalidResponse") == 0) { - err = LOCKDOWN_E_INVALID_RESPONSE; - } else if (strcmp(name, "MissingKey") == 0) { - err = LOCKDOWN_E_MISSING_KEY; - } else if (strcmp(name, "MissingValue") == 0) { - err = LOCKDOWN_E_MISSING_VALUE; - } else if (strcmp(name, "GetProhibited") == 0) { - err = LOCKDOWN_E_GET_PROHIBITED; - } else if (strcmp(name, "SetProhibited") == 0) { - err = LOCKDOWN_E_SET_PROHIBITED; - } else if (strcmp(name, "RemoveProhibited") == 0) { - err = LOCKDOWN_E_REMOVE_PROHIBITED; - } else if (strcmp(name, "ImmutableValue") == 0) { - err = LOCKDOWN_E_IMMUTABLE_VALUE; - } else if (strcmp(name, "PasswordProtected") == 0) { - err = LOCKDOWN_E_PASSWORD_PROTECTED; - } else if (strcmp(name, "UserDeniedPairing") == 0) { - err = LOCKDOWN_E_USER_DENIED_PAIRING; - } else if (strcmp(name, "PairingDialogResponsePending") == 0) { - err = LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING; - } else if (strcmp(name, "MissingHostID") == 0) { - err = LOCKDOWN_E_MISSING_HOST_ID; - } else if (strcmp(name, "InvalidHostID") == 0) { - err = LOCKDOWN_E_INVALID_HOST_ID; - } else if (strcmp(name, "SessionActive") == 0) { - err = LOCKDOWN_E_SESSION_ACTIVE; - } else if (strcmp(name, "SessionInactive") == 0) { - err = LOCKDOWN_E_SESSION_INACTIVE; - } else if (strcmp(name, "MissingSessionID") == 0) { - err = LOCKDOWN_E_MISSING_SESSION_ID; - } else if (strcmp(name, "InvalidSessionID") == 0) { - err = LOCKDOWN_E_INVALID_SESSION_ID; - } else if (strcmp(name, "MissingService") == 0) { - err = LOCKDOWN_E_MISSING_SERVICE; - } else if (strcmp(name, "InvalidService") == 0) { - err = LOCKDOWN_E_INVALID_SERVICE; - } else if (strcmp(name, "ServiceLimit") == 0) { - err = LOCKDOWN_E_SERVICE_LIMIT; - } else if (strcmp(name, "MissingPairRecord") == 0) { - err = LOCKDOWN_E_MISSING_PAIR_RECORD; - } else if (strcmp(name, "SavePairRecordFailed") == 0) { - err = LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED; - } else if (strcmp(name, "InvalidPairRecord") == 0) { - err = LOCKDOWN_E_INVALID_PAIR_RECORD; - } else if (strcmp(name, "InvalidActivationRecord") == 0) { - err = LOCKDOWN_E_INVALID_ACTIVATION_RECORD; - } else if (strcmp(name, "MissingActivationRecord") == 0) { - err = LOCKDOWN_E_MISSING_ACTIVATION_RECORD; - } else if (strcmp(name, "ServiceProhibited") == 0) { - err = LOCKDOWN_E_SERVICE_PROHIBITED; - } else if (strcmp(name, "EscrowLocked") == 0) { - err = LOCKDOWN_E_ESCROW_LOCKED; - } else if (strcmp(name, "PairingProhibitedOverThisConnection") == 0) { - err = LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION; - } else if (strcmp(name, "FMiPProtected") == 0) { - err = LOCKDOWN_E_FMIP_PROTECTED; - } else if (strcmp(name, "MCProtected") == 0) { - err = LOCKDOWN_E_MC_PROTECTED; - } else if (strcmp(name, "MCChallengeRequired") == 0) { - err = LOCKDOWN_E_MC_CHALLENGE_REQUIRED; + int i = 0; + while (lockdownd_error_str_map[i].lockdown_errstr) { + if (strcmp(lockdownd_error_str_map[i].lockdown_errstr, name) == 0) { + return lockdownd_error_str_map[i].errcode; + } + i++; } - return err; } @@ -177,7 +152,7 @@ static lockdownd_error_t lockdownd_error(property_list_service_error_t err) * LOCKDOWN_E_UNKNOWN_ERROR when the result is 'Failure', * or a specific error code if derieved from the result. */ -static lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match) +lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; @@ -188,53 +163,40 @@ static lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_m if (plist_get_node_type(query_node) != PLIST_STRING) { return ret; - } else { - char *query_value = NULL; - - plist_get_string_val(query_node, &query_value); - if (!query_value) { - return ret; - } + } - if (query_match && (strcmp(query_value, query_match) != 0)) { - free(query_value); - return ret; - } + const char *query_value = plist_get_string_ptr(query_node, NULL); + if (!query_value) { + return ret; + } - free(query_value); + if (query_match && (strcmp(query_value, query_match) != 0)) { + return ret; } - plist_t result_node = plist_dict_get_item(dict, "Result"); - if (!result_node) { - /* iOS 5: the 'Result' key is not present anymore. - But we need to check for the 'Error' key. */ - plist_t err_node = plist_dict_get_item(dict, "Error"); - if (err_node) { - if (plist_get_node_type(err_node) == PLIST_STRING) { - char *err_value = NULL; - - plist_get_string_val(err_node, &err_value); - if (err_value) { - debug_info("ERROR: %s", err_value); - ret = lockdownd_strtoerr(err_value); - free(err_value); - } else { - debug_info("ERROR: unknown error occured"); - } + /* Check for 'Error' in reply */ + plist_t err_node = plist_dict_get_item(dict, "Error"); + if (err_node) { + if (plist_get_node_type(err_node) == PLIST_STRING) { + const char *err_value = plist_get_string_ptr(err_node, NULL); + if (err_value) { + debug_info("ERROR: %s", err_value); + ret = lockdownd_strtoerr(err_value); + } else { + debug_info("ERROR: unknown error occurred"); } - return ret; } - - ret = LOCKDOWN_E_SUCCESS; - return ret; } - plist_type result_type = plist_get_node_type(result_node); - if (result_type == PLIST_STRING) { - char *result_value = NULL; - - plist_get_string_val(result_node, &result_value); + plist_t result_node = plist_dict_get_item(dict, "Result"); + if (!result_node) { + /* With iOS 5+ 'Result' is not present anymore. + If there is no 'Error', we can just assume success. */ + return LOCKDOWN_E_SUCCESS; + } + if (plist_get_node_type(result_node) == PLIST_STRING) { + const char *result_value = plist_get_string_ptr(result_node, NULL); if (result_value) { if (!strcmp(result_value, "Success")) { ret = LOCKDOWN_E_SUCCESS; @@ -244,9 +206,6 @@ static lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_m debug_info("ERROR: unknown result value '%s'", result_value); } } - - if (result_value) - free(result_value); } return ret; @@ -267,7 +226,7 @@ static void plist_dict_add_label(plist_t plist, const char *label) } } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) +lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -336,12 +295,13 @@ static lockdownd_error_t lockdownd_client_free_simple(lockdownd_client_t client) free(client->session_id); client->session_id = NULL; } - if (client->udid) { - free(client->udid); - } if (client->label) { free(client->label); } + if (client->cu_key) { + free(client->cu_key); + client->cu_key = NULL; + } free(client); client = NULL; @@ -349,7 +309,7 @@ static lockdownd_error_t lockdownd_client_free_simple(lockdownd_client_t client) return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) +lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -365,7 +325,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_free(lockdownd_client_t return ret; } -LIBIMOBILEDEVICE_API void lockdownd_client_set_label(lockdownd_client_t client, const char *label) +void lockdownd_client_set_label(lockdownd_client_t client, const char *label) { if (client) { if (client->label) @@ -375,7 +335,7 @@ LIBIMOBILEDEVICE_API void lockdownd_client_set_label(lockdownd_client_t client, } } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) +lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) { if (!client || !plist || (plist && *plist)) return LOCKDOWN_E_INVALID_ARG; @@ -383,7 +343,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_receive(lockdownd_client_t clie return lockdownd_error(property_list_service_receive_plist(client->parent, plist)); } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) +lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) { if (!client || !plist) return LOCKDOWN_E_INVALID_ARG; @@ -391,7 +351,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_send(lockdownd_client_t client, return lockdownd_error(property_list_service_send_xml_plist(client->parent, plist)); } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) +lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -436,7 +396,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_query_type(lockdownd_client_t c return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) +lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -490,7 +450,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_get_value(lockdownd_client_t cl return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) +lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) { if (!client || !value) return LOCKDOWN_E_INVALID_ARG; @@ -538,7 +498,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_set_value(lockdownd_client_t cl return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) +lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -585,7 +545,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_remove_value(lockdownd_client_t return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_get_device_udid(lockdownd_client_t client, char **udid) +lockdownd_error_t lockdownd_get_device_udid(lockdownd_client_t client, char **udid) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t value = NULL; @@ -631,7 +591,7 @@ static lockdownd_error_t lockdownd_get_device_public_key_as_key_data(lockdownd_c return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) +lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) { lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t value = NULL; @@ -648,7 +608,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_get_device_name(lockdownd_clien return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) +lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) { if (!device || !client) return LOCKDOWN_E_INVALID_ARG; @@ -668,12 +628,13 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new(idevice_t device, lo client_loc->parent = plistclient; client_loc->ssl_enabled = 0; client_loc->session_id = NULL; - client_loc->mux_id = device->mux_id; + client_loc->device = device; + client_loc->cu_key = NULL; + client_loc->cu_key_len = 0; - if (idevice_get_udid(device, &client_loc->udid) != IDEVICE_E_SUCCESS) { - debug_info("failed to get device udid."); + if (device->udid) { + debug_info("device udid: %s", device->udid); } - debug_info("device udid: %s", client_loc->udid); client_loc->label = label ? strdup(label) : NULL; @@ -682,7 +643,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new(idevice_t device, lo return LOCKDOWN_E_SUCCESS; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) +lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -703,7 +664,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevi ret = lockdownd_query_type(client_loc, &type); if (LOCKDOWN_E_SUCCESS != ret) { debug_info("QueryType failed in the lockdownd client."); - } else if (strcmp("com.apple.mobile.lockdown", type)) { + } else if (strcmp("com.apple.mobile.lockdown", type) != 0) { debug_info("Warning QueryType request returned \"%s\".", type); } free(type); @@ -715,14 +676,43 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevi char *s_version = NULL; plist_get_string_val(p_version, &s_version); if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { - device->version = ((vers[0] & 0xFF) << 16) | ((vers[1] & 0xFF) << 8) | (vers[2] & 0xFF); + device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); } free(s_version); } plist_free(p_version); } + if (device->device_class == 0) { + plist_t p_device_class = NULL; + if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) { + char* s_device_class = NULL; + plist_get_string_val(p_device_class, &s_device_class); + if (s_device_class != NULL) { + if (!strcmp(s_device_class, "iPhone")) { + device->device_class = DEVICE_CLASS_IPHONE; + } else if (!strcmp(s_device_class, "iPad")) { + device->device_class = DEVICE_CLASS_IPAD; + } else if (!strcmp(s_device_class, "iPod")) { + device->device_class = DEVICE_CLASS_IPOD; + } else if (!strcmp(s_device_class, "Watch")) { + device->device_class = DEVICE_CLASS_WATCH; + } else if (!strcmp(s_device_class, "AppleTV")) { + device->device_class = DEVICE_CLASS_APPLETV; + } else { + device->device_class = DEVICE_CLASS_UNKNOWN; + } + free(s_device_class); + } + } + plist_free(p_device_class); + } - userpref_read_pair_record(client_loc->udid, &pair_record); + userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } if (pair_record) { pair_record_get_host_id(pair_record, &host_id); } @@ -732,13 +722,15 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevi if (LOCKDOWN_E_SUCCESS == ret && !pair_record) { /* attempt pairing */ + free(host_id); + host_id = NULL; ret = lockdownd_pair(client_loc, NULL); } plist_free(pair_record); pair_record = NULL; - if (device->version < 0x070000) { + if (device->version < DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) { /* for older devices, we need to validate pairing to receive trusted host status */ ret = lockdownd_validate_pair(client_loc, NULL); @@ -755,7 +747,20 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevi if (LOCKDOWN_E_SUCCESS == ret) { if (!host_id) { - userpref_read_pair_record(client_loc->udid, &pair_record); + uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } else if (uerr == USERPREF_E_NOENT) { + debug_info("ERROR: No pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_INVALID_CONF; + } else if (uerr != USERPREF_E_SUCCESS) { + debug_info("ERROR: Failed to retrieve or parse pair record for %s", client_loc->device->udid); + lockdownd_client_free(client_loc); + return LOCKDOWN_E_INVALID_CONF; + } if (pair_record) { pair_record_get_host_id(pair_record, &host_id); plist_free(pair_record); @@ -919,9 +924,16 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node); } else { /* use existing pair record */ - userpref_read_pair_record(client->udid, &pair_record_plist); - if (!pair_record_plist) { - return LOCKDOWN_E_INVALID_HOST_ID; + userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record_plist); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } else if (uerr == USERPREF_E_NOENT) { + debug_info("ERROR: No pair record for %s", client->device->udid); + return LOCKDOWN_E_INVALID_CONF; + } else if (uerr != USERPREF_E_SUCCESS) { + debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); + return LOCKDOWN_E_INVALID_CONF; } } } @@ -984,7 +996,7 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ debug_info("internal pairing mode"); if (!strcmp("Unpair", verb)) { /* remove public key from config */ - userpref_delete_pair_record(client->udid); + userpref_delete_pair_record(client->device->udid); } else { if (!strcmp("Pair", verb)) { /* add returned escrow bag if available */ @@ -1002,7 +1014,7 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ wifi_node = NULL; } - userpref_save_pair_record(client->udid, client->mux_id, pair_record_plist); + userpref_save_pair_record(client->device->udid, client->device->mux_id, pair_record_plist); } } } else { @@ -1044,7 +1056,7 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) +lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) { plist_t options = plist_new_dict(); @@ -1057,22 +1069,22 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_pair(lockdownd_client_t client, return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response) +lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response) { return lockdownd_do_pair(client, pair_record, "Pair", options, response); } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) +lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) { return lockdownd_do_pair(client, pair_record, "ValidatePair", NULL, NULL); } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) +lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) { return lockdownd_do_pair(client, pair_record, "Unpair", NULL, NULL); } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) +lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -1102,7 +1114,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_enter_recovery(lockdownd_client return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) +lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -1136,7 +1148,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_goodbye(lockdownd_client_t clie return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) +lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) { lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; plist_t dict = NULL; @@ -1251,9 +1263,17 @@ static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_ if (send_escrow_bag) { /* get the pairing record */ plist_t pair_record = NULL; - userpref_read_pair_record(client->udid, &pair_record); - if (!pair_record) { - debug_info("ERROR: failed to read pair record for device: %s", client->udid); + userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record); + if (uerr == USERPREF_E_READ_ERROR) { + debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); + plist_free(dict); + return LOCKDOWN_E_RECEIVE_TIMEOUT; + } else if (uerr == USERPREF_E_NOENT) { + debug_info("ERROR: No pair record for %s", client->device->udid); + plist_free(dict); + return LOCKDOWN_E_INVALID_CONF; + } else if (uerr != USERPREF_E_SUCCESS) { + debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); plist_free(dict); return LOCKDOWN_E_INVALID_CONF; } @@ -1332,6 +1352,7 @@ static lockdownd_error_t lockdownd_do_start_service(lockdownd_client_t client, c *service = (lockdownd_service_descriptor_t)malloc(sizeof(struct lockdownd_service_descriptor)); (*service)->port = 0; (*service)->ssl_enabled = 0; + (*service)->identifier = strdup(identifier); /* read service port number */ plist_t node = plist_dict_get_item(dict, "Port"); @@ -1371,17 +1392,17 @@ static lockdownd_error_t lockdownd_do_start_service(lockdownd_client_t client, c return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) +lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) { return lockdownd_do_start_service(client, identifier, 0, service); } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) +lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) { return lockdownd_do_start_service(client, identifier, 1, service); } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record) +lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -1420,7 +1441,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_activate(lockdownd_client_t cli return ret; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) +lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -1467,7 +1488,7 @@ static void str_remove_spaces(char *source) *dest = 0; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) +lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) { if (!client) return LOCKDOWN_E_INVALID_ARG; @@ -1522,7 +1543,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd return LOCKDOWN_E_SUCCESS; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_data_classes_free(char **classes) +lockdownd_error_t lockdownd_data_classes_free(char **classes) { if (classes) { int i = 0; @@ -1534,10 +1555,50 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_data_classes_free(char **classe return LOCKDOWN_E_SUCCESS; } -LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service) +lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service) { - if (service) + if (service) { + free(service->identifier); free(service); + } return LOCKDOWN_E_SUCCESS; } + +const char* lockdownd_strerror(lockdownd_error_t err) +{ + switch (err) { + case LOCKDOWN_E_SUCCESS: + return "Success"; + case LOCKDOWN_E_INVALID_ARG: + return "Invalid argument"; + case LOCKDOWN_E_INVALID_CONF: + return "Invalid configuration"; + case LOCKDOWN_E_PLIST_ERROR: + return "PropertyList error"; + case LOCKDOWN_E_PAIRING_FAILED: + return "Pairing failed"; + case LOCKDOWN_E_SSL_ERROR: + return "SSL error"; + case LOCKDOWN_E_DICT_ERROR: + return "Invalid dictionary"; + case LOCKDOWN_E_RECEIVE_TIMEOUT: + return "Receive timeout"; + case LOCKDOWN_E_MUX_ERROR: + return "Mux error"; + case LOCKDOWN_E_NO_RUNNING_SESSION: + return "No running session"; + case LOCKDOWN_E_UNKNOWN_ERROR: + return "Unknown Error"; + default: { + int i = 0; + while (lockdownd_error_str_map[i].lockdown_errstr) { + if (lockdownd_error_str_map[i].errcode == err) { + return lockdownd_error_str_map[i].errstr; + } + i++; + } + } break; + } + return "Unknown Error"; +} diff --git a/src/lockdown.h b/src/lockdown.h index bf595df..ba291ec 100644 --- a/src/lockdown.h +++ b/src/lockdown.h @@ -23,6 +23,7 @@ #ifndef __LOCKDOWND_H #define __LOCKDOWND_H +#include "idevice.h" #include "libimobiledevice/lockdown.h" #include "property_list_service.h" @@ -32,9 +33,12 @@ struct lockdownd_client_private { property_list_service_client_t parent; int ssl_enabled; char *session_id; - char *udid; char *label; - uint32_t mux_id; + idevice_t device; + unsigned char* cu_key; + unsigned int cu_key_len; }; +lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match); + #endif diff --git a/src/misagent.c b/src/misagent.c index 095edba..e3da997 100644 --- a/src/misagent.c +++ b/src/misagent.c @@ -19,6 +19,9 @@ * 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 <unistd.h> @@ -57,7 +60,7 @@ static misagent_error_t misagent_error(property_list_service_error_t err) /** * Checks the response from misagent to determine if the operation - * was successful or an error occured. Internally used only. + * was successful or an error occurred. Internally used only. * * @param response a PLIST_DICT received from device's misagent * @param status_code pointer to an int that will be set to the status code @@ -82,12 +85,11 @@ static misagent_error_t misagent_check_result(plist_t response, int* status_code *status_code = (int)(val & 0xFFFFFFFF); if (*status_code == 0) { return MISAGENT_E_SUCCESS; - } else { - return MISAGENT_E_REQUEST_FAILED; } + return MISAGENT_E_REQUEST_FAILED; } -LIBIMOBILEDEVICE_API misagent_error_t misagent_client_new(idevice_t device, lockdownd_service_descriptor_t service, misagent_client_t *client) +misagent_error_t misagent_client_new(idevice_t device, lockdownd_service_descriptor_t service, misagent_client_t *client) { property_list_service_client_t plistclient = NULL; misagent_error_t err = misagent_error(property_list_service_client_new(device, service, &plistclient)); @@ -103,14 +105,14 @@ LIBIMOBILEDEVICE_API misagent_error_t misagent_client_new(idevice_t device, lock return MISAGENT_E_SUCCESS; } -LIBIMOBILEDEVICE_API misagent_error_t misagent_client_start_service(idevice_t device, misagent_client_t * client, const char* label) +misagent_error_t misagent_client_start_service(idevice_t device, misagent_client_t * client, const char* label) { misagent_error_t err = MISAGENT_E_UNKNOWN_ERROR; service_client_factory_start_service(device, MISAGENT_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(misagent_client_new), &err); return err; } -LIBIMOBILEDEVICE_API misagent_error_t misagent_client_free(misagent_client_t client) +misagent_error_t misagent_client_free(misagent_client_t client) { if (!client) return MISAGENT_E_INVALID_ARG; @@ -125,7 +127,7 @@ LIBIMOBILEDEVICE_API misagent_error_t misagent_client_free(misagent_client_t cli return err; } -LIBIMOBILEDEVICE_API misagent_error_t misagent_install(misagent_client_t client, plist_t profile) +misagent_error_t misagent_install(misagent_client_t client, plist_t profile) { if (!client || !client->parent || !profile || (plist_get_node_type(profile) != PLIST_DATA)) return MISAGENT_E_INVALID_ARG; @@ -162,7 +164,7 @@ LIBIMOBILEDEVICE_API misagent_error_t misagent_install(misagent_client_t client, return res; } -LIBIMOBILEDEVICE_API misagent_error_t misagent_copy(misagent_client_t client, plist_t* profiles) +misagent_error_t misagent_copy(misagent_client_t client, plist_t* profiles) { if (!client || !client->parent || !profiles) return MISAGENT_E_INVALID_ARG; @@ -202,7 +204,7 @@ LIBIMOBILEDEVICE_API misagent_error_t misagent_copy(misagent_client_t client, pl } -LIBIMOBILEDEVICE_API misagent_error_t misagent_copy_all(misagent_client_t client, plist_t* profiles) +misagent_error_t misagent_copy_all(misagent_client_t client, plist_t* profiles) { if (!client || !client->parent || !profiles) return MISAGENT_E_INVALID_ARG; @@ -242,7 +244,7 @@ LIBIMOBILEDEVICE_API misagent_error_t misagent_copy_all(misagent_client_t client } -LIBIMOBILEDEVICE_API misagent_error_t misagent_remove(misagent_client_t client, const char* profileID) +misagent_error_t misagent_remove(misagent_client_t client, const char* profileID) { if (!client || !client->parent || !profileID) return MISAGENT_E_INVALID_ARG; @@ -279,7 +281,7 @@ LIBIMOBILEDEVICE_API misagent_error_t misagent_remove(misagent_client_t client, return res; } -LIBIMOBILEDEVICE_API int misagent_get_status_code(misagent_client_t client) +int misagent_get_status_code(misagent_client_t client) { if (!client) { return -1; diff --git a/src/misagent.h b/src/misagent.h index 08ad063..e394087 100644 --- a/src/misagent.h +++ b/src/misagent.h @@ -22,6 +22,7 @@ #ifndef __MISAGENT_H #define __MISAGENT_H +#include "idevice.h" #include "libimobiledevice/misagent.h" #include "property_list_service.h" diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c index c8c4c6f..5df8e86 100644 --- a/src/mobile_image_mounter.c +++ b/src/mobile_image_mounter.c @@ -2,7 +2,7 @@ * mobile_image_mounter.c * com.apple.mobile.mobile_image_mounter service implementation. * - * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2010-2019 Nikias Bassen, 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 @@ -19,6 +19,9 @@ * 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 <unistd.h> @@ -75,7 +78,7 @@ static mobile_image_mounter_error_t mobile_image_mounter_error(property_list_ser return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, lockdownd_service_descriptor_t service, mobile_image_mounter_client_t *client) +mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, lockdownd_service_descriptor_t service, mobile_image_mounter_client_t *client) { property_list_service_client_t plistclient = NULL; mobile_image_mounter_error_t err = mobile_image_mounter_error(property_list_service_client_new(device, service, &plistclient)); @@ -92,14 +95,14 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_new(idevi return MOBILE_IMAGE_MOUNTER_E_SUCCESS; } -LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_start_service(idevice_t device, mobile_image_mounter_client_t * client, const char* label) +mobile_image_mounter_error_t mobile_image_mounter_start_service(idevice_t device, mobile_image_mounter_client_t * client, const char* label) { mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; service_client_factory_start_service(device, MOBILE_IMAGE_MOUNTER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobile_image_mounter_new), &err); return err; } -LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) +mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) { if (!client) return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; @@ -112,7 +115,7 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_free(mobi return MOBILE_IMAGE_MOUNTER_E_SUCCESS; } -LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result) +mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result) { if (!client || !image_type || !result) { return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; @@ -141,7 +144,44 @@ leave_unlock: return res; } -LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const char *signature, uint16_t signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata) +static mobile_image_mounter_error_t process_result(plist_t result, const char *expected_status) +{ + mobile_image_mounter_error_t res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; + char* strval = NULL; + plist_t node; + + node = plist_dict_get_item(result, "Error"); + if (node && plist_get_node_type(node) == PLIST_STRING) { + plist_get_string_val(node, &strval); + } + if (strval) { + if (!strcmp(strval, "DeviceLocked")) { + debug_info("Device is locked, can't mount"); + res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED; + } else { + debug_info("Unhandled error '%s' received", strval); + } + free(strval); + return res; + } + + node = plist_dict_get_item(result, "Status"); + if (node && plist_get_node_type(node) == PLIST_STRING) { + plist_get_string_val(node, &strval); + } + if (!strval) { + debug_info("Error: Unexpected response received!"); + } else if (strcmp(strval, expected_status) == 0) { + res = MOBILE_IMAGE_MOUNTER_E_SUCCESS; + } else { + debug_info("Error: didn't get %s but %s", expected_status, strval); + } + free(strval); + + return res; +} + +mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const char *signature, uint16_t signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata) { if (!client || !image_type || (image_size == 0) || !upload_cb) { return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; @@ -169,23 +209,10 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_im debug_info("Error receiving response from device!"); goto leave_unlock; } - res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; - - char* strval = NULL; - plist_t node = plist_dict_get_item(result, "Status"); - if (node && plist_get_node_type(node) == PLIST_STRING) { - plist_get_string_val(node, &strval); - } - if (!strval) { - debug_info("Error: Unexpected response received!"); - goto leave_unlock; - } - if (strcmp(strval, "ReceiveBytesAck") != 0) { - debug_info("Error: didn't get ReceiveBytesAck but %s", strval); - free(strval); + res = process_result(result, "ReceiveBytesAck"); + if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { goto leave_unlock; } - free(strval); size_t tx = 0; size_t bufsize = 65536; @@ -223,26 +250,7 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_im debug_info("Error receiving response from device!"); goto leave_unlock; } - res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; - - strval = NULL; - node = plist_dict_get_item(result, "Status"); - if (node && plist_get_node_type(node) == PLIST_STRING) { - plist_get_string_val(node, &strval); - } - if (!strval) { - debug_info("Error: Unexpected response received!"); - goto leave_unlock; - } - if (strcmp(strval, "Complete") != 0) { - debug_info("Error: didn't get Complete but %s", strval); - free(strval); - goto leave_unlock; - } else { - res = MOBILE_IMAGE_MOUNTER_E_SUCCESS; - } - free(strval); - + res = process_result(result, "Complete"); leave_unlock: mobile_image_mounter_unlock(client); @@ -252,7 +260,7 @@ leave_unlock: } -LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *signature, uint16_t signature_size, const char *image_type, plist_t *result) +mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *signature, uint16_t signature_size, const char *image_type, plist_t *result) { if (!client || !image_path || !image_type || !result) { return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; @@ -284,7 +292,7 @@ leave_unlock: return res; } -LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) +mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) { if (!client) { return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; diff --git a/src/mobile_image_mounter.h b/src/mobile_image_mounter.h index e9754e4..9a8fcdd 100644 --- a/src/mobile_image_mounter.h +++ b/src/mobile_image_mounter.h @@ -22,9 +22,10 @@ #ifndef __MOBILE_IMAGE_MOUNTER_H #define __MOBILE_IMAGE_MOUNTER_H +#include "idevice.h" #include "libimobiledevice/mobile_image_mounter.h" #include "property_list_service.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h> struct mobile_image_mounter_client_private { property_list_service_client_t parent; diff --git a/src/mobileactivation.c b/src/mobileactivation.c index 010484e..fce5f16 100644 --- a/src/mobileactivation.c +++ b/src/mobileactivation.c @@ -18,6 +18,10 @@ * 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 "mobileactivation.h" @@ -50,7 +54,7 @@ static mobileactivation_error_t mobileactivation_error(property_list_service_err return MOBILEACTIVATION_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobileactivation_client_t *client) +mobileactivation_error_t mobileactivation_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobileactivation_client_t *client) { if (!device || !service || service->port == 0 || !client || *client) { return MOBILEACTIVATION_E_INVALID_ARG; @@ -70,14 +74,14 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_client_new(idevic return MOBILEACTIVATION_E_SUCCESS; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_client_start_service(idevice_t device, mobileactivation_client_t * client, const char* label) +mobileactivation_error_t mobileactivation_client_start_service(idevice_t device, mobileactivation_client_t * client, const char* label) { mobileactivation_error_t err = MOBILEACTIVATION_E_UNKNOWN_ERROR; service_client_factory_start_service(device, MOBILEACTIVATION_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobileactivation_client_new), &err); return err; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_client_free(mobileactivation_client_t client) +mobileactivation_error_t mobileactivation_client_free(mobileactivation_client_t client) { if (!client) return MOBILEACTIVATION_E_INVALID_ARG; @@ -105,8 +109,6 @@ static plist_t plist_data_from_plist(plist_t plist) static mobileactivation_error_t mobileactivation_check_result(plist_t dict, const char *command) { - mobileactivation_error_t ret = MOBILEACTIVATION_E_UNKNOWN_ERROR; - if (!dict || plist_get_node_type(dict) != PLIST_DICT) { return MOBILEACTIVATION_E_PLIST_ERROR; } @@ -114,14 +116,13 @@ static mobileactivation_error_t mobileactivation_check_result(plist_t dict, cons plist_t err_node = plist_dict_get_item(dict, "Error"); if (!err_node) { return MOBILEACTIVATION_E_SUCCESS; - } else { - char *errmsg = NULL; - plist_get_string_val(err_node, &errmsg); - debug_info("ERROR: %s: %s", command, errmsg); - ret = MOBILEACTIVATION_E_REQUEST_FAILED; - free(errmsg); } - return ret; + + char *errmsg = NULL; + plist_get_string_val(err_node, &errmsg); + debug_info("ERROR: %s: %s", command, errmsg); + free(errmsg); + return MOBILEACTIVATION_E_REQUEST_FAILED; } static mobileactivation_error_t mobileactivation_send_command_plist(mobileactivation_client_t client, plist_t command, plist_t *result) @@ -175,7 +176,7 @@ static mobileactivation_error_t mobileactivation_send_command(mobileactivation_c return ret; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_get_activation_state(mobileactivation_client_t client, plist_t *state) +mobileactivation_error_t mobileactivation_get_activation_state(mobileactivation_client_t client, plist_t *state) { if (!client || !state) return MOBILEACTIVATION_E_INVALID_ARG; @@ -197,7 +198,7 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_get_activation_st return ret; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation_session_info(mobileactivation_client_t client, plist_t *blob) +mobileactivation_error_t mobileactivation_create_activation_session_info(mobileactivation_client_t client, plist_t *blob) { if (!client || !blob) return MOBILEACTIVATION_E_INVALID_ARG; @@ -217,7 +218,7 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation return ret; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation_info(mobileactivation_client_t client, plist_t *info) +mobileactivation_error_t mobileactivation_create_activation_info(mobileactivation_client_t client, plist_t *info) { if (!client || !info) return MOBILEACTIVATION_E_INVALID_ARG; @@ -239,7 +240,7 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation return ret; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation_info_with_session(mobileactivation_client_t client, plist_t handshake_response, plist_t *info) +mobileactivation_error_t mobileactivation_create_activation_info_with_session(mobileactivation_client_t client, plist_t handshake_response, plist_t *info) { if (!client || !info) return MOBILEACTIVATION_E_INVALID_ARG; @@ -260,10 +261,10 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation plist_free(result); result = NULL; - return ret; + return ret; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_activate(mobileactivation_client_t client, plist_t activation_record) +mobileactivation_error_t mobileactivation_activate(mobileactivation_client_t client, plist_t activation_record) { if (!client || !activation_record) return MOBILEACTIVATION_E_INVALID_ARG; @@ -276,7 +277,7 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_activate(mobileac return ret; } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_activate_with_session(mobileactivation_client_t client, plist_t activation_record, plist_t headers) +mobileactivation_error_t mobileactivation_activate_with_session(mobileactivation_client_t client, plist_t activation_record, plist_t headers) { if (!client || !activation_record) return MOBILEACTIVATION_E_INVALID_ARG; @@ -299,7 +300,7 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_activate_with_ses } -LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_deactivate(mobileactivation_client_t client) +mobileactivation_error_t mobileactivation_deactivate(mobileactivation_client_t client) { if (!client) return MOBILEACTIVATION_E_INVALID_ARG; diff --git a/src/mobileactivation.h b/src/mobileactivation.h index 49b9ebc..a8dff5d 100644 --- a/src/mobileactivation.h +++ b/src/mobileactivation.h @@ -22,6 +22,7 @@ #ifndef __MOBILEACTIVATION_H #define __MOBILEACTIVATION_H +#include "idevice.h" #include "libimobiledevice/mobileactivation.h" #include "property_list_service.h" diff --git a/src/mobilebackup.c b/src/mobilebackup.c index b32e0ba..36986a4 100644 --- a/src/mobilebackup.c +++ b/src/mobilebackup.c @@ -2,6 +2,7 @@ * mobilebackup.c * Contains functions for the built-in MobileBackup client. * + * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved. * Copyright (c) 2009 Martin Szulecki All Rights Reserved. * * This library is free software; you can redistribute it and/or @@ -19,9 +20,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #include <plist/plist.h> #include <string.h> #include <stdlib.h> +#include <stdio.h> #include "mobilebackup.h" #include "device_link_service.h" @@ -30,7 +35,7 @@ #define MBACKUP_VERSION_INT1 100 #define MBACKUP_VERSION_INT2 0 -#define IS_FLAG_SET(x, y) ((x & y) == y) +#define IS_FLAG_SET(x, y) (((x) & (y)) == (y)) /** * Convert an device_link_service_error_t value to an mobilebackup_error_t value. @@ -52,6 +57,10 @@ static mobilebackup_error_t mobilebackup_error(device_link_service_error_t err) return MOBILEBACKUP_E_PLIST_ERROR; case DEVICE_LINK_SERVICE_E_MUX_ERROR: return MOBILEBACKUP_E_MUX_ERROR; + case DEVICE_LINK_SERVICE_E_SSL_ERROR: + return MOBILEBACKUP_E_SSL_ERROR; + case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT: + return MOBILEBACKUP_E_RECEIVE_TIMEOUT; case DEVICE_LINK_SERVICE_E_BAD_VERSION: return MOBILEBACKUP_E_BAD_VERSION; default: @@ -60,7 +69,7 @@ static mobilebackup_error_t mobilebackup_error(device_link_service_error_t err) return MOBILEBACKUP_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobilebackup_client_t * client) +mobilebackup_error_t mobilebackup_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobilebackup_client_t * client) { if (!device || !service || service->port == 0 || !client || *client) return MOBILEBACKUP_E_INVALID_ARG; @@ -87,14 +96,14 @@ LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_client_new(idevice_t devi return ret; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_client_start_service(idevice_t device, mobilebackup_client_t * client, const char* label) +mobilebackup_error_t mobilebackup_client_start_service(idevice_t device, mobilebackup_client_t * client, const char* label) { mobilebackup_error_t err = MOBILEBACKUP_E_UNKNOWN_ERROR; service_client_factory_start_service(device, MOBILEBACKUP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobilebackup_client_new), &err); return err; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client) +mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client) { if (!client) return MOBILEBACKUP_E_INVALID_ARG; @@ -107,7 +116,7 @@ LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_client_free(mobilebackup_ return err; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t * plist) +mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t * plist) { if (!client) return MOBILEBACKUP_E_INVALID_ARG; @@ -115,7 +124,7 @@ LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_receive(mobilebackup_clie return ret; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist) +mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist) { if (!client || !plist) return MOBILEBACKUP_E_INVALID_ARG; @@ -232,7 +241,7 @@ leave: return err; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_request_backup(mobilebackup_client_t client, plist_t backup_manifest, const char *base_path, const char *proto_version) +mobilebackup_error_t mobilebackup_request_backup(mobilebackup_client_t client, plist_t backup_manifest, const char *base_path, const char *proto_version) { if (!client || !client->parent || !base_path || !proto_version) return MOBILEBACKUP_E_INVALID_ARG; @@ -271,7 +280,15 @@ LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_request_backup(mobileback char *str = NULL; plist_get_string_val(node, &str); if (str) { - if (strcmp(str, proto_version) != 0) { + int maj = 0; + int min = 0; + sscanf(str, "%u.%u", &maj, &min); + uint32_t this_ver = ((maj & 0xFF) << 8) | (min & 0xFF); + maj = 0; + min = 0; + sscanf(proto_version, "%u.%u", &maj, &min); + uint32_t proto_ver = ((maj & 0xFF) << 8) | (min & 0xFF); + if (this_ver > proto_ver) { err = MOBILEBACKUP_E_BAD_VERSION; } free(str); @@ -292,12 +309,12 @@ leave: return err; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_send_backup_file_received(mobilebackup_client_t client) +mobilebackup_error_t mobilebackup_send_backup_file_received(mobilebackup_client_t client) { return mobilebackup_send_message(client, "kBackupMessageBackupFileReceived", NULL); } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_request_restore(mobilebackup_client_t client, plist_t backup_manifest, mobilebackup_flags_t flags, const char *proto_version) +mobilebackup_error_t mobilebackup_request_restore(mobilebackup_client_t client, plist_t backup_manifest, mobilebackup_flags_t flags, const char *proto_version) { if (!client || !client->parent || !backup_manifest || !proto_version) return MOBILEBACKUP_E_INVALID_ARG; @@ -338,7 +355,15 @@ LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_request_restore(mobilebac char *str = NULL; plist_get_string_val(node, &str); if (str) { - if (strcmp(str, proto_version) != 0) { + int maj = 0; + int min = 0; + sscanf(str, "%u.%u", &maj, &min); + uint32_t this_ver = ((maj & 0xFF) << 8) | (min & 0xFF); + maj = 0; + min = 0; + sscanf(proto_version, "%u.%u", &maj, &min); + uint32_t proto_ver = ((maj & 0xFF) << 8) | (min & 0xFF); + if (this_ver > proto_ver) { err = MOBILEBACKUP_E_BAD_VERSION; } free(str); @@ -351,17 +376,17 @@ leave: return err; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_receive_restore_file_received(mobilebackup_client_t client, plist_t *result) +mobilebackup_error_t mobilebackup_receive_restore_file_received(mobilebackup_client_t client, plist_t *result) { return mobilebackup_receive_message(client, "BackupMessageRestoreFileReceived", result); } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_receive_restore_application_received(mobilebackup_client_t client, plist_t *result) +mobilebackup_error_t mobilebackup_receive_restore_application_received(mobilebackup_client_t client, plist_t *result) { return mobilebackup_receive_message(client, "BackupMessageRestoreApplicationReceived", result); } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_send_restore_complete(mobilebackup_client_t client) +mobilebackup_error_t mobilebackup_send_restore_complete(mobilebackup_client_t client) { mobilebackup_error_t err = mobilebackup_send_message(client, "BackupMessageRestoreComplete", NULL); if (err != MOBILEBACKUP_E_SUCCESS) { @@ -406,7 +431,7 @@ LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_send_restore_complete(mob return err; } -LIBIMOBILEDEVICE_API mobilebackup_error_t mobilebackup_send_error(mobilebackup_client_t client, const char *reason) +mobilebackup_error_t mobilebackup_send_error(mobilebackup_client_t client, const char *reason) { if (!client || !client->parent || !reason) return MOBILEBACKUP_E_INVALID_ARG; diff --git a/src/mobilebackup.h b/src/mobilebackup.h index 19b9999..04ec479 100644 --- a/src/mobilebackup.h +++ b/src/mobilebackup.h @@ -2,6 +2,7 @@ * mobilebackup.h * Definitions for the mobilebackup service * + * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved. * Copyright (c) 2009 Martin Szulecki All Rights Reserved. * * This library is free software; you can redistribute it and/or @@ -22,6 +23,7 @@ #ifndef __MOBILEBACKUP_H #define __MOBILEBACKUP_H +#include "idevice.h" #include "libimobiledevice/mobilebackup.h" #include "device_link_service.h" diff --git a/src/mobilebackup2.c b/src/mobilebackup2.c index 08ce22b..a8d673f 100644 --- a/src/mobilebackup2.c +++ b/src/mobilebackup2.c @@ -2,7 +2,7 @@ * mobilebackup2.c * Contains functions for the built-in MobileBackup2 client (iOS4+ only) * - * Copyright (c) 2010 Nikias Bassen All Rights Reserved. + * Copyright (c) 2010-2019 Nikias Bassen, 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 @@ -19,6 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #include <plist/plist.h> #include <string.h> #include <stdlib.h> @@ -27,10 +30,10 @@ #include "device_link_service.h" #include "common/debug.h" -#define MBACKUP2_VERSION_INT1 300 +#define MBACKUP2_VERSION_INT1 400 #define MBACKUP2_VERSION_INT2 0 -#define IS_FLAG_SET(x, y) ((x & y) == y) +#define IS_FLAG_SET(x, y) (((x) & (y)) == (y)) /** * Convert an device_link_service_error_t value to an mobilebackup2_error_t value. @@ -53,6 +56,10 @@ static mobilebackup2_error_t mobilebackup2_error(device_link_service_error_t err return MOBILEBACKUP2_E_PLIST_ERROR; case DEVICE_LINK_SERVICE_E_MUX_ERROR: return MOBILEBACKUP2_E_MUX_ERROR; + case DEVICE_LINK_SERVICE_E_SSL_ERROR: + return MOBILEBACKUP2_E_SSL_ERROR; + case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT: + return MOBILEBACKUP2_E_RECEIVE_TIMEOUT; case DEVICE_LINK_SERVICE_E_BAD_VERSION: return MOBILEBACKUP2_E_BAD_VERSION; default: @@ -61,7 +68,7 @@ static mobilebackup2_error_t mobilebackup2_error(device_link_service_error_t err return MOBILEBACKUP2_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_client_new(idevice_t device, lockdownd_service_descriptor_t service, +mobilebackup2_error_t mobilebackup2_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobilebackup2_client_t * client) { if (!device || !service || service->port == 0 || !client || *client) @@ -89,14 +96,14 @@ LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_client_new(idevice_t de return ret; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_client_start_service(idevice_t device, mobilebackup2_client_t * client, const char* label) +mobilebackup2_error_t mobilebackup2_client_start_service(idevice_t device, mobilebackup2_client_t * client, const char* label) { mobilebackup2_error_t err = MOBILEBACKUP2_E_UNKNOWN_ERROR; service_client_factory_start_service(device, MOBILEBACKUP2_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobilebackup2_client_new), &err); return err; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_client_free(mobilebackup2_client_t client) +mobilebackup2_error_t mobilebackup2_client_free(mobilebackup2_client_t client) { if (!client) return MOBILEBACKUP2_E_INVALID_ARG; @@ -109,7 +116,7 @@ LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_client_free(mobilebacku return err; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_send_message(mobilebackup2_client_t client, const char *message, plist_t options) +mobilebackup2_error_t mobilebackup2_send_message(mobilebackup2_client_t client, const char *message, plist_t options) { if (!client || !client->parent || (!message && !options)) return MOBILEBACKUP2_E_INVALID_ARG; @@ -207,12 +214,12 @@ leave: return err; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_receive_message(mobilebackup2_client_t client, plist_t *msg_plist, char **dlmessage) +mobilebackup2_error_t mobilebackup2_receive_message(mobilebackup2_client_t client, plist_t *msg_plist, char **dlmessage) { return mobilebackup2_error(device_link_service_receive_message(client->parent, msg_plist, dlmessage)); } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_send_raw(mobilebackup2_client_t client, const char *data, uint32_t length, uint32_t *bytes) +mobilebackup2_error_t mobilebackup2_send_raw(mobilebackup2_client_t client, const char *data, uint32_t length, uint32_t *bytes) { if (!client || !client->parent || !data || (length == 0) || !bytes) return MOBILEBACKUP2_E_INVALID_ARG; @@ -233,12 +240,11 @@ LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_send_raw(mobilebackup2_ if (sent > 0) { *bytes = sent; return MOBILEBACKUP2_E_SUCCESS; - } else { - return MOBILEBACKUP2_E_MUX_ERROR; } + return MOBILEBACKUP2_E_MUX_ERROR; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_receive_raw(mobilebackup2_client_t client, char *data, uint32_t length, uint32_t *bytes) +mobilebackup2_error_t mobilebackup2_receive_raw(mobilebackup2_client_t client, char *data, uint32_t length, uint32_t *bytes) { if (!client || !client->parent || !data || (length == 0) || !bytes) return MOBILEBACKUP2_E_INVALID_ARG; @@ -258,14 +264,14 @@ LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_receive_raw(mobilebacku if (received > 0) { *bytes = received; return MOBILEBACKUP2_E_SUCCESS; - } else if (received == 0) { + } + if (received == 0) { return MOBILEBACKUP2_E_SUCCESS; - } else { - return MOBILEBACKUP2_E_MUX_ERROR; } + return MOBILEBACKUP2_E_MUX_ERROR; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_version_exchange(mobilebackup2_client_t client, double local_versions[], char count, double *remote_version) +mobilebackup2_error_t mobilebackup2_version_exchange(mobilebackup2_client_t client, double local_versions[], char count, double *remote_version) { int i; @@ -323,7 +329,7 @@ leave: return err; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_send_request(mobilebackup2_client_t client, const char *request, const char *target_identifier, const char *source_identifier, plist_t options) +mobilebackup2_error_t mobilebackup2_send_request(mobilebackup2_client_t client, const char *request, const char *target_identifier, const char *source_identifier, plist_t options) { if (!client || !client->parent || !request || !target_identifier) return MOBILEBACKUP2_E_INVALID_ARG; @@ -354,7 +360,7 @@ LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_send_request(mobileback return err; } -LIBIMOBILEDEVICE_API mobilebackup2_error_t mobilebackup2_send_status_response(mobilebackup2_client_t client, int status_code, const char *status1, plist_t status2) +mobilebackup2_error_t mobilebackup2_send_status_response(mobilebackup2_client_t client, int status_code, const char *status1, plist_t status2) { if (!client || !client->parent) return MOBILEBACKUP2_E_INVALID_ARG; diff --git a/src/mobilebackup2.h b/src/mobilebackup2.h index 4dba22a..e232b97 100644 --- a/src/mobilebackup2.h +++ b/src/mobilebackup2.h @@ -2,7 +2,7 @@ * mobilebackup2.h * Definitions for the mobilebackup2 service (iOS4+) * - * Copyright (c) 2010 Nikias Bassen All Rights Reserved. + * Copyright (c) 2010-2019 Nikias Bassen, 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 @@ -22,6 +22,7 @@ #ifndef __MOBILEBACKUP2_H #define __MOBILEBACKUP2_H +#include "idevice.h" #include "libimobiledevice/mobilebackup2.h" #include "device_link_service.h" diff --git a/src/mobilesync.c b/src/mobilesync.c index d903cfe..9b81a49 100644 --- a/src/mobilesync.c +++ b/src/mobilesync.c @@ -20,9 +20,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #define _GNU_SOURCE 1 #define __USE_GNU 1 - #include <plist/plist.h> #include <string.h> #include <stdlib.h> @@ -32,7 +34,7 @@ #include "device_link_service.h" #include "common/debug.h" -#define MSYNC_VERSION_INT1 300 +#define MSYNC_VERSION_INT1 400 #define MSYNC_VERSION_INT2 100 #define EMPTY_PARAMETER_STRING "___EmptyParameterString___" @@ -57,6 +59,10 @@ static mobilesync_error_t mobilesync_error(device_link_service_error_t err) return MOBILESYNC_E_PLIST_ERROR; case DEVICE_LINK_SERVICE_E_MUX_ERROR: return MOBILESYNC_E_MUX_ERROR; + case DEVICE_LINK_SERVICE_E_SSL_ERROR: + return MOBILESYNC_E_SSL_ERROR; + case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT: + return MOBILESYNC_E_RECEIVE_TIMEOUT; case DEVICE_LINK_SERVICE_E_BAD_VERSION: return MOBILESYNC_E_BAD_VERSION; default: @@ -65,7 +71,7 @@ static mobilesync_error_t mobilesync_error(device_link_service_error_t err) return MOBILESYNC_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_client_new(idevice_t device, lockdownd_service_descriptor_t service, +mobilesync_error_t mobilesync_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobilesync_client_t * client) { if (!device || !service || service->port == 0 || !client || *client) @@ -95,14 +101,14 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_client_new(idevice_t device, return ret; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_client_start_service(idevice_t device, mobilesync_client_t * client, const char* label) +mobilesync_error_t mobilesync_client_start_service(idevice_t device, mobilesync_client_t * client, const char* label) { mobilesync_error_t err = MOBILESYNC_E_UNKNOWN_ERROR; service_client_factory_start_service(device, MOBILESYNC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobilesync_client_new), &err); return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_client_free(mobilesync_client_t client) +mobilesync_error_t mobilesync_client_free(mobilesync_client_t client) { if (!client) return MOBILESYNC_E_INVALID_ARG; @@ -112,7 +118,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_client_free(mobilesync_client return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plist) +mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plist) { if (!client) return MOBILESYNC_E_INVALID_ARG; @@ -120,14 +126,14 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_receive(mobilesync_client_t c return ret; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist) +mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist) { if (!client || !plist) return MOBILESYNC_E_INVALID_ARG; return mobilesync_error(device_link_service_send(client->parent, plist)); } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_start(mobilesync_client_t client, const char *data_class, mobilesync_anchors_t anchors, uint64_t computer_data_class_version, mobilesync_sync_type_t *sync_type, uint64_t *device_data_class_version, char** error_description) +mobilesync_error_t mobilesync_start(mobilesync_client_t client, const char *data_class, mobilesync_anchors_t anchors, uint64_t computer_data_class_version, mobilesync_sync_type_t *sync_type, uint64_t *device_data_class_version, char** error_description) { if (!client || client->data_class || !data_class || !anchors || !anchors->computer_anchor) { @@ -253,7 +259,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_start(mobilesync_client_t cli return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_finish(mobilesync_client_t client) +mobilesync_error_t mobilesync_finish(mobilesync_client_t client) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; @@ -338,17 +344,17 @@ static mobilesync_error_t mobilesync_get_records(mobilesync_client_t client, con return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_get_all_records_from_device(mobilesync_client_t client) +mobilesync_error_t mobilesync_get_all_records_from_device(mobilesync_client_t client) { return mobilesync_get_records(client, "SDMessageGetAllRecordsFromDevice"); } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_get_changes_from_device(mobilesync_client_t client) +mobilesync_error_t mobilesync_get_changes_from_device(mobilesync_client_t client) { return mobilesync_get_records(client, "SDMessageGetChangesFromDevice"); } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_t *entities, uint8_t *is_last_record, plist_t *actions) +mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_t *entities, uint8_t *is_last_record, plist_t *actions) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; @@ -415,7 +421,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_receive_changes(mobilesync_cl return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_clear_all_records_on_device(mobilesync_client_t client) +mobilesync_error_t mobilesync_clear_all_records_on_device(mobilesync_client_t client) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; @@ -467,7 +473,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_clear_all_records_on_device(m goto out; } - if (strcmp(response_type, "SDMessageDeviceWillClearAllRecords")) { + if (strcmp(response_type, "SDMessageDeviceWillClearAllRecords") != 0) { err = MOBILESYNC_E_PLIST_ERROR; } @@ -484,7 +490,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_clear_all_records_on_device(m return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_acknowledge_changes_from_device(mobilesync_client_t client) +mobilesync_error_t mobilesync_acknowledge_changes_from_device(mobilesync_client_t client) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; @@ -518,7 +524,7 @@ static plist_t create_process_changes_message(const char *data_class, plist_t en return msg; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_ready_to_send_changes_from_computer(mobilesync_client_t client) +mobilesync_error_t mobilesync_ready_to_send_changes_from_computer(mobilesync_client_t client) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; @@ -585,7 +591,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_ready_to_send_changes_from_co return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_send_changes(mobilesync_client_t client, plist_t entities, uint8_t is_last_record, plist_t actions) +mobilesync_error_t mobilesync_send_changes(mobilesync_client_t client, plist_t entities, uint8_t is_last_record, plist_t actions) { if (!client || !client->data_class || !entities) { return MOBILESYNC_E_INVALID_ARG; @@ -613,7 +619,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_send_changes(mobilesync_clien return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_remap_identifiers(mobilesync_client_t client, plist_t *mapping) +mobilesync_error_t mobilesync_remap_identifiers(mobilesync_client_t client, plist_t *mapping) { if (!client || !client->data_class) { return MOBILESYNC_E_INVALID_ARG; @@ -682,7 +688,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_remap_identifiers(mobilesync_ return err; } -LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_cancel(mobilesync_client_t client, const char* reason) +mobilesync_error_t mobilesync_cancel(mobilesync_client_t client, const char* reason) { if (!client || !client->data_class || !reason) { return MOBILESYNC_E_INVALID_ARG; @@ -708,7 +714,7 @@ LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_cancel(mobilesync_client_t cl return err; } -LIBIMOBILEDEVICE_API mobilesync_anchors_t mobilesync_anchors_new(const char *device_anchor, const char *computer_anchor) +mobilesync_anchors_t mobilesync_anchors_new(const char *device_anchor, const char *computer_anchor) { mobilesync_anchors_t anchors = (mobilesync_anchors_t) malloc(sizeof(mobilesync_anchors)); if (device_anchor != NULL) { @@ -725,7 +731,7 @@ LIBIMOBILEDEVICE_API mobilesync_anchors_t mobilesync_anchors_new(const char *dev return anchors; } -LIBIMOBILEDEVICE_API void mobilesync_anchors_free(mobilesync_anchors_t anchors) +void mobilesync_anchors_free(mobilesync_anchors_t anchors) { if (anchors->device_anchor != NULL) { free(anchors->device_anchor); @@ -739,12 +745,12 @@ LIBIMOBILEDEVICE_API void mobilesync_anchors_free(mobilesync_anchors_t anchors) anchors = NULL; } -LIBIMOBILEDEVICE_API plist_t mobilesync_actions_new(void) +plist_t mobilesync_actions_new(void) { return plist_new_dict(); } -LIBIMOBILEDEVICE_API void mobilesync_actions_add(plist_t actions, ...) +void mobilesync_actions_add(plist_t actions, ...) { if (!actions) return; @@ -776,7 +782,7 @@ LIBIMOBILEDEVICE_API void mobilesync_actions_add(plist_t actions, ...) va_end(args); } -LIBIMOBILEDEVICE_API void mobilesync_actions_free(plist_t actions) +void mobilesync_actions_free(plist_t actions) { if (actions) { plist_free(actions); diff --git a/src/mobilesync.h b/src/mobilesync.h index f672252..3b5ece9 100644 --- a/src/mobilesync.h +++ b/src/mobilesync.h @@ -23,6 +23,7 @@ #ifndef __MOBILESYNC_H #define __MOBILESYNC_H +#include "idevice.h" #include "libimobiledevice/mobilesync.h" #include "device_link_service.h" diff --git a/src/notification_proxy.c b/src/notification_proxy.c index c0b216e..60b2e03 100644 --- a/src/notification_proxy.c +++ b/src/notification_proxy.c @@ -19,6 +19,9 @@ * 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 <unistd.h> @@ -86,7 +89,7 @@ static np_error_t np_error(property_list_service_error_t err) return NP_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API np_error_t np_client_new(idevice_t device, lockdownd_service_descriptor_t service, np_client_t *client) +np_error_t np_client_new(idevice_t device, lockdownd_service_descriptor_t service, np_client_t *client) { property_list_service_client_t plistclient = NULL; np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient)); @@ -98,20 +101,20 @@ LIBIMOBILEDEVICE_API np_error_t np_client_new(idevice_t device, lockdownd_servic client_loc->parent = plistclient; mutex_init(&client_loc->mutex); - client_loc->notifier = (thread_t)NULL; + client_loc->notifier = THREAD_T_NULL; *client = client_loc; return NP_E_SUCCESS; } -LIBIMOBILEDEVICE_API np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label) +np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label) { np_error_t err = NP_E_UNKNOWN_ERROR; service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err); return err; } -LIBIMOBILEDEVICE_API np_error_t np_client_free(np_client_t client) +np_error_t np_client_free(np_client_t client) { plist_t dict; property_list_service_client_t parent; @@ -132,7 +135,7 @@ LIBIMOBILEDEVICE_API np_error_t np_client_free(np_client_t client) debug_info("joining np callback"); thread_join(client->notifier); thread_free(client->notifier); - client->notifier = (thread_t)NULL; + client->notifier = THREAD_T_NULL; } else { dict = NULL; property_list_service_receive_plist(parent, &dict); @@ -165,7 +168,7 @@ LIBIMOBILEDEVICE_API np_error_t np_client_free(np_client_t client) return NP_E_SUCCESS; } -LIBIMOBILEDEVICE_API np_error_t np_post_notification(np_client_t client, const char *notification) +np_error_t np_post_notification(np_client_t client, const char *notification) { if (!client || !notification) { return NP_E_INVALID_ARG; @@ -186,13 +189,8 @@ LIBIMOBILEDEVICE_API np_error_t np_post_notification(np_client_t client, const c return res; } -LIBIMOBILEDEVICE_API np_error_t np_observe_notification( np_client_t client, const char *notification ) +static np_error_t internal_np_observe_notification(np_client_t client, const char *notification) { - if (!client || !notification) { - return NP_E_INVALID_ARG; - } - np_lock(client); - plist_t dict = plist_new_dict(); plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification")); plist_dict_set_item(dict,"Name", plist_new_string(notification)); @@ -203,11 +201,21 @@ LIBIMOBILEDEVICE_API np_error_t np_observe_notification( np_client_t client, con } plist_free(dict); + return res; +} + +np_error_t np_observe_notification( np_client_t client, const char *notification ) +{ + if (!client || !notification) { + return NP_E_INVALID_ARG; + } + np_lock(client); + np_error_t res = internal_np_observe_notification(client, notification); np_unlock(client); return res; } -LIBIMOBILEDEVICE_API np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) +np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) { int i = 0; np_error_t res = NP_E_UNKNOWN_ERROR; @@ -221,13 +229,15 @@ LIBIMOBILEDEVICE_API np_error_t np_observe_notifications(np_client_t client, con return NP_E_INVALID_ARG; } + np_lock(client); while (notifications[i]) { - res = np_observe_notification(client, notifications[i]); + res = internal_np_observe_notification(client, notifications[i]); if (res != NP_E_SUCCESS) { break; } i++; } + np_unlock(client); return res; } @@ -240,7 +250,7 @@ LIBIMOBILEDEVICE_API np_error_t np_observe_notifications(np_client_t client, con * with the notification that has been received. * * @return 0 if a notification has been received or nothing has been received, - * or a negative value if an error occured. + * or a negative value if an error occurred. * * @note You probably want to check out np_set_notify_callback * @see np_set_notify_callback @@ -260,7 +270,7 @@ static int np_get_notification(np_client_t client, char **notification) debug_info("NotificationProxy: no notification received!"); res = 0; } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) { - debug_info("NotificationProxy: error %d occured!", perr); + debug_info("NotificationProxy: error %d occurred!", perr); res = perr; } if (dict) { @@ -336,7 +346,7 @@ void* np_notifier( void* arg ) return NULL; } -LIBIMOBILEDEVICE_API np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) +np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) { if (!client) return NP_E_INVALID_ARG; @@ -350,7 +360,7 @@ LIBIMOBILEDEVICE_API np_error_t np_set_notify_callback( np_client_t client, np_n client->parent = NULL; thread_join(client->notifier); thread_free(client->notifier); - client->notifier = (thread_t)NULL; + client->notifier = THREAD_T_NULL; client->parent = parent; } diff --git a/src/notification_proxy.h b/src/notification_proxy.h index cc25a95..595cb01 100644 --- a/src/notification_proxy.h +++ b/src/notification_proxy.h @@ -22,14 +22,15 @@ #ifndef __NOTIFICATION_PROXY_H #define __NOTIFICATION_PROXY_H +#include "idevice.h" #include "libimobiledevice/notification_proxy.h" #include "property_list_service.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h> struct np_client_private { property_list_service_client_t parent; mutex_t mutex; - thread_t notifier; + THREAD_T notifier; }; void* np_notifier(void* arg); diff --git a/src/preboard.c b/src/preboard.c new file mode 100644 index 0000000..c3eff02 --- /dev/null +++ b/src/preboard.c @@ -0,0 +1,256 @@ +/* + * preboard.c + * com.apple.preboardservice_v2 service implementation. + * + * Copyright (c) 2019 Nikias Bassen, 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 <plist/plist.h> + +#include "preboard.h" +#include "lockdown.h" +#include "common/debug.h" + +/** + * Convert a property_list_service_error_t value to a preboard_error_t value. + * Used internally to get correct error codes. + * + * @param err An property_list_service_error_t error code + * + * @return A matching preboard_error_t error code, + * PREBOARD_E_UNKNOWN_ERROR otherwise. + */ +static preboard_error_t preboard_error(property_list_service_error_t err) +{ + switch (err) { + case PROPERTY_LIST_SERVICE_E_SUCCESS: + return PREBOARD_E_SUCCESS; + case PROPERTY_LIST_SERVICE_E_INVALID_ARG: + return PREBOARD_E_INVALID_ARG; + case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: + return PREBOARD_E_PLIST_ERROR; + case PROPERTY_LIST_SERVICE_E_MUX_ERROR: + return PREBOARD_E_MUX_ERROR; + case PROPERTY_LIST_SERVICE_E_SSL_ERROR: + return PREBOARD_E_SSL_ERROR; + case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA: + return PREBOARD_E_NOT_ENOUGH_DATA; + case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: + return PREBOARD_E_TIMEOUT; + default: + break; + } + return PREBOARD_E_UNKNOWN_ERROR; +} + +preboard_error_t preboard_client_new(idevice_t device, lockdownd_service_descriptor_t service, preboard_client_t * client) +{ + *client = NULL; + + if (!device || !service || service->port == 0 || !client || *client) { + debug_info("Incorrect parameter passed to preboard_client_new."); + return PREBOARD_E_INVALID_ARG; + } + + debug_info("Creating preboard_client, port = %d.", service->port); + + property_list_service_client_t plclient = NULL; + preboard_error_t ret = preboard_error(property_list_service_client_new(device, service, &plclient)); + if (ret != PREBOARD_E_SUCCESS) { + debug_info("Creating a property list client failed. Error: %i", ret); + return ret; + } + + preboard_client_t client_loc = (preboard_client_t) malloc(sizeof(struct preboard_client_private)); + client_loc->parent = plclient; + client_loc->receive_status_thread = THREAD_T_NULL; + + *client = client_loc; + + debug_info("preboard_client successfully created."); + return 0; +} + +preboard_error_t preboard_client_start_service(idevice_t device, preboard_client_t * client, const char* label) +{ + preboard_error_t err = PREBOARD_E_UNKNOWN_ERROR; + service_client_factory_start_service(device, PREBOARD_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(preboard_client_new), &err); + return err; +} + +preboard_error_t preboard_client_free(preboard_client_t client) +{ + if (!client) + return PREBOARD_E_INVALID_ARG; + + property_list_service_client_t parent = client->parent; + client->parent = NULL; + if (client->receive_status_thread) { + debug_info("joining receive_status_thread"); + thread_join(client->receive_status_thread); + thread_free(client->receive_status_thread); + client->receive_status_thread = THREAD_T_NULL; + } + preboard_error_t err = preboard_error(property_list_service_client_free(parent)); + free(client); + + return err; +} + +preboard_error_t preboard_send(preboard_client_t client, plist_t plist) +{ + preboard_error_t res = PREBOARD_E_UNKNOWN_ERROR; + res = preboard_error(property_list_service_send_binary_plist(client->parent, plist)); + if (res != PREBOARD_E_SUCCESS) { + debug_info("Sending plist failed with error %d", res); + return res; + } + return res; +} + +preboard_error_t preboard_receive_with_timeout(preboard_client_t client, plist_t * plist, uint32_t timeout_ms) +{ + preboard_error_t res = PREBOARD_E_UNKNOWN_ERROR; + plist_t outplist = NULL; + res = preboard_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, timeout_ms)); + if (res != PREBOARD_E_SUCCESS && res != PREBOARD_E_TIMEOUT) { + debug_info("Could not receive plist, error %d", res); + plist_free(outplist); + } else if (res == PREBOARD_E_SUCCESS) { + *plist = outplist; + } + return res; +} + +preboard_error_t preboard_receive(preboard_client_t client, plist_t * plist) +{ + return preboard_receive_with_timeout(client, plist, 5000); +} + +struct preboard_status_data { + preboard_client_t client; + preboard_status_cb_t cbfunc; + void *user_data; +}; + +static void* preboard_receive_status_loop_thread(void* arg) +{ + struct preboard_status_data *data = (struct preboard_status_data*)arg; + + /* run until the service disconnects or an error occurs */ + while (data->client && data->client->parent) { + plist_t pl = NULL; + preboard_error_t perr = preboard_receive_with_timeout(data->client, &pl, 1000); + if (perr == PREBOARD_E_TIMEOUT) { + continue; + } + if (perr == PREBOARD_E_SUCCESS) { + data->cbfunc(pl, data->user_data); + } + plist_free(pl); + if (perr != PREBOARD_E_SUCCESS) { + data->cbfunc(NULL, data->user_data); + break; + } + } + + /* cleanup */ + debug_info("done, cleaning up."); + + if (data->client->receive_status_thread) { + thread_free(data->client->receive_status_thread); + data->client->receive_status_thread = THREAD_T_NULL; + } + free(data); + + return NULL; +} + +static preboard_error_t preboard_receive_status_loop_with_callback(preboard_client_t client, preboard_status_cb_t status_cb, void *user_data) +{ + if (!client || !client->parent) { + return PREBOARD_E_INVALID_ARG; + } + + if (client->receive_status_thread) { + return PREBOARD_E_OP_IN_PROGRESS; + } + + preboard_error_t res = PREBOARD_E_UNKNOWN_ERROR; + struct preboard_status_data *data = (struct preboard_status_data*)malloc(sizeof(struct preboard_status_data)); + if (data) { + data->client = client; + data->cbfunc = status_cb; + data->user_data = user_data; + if (thread_new(&client->receive_status_thread, preboard_receive_status_loop_thread, data) == 0) { + res = PREBOARD_E_SUCCESS; + } + } + + return res; +} + +preboard_error_t preboard_create_stashbag(preboard_client_t client, plist_t manifest, preboard_status_cb_t status_cb, void *user_data) +{ + if (!client) { + return PREBOARD_E_INVALID_ARG; + } + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string("CreateStashbag")); + if (manifest) { + plist_dict_set_item(dict, "Manifest", plist_copy(manifest)); + } + preboard_error_t perr = preboard_send(client, dict); + plist_free(dict); + if (perr != PREBOARD_E_SUCCESS) { + return perr; + } + if (!status_cb) { + return PREBOARD_E_SUCCESS; + } + + return preboard_receive_status_loop_with_callback(client, status_cb, user_data); +} + +preboard_error_t preboard_commit_stashbag(preboard_client_t client, plist_t manifest, preboard_status_cb_t status_cb, void *user_data) +{ + if (!client) { + return PREBOARD_E_INVALID_ARG; + } + + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string("CommitStashbag")); + if (manifest) { + plist_dict_set_item(dict, "Manifest", plist_copy(manifest)); + } + preboard_error_t perr = preboard_send(client, dict); + plist_free(dict); + if (perr != PREBOARD_E_SUCCESS) { + return perr; + } + if (!status_cb) { + return PREBOARD_E_SUCCESS; + } + + return preboard_receive_status_loop_with_callback(client, status_cb, user_data); +} diff --git a/src/preboard.h b/src/preboard.h new file mode 100644 index 0000000..f8164eb --- /dev/null +++ b/src/preboard.h @@ -0,0 +1,35 @@ +/* + * preboard.h + * com.apple.preboard_v2 service header file. + * + * Copyright (c) 2019 Nikias Bassen, 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 __PREBOARD_H +#define __PREBOARD_H + +#include "idevice.h" +#include "libimobiledevice/preboard.h" +#include "property_list_service.h" +#include <libimobiledevice-glue/thread.h> + +struct preboard_client_private { + property_list_service_client_t parent; + THREAD_T receive_status_thread; +}; + +#endif diff --git a/src/property_list_service.c b/src/property_list_service.c index f411699..2fca4e7 100644 --- a/src/property_list_service.c +++ b/src/property_list_service.c @@ -48,13 +48,17 @@ static property_list_service_error_t service_to_property_list_service_error(serv return PROPERTY_LIST_SERVICE_E_MUX_ERROR; case SERVICE_E_SSL_ERROR: return PROPERTY_LIST_SERVICE_E_SSL_ERROR; + case SERVICE_E_NOT_ENOUGH_DATA: + return PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA; + case SERVICE_E_TIMEOUT: + return PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT; default: break; } return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_client_new(idevice_t device, lockdownd_service_descriptor_t service, property_list_service_client_t *client) +property_list_service_error_t property_list_service_client_new(idevice_t device, lockdownd_service_descriptor_t service, property_list_service_client_t *client) { if (!device || !service || service->port == 0 || !client || *client) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; @@ -74,7 +78,7 @@ LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_client_ return PROPERTY_LIST_SERVICE_E_SUCCESS; } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_client_free(property_list_service_client_t client) +property_list_service_error_t property_list_service_client_free(property_list_service_client_t client) { if (!client) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; @@ -108,7 +112,7 @@ static property_list_service_error_t internal_plist_send(property_list_service_c char *content = NULL; uint32_t length = 0; uint32_t nlen = 0; - int bytes = 0; + uint32_t bytes = 0; if (!client || (client && !client->parent) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; @@ -126,13 +130,13 @@ static property_list_service_error_t internal_plist_send(property_list_service_c nlen = htobe32(length); debug_info("sending %d bytes", length); - service_send(client->parent, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes); + service_send(client->parent, (const char*)&nlen, sizeof(nlen), &bytes); if (bytes == sizeof(nlen)) { - service_send(client->parent, content, length, (uint32_t*)&bytes); + service_send(client->parent, content, length, &bytes); if (bytes > 0) { debug_info("sent %d bytes", bytes); debug_plist(plist); - if ((uint32_t)bytes == length) { + if (bytes == length) { res = PROPERTY_LIST_SERVICE_E_SUCCESS; } else { debug_info("ERROR: Could not send all data (%d of %d)!", bytes, length); @@ -145,16 +149,15 @@ static property_list_service_error_t internal_plist_send(property_list_service_c } free(content); - return res; } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_send_xml_plist(property_list_service_client_t client, plist_t plist) +property_list_service_error_t property_list_service_send_xml_plist(property_list_service_client_t client, plist_t plist) { return internal_plist_send(client, plist, 0); } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_send_binary_plist(property_list_service_client_t client, plist_t plist) +property_list_service_error_t property_list_service_send_binary_plist(property_list_service_client_t client, plist_t plist) { return internal_plist_send(client, plist, 1); } @@ -170,6 +173,8 @@ LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_send_bi * * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL, + * PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA when not enough data + * received, PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT when the connection times out, * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR @@ -187,89 +192,104 @@ static property_list_service_error_t internal_plist_receive_timeout(property_lis *plist = NULL; service_error_t serr = service_receive_with_timeout(client->parent, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); - if ((serr == SERVICE_E_SUCCESS) && (bytes == 0)) { + if (serr != SERVICE_E_SUCCESS) { + debug_info("initial read failed!"); + return service_to_property_list_service_error(serr); + } + + if (bytes == 0) { + /* success but 0 bytes length, assume timeout */ return PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT; } + debug_info("initial read=%i", bytes); - if (bytes < 4) { - debug_info("initial read failed!"); - return PROPERTY_LIST_SERVICE_E_MUX_ERROR; - } else { - uint32_t curlen = 0; - char *content = NULL; - - pktlen = be32toh(pktlen); - debug_info("%d bytes following", pktlen); - content = (char*)malloc(pktlen); - if (!content) { - debug_info("out of memory when allocating %d bytes", pktlen); - return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; - } - while (curlen < pktlen) { - service_receive(client->parent, content+curlen, pktlen-curlen, &bytes); - if (bytes <= 0) { - res = PROPERTY_LIST_SERVICE_E_MUX_ERROR; - break; - } - debug_info("received %d bytes", bytes); - curlen += bytes; - } - if (curlen < pktlen) { - debug_info("received incomplete packet (%d of %d bytes)", curlen, pktlen); - if (curlen > 0) { - debug_info("incomplete packet following:"); - debug_buffer(content, curlen); - } - free(content); - return res; - } - if ((pktlen > 8) && !memcmp(content, "bplist00", 8)) { - plist_from_bin(content, pktlen, plist); - } else if ((pktlen > 5) && !memcmp(content, "<?xml", 5)) { - /* iOS 4.3+ hack: plist data might contain invalid characters, thus we convert those to spaces */ - for (bytes = 0; bytes < pktlen-1; bytes++) { - if ((content[bytes] >= 0) && (content[bytes] < 0x20) && (content[bytes] != 0x09) && (content[bytes] != 0x0a) && (content[bytes] != 0x0d)) - content[bytes] = 0x20; - } - plist_from_xml(content, pktlen, plist); - } else { - debug_info("WARNING: received unexpected non-plist content"); - debug_buffer(content, pktlen); + uint32_t curlen = 0; + char *content = NULL; + + pktlen = be32toh(pktlen); + debug_info("%d bytes following", pktlen); + content = (char*)malloc(pktlen); + if (!content) { + debug_info("out of memory when allocating %d bytes", pktlen); + return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; + } + + while (curlen < pktlen) { + serr = service_receive(client->parent, content+curlen, pktlen-curlen, &bytes); + if (serr != SERVICE_E_SUCCESS) { + res = service_to_property_list_service_error(serr); + break; } - if (*plist) { - debug_plist(*plist); - res = PROPERTY_LIST_SERVICE_E_SUCCESS; - } else { - res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR; + debug_info("received %d bytes", bytes); + curlen += bytes; + } + + if (curlen < pktlen) { + debug_info("received incomplete packet (%d of %d bytes)", curlen, pktlen); + if (curlen > 0) { + debug_info("incomplete packet following:"); + debug_buffer(content, curlen); } free(content); - content = NULL; + return res; } + + if ((pktlen > 8) && !memcmp(content, "bplist00", 8)) { + plist_from_bin(content, pktlen, plist); + } else if ((pktlen > 5) && !memcmp(content, "<?xml", 5)) { + /* iOS 4.3+ hack: plist data might contain invalid characters, thus we convert those to spaces */ + for (bytes = 0; bytes < pktlen-1; bytes++) { + if ((content[bytes] >= 0) && (content[bytes] < 0x20) && (content[bytes] != 0x09) && (content[bytes] != 0x0a) && (content[bytes] != 0x0d)) + content[bytes] = 0x20; + } + plist_from_xml(content, pktlen, plist); + } else { + debug_info("WARNING: received unexpected non-plist content"); + debug_buffer(content, pktlen); + } + + if (*plist) { + debug_plist(*plist); + res = PROPERTY_LIST_SERVICE_E_SUCCESS; + } else { + res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR; + } + + free(content); + content = NULL; + return res; } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_receive_plist_with_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) +property_list_service_error_t property_list_service_receive_plist_with_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) { return internal_plist_receive_timeout(client, plist, timeout); } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_receive_plist(property_list_service_client_t client, plist_t *plist) +property_list_service_error_t property_list_service_receive_plist(property_list_service_client_t client, plist_t *plist) { - return internal_plist_receive_timeout(client, plist, 10000); + return internal_plist_receive_timeout(client, plist, 30000); } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client) +property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client) { if (!client || !client->parent) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; return service_to_property_list_service_error(service_enable_ssl(client->parent)); } -LIBIMOBILEDEVICE_API property_list_service_error_t property_list_service_disable_ssl(property_list_service_client_t client) +property_list_service_error_t property_list_service_disable_ssl(property_list_service_client_t client) { if (!client || !client->parent) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; return service_to_property_list_service_error(service_disable_ssl(client->parent)); } +property_list_service_error_t property_list_service_get_service_client(property_list_service_client_t client, service_client_t *service_client) +{ + if (!client || !client->parent || !service_client) + return PROPERTY_LIST_SERVICE_E_INVALID_ARG; + *service_client = client->parent; + return PROPERTY_LIST_SERVICE_E_SUCCESS; +} diff --git a/src/property_list_service.h b/src/property_list_service.h index 3c9e14d..0e9e948 100644 --- a/src/property_list_service.h +++ b/src/property_list_service.h @@ -22,6 +22,7 @@ #ifndef __PROPERTY_LIST_SERVICE_H #define __PROPERTY_LIST_SERVICE_H +#include "idevice.h" #include "libimobiledevice/property_list_service.h" #include "service.h" diff --git a/src/restore.c b/src/restore.c index 4b578c2..d13a28a 100644 --- a/src/restore.c +++ b/src/restore.c @@ -19,6 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #include <errno.h> #include <string.h> #include <stdlib.h> @@ -41,7 +44,7 @@ * * @return RESULT_SUCCESS when the result is 'Success', * RESULT_FAILURE when the result is 'Failure', - * or a negative value if an error occured during evaluation. + * or a negative value if an error occurred during evaluation. */ static int restored_check_result(plist_t dict) { @@ -89,7 +92,26 @@ static void plist_dict_add_label(plist_t plist, const char *label) } } -LIBIMOBILEDEVICE_API restored_error_t restored_client_free(restored_client_t client) +static restored_error_t restored_error(property_list_service_error_t err) +{ + switch (err) { + case PROPERTY_LIST_SERVICE_E_SUCCESS: + return RESTORE_E_SUCCESS; + case PROPERTY_LIST_SERVICE_E_INVALID_ARG: + return RESTORE_E_INVALID_ARG; + case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: + return RESTORE_E_PLIST_ERROR; + case PROPERTY_LIST_SERVICE_E_MUX_ERROR: + return RESTORE_E_MUX_ERROR; + case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: + return RESTORE_E_RECEIVE_TIMEOUT; + default: + break; + } + return RESTORE_E_UNKNOWN_ERROR; +} + +restored_error_t restored_client_free(restored_client_t client) { if (!client) return RESTORE_E_INVALID_ARG; @@ -99,9 +121,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_client_free(restored_client_t cli if (client->parent) { restored_goodbye(client); - if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { - ret = RESTORE_E_SUCCESS; - } + ret = restored_error(property_list_service_client_free(client->parent)); } if (client->udid) { @@ -119,7 +139,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_client_free(restored_client_t cli return ret; } -LIBIMOBILEDEVICE_API void restored_client_set_label(restored_client_t client, const char *label) +void restored_client_set_label(restored_client_t client, const char *label) { if (client) { if (client->label) @@ -129,41 +149,23 @@ LIBIMOBILEDEVICE_API void restored_client_set_label(restored_client_t client, co } } -LIBIMOBILEDEVICE_API restored_error_t restored_receive(restored_client_t client, plist_t *plist) +restored_error_t restored_receive(restored_client_t client, plist_t *plist) { if (!client || !plist || (plist && *plist)) return RESTORE_E_INVALID_ARG; - restored_error_t ret = RESTORE_E_SUCCESS; - property_list_service_error_t err; - - err = property_list_service_receive_plist(client->parent, plist); - if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { - ret = RESTORE_E_UNKNOWN_ERROR; - } - - if (!*plist) - ret = RESTORE_E_PLIST_ERROR; - - return ret; + return restored_error(property_list_service_receive_plist(client->parent, plist)); } -LIBIMOBILEDEVICE_API restored_error_t restored_send(restored_client_t client, plist_t plist) +restored_error_t restored_send(restored_client_t client, plist_t plist) { if (!client || !plist) return RESTORE_E_INVALID_ARG; - restored_error_t ret = RESTORE_E_SUCCESS; - property_list_service_error_t err; - - err = property_list_service_send_xml_plist(client->parent, plist); - if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { - ret = RESTORE_E_UNKNOWN_ERROR; - } - return ret; + return restored_error(property_list_service_send_xml_plist(client->parent, plist)); } -LIBIMOBILEDEVICE_API restored_error_t restored_query_type(restored_client_t client, char **type, uint64_t *version) +restored_error_t restored_query_type(restored_client_t client, char **type, uint64_t *version) { if (!client) return RESTORE_E_INVALID_ARG; @@ -222,7 +224,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_query_type(restored_client_t clie return ret; } -LIBIMOBILEDEVICE_API restored_error_t restored_query_value(restored_client_t client, const char *key, plist_t *value) +restored_error_t restored_query_value(restored_client_t client, const char *key, plist_t *value) { if (!client || !key) return RESTORE_E_INVALID_ARG; @@ -264,34 +266,32 @@ LIBIMOBILEDEVICE_API restored_error_t restored_query_value(restored_client_t cli return ret; } -LIBIMOBILEDEVICE_API restored_error_t restored_get_value(restored_client_t client, const char *key, plist_t *value) +restored_error_t restored_get_value(restored_client_t client, const char *key, plist_t *value) { + plist_t item; + if (!client || !value || (value && *value)) return RESTORE_E_INVALID_ARG; if (!client->info) return RESTORE_E_NOT_ENOUGH_DATA; - restored_error_t ret = RESTORE_E_SUCCESS; - plist_t item = NULL; - if (!key) { *value = plist_copy(client->info); return RESTORE_E_SUCCESS; - } else { - item = plist_dict_get_item(client->info, key); } - if (item) { - *value = plist_copy(item); - } else { - ret = RESTORE_E_PLIST_ERROR; + item = plist_dict_get_item(client->info, key); + if (!item) { + return RESTORE_E_PLIST_ERROR; } - return ret; + *value = plist_copy(item); + + return RESTORE_E_SUCCESS; } -LIBIMOBILEDEVICE_API restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label) +restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label) { if (!client) return RESTORE_E_INVALID_ARG; @@ -305,9 +305,10 @@ LIBIMOBILEDEVICE_API restored_error_t restored_client_new(idevice_t device, rest }; property_list_service_client_t plistclient = NULL; - if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { + ret = restored_error(property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient)); + if (ret != RESTORE_E_SUCCESS) { debug_info("could not connect to restored (device %s)", device->udid); - return RESTORE_E_MUX_ERROR; + return ret; } restored_client_t client_loc = (restored_client_t) malloc(sizeof(struct restored_client_private)); @@ -321,7 +322,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_client_new(idevice_t device, rest idev_ret = idevice_get_udid(device, &client_loc->udid); if (IDEVICE_E_SUCCESS != idev_ret) { debug_info("failed to get device udid."); - ret = RESTORE_E_DEVICE_ERROR; + ret = RESTORE_E_UNKNOWN_ERROR; } debug_info("device udid: %s", client_loc->udid); @@ -334,7 +335,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_client_new(idevice_t device, rest return ret; } -LIBIMOBILEDEVICE_API restored_error_t restored_goodbye(restored_client_t client) +restored_error_t restored_goodbye(restored_client_t client) { if (!client) return RESTORE_E_INVALID_ARG; @@ -366,7 +367,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_goodbye(restored_client_t client) return ret; } -LIBIMOBILEDEVICE_API restored_error_t restored_start_restore(restored_client_t client, plist_t options, uint64_t version) +restored_error_t restored_start_restore(restored_client_t client, plist_t options, uint64_t version) { if (!client) return RESTORE_E_INVALID_ARG; @@ -390,7 +391,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_start_restore(restored_client_t c return ret; } -LIBIMOBILEDEVICE_API restored_error_t restored_reboot(restored_client_t client) +restored_error_t restored_reboot(restored_client_t client) { if (!client) return RESTORE_E_INVALID_ARG; diff --git a/src/restore.h b/src/restore.h index 646d1d1..ec6fa04 100644 --- a/src/restore.h +++ b/src/restore.h @@ -24,6 +24,7 @@ #include <string.h> +#include "idevice.h" #include "libimobiledevice/restore.h" #include "property_list_service.h" diff --git a/src/reverse_proxy.c b/src/reverse_proxy.c new file mode 100644 index 0000000..2fcfdd1 --- /dev/null +++ b/src/reverse_proxy.c @@ -0,0 +1,810 @@ +/* + * reverse_proxy.c + * com.apple.PurpleReverseProxy service implementation. + * + * Copyright (c) 2021 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2014 BALATON Zoltan. 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> +#define _GNU_SOURCE 1 +#define __USE_GNU 1 +#include <stdio.h> +#include <errno.h> + +#include <plist/plist.h> +#include <libimobiledevice-glue/thread.h> +#include <libimobiledevice-glue/socket.h> + +#include "reverse_proxy.h" +#include "lockdown.h" +#include "common/debug.h" +#include "endianness.h" +#include "asprintf.h" + +#ifndef ECONNRESET +#define ECONNRESET 108 +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT 138 +#endif + +#define CTRL_PORT 1082 +#define CTRLCMD "BeginCtrl" +#define HELLOCTRLCMD "HelloCtrl" +#define HELLOCMD "HelloConn" + +#define RP_SYNC_MSG 0x1 +#define RP_PROXY_MSG 0x105 +#define RP_PLIST_MSG 0xbbaa + +/** + * Convert a service_error_t value to a reverse_proxy_error_t value. + * Used internally to get correct error codes. + * + * @param err A service_error_t error code + * + * @return A matching reverse_proxy_error_t error code, + * REVERSE_PROXY_E_UNKNOWN_ERROR otherwise. + */ +static reverse_proxy_error_t reverse_proxy_error(service_error_t err) +{ + switch (err) { + case SERVICE_E_SUCCESS: + return REVERSE_PROXY_E_SUCCESS; + case SERVICE_E_INVALID_ARG: + return REVERSE_PROXY_E_INVALID_ARG; + case SERVICE_E_MUX_ERROR: + return REVERSE_PROXY_E_MUX_ERROR; + case SERVICE_E_SSL_ERROR: + return REVERSE_PROXY_E_SSL_ERROR; + case SERVICE_E_NOT_ENOUGH_DATA: + return REVERSE_PROXY_E_NOT_ENOUGH_DATA; + case SERVICE_E_TIMEOUT: + return REVERSE_PROXY_E_TIMEOUT; + default: + break; + } + return REVERSE_PROXY_E_UNKNOWN_ERROR; +} + +static void _reverse_proxy_log(reverse_proxy_client_t client, const char* format, ...) +{ + if (!client || !client->log_cb) { + return; + } + va_list args; + va_start(args, format); + char* buffer = NULL; + if(vasprintf(&buffer, format, args)<0){} + va_end(args); + client->log_cb(client, buffer, client->log_cb_user_data); + free(buffer); +} + +static void _reverse_proxy_data(reverse_proxy_client_t client, int direction, char* buffer, uint32_t length) +{ + if (!client || !client->data_cb) { + return; + } + client->data_cb(client, direction, buffer, length, client->data_cb_user_data); +} + +static void _reverse_proxy_status(reverse_proxy_client_t client, int status, const char* format, ...) +{ + if (!client || !client->status_cb) { + return; + } + va_list args; + va_start(args, format); + char* buffer = NULL; + if(vasprintf(&buffer, format, args)<0){} + va_end(args); + client->status_cb(client, status, buffer, client->status_cb_user_data); + free(buffer); +} + +static int _reverse_proxy_handle_proxy_cmd(reverse_proxy_client_t client) +{ + reverse_proxy_error_t err = REVERSE_PROXY_E_SUCCESS; + char *buf = NULL; + size_t bufsize = 1048576; + uint32_t sent = 0, bytes = 0; + uint32_t sent_total = 0; + uint32_t recv_total = 0; + char *host = NULL; + uint16_t port = 0; + + buf = malloc(bufsize); + if (!buf) { + _reverse_proxy_log(client, "ERROR: Failed to allocate buffer"); + return -1; + } + + err = reverse_proxy_receive(client, buf, bufsize, &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + free(buf); + _reverse_proxy_log(client, "ERROR: Unable to read data for proxy command"); + return -1; + } + _reverse_proxy_log(client, "Handling proxy command"); + + /* Just return success here unconditionally because we don't know + * anything else and we will eventually abort on failure anyway */ + uint16_t ack = 5; + err = reverse_proxy_send(client, (char *)&ack, sizeof(ack), &sent); + if (err != REVERSE_PROXY_E_SUCCESS || sent != sizeof(ack)) { + free(buf); + _reverse_proxy_log(client, "ERROR: Unable to send ack. Sent %u of %u bytes.", sent, (uint32_t)sizeof(ack)); + return -1; + } + + if (bytes < 3) { + free(buf); + _reverse_proxy_log(client, "Proxy command data too short, retrying"); + return 0; + } + + /* ack command data too */ + err = reverse_proxy_send(client, buf, bytes, &sent); + if (err != REVERSE_PROXY_E_SUCCESS || sent != bytes) { + free(buf); + _reverse_proxy_log(client, "ERROR: Unable to send data. Sent %u of %u bytes.", sent, bytes); + return -1; + } + + /* Now try to handle actual messages */ + /* Connect: 0 3 hostlen <host> <port> */ + if (buf[0] == 0 && buf[1] == 3) { + uint16_t *p = (uint16_t *)&buf[bytes - 2]; + port = be16toh(*p); + buf[bytes - 2] = '\0'; + host = strdup(&buf[3]); + _reverse_proxy_log(client, "Connect request to %s:%u", host, port); + } + + if (!host || !buf[2]) { + /* missing or zero length host name */ + free(buf); + return 0; + } + + /* else wait for messages and forward them */ + int sockfd = socket_connect(host, port); + if (sockfd < 0) { + free(buf); + _reverse_proxy_log(client, "ERROR: Connection to %s:%u failed: %s", host, port, strerror(errno)); + free(host); + return -1; + } + + _reverse_proxy_status(client, RP_STATUS_CONNECTED, "Connected to %s:%u", host, port); + + int res = 0, bytes_ret; + while (1) { + bytes = 0; + err = reverse_proxy_receive_with_timeout(client, buf, bufsize, &bytes, 100); + if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && !bytes)) { + /* just a timeout condition */ + } + else if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "Connection closed"); + res = -1; + break; + } + if (bytes) { + _reverse_proxy_log(client, "Proxying %u bytes of data", bytes); + _reverse_proxy_data(client, RP_DATA_DIRECTION_OUT, buf, bytes); + sent = 0; + while (sent < bytes) { + int s = socket_send(sockfd, buf + sent, bytes - sent); + if (s < 0) { + break; + } + sent += s; + } + sent_total += sent; + if (sent != bytes) { + _reverse_proxy_log(client, "ERROR: Sending proxy payload failed: %s. Sent %u of %u bytes.", strerror(errno), sent, bytes); + socket_close(sockfd); + res = -1; + break; + } + } + bytes_ret = socket_receive_timeout(sockfd, buf, bufsize, 0, 100); + if (bytes_ret == -ETIMEDOUT) { + bytes_ret = 0; + } else if (bytes_ret == -ECONNRESET) { + res = 1; + break; + } else if (bytes_ret < 0) { + _reverse_proxy_log(client, "ERROR: Failed to receive from host: %s", strerror(-bytes_ret)); + break; + } + + bytes = bytes_ret; + if (bytes) { + _reverse_proxy_log(client, "Received %u bytes reply data, sending to device\n", bytes); + _reverse_proxy_data(client, RP_DATA_DIRECTION_IN, buf, bytes); + recv_total += bytes; + sent = 0; + while (sent < bytes) { + uint32_t s; + err = reverse_proxy_send(client, buf + sent, bytes - sent, &s); + if (err != REVERSE_PROXY_E_SUCCESS) { + break; + } + sent += s; + } + if (err != REVERSE_PROXY_E_SUCCESS || bytes != sent) { + _reverse_proxy_log(client, "ERROR: Unable to send data (%d). Sent %u of %u bytes.", err, sent, bytes); + res = -1; + break; + } + } + } + socket_close(sockfd); + free(host); + free(buf); + + _reverse_proxy_status(client, RP_STATUS_DISCONNECTED, "Disconnected (out: %u / in: %u)", sent_total, recv_total); + + return res; +} + +static int _reverse_proxy_handle_plist_cmd(reverse_proxy_client_t client) +{ + plist_t dict; + reverse_proxy_error_t err; + + err = reverse_proxy_receive_plist(client, &dict); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "ERROR: Unable to receive plist command, error", err); + return -1; + } + plist_t node = plist_dict_get_item(dict, "Command"); + if (!node || (plist_get_node_type(node) != PLIST_STRING)) { + _reverse_proxy_log(client, "ERROR: No 'Command' in reply", err); + plist_free(dict); + return -1; + } + char *command = NULL; + plist_get_string_val(node, &command); + plist_free(dict); + + if (!command) { + _reverse_proxy_log(client, "ERROR: Empty 'Command' string"); + return -1; + } + + if (!strcmp(command, "Ping")) { + _reverse_proxy_log(client, "Received Ping command, replying with Pong"); + dict = plist_new_dict(); + plist_dict_set_item(dict, "Pong", plist_new_bool(1)); + err = reverse_proxy_send_plist(client, dict); + plist_free(dict); + if (err) { + _reverse_proxy_log(client, "ERROR: Unable to send Ping command reply"); + free(command); + return -1; + } + } else { + _reverse_proxy_log(client, "WARNING: Received unhandled plist command '%s'", command); + free(command); + return -1; + } + + free(command); + /* reverse proxy connection will be terminated remotely. Next receive will get nothing, error and terminate this worker thread. */ + return 0; +} + +static reverse_proxy_error_t reverse_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, reverse_proxy_client_t * client) +{ + *client = NULL; + + if (!device || !service || service->port == 0 || !client || *client) { + return REVERSE_PROXY_E_INVALID_ARG; + } + + debug_info("Creating reverse_proxy_client, port = %d.", service->port); + + service_client_t sclient = NULL; + reverse_proxy_error_t ret = reverse_proxy_error(service_client_new(device, service, &sclient)); + if (ret != REVERSE_PROXY_E_SUCCESS) { + debug_info("Creating service client failed. Error: %i", ret); + return ret; + } + + reverse_proxy_client_t client_loc = (reverse_proxy_client_t) calloc(1, sizeof(struct reverse_proxy_client_private)); + client_loc->parent = sclient; + client_loc->th_ctrl = THREAD_T_NULL; + *client = client_loc; + + return 0; +} + +static void* _reverse_proxy_connection_thread(void *cdata) +{ + reverse_proxy_client_t client = (reverse_proxy_client_t)cdata; + uint32_t bytes = 0; + reverse_proxy_client_t conn_client = NULL; + reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR; + + if (client->conn_port == 0) { + service_client_factory_start_service(client->parent->connection->device, "com.apple.PurpleReverseProxy.Conn", (void**)&conn_client, client->label, SERVICE_CONSTRUCTOR(reverse_proxy_client_new), &err); + if (!conn_client) { + _reverse_proxy_log(client, "ERROR: Failed to start proxy connection service, error %d", err); + } + } else { + struct lockdownd_service_descriptor svc; + svc.port = client->conn_port; + svc.ssl_enabled = 0; + svc.identifier = NULL; + err = reverse_proxy_client_new(client->parent->connection->device, &svc, &conn_client); + if (!conn_client) { + _reverse_proxy_log(client, "ERROR: Failed to connect to proxy connection port %u, error %d", client->conn_port, err); + } + } + if (!conn_client) { + goto leave; + } + conn_client->type = RP_TYPE_CONN; + conn_client->protoversion = client->protoversion; + conn_client->log_cb = client->log_cb; + conn_client->log_cb_user_data = client->log_cb_user_data; + conn_client->status_cb = client->status_cb; + conn_client->status_cb_user_data = client->status_cb_user_data; + + err = reverse_proxy_send(conn_client, HELLOCMD, sizeof(HELLOCMD), &bytes); + if (err != REVERSE_PROXY_E_SUCCESS || bytes != sizeof(HELLOCMD)) { + _reverse_proxy_log(conn_client, "ERROR: Unable to send " HELLOCMD " (sent %u/%u bytes)", bytes, sizeof(HELLOCMD)); + goto leave; + } + + if (conn_client->protoversion == 2) { + plist_t reply = NULL; + err = reverse_proxy_receive_plist(conn_client, &reply); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " reply, error %d", err); + goto leave; + } + char* identifier = NULL; + char* cmd = NULL; + plist_t node = NULL; + node = plist_dict_get_item(reply, "Command"); + if (node) { + plist_get_string_val(node, &cmd); + } + node = plist_dict_get_item(reply, "Identifier"); + if (node) { + plist_get_string_val(node, &identifier); + } + plist_free(reply); + + if (!cmd || (strcmp(cmd, HELLOCMD) != 0)) { + free(cmd); + free(identifier); + _reverse_proxy_log(conn_client, "ERROR: Unexpected reply to " HELLOCMD " received"); + goto leave; + } + free(cmd); + + if (identifier) { + _reverse_proxy_log(conn_client, "Got device identifier %s", identifier); + free(identifier); + } + } else { + char buf[16]; + memset(buf, '\0', sizeof(buf)); + bytes = 0; + err = reverse_proxy_receive(conn_client, buf, sizeof(HELLOCMD), &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " reply, error %d", err); + goto leave; + } + if (memcmp(buf, HELLOCMD, sizeof(HELLOCMD)) != 0) { + _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " as reply, but %.*s", (int)bytes, buf); + goto leave; + } + } + + _reverse_proxy_status(conn_client, RP_STATUS_READY, "Ready"); + + int running = 1; + while (client->th_ctrl != THREAD_T_NULL && conn_client && running) { + uint16_t cmd = 0; + bytes = 0; + err = reverse_proxy_receive_with_timeout(conn_client, (char*)&cmd, sizeof(cmd), &bytes, 1000); + if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && bytes != sizeof(cmd))) { + continue; + } else if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(conn_client, "Connection closed"); + break; + } + cmd = le16toh(cmd); + switch (cmd) { + case 0xBBAA: + /* plist command */ + if (_reverse_proxy_handle_plist_cmd(conn_client) < 0) { + running = 0; + } + break; + case 0x105: + /* proxy command */ + if (_reverse_proxy_handle_proxy_cmd(conn_client) < 0) { + running = 0; + } + break; + default: + /* unknown */ + debug_info("ERROR: Unknown request 0x%x", cmd); + _reverse_proxy_log(conn_client, "ERROR: Unknown request 0x%x", cmd); + running = 0; + break; + } + } + +leave: + _reverse_proxy_status(conn_client, RP_STATUS_TERMINATE, "Terminated"); + if (conn_client) { + reverse_proxy_client_free(conn_client); + } + + return NULL; +} + +static void* _reverse_proxy_control_thread(void *cdata) +{ + reverse_proxy_client_t client = (reverse_proxy_client_t)cdata; + THREAD_T th_conn = THREAD_T_NULL; + int running = 1; + _reverse_proxy_status(client, RP_STATUS_READY, "Ready"); + while (client && client->parent && running) { + uint32_t cmd = 0; + uint32_t bytes = 0; + reverse_proxy_error_t err = reverse_proxy_receive_with_timeout(client, (char*)&cmd, sizeof(cmd), &bytes, 1000); + if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && bytes != sizeof(cmd))) { + continue; + } else if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "Connection closed"); + break; + } + cmd = le32toh(cmd); + switch (cmd) { + case 1: + /* connection request */ + debug_info("ReverseProxy<%p> got connect request", client); + _reverse_proxy_status(client, RP_STATUS_CONNECT_REQ, "Connect Request"); + if (thread_new(&th_conn, _reverse_proxy_connection_thread, client) != 0) { + debug_info("ERROR: Failed to start connection thread"); + th_conn = THREAD_T_NULL; + running = 0; + } + break; + case 2: + /* shutdown request */ + debug_info("ReverseProxy<%p> got shutdown request", client); + _reverse_proxy_status(client, RP_STATUS_SHUTDOWN_REQ, "Shutdown Request"); + running = 0; + break; + default: + /* unknown */ + debug_info("ERROR: Unknown request 0x%x", cmd); + _reverse_proxy_log(client, "ERROR: Unknown request 0x%x", cmd); + running = 0; + break; + } + } + _reverse_proxy_log(client, "Terminating"); + + client->th_ctrl = THREAD_T_NULL; + if (th_conn) { + debug_info("joining connection thread"); + thread_join(th_conn); + thread_free(th_conn); + } + + _reverse_proxy_status(client, RP_STATUS_TERMINATE, "Terminated"); + + return NULL; +} + +reverse_proxy_error_t reverse_proxy_client_start_proxy(reverse_proxy_client_t client, int control_protocol_version) +{ + char buf[16] = {0, }; + uint32_t bytes = 0; + reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR; + + if (!client) { + return REVERSE_PROXY_E_INVALID_ARG; + } + if (control_protocol_version < 1 || control_protocol_version > 2) { + debug_info("invalid protocol version %d, must be 1 or 2", control_protocol_version); + return REVERSE_PROXY_E_INVALID_ARG; + } + + if (control_protocol_version == 2) { + err = reverse_proxy_send(client, CTRLCMD, sizeof(CTRLCMD), &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "ERROR: Failed to send " CTRLCMD " to device, error %d", err); + return err; + } + plist_t dict = plist_new_dict(); + plist_dict_set_item(dict, "Command", plist_new_string(CTRLCMD)); + plist_dict_set_item(dict, "CtrlProtoVersion", plist_new_uint(client->protoversion)); + err = reverse_proxy_send_plist(client, dict); + plist_free(dict); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "ERROR: Could not send " CTRLCMD " plist command, error %d", err); + return err; + } + dict = NULL; + err = reverse_proxy_receive_plist(client, &dict); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "ERROR: Could not receive " CTRLCMD " plist reply, error %d", err); + return err; + } + plist_t node = plist_dict_get_item(dict, "ConnPort"); + if (node && plist_get_node_type(node) == PLIST_UINT) { + uint64_t u64val = 0; + plist_get_uint_val(node, &u64val); + client->conn_port = (uint16_t)u64val; + } else { + _reverse_proxy_log(client, "ERROR: Could not get ConnPort value"); + return REVERSE_PROXY_E_UNKNOWN_ERROR; + } + client->protoversion = 2; + } else { + err = reverse_proxy_send(client, HELLOCTRLCMD, sizeof(HELLOCTRLCMD), &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "ERROR: Failed to send " HELLOCTRLCMD " to device, error %d", err); + return err; + } + + bytes = 0; + err = reverse_proxy_receive(client, buf, sizeof(HELLOCTRLCMD)-1, &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "ERROR: Could not receive " HELLOCTRLCMD " reply, error %d", err); + return err; + } + + uint16_t cport = 0; + bytes = 0; + err = reverse_proxy_receive(client, (char*)&cport, 2, &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + _reverse_proxy_log(client, "ERROR: Failed to receive connection port, error %d", err); + return err; + } + client->conn_port = le16toh(cport); + client->protoversion = 1; + } + + if (thread_new(&(client->th_ctrl), _reverse_proxy_control_thread, client) != 0) { + _reverse_proxy_log(client, "ERROR: Failed to start control thread"); + client->th_ctrl = THREAD_T_NULL; /* undefined after failure */ + err = REVERSE_PROXY_E_UNKNOWN_ERROR; + } + + return err; +} + +reverse_proxy_error_t reverse_proxy_client_create_with_service(idevice_t device, reverse_proxy_client_t* client, const char* label) +{ + reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR; + service_client_factory_start_service(device, "com.apple.PurpleReverseProxy.Ctrl", (void**)client, label, SERVICE_CONSTRUCTOR(reverse_proxy_client_new), &err); + if (!*client) { + return err; + } + (*client)->label = strdup(label); + (*client)->type = RP_TYPE_CTRL; + + return REVERSE_PROXY_E_SUCCESS; +} + +reverse_proxy_error_t reverse_proxy_client_create_with_port(idevice_t device, reverse_proxy_client_t* client, uint16_t device_port) +{ + reverse_proxy_client_t client_loc = NULL; + reverse_proxy_error_t err; + + struct lockdownd_service_descriptor svc; + svc.port = device_port; + svc.ssl_enabled = 0; + svc.identifier = NULL; + + err = reverse_proxy_client_new(device, &svc, &client_loc); + if (err != REVERSE_PROXY_E_SUCCESS) { + return err; + } + + client_loc->type = RP_TYPE_CTRL; + *client = client_loc; + + return REVERSE_PROXY_E_SUCCESS; +} + +reverse_proxy_error_t reverse_proxy_client_free(reverse_proxy_client_t client) +{ + if (!client) + return REVERSE_PROXY_E_INVALID_ARG; + service_client_t parent = client->parent; + client->parent = NULL; + if (client->th_ctrl) { + debug_info("joining control thread"); + thread_join(client->th_ctrl); + thread_free(client->th_ctrl); + client->th_ctrl = THREAD_T_NULL; + } + reverse_proxy_error_t err = reverse_proxy_error(service_client_free(parent)); + free(client->label); + free(client); + + return err; +} + +reverse_proxy_client_type_t reverse_proxy_get_type(reverse_proxy_client_t client) +{ + if (!client) + return 0; + return client->type; +} + +void reverse_proxy_client_set_status_callback(reverse_proxy_client_t client, reverse_proxy_status_cb_t status_callback, void* user_data) +{ + if (!client) { + return; + } + client->status_cb = status_callback; + client->status_cb_user_data = user_data; +} + +void reverse_proxy_client_set_log_callback(reverse_proxy_client_t client, reverse_proxy_log_cb_t log_callback, void* user_data) +{ + if (!client) { + return; + } + client->log_cb = log_callback; + client->log_cb_user_data = user_data; +} + +void reverse_proxy_client_set_data_callback(reverse_proxy_client_t client, reverse_proxy_data_cb_t data_callback, void* user_data) +{ + if (!client) { + return; + } + client->data_cb = data_callback; + client->data_cb_user_data = user_data; +} + +reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent) +{ + reverse_proxy_error_t err = reverse_proxy_error(service_send(client->parent, data, len, sent)); + return err; +} + +reverse_proxy_error_t reverse_proxy_receive_with_timeout(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received, unsigned int timeout) +{ + if (!client) + return REVERSE_PROXY_E_INVALID_ARG; + return reverse_proxy_error(service_receive_with_timeout(client->parent, buffer, len, received, timeout)); +} + +reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received) +{ + return reverse_proxy_receive_with_timeout(client, buffer, len, received, 20000); +} + +reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist) +{ + reverse_proxy_error_t err; + uint32_t len = 0; + char* buf = NULL; + uint32_t bytes = 0; + + plist_to_bin(plist, &buf, &len); + + if (!buf) { + return REVERSE_PROXY_E_INVALID_ARG; + } + + debug_info("Sending %u bytes", len); + + uint32_t slen = htole32(len); + err = reverse_proxy_send(client, (char*)&slen, sizeof(slen), &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + free(buf); + debug_info("ERROR: Unable to send data length, error %d. Sent %u/%u bytes.", err, bytes, (uint32_t)sizeof(slen)); + return err; + } + uint32_t done = 0; + do { + bytes = 0; + err = reverse_proxy_send(client, buf+done, len-done, &bytes); + if (err != REVERSE_PROXY_E_SUCCESS) { + break; + } + done += bytes; + } while (done < len); + free(buf); + if (err != REVERSE_PROXY_E_SUCCESS || done != len) { + debug_info("ERROR: Unable to send data, error %d. Sent %u/%u bytes.", err, done, len); + return err; + } + + debug_info("Sent %u bytes", len); + + return REVERSE_PROXY_E_SUCCESS; +} + +reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist) +{ + return reverse_proxy_receive_plist_with_timeout(client, plist, 20000); +} + +reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms) +{ + uint32_t len; + uint32_t bytes; + reverse_proxy_error_t err; + + err = reverse_proxy_receive_with_timeout(client, (char*)&len, sizeof(len), &bytes, timeout_ms); + if (err != REVERSE_PROXY_E_SUCCESS) { + if (err != REVERSE_PROXY_E_TIMEOUT) { + debug_info("ERROR: Unable to receive packet length, error %d\n", err); + } + return err; + } + + len = le32toh(len); + char* buf = calloc(1, len); + if (!buf) { + debug_info("ERROR: Out of memory"); + return REVERSE_PROXY_E_UNKNOWN_ERROR; + } + + uint32_t done = 0; + do { + bytes = 0; + err = reverse_proxy_receive_with_timeout(client, buf+done, len-done, &bytes, timeout_ms); + if (err != REVERSE_PROXY_E_SUCCESS) { + break; + } + done += bytes; + } while (done < len); + + if (err != REVERSE_PROXY_E_SUCCESS || done != len) { + free(buf); + debug_info("ERROR: Unable to receive data, error %d. Received %u/%u bytes.", err, done, len); + return err; + } + + debug_info("Received %u bytes", len); + + plist_from_bin(buf, len, plist); + free(buf); + + if (!(*plist)) { + debug_info("ERROR: Failed to convert buffer to plist"); + return REVERSE_PROXY_E_PLIST_ERROR; + } + + return REVERSE_PROXY_E_SUCCESS; +} diff --git a/src/reverse_proxy.h b/src/reverse_proxy.h new file mode 100644 index 0000000..7f441bd --- /dev/null +++ b/src/reverse_proxy.h @@ -0,0 +1,51 @@ +/* + * reverse_proxy.h + * com.apple.PurpleReverseProxy service header file. + * + * Copyright (c) 2021 Nikias Bassen, 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 __REVERSE_PROXY_H +#define __REVERSE_PROXY_H + +#include "idevice.h" +#include "libimobiledevice/reverse_proxy.h" +#include "service.h" + +struct reverse_proxy_client_private { + service_client_t parent; + char* label; + int type; + int protoversion; + THREAD_T th_ctrl; + uint16_t conn_port; + reverse_proxy_log_cb_t log_cb; + void* log_cb_user_data; + reverse_proxy_data_cb_t data_cb; + void* data_cb_user_data; + reverse_proxy_status_cb_t status_cb; + void* status_cb_user_data; +}; + +reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent); +reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received); +reverse_proxy_error_t reverse_proxy_receive_with_timeout(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received, unsigned int timeout); +reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist); +reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist); +reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms); + +#endif diff --git a/src/sbservices.c b/src/sbservices.c index 0591f4a..365e130 100644 --- a/src/sbservices.c +++ b/src/sbservices.c @@ -19,6 +19,9 @@ * 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 <unistd.h> @@ -76,7 +79,7 @@ static sbservices_error_t sbservices_error(property_list_service_error_t err) return SBSERVICES_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_client_new(idevice_t device, lockdownd_service_descriptor_t service, sbservices_client_t *client) +sbservices_error_t sbservices_client_new(idevice_t device, lockdownd_service_descriptor_t service, sbservices_client_t *client) { property_list_service_client_t plistclient = NULL; sbservices_error_t err = sbservices_error(property_list_service_client_new(device, service, &plistclient)); @@ -92,14 +95,14 @@ LIBIMOBILEDEVICE_API sbservices_error_t sbservices_client_new(idevice_t device, return SBSERVICES_E_SUCCESS; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_client_start_service(idevice_t device, sbservices_client_t * client, const char* label) +sbservices_error_t sbservices_client_start_service(idevice_t device, sbservices_client_t * client, const char* label) { sbservices_error_t err = SBSERVICES_E_UNKNOWN_ERROR; service_client_factory_start_service(device, SBSERVICES_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(sbservices_client_new), &err); return err; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_client_free(sbservices_client_t client) +sbservices_error_t sbservices_client_free(sbservices_client_t client) { if (!client) return SBSERVICES_E_INVALID_ARG; @@ -112,7 +115,7 @@ LIBIMOBILEDEVICE_API sbservices_error_t sbservices_client_free(sbservices_client return err; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version) +sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version) { if (!client || !client->parent || !state) return SBSERVICES_E_INVALID_ARG; @@ -152,7 +155,7 @@ leave_unlock: return res; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) +sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) { if (!client || !client->parent || !newstate) return SBSERVICES_E_INVALID_ARG; @@ -169,7 +172,10 @@ LIBIMOBILEDEVICE_API sbservices_error_t sbservices_set_icon_state(sbservices_cli if (res != SBSERVICES_E_SUCCESS) { debug_info("could not send plist, error %d", res); } - /* NO RESPONSE */ + + uint32_t bytes = 0; + service_receive_with_timeout(client->parent->parent, malloc(4), 4, &bytes, 2000); + debug_info("setIconState response: %u", bytes); if (dict) { plist_free(dict); @@ -178,7 +184,7 @@ LIBIMOBILEDEVICE_API sbservices_error_t sbservices_set_icon_state(sbservices_cli return res; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) +sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) { if (!client || !client->parent || !bundleId || !pngdata) return SBSERVICES_E_INVALID_ARG; @@ -215,7 +221,7 @@ leave_unlock: return res; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_get_interface_orientation(sbservices_client_t client, sbservices_interface_orientation_t* interface_orientation) +sbservices_error_t sbservices_get_interface_orientation(sbservices_client_t client, sbservices_interface_orientation_t* interface_orientation) { if (!client || !client->parent || !interface_orientation) return SBSERVICES_E_INVALID_ARG; @@ -253,7 +259,7 @@ leave_unlock: return res; } -LIBIMOBILEDEVICE_API sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize) +sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize) { if (!client || !client->parent || !pngdata) return SBSERVICES_E_INVALID_ARG; diff --git a/src/sbservices.h b/src/sbservices.h index 6c047ce..b67281e 100644 --- a/src/sbservices.h +++ b/src/sbservices.h @@ -22,9 +22,10 @@ #ifndef __SBSERVICES_H #define __SBSERVICES_H +#include "idevice.h" #include "libimobiledevice/sbservices.h" #include "property_list_service.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h> struct sbservices_client_private { property_list_service_client_t parent; diff --git a/src/screenshotr.c b/src/screenshotr.c index 5c4a53f..c3cc9ba 100644 --- a/src/screenshotr.c +++ b/src/screenshotr.c @@ -2,7 +2,7 @@ * screenshotr.c * com.apple.mobile.screenshotr service implementation. * - * Copyright (c) 2010 Nikias Bassen All Rights Reserved. + * Copyright (c) 2010-2019 Nikias Bassen, 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 @@ -19,6 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #include <plist/plist.h> #include <string.h> #include <stdlib.h> @@ -27,7 +30,7 @@ #include "device_link_service.h" #include "common/debug.h" -#define SCREENSHOTR_VERSION_INT1 300 +#define SCREENSHOTR_VERSION_INT1 400 #define SCREENSHOTR_VERSION_INT2 0 /** @@ -50,6 +53,10 @@ static screenshotr_error_t screenshotr_error(device_link_service_error_t err) return SCREENSHOTR_E_PLIST_ERROR; case DEVICE_LINK_SERVICE_E_MUX_ERROR: return SCREENSHOTR_E_MUX_ERROR; + case DEVICE_LINK_SERVICE_E_SSL_ERROR: + return SCREENSHOTR_E_SSL_ERROR; + case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT: + return SCREENSHOTR_E_RECEIVE_TIMEOUT; case DEVICE_LINK_SERVICE_E_BAD_VERSION: return SCREENSHOTR_E_BAD_VERSION; default: @@ -58,7 +65,7 @@ static screenshotr_error_t screenshotr_error(device_link_service_error_t err) return SCREENSHOTR_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API screenshotr_error_t screenshotr_client_new(idevice_t device, lockdownd_service_descriptor_t service, +screenshotr_error_t screenshotr_client_new(idevice_t device, lockdownd_service_descriptor_t service, screenshotr_client_t * client) { if (!device || !service || service->port == 0 || !client || *client) @@ -86,14 +93,14 @@ LIBIMOBILEDEVICE_API screenshotr_error_t screenshotr_client_new(idevice_t device return ret; } -LIBIMOBILEDEVICE_API screenshotr_error_t screenshotr_client_start_service(idevice_t device, screenshotr_client_t * client, const char* label) +screenshotr_error_t screenshotr_client_start_service(idevice_t device, screenshotr_client_t * client, const char* label) { screenshotr_error_t err = SCREENSHOTR_E_UNKNOWN_ERROR; service_client_factory_start_service(device, SCREENSHOTR_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(screenshotr_client_new), &err); return err; } -LIBIMOBILEDEVICE_API screenshotr_error_t screenshotr_client_free(screenshotr_client_t client) +screenshotr_error_t screenshotr_client_free(screenshotr_client_t client) { if (!client) return SCREENSHOTR_E_INVALID_ARG; @@ -103,7 +110,7 @@ LIBIMOBILEDEVICE_API screenshotr_error_t screenshotr_client_free(screenshotr_cli return err; } -LIBIMOBILEDEVICE_API screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, char **imgdata, uint64_t *imgsize) +screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, char **imgdata, uint64_t *imgsize) { if (!client || !client->parent || !imgdata) return SCREENSHOTR_E_INVALID_ARG; @@ -135,7 +142,7 @@ LIBIMOBILEDEVICE_API screenshotr_error_t screenshotr_take_screenshot(screenshotr plist_t node = plist_dict_get_item(dict, "MessageType"); char *strval = NULL; plist_get_string_val(node, &strval); - if (!strval || strcmp(strval, "ScreenShotReply")) { + if (!strval || strcmp(strval, "ScreenShotReply") != 0) { debug_info("invalid screenshot data received!"); res = SCREENSHOTR_E_PLIST_ERROR; goto leave; diff --git a/src/screenshotr.h b/src/screenshotr.h index 47d4e42..1319ec0 100644 --- a/src/screenshotr.h +++ b/src/screenshotr.h @@ -22,6 +22,7 @@ #ifndef __SCREENSHOTR_H #define __SCREENSHOTR_H +#include "idevice.h" #include "libimobiledevice/screenshotr.h" #include "device_link_service.h" diff --git a/src/service.c b/src/service.c index 2dc42b2..9474021 100644 --- a/src/service.c +++ b/src/service.c @@ -46,13 +46,17 @@ static service_error_t idevice_to_service_error(idevice_error_t err) return SERVICE_E_INVALID_ARG; case IDEVICE_E_SSL_ERROR: return SERVICE_E_SSL_ERROR; + case IDEVICE_E_NOT_ENOUGH_DATA: + return SERVICE_E_NOT_ENOUGH_DATA; + case IDEVICE_E_TIMEOUT: + return SERVICE_E_TIMEOUT; default: break; } return SERVICE_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API service_error_t service_client_new(idevice_t device, lockdownd_service_descriptor_t service, service_client_t *client) +service_error_t service_client_new(idevice_t device, lockdownd_service_descriptor_t service, service_client_t *client) { if (!device || !service || service->port == 0 || !client || *client) return SERVICE_E_INVALID_ARG; @@ -76,7 +80,7 @@ LIBIMOBILEDEVICE_API service_error_t service_client_new(idevice_t device, lockdo return SERVICE_E_SUCCESS; } -LIBIMOBILEDEVICE_API service_error_t service_client_factory_start_service(idevice_t device, const char* service_name, void **client, const char* label, int32_t (*constructor_func)(idevice_t, lockdownd_service_descriptor_t, void**), int32_t *error_code) +service_error_t service_client_factory_start_service(idevice_t device, const char* service_name, void **client, const char* label, int32_t (*constructor_func)(idevice_t, lockdownd_service_descriptor_t, void**), int32_t *error_code) { *client = NULL; @@ -87,11 +91,11 @@ LIBIMOBILEDEVICE_API service_error_t service_client_factory_start_service(idevic } lockdownd_service_descriptor_t service = NULL; - lockdownd_start_service(lckd, service_name, &service); + lockdownd_error_t lerr = lockdownd_start_service(lckd, service_name, &service); lockdownd_client_free(lckd); - if (!service || service->port == 0) { - debug_info("Could not start service %s!", service_name); + if (lerr != LOCKDOWN_E_SUCCESS) { + debug_info("Could not start service %s: %s", service_name, lockdownd_strerror(lerr)); return SERVICE_E_START_SERVICE_ERROR; } @@ -115,7 +119,7 @@ LIBIMOBILEDEVICE_API service_error_t service_client_factory_start_service(idevic return (ec == SERVICE_E_SUCCESS) ? SERVICE_E_SUCCESS : SERVICE_E_START_SERVICE_ERROR; } -LIBIMOBILEDEVICE_API service_error_t service_client_free(service_client_t client) +service_error_t service_client_free(service_client_t client) { if (!client) return SERVICE_E_INVALID_ARG; @@ -128,63 +132,76 @@ LIBIMOBILEDEVICE_API service_error_t service_client_free(service_client_t client return err; } -LIBIMOBILEDEVICE_API service_error_t service_send(service_client_t client, const char* data, uint32_t size, uint32_t *sent) +service_error_t service_send(service_client_t client, const char* data, uint32_t size, uint32_t *sent) { service_error_t res = SERVICE_E_UNKNOWN_ERROR; - int bytes = 0; + uint32_t bytes = 0; if (!client || (client && !client->connection) || !data || (size == 0)) { return SERVICE_E_INVALID_ARG; } debug_info("sending %d bytes", size); - res = idevice_to_service_error(idevice_connection_send(client->connection, data, size, (uint32_t*)&bytes)); - if (bytes <= 0) { + res = idevice_to_service_error(idevice_connection_send(client->connection, data, size, &bytes)); + if (res != SERVICE_E_SUCCESS) { debug_info("ERROR: sending to device failed."); } if (sent) { - *sent = (uint32_t)bytes; + *sent = bytes; } return res; } -LIBIMOBILEDEVICE_API service_error_t service_receive_with_timeout(service_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) +service_error_t service_receive_with_timeout(service_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) { service_error_t res = SERVICE_E_UNKNOWN_ERROR; - int bytes = 0; + uint32_t bytes = 0; if (!client || (client && !client->connection) || !data || (size == 0)) { return SERVICE_E_INVALID_ARG; } - res = idevice_to_service_error(idevice_connection_receive_timeout(client->connection, data, size, (uint32_t*)&bytes, timeout)); - if (bytes <= 0) { + res = idevice_to_service_error(idevice_connection_receive_timeout(client->connection, data, size, &bytes, timeout)); + if (res != SERVICE_E_SUCCESS && res != SERVICE_E_TIMEOUT) { debug_info("could not read data"); + return res; } if (received) { - *received = (uint32_t)bytes; + *received = bytes; } return res; } -LIBIMOBILEDEVICE_API service_error_t service_receive(service_client_t client, char* data, uint32_t size, uint32_t *received) +service_error_t service_receive(service_client_t client, char* data, uint32_t size, uint32_t *received) { - return service_receive_with_timeout(client, data, size, received, 10000); + return service_receive_with_timeout(client, data, size, received, 30000); } -LIBIMOBILEDEVICE_API service_error_t service_enable_ssl(service_client_t client) +service_error_t service_enable_ssl(service_client_t client) { if (!client || !client->connection) return SERVICE_E_INVALID_ARG; return idevice_to_service_error(idevice_connection_enable_ssl(client->connection)); } -LIBIMOBILEDEVICE_API service_error_t service_disable_ssl(service_client_t client) +service_error_t service_disable_ssl(service_client_t client) +{ + return service_disable_bypass_ssl(client, 0); +} + +service_error_t service_disable_bypass_ssl(service_client_t client, uint8_t sslBypass) { if (!client || !client->connection) return SERVICE_E_INVALID_ARG; - return idevice_to_service_error(idevice_connection_disable_ssl(client->connection)); + return idevice_to_service_error(idevice_connection_disable_bypass_ssl(client->connection, sslBypass)); } +service_error_t service_get_connection(service_client_t client, idevice_connection_t *connection) +{ + if (!client || !client->connection || !connection) + return SERVICE_E_INVALID_ARG; + *connection = client->connection; + return SERVICE_E_SUCCESS; +} diff --git a/src/service.h b/src/service.h index 3fc3077..071fe3f 100644 --- a/src/service.h +++ b/src/service.h @@ -21,9 +21,9 @@ #ifndef SERVICE_H #define SERVICE_H +#include "idevice.h" #include "libimobiledevice/service.h" #include "libimobiledevice/lockdown.h" -#include "idevice.h" struct service_client_private { idevice_connection_t connection; diff --git a/src/syslog_relay.c b/src/syslog_relay.c index 29f4de5..9f4296e 100644 --- a/src/syslog_relay.c +++ b/src/syslog_relay.c @@ -2,7 +2,8 @@ * syslog_relay.c * com.apple.syslog_relay service implementation. * - * Copyright (c) 2013 Martin Szulecki All Rights Reserved. + * Copyright (c) 2019-2020 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2013-2015 Martin Szulecki, 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 @@ -33,6 +34,7 @@ struct syslog_relay_worker_thread { syslog_relay_client_t client; syslog_relay_receive_cb_t cbfunc; void *user_data; + int is_raw; }; /** @@ -55,13 +57,17 @@ static syslog_relay_error_t syslog_relay_error(service_error_t err) return SYSLOG_RELAY_E_MUX_ERROR; case SERVICE_E_SSL_ERROR: return SYSLOG_RELAY_E_SSL_ERROR; + case SERVICE_E_NOT_ENOUGH_DATA: + return SYSLOG_RELAY_E_NOT_ENOUGH_DATA; + case SERVICE_E_TIMEOUT: + return SYSLOG_RELAY_E_TIMEOUT; default: break; } return SYSLOG_RELAY_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, syslog_relay_client_t * client) +syslog_relay_error_t syslog_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, syslog_relay_client_t * client) { *client = NULL; @@ -81,7 +87,7 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_client_new(idevice_t devi syslog_relay_client_t client_loc = (syslog_relay_client_t) malloc(sizeof(struct syslog_relay_client_private)); client_loc->parent = parent; - client_loc->worker = (thread_t)NULL; + client_loc->worker = THREAD_T_NULL; *client = client_loc; @@ -89,37 +95,30 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_client_new(idevice_t devi return 0; } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_client_start_service(idevice_t device, syslog_relay_client_t * client, const char* label) +syslog_relay_error_t syslog_relay_client_start_service(idevice_t device, syslog_relay_client_t * client, const char* label) { syslog_relay_error_t err = SYSLOG_RELAY_E_UNKNOWN_ERROR; service_client_factory_start_service(device, SYSLOG_RELAY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(syslog_relay_client_new), &err); return err; } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_client_free(syslog_relay_client_t client) +syslog_relay_error_t syslog_relay_client_free(syslog_relay_client_t client) { if (!client) return SYSLOG_RELAY_E_INVALID_ARG; - + syslog_relay_stop_capture(client); syslog_relay_error_t err = syslog_relay_error(service_client_free(client->parent)); - client->parent = NULL; - if (client->worker) { - debug_info("Joining syslog capture callback worker thread"); - thread_join(client->worker); - thread_free(client->worker); - client->worker = (thread_t)NULL; - } free(client); return err; } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_receive(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received) +syslog_relay_error_t syslog_relay_receive(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received) { return syslog_relay_receive_with_timeout(client, data, size, received, 1000); } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_receive_with_timeout(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) +syslog_relay_error_t syslog_relay_receive_with_timeout(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) { syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR; int bytes = 0; @@ -129,7 +128,7 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_receive_with_timeout(sysl } res = syslog_relay_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout)); - if (bytes <= 0) { + if (res != SYSLOG_RELAY_E_SUCCESS && res != SYSLOG_RELAY_E_TIMEOUT && res != SYSLOG_RELAY_E_NOT_ENOUGH_DATA) { debug_info("Could not read data, error %d", res); } if (received) { @@ -153,13 +152,16 @@ void *syslog_relay_worker(void *arg) char c; uint32_t bytes = 0; ret = syslog_relay_receive_with_timeout(srwt->client, &c, 1, &bytes, 100); - if ((bytes == 0) && (ret == SYSLOG_RELAY_E_SUCCESS)) { + if (ret == SYSLOG_RELAY_E_TIMEOUT || ret == SYSLOG_RELAY_E_NOT_ENOUGH_DATA || ((bytes == 0) && (ret == SYSLOG_RELAY_E_SUCCESS))) { continue; - } else if (ret < 0) { + } + if (ret < 0) { debug_info("Connection to syslog relay interrupted"); break; } - if(c != 0) { + if (srwt->is_raw) { + srwt->cbfunc(c, srwt->user_data); + } else if (c != 0) { srwt->cbfunc(c, srwt->user_data); } } @@ -173,7 +175,35 @@ void *syslog_relay_worker(void *arg) return NULL; } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_start_capture(syslog_relay_client_t client, syslog_relay_receive_cb_t callback, void* user_data) +syslog_relay_error_t syslog_relay_start_capture(syslog_relay_client_t client, syslog_relay_receive_cb_t callback, void* user_data) +{ + if (!client || !callback) + return SYSLOG_RELAY_E_INVALID_ARG; + + syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR; + + if (client->worker) { + debug_info("Another syslog capture thread appears to be running already."); + return res; + } + + /* start worker thread */ + struct syslog_relay_worker_thread *srwt = (struct syslog_relay_worker_thread*)malloc(sizeof(struct syslog_relay_worker_thread)); + if (srwt) { + srwt->client = client; + srwt->cbfunc = callback; + srwt->user_data = user_data; + srwt->is_raw = 0; + + if (thread_new(&client->worker, syslog_relay_worker, srwt) == 0) { + res = SYSLOG_RELAY_E_SUCCESS; + } + } + + return res; +} + +syslog_relay_error_t syslog_relay_start_capture_raw(syslog_relay_client_t client, syslog_relay_receive_cb_t callback, void* user_data) { if (!client || !callback) return SYSLOG_RELAY_E_INVALID_ARG; @@ -191,6 +221,7 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_start_capture(syslog_rela srwt->client = client; srwt->cbfunc = callback; srwt->user_data = user_data; + srwt->is_raw = 1; if (thread_new(&client->worker, syslog_relay_worker, srwt) == 0) { res = SYSLOG_RELAY_E_SUCCESS; @@ -200,7 +231,7 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_start_capture(syslog_rela return res; } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_stop_capture(syslog_relay_client_t client) +syslog_relay_error_t syslog_relay_stop_capture(syslog_relay_client_t client) { if (client->worker) { /* notify thread to finish */ @@ -209,9 +240,9 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_stop_capture(syslog_relay /* join thread to make it exit */ thread_join(client->worker); thread_free(client->worker); - client->worker = (thread_t)NULL; + client->worker = THREAD_T_NULL; client->parent = parent; } return SYSLOG_RELAY_E_SUCCESS; -}
\ No newline at end of file +} diff --git a/src/syslog_relay.h b/src/syslog_relay.h index cd45775..d5263e2 100644 --- a/src/syslog_relay.h +++ b/src/syslog_relay.h @@ -22,13 +22,14 @@ #ifndef _SYSLOG_RELAY_H #define _SYSLOG_RELAY_H +#include "idevice.h" #include "libimobiledevice/syslog_relay.h" #include "service.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h> struct syslog_relay_client_private { service_client_t parent; - thread_t worker; + THREAD_T worker; }; void *syslog_relay_worker(void *arg); diff --git a/src/webinspector.c b/src/webinspector.c index c81f4c7..f960fcc 100644 --- a/src/webinspector.c +++ b/src/webinspector.c @@ -52,13 +52,17 @@ static webinspector_error_t webinspector_error(property_list_service_error_t err return WEBINSPECTOR_E_MUX_ERROR; case PROPERTY_LIST_SERVICE_E_SSL_ERROR: return WEBINSPECTOR_E_SSL_ERROR; + case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: + return WEBINSPECTOR_E_RECEIVE_TIMEOUT; + case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA: + return WEBINSPECTOR_E_NOT_ENOUGH_DATA; default: break; } return WEBINSPECTOR_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_new(idevice_t device, lockdownd_service_descriptor_t service, webinspector_client_t * client) +webinspector_error_t webinspector_client_new(idevice_t device, lockdownd_service_descriptor_t service, webinspector_client_t * client) { *client = NULL; @@ -85,14 +89,14 @@ LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_new(idevice_t devi return 0; } -LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_start_service(idevice_t device, webinspector_client_t * client, const char* label) +webinspector_error_t webinspector_client_start_service(idevice_t device, webinspector_client_t * client, const char* label) { webinspector_error_t err = WEBINSPECTOR_E_UNKNOWN_ERROR; service_client_factory_start_service(device, WEBINSPECTOR_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(webinspector_client_new), &err); return err; } -LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_free(webinspector_client_t client) +webinspector_error_t webinspector_client_free(webinspector_client_t client) { if (!client) return WEBINSPECTOR_E_INVALID_ARG; @@ -103,7 +107,7 @@ LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_free(webinspector_ return err; } -LIBIMOBILEDEVICE_API webinspector_error_t webinspector_send(webinspector_client_t client, plist_t plist) +webinspector_error_t webinspector_send(webinspector_client_t client, plist_t plist) { webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR; @@ -160,12 +164,12 @@ LIBIMOBILEDEVICE_API webinspector_error_t webinspector_send(webinspector_client_ return res; } -LIBIMOBILEDEVICE_API webinspector_error_t webinspector_receive(webinspector_client_t client, plist_t * plist) +webinspector_error_t webinspector_receive(webinspector_client_t client, plist_t * plist) { return webinspector_receive_with_timeout(client, plist, 5000); } -LIBIMOBILEDEVICE_API webinspector_error_t webinspector_receive_with_timeout(webinspector_client_t client, plist_t * plist, uint32_t timeout_ms) +webinspector_error_t webinspector_receive_with_timeout(webinspector_client_t client, plist_t * plist, uint32_t timeout_ms) { webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR; plist_t message = NULL; diff --git a/src/webinspector.h b/src/webinspector.h index 67421bc..d249c58 100644 --- a/src/webinspector.h +++ b/src/webinspector.h @@ -22,6 +22,7 @@ #ifndef __WEBINSPECTOR_H #define __WEBINSPECTOR_H +#include "idevice.h" #include "libimobiledevice/webinspector.h" #include "property_list_service.h" |