diff options
Diffstat (limited to 'src')
61 files changed, 4679 insertions, 878 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index 1ef47fc..1ee9be8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,35 +1,41 @@  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) \ +	$(ssl_lib_CFLAGS) \ +	$(LFS_CFLAGS) \ +	$(PTHREAD_CFLAGS) \  	$(libusbmuxd_CFLAGS) \ -	$(libgnutls_CFLAGS) \ -	$(libtasn1_CFLAGS) \  	$(libplist_CFLAGS) \ -	$(LFS_CFLAGS) \ -	$(openssl_CFLAGS) \ -	$(PTHREAD_CFLAGS) +	$(limd_glue_CFLAGS)  AM_LDFLAGS = \ -	$(libgnutls_LIBS) \ -	$(libtasn1_LIBS) \ -	$(libplist_LIBS) \ +	$(ssl_lib_LIBS) \ +	$(PTHREAD_LIBS) \  	$(libusbmuxd_LIBS) \ -	$(libgcrypt_LIBS) \ -	$(openssl_LIBS) \ -	$(PTHREAD_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 \ @@ -50,7 +56,10 @@ libimobiledevice_1_0_la_SOURCES = \  	mobileactivation.c mobileactivation.h \  	preboard.c preboard.h  \  	companion_proxy.c companion_proxy.h \ -	syslog_relay.c syslog_relay.h +	reverse_proxy.c reverse_proxy.h \ +	syslog_relay.c syslog_relay.h \ +	ostrace.c ostrace.h \ +	bt_packet_logger.c bt_packet_logger.h  if WIN32  libimobiledevice_1_0_la_LDFLAGS += -avoid-version -static-libgcc @@ -26,11 +26,14 @@  #endif  #include <stdio.h>  #include <stdlib.h> -#include <unistd.h>  #include <string.h> -#include "afc.h" +#ifndef _MSC_VER +#include <unistd.h> +#endif +  #include "idevice.h" +#include "afc.h"  #include "common/debug.h"  #include "endianness.h" @@ -68,7 +71,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; @@ -94,7 +97,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_client_new_with_service_client(service_clie  	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; @@ -113,14 +116,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; +	int32_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; @@ -213,7 +216,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; @@ -223,18 +227,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 ")!");  	} @@ -249,15 +255,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); @@ -265,15 +270,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;  		} @@ -283,12 +290,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); @@ -296,16 +304,16 @@ 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);  	if (current_count > 256) {  		debug_info("packet data follows (256/%u)", current_count); -		debug_buffer(dump_here, 256); +		debug_buffer(buf, 256);  	} else {  		debug_info("packet data follows"); -		debug_buffer(dump_here, current_count); +		debug_buffer(buf, current_count);  	}  	/* check operation types */ @@ -316,7 +324,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) { @@ -330,11 +338,10 @@ 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 +#ifndef _WIN32  		fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1);  #endif @@ -342,9 +349,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; @@ -354,7 +361,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; @@ -395,6 +402,50 @@ static char **make_strings_list(char *tokens, uint32_t length)  	return list;  } +static plist_t *make_dictionary(char *tokens, size_t length) +{ +	size_t j = 0; +	plist_t dict = NULL; + +	if (!tokens || !length) +		return NULL; + +	dict = plist_new_dict(); + +	while (j < length) { +		size_t key_len = strnlen(tokens + j, length - j); +		if (j + key_len >= length) { +			plist_free(dict); +			return NULL; +		} +		char* key = tokens + j; +		j += key_len + 1; + +		if (j >= length) { +			plist_free(dict); +			return NULL; +		} + +		size_t val_len = strnlen(tokens + j, length - j); +		if (j + val_len >= length) { +			plist_free(dict); +			return NULL; +		} +		char* val = tokens + j; +		j += val_len + 1; + +		char* endp = NULL; +		unsigned long long u64val = strtoull(val, &endp, 10); +		if (endp && *endp == '\0') { +			plist_dict_set_item(dict, key, plist_new_uint(u64val)); +		} else { +			plist_dict_set_item(dict, key, plist_new_string(val)); +		} +	} + +	return dict; +} +  static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len)  {  	if (data_len > client->packet_extra) { @@ -410,7 +461,7 @@ static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len)  #define AFC_PACKET_DATA_PTR ((char*)client->afc_packet + sizeof(AFCPacket)) -LIBIMOBILEDEVICE_API afc_error_t afc_read_directory(afc_client_t client, const char *path, char ***directory_information) +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; @@ -454,7 +505,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; @@ -491,7 +542,41 @@ 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_plist(afc_client_t client, plist_t *device_information) +{ +	uint32_t bytes = 0; +	char *data = NULL; +	afc_error_t ret = AFC_E_UNKNOWN_ERROR; + +	if (!client || !device_information) +		return AFC_E_INVALID_ARG; + +	afc_lock(client); + +	/* Send the command */ +	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; +	} +	/* Receive the data */ +	ret = afc_receive_data(client, &data, &bytes); +	if (ret != AFC_E_SUCCESS) { +		if (data) +			free(data); +		afc_unlock(client); +		return ret; +	} +	/* Parse the data */ +	*device_information = make_dictionary(data, bytes); +	free(data); + +	afc_unlock(client); + +	return ret; +} + +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; @@ -518,7 +603,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; @@ -554,7 +639,7 @@ 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; @@ -590,7 +675,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; @@ -622,7 +707,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; @@ -640,8 +725,6 @@ LIBIMOBILEDEVICE_API afc_error_t afc_get_file_info(afc_client_t client, const ch  		return AFC_E_NO_MEM;  	} -	debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR); -  	/* Send command */  	memcpy(AFC_PACKET_DATA_PTR, path, data_len);  	ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); @@ -662,7 +745,45 @@ 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_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information) +{ +	char *received = NULL; +	uint32_t bytes = 0; +	afc_error_t ret = AFC_E_UNKNOWN_ERROR; + +	if (!client || !path || !file_information) +		return AFC_E_INVALID_ARG; + +	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 */ +	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; +	} + +	/* Receive data */ +	ret = afc_receive_data(client, &received, &bytes); +	if (received) { +		*file_information = make_dictionary(received, bytes); +		free(received); +	} + +	afc_unlock(client); + +	return ret; +} + +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; @@ -714,7 +835,7 @@ 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; @@ -748,28 +869,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; @@ -798,13 +920,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; @@ -835,7 +957,7 @@ 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 lockinfo { @@ -869,7 +991,7 @@ 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 seekinfo { @@ -903,7 +1025,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; @@ -938,7 +1060,7 @@ 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 truncinfo { @@ -970,7 +1092,7 @@ 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; @@ -1003,13 +1125,12 @@ 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;  	uint32_t bytes = 0; -	uint64_t type = htole64(linktype);  	afc_error_t ret = AFC_E_UNKNOWN_ERROR;  	size_t target_len = strlen(target); @@ -1024,7 +1145,7 @@ LIBIMOBILEDEVICE_API afc_error_t afc_make_link(afc_client_t client, afc_link_typ  		return AFC_E_NO_MEM;  	} -	debug_info("link type: %lld", type); +	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); @@ -1045,7 +1166,7 @@ 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; @@ -1078,7 +1199,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; @@ -1110,7 +1231,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; @@ -1124,3 +1245,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) 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 index f09b416..421fa9a 100644 --- a/src/companion_proxy.c +++ b/src/companion_proxy.c @@ -29,7 +29,6 @@  #include "companion_proxy.h"  #include "lockdown.h"  #include "common/debug.h" -#include "common/thread.h"  /**   * Convert a property_list_service_error_t value to a companion_proxy_error_t value. @@ -63,7 +62,7 @@ static companion_proxy_error_t companion_proxy_error(property_list_service_error  	return COMPANION_PROXY_E_UNKNOWN_ERROR;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, companion_proxy_client_t * client) +companion_proxy_error_t companion_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, companion_proxy_client_t * client)  {  	*client = NULL; @@ -91,14 +90,14 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_client_new(idevice_  	return COMPANION_PROXY_E_SUCCESS;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_client_start_service(idevice_t device, companion_proxy_client_t * client, const char* label) +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;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_client_free(companion_proxy_client_t client) +companion_proxy_error_t companion_proxy_client_free(companion_proxy_client_t client)  {  	if (!client)  		return COMPANION_PROXY_E_INVALID_ARG; @@ -117,7 +116,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_client_free(compani  	return err;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_send(companion_proxy_client_t client, plist_t plist) +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; @@ -130,7 +129,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_send(companion_prox  	return res;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_receive(companion_proxy_client_t client, plist_t * plist) +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; @@ -144,7 +143,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_receive(companion_p  	return res;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_get_device_registry(companion_proxy_client_t client, plist_t* paired_devices) +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; @@ -227,7 +226,7 @@ static void* companion_proxy_event_thread(void* arg)  	return NULL;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_start_listening_for_devices(companion_proxy_client_t client, companion_proxy_device_event_cb_t callback, void* userdata) +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; @@ -253,7 +252,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_start_listening_for  	return res;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_stop_listening_for_devices(companion_proxy_client_t client) +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; @@ -267,7 +266,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_stop_listening_for_  	return COMPANION_PROXY_E_SUCCESS;  } -LIBIMOBILEDEVICE_API 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) +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; @@ -311,7 +310,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_get_value_from_regi  	return res;  } -LIBIMOBILEDEVICE_API 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) +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; @@ -326,7 +325,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_start_forwarding_se  	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); +		plist_dict_merge(&dict, options);  	}  	companion_proxy_error_t res = companion_proxy_send(client, dict); @@ -354,7 +353,7 @@ LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_start_forwarding_se  	return res;  } -LIBIMOBILEDEVICE_API companion_proxy_error_t companion_proxy_stop_forwarding_service_port(companion_proxy_client_t client, uint16_t remote_port) +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; diff --git a/src/companion_proxy.h b/src/companion_proxy.h index 0314b67..e36932a 100644 --- a/src/companion_proxy.h +++ b/src/companion_proxy.h @@ -22,9 +22,10 @@  #ifndef __COMPANION_PROXY_H  #define __COMPANION_PROXY_H +#include "idevice.h"  #include "libimobiledevice/companion_proxy.h"  #include "property_list_service.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h>  struct companion_proxy_client_private {  	property_list_service_client_t parent; diff --git a/src/debugserver.c b/src/debugserver.c index cc0e73f..74ade8a 100644 --- a/src/debugserver.c +++ b/src/debugserver.c @@ -29,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"  /** @@ -63,7 +64,7 @@ static debugserver_error_t debugserver_error(service_error_t err)  	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; @@ -88,6 +89,8 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device  	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; @@ -95,7 +98,7 @@ 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_SECURE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); @@ -106,7 +109,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_start_service(idevic  	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; @@ -118,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; @@ -139,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; @@ -149,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)); @@ -189,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; @@ -231,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)  { @@ -267,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; @@ -283,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; @@ -342,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; @@ -354,130 +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, size_t* response_size) +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 = 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 = malloc(1024); -			buffer_capacity = 1024; -			strcpy(buffer, 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); - -	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) { -				strcpy(buffer, command_prefix); -			} else { -				buffer = malloc(1024); -				buffer_capacity = 1024; -				strcpy(buffer, command_prefix); -				buffer_size += sizeof(char); -			} +	debug_info("skip_prefix: %d", skip_prefix); + +	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); - -	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"); +	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--; -			} -			if (buffer_size + 1 >= buffer_capacity) { -				char* newbuffer = realloc(buffer, buffer_capacity+1024); -				if (!newbuffer) { -					return DEBUGSERVER_E_UNKNOWN_ERROR; -				} -				buffer = newbuffer; -				buffer[buffer_capacity] = '\0'; -				buffer_capacity += 1024; -			} -			strcat(buffer, data); -			buffer_size += sizeof(char); +	while ((checksum_length > 0)) { +		res = debugserver_client_receive_internal_char(client, &data); +		if (res != DEBUGSERVER_E_SUCCESS) { +			goto cleanup;  		} -		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); +		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;  			} +			buffer = newbuffer; +			buffer_capacity += 1024; +		} +		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);  	} @@ -485,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, size_t* response_size) +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; @@ -547,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; @@ -566,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++;  	} @@ -597,10 +613,14 @@ 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; 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 6daf84d..66c2461 100644 --- a/src/device_link_service.c +++ b/src/device_link_service.c @@ -83,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;  	} @@ -184,7 +184,7 @@ device_link_service_error_t device_link_service_version_exchange(device_link_ser  		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; @@ -239,7 +239,7 @@ device_link_service_error_t device_link_service_version_exchange(device_link_ser  		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; @@ -403,7 +403,7 @@ device_link_service_error_t device_link_service_receive_process_message(device_l  	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; diff --git a/src/device_link_service.h b/src/device_link_service.h index eae912a..0255b21 100644 --- a/src/device_link_service.h +++ b/src/device_link_service.h @@ -22,6 +22,7 @@  #ifndef __DEVICE_LINK_SERVICE_H  #define __DEVICE_LINK_SERVICE_H +#include "idevice.h"  #include "property_list_service.h"  /* Error Codes */ diff --git a/src/diagnostics_relay.c b/src/diagnostics_relay.c index b6cf4d9..6ee3150 100644 --- a/src/diagnostics_relay.c +++ b/src/diagnostics_relay.c @@ -73,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; @@ -93,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; @@ -167,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; @@ -201,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; @@ -277,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, diagnostics_relay_action_t 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, diagnostics_relay_action_t 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; @@ -299,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) { @@ -328,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; @@ -341,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) { @@ -370,7 +376,7 @@ 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* entry_name, const char* entry_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 || (entry_name == NULL && entry_class == NULL) || result == NULL)  		return DIAGNOSTICS_RELAY_E_INVALID_ARG; @@ -386,6 +392,9 @@ LIBIMOBILEDEVICE_API diagnostics_relay_error_t diagnostics_relay_query_ioregistr  	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) { @@ -415,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 e304d31..fbe7cbf 100644 --- a/src/file_relay.c +++ b/src/file_relay.c @@ -28,7 +28,7 @@  #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; @@ -48,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; @@ -67,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; @@ -143,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;  	} @@ -159,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 9a527cc..3945d73 100644 --- a/src/heartbeat.c +++ b/src/heartbeat.c @@ -62,7 +62,7 @@ static heartbeat_error_t heartbeat_error(property_list_service_error_t err)  	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; @@ -89,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; @@ -107,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; @@ -122,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 ac92130..06068c6 100644 --- a/src/house_arrest.c +++ b/src/house_arrest.c @@ -24,7 +24,11 @@  #endif  #include <string.h>  #include <stdlib.h> + +#ifndef _MSC_VER  #include <unistd.h> +#endif +  #include <plist/plist.h>  #include "house_arrest.h" @@ -58,7 +62,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)); @@ -74,14 +78,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; @@ -96,7 +100,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; @@ -112,7 +116,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; @@ -132,7 +136,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; @@ -150,7 +154,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 c71c49b..0af27fd 100644 --- a/src/idevice.c +++ b/src/idevice.c @@ -2,7 +2,7 @@   * idevice.c   * Device discovery and communication interface.   * - * Copyright (c) 2009-2019 Nikias Bassen. All Rights Reserved. + * Copyright (c) 2009-2021 Nikias Bassen. All Rights Reserved.   * Copyright (c) 2014 Martin Szulecki All Rights Reserved.   * Copyright (c) 2008 Zach C. All Rights Reserved.   * @@ -30,29 +30,49 @@  #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/socket.h" -#include "common/thread.h"  #include "common/debug.h" -#ifdef WIN32 -#include <windows.h> +#ifndef ECONNREFUSED +#define ECONNREFUSED 107  #endif -  #ifndef ETIMEDOUT  #define ETIMEDOUT 138  #endif +  #ifdef HAVE_OPENSSL  #if OPENSSL_VERSION_NUMBER < 0x10100000L || \ @@ -104,34 +124,36 @@ static void id_function(CRYPTO_THREADID *thread)  #endif  #endif /* HAVE_OPENSSL */ -static void internal_idevice_init(void) -{ -#ifdef HAVE_OPENSSL -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) -	int i; -	SSL_library_init(); - -	mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); -	if (!mutex_buf) -		return; -	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 +// Reference: https://stackoverflow.com/a/2390626/1806760 +// Initializer/finalizer sample for MSVC and GCC/Clang. +// 2010-2016 Joe Lowe. Released into the public domain. + +#ifdef __cplusplus +    #define INITIALIZER(f) \ +        static void f(void); \ +        struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ +        static void f(void) +#elif defined(_MSC_VER) +    #pragma section(".CRT$XCU",read) +    #define INITIALIZER2_(f,p) \ +        static void f(void); \ +        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ +        __pragma(comment(linker,"/include:" p #f "_")) \ +        static void f(void) +    #ifdef _WIN64 +        #define INITIALIZER(f) INITIALIZER2_(f,"") +    #else +        #define INITIALIZER(f) INITIALIZER2_(f,"_") +    #endif  #else -	gnutls_global_init(); +    #define INITIALIZER(f) \ +        static void f(void) __attribute__((__constructor__)); \ +        static void f(void)  #endif -}  static void internal_idevice_deinit(void)  { -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL)  #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)  	int i;  	if (mutex_buf) { @@ -152,45 +174,60 @@ 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 -BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) +INITIALIZER(internal_idevice_init)  { -	switch (dwReason) { -	case DLL_PROCESS_ATTACH: -		thread_once(&init_once,	internal_idevice_init); -		break; -	case DLL_PROCESS_DETACH: -		thread_once(&deinit_once, internal_idevice_deinit); -		break; -	default: -		break; -	} -	return 1; -} +#if defined(HAVE_OPENSSL) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +	int i; +	SSL_library_init(); + +	mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); +	if (!mutex_buf) +		return; +	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 -static void __attribute__((constructor)) libimobiledevice_initialize(void) -{ -	thread_once(&init_once, internal_idevice_init); +	CRYPTO_THREADID_set_callback(id_function); +#endif +	CRYPTO_set_locking_callback(locking_function); +#endif +#elif defined(HAVE_GNUTLS) +	gnutls_global_init(); +#elif defined(HAVE_MBEDTLS) +	// NO-OP +#endif +	atexit(internal_idevice_deinit);  } -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; @@ -204,35 +241,68 @@ static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data)  		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_extended(idevice_info_t **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; @@ -256,7 +326,21 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_device_list_extended(idevice_in  			newlist[newcount]->conn_data = NULL;  		} else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) {  			newlist[newcount]->conn_type = CONNECTION_NETWORK; -			size_t addrlen = dev_list[i].conn_data[0]; +			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);  		} @@ -273,7 +357,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_get_device_list_extended(idevice_in  	return IDEVICE_E_SUCCESS;  } -LIBIMOBILEDEVICE_API idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices) +idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices)  {  	if (devices) {  		int i = 0; @@ -288,7 +372,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_device_list_extended_free(idevice_i  	return IDEVICE_E_SUCCESS;  } -LIBIMOBILEDEVICE_API idevice_error_t idevice_get_device_list(char ***devices, int *count) +idevice_error_t idevice_get_device_list(char ***devices, int *count)  {  	usbmuxd_device_info_t *dev_list; @@ -320,7 +404,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; @@ -333,7 +417,7 @@ 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);  } @@ -350,6 +434,7 @@ static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev)  	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; @@ -357,9 +442,25 @@ static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev)  		break;  	case CONNECTION_TYPE_NETWORK:  		device->conn_type = CONNECTION_NETWORK; -		size_t len = ((uint8_t*)muxdev->conn_data)[0]; -		device->conn_data = malloc(len); -		memcpy(device->conn_data, muxdev->conn_data, len); +		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; @@ -369,7 +470,7 @@ static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev)  	return device;  } -LIBIMOBILEDEVICE_API idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options) +idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options)  {  	usbmuxd_device_info_t muxdev;  	int usbmux_options = 0; @@ -393,12 +494,12 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_new_with_options(idevice_t * device  	return IDEVICE_E_NO_DEVICE;  } -LIBIMOBILEDEVICE_API idevice_error_t idevice_new(idevice_t * device, const char *udid) +idevice_error_t idevice_new(idevice_t * device, const char *udid)  {  	return idevice_new_with_options(device, udid, 0);  } -LIBIMOBILEDEVICE_API idevice_error_t idevice_free(idevice_t device) +idevice_error_t idevice_free(idevice_t device)  {  	if (!device)  		return IDEVICE_E_INVALID_ARG; @@ -415,7 +516,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; @@ -424,38 +525,38 @@ 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->data = (void*)(uintptr_t)sfd;  		new_connection->ssl_data = NULL;  		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 if (device->conn_type == CONNECTION_NETWORK) { -		struct sockaddr_storage saddr_storage; -		struct sockaddr* saddr = (struct sockaddr*)&saddr_storage; - -		/* FIXME: Improve handling of this platform/host dependent connection data */ -		if (((char*)device->conn_data)[1] == 0x02) { // AF_INET -			saddr->sa_family = AF_INET; -			memcpy(&saddr->sa_data[0], (char*)device->conn_data + 2, 14); -		} -		else if (((char*)device->conn_data)[1] == 0x1E) { // AF_INET6 (bsd) +	} +	if (device->conn_type == CONNECTION_NETWORK) { +		struct sockaddr* saddr = (struct sockaddr*)(device->conn_data); +		switch (saddr->sa_family) { +			case AF_INET:  #ifdef AF_INET6 -			saddr->sa_family = AF_INET6; -			/* copy the address and the host dependent scope id */ -			memcpy(&saddr->sa_data[0], (char*)device->conn_data + 2, 26); -#else -			debug_info("ERROR: Got an IPv6 address but this system doesn't support IPv6"); -			return IDEVICE_E_UNKNOWN_ERROR; +			case AF_INET6:  #endif -		} -		else { -			debug_info("Unsupported address family 0x%02x", ((char*)device->conn_data)[1]); -			return IDEVICE_E_UNKNOWN_ERROR; +				break; +			default: +				debug_info("Unsupported address family 0x%02x", saddr->sa_family); +				return IDEVICE_E_UNKNOWN_ERROR;  		}  		char addrtxt[48]; @@ -469,27 +570,34 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connect(idevice_t device, uint16_t  		int sfd = socket_connect_addr(saddr, port);  		if (sfd < 0) { -			debug_info("ERROR: Connecting to network device failed: %d (%s)", errno, strerror(errno)); +			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->data = (void*)(uintptr_t)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; -	} else { -		debug_info("Unknown connection type %d", device->conn_type);  	} +	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; @@ -500,11 +608,11 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_disconnect(idevice_connection_t con  	}  	idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR;  	if (connection->type == CONNECTION_USBMUXD) { -		usbmuxd_disconnect((int)(long)connection->data); +		usbmuxd_disconnect((int)(uintptr_t)connection->data);  		connection->data = NULL;  		result = IDEVICE_E_SUCCESS;  	} else if (connection->type == CONNECTION_NETWORK) { -		socket_close((int)(long)connection->data); +		socket_close((int)(uintptr_t)connection->data);  		connection->data = NULL;  		result = IDEVICE_E_SUCCESS;  	} else { @@ -529,44 +637,44 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection,  	if (connection->type == CONNECTION_USBMUXD) {  		int res;  		do { -			res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); +			res = usbmuxd_send((int)(uintptr_t)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 if (connection->type == CONNECTION_NETWORK) { -		int s = socket_send((int)(long)connection->data, (void*)data, len); +	} +	if (connection->type == CONNECTION_NETWORK) { +		int s = socket_send((int)(uintptr_t)connection->data, (void*)data, len);  		if (s < 0) {  			*sent_bytes = 0;  			return IDEVICE_E_UNKNOWN_ERROR;  		}  		*sent_bytes = s;  		return IDEVICE_E_SUCCESS; -	} else { -		debug_info("Unknown connection type %d", connection->type);  	} + +	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) { +		connection->status = IDEVICE_E_SUCCESS;  		uint32_t sent = 0;  		while (sent < len) { -#ifdef HAVE_OPENSSL -			int c = socket_check_fd((int)(long)connection->data, FDM_WRITE, 100); -			if (c == 0 || c == -ETIMEDOUT || c == -EAGAIN) { -				continue; -			} else if (c < 0) { -				break; -			} +#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); @@ -575,8 +683,10 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_send(idevice_connection_  				}  				break;  			} -#else +#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 (s < 0) {  				break; @@ -586,36 +696,42 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_send(idevice_connection_  		debug_info("SSL_write %d, sent %d", len, sent);  		if (sent < len) {  			*sent_bytes = 0; -			return IDEVICE_E_SSL_ERROR; +			return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status;  		}  		*sent_bytes = sent;  		return IDEVICE_E_SUCCESS; -	} else { -		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 = 0; -			return IDEVICE_E_NOT_ENOUGH_DATA; +	} +	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; -		return IDEVICE_E_SUCCESS; +		if (sent == 0) { +			return IDEVICE_E_UNKNOWN_ERROR; +		} +		return IDEVICE_E_NOT_ENOUGH_DATA;  	} +	*sent_bytes = sent; +	return IDEVICE_E_SUCCESS;  } -static idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received) +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: -				debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error)); +				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; @@ -623,7 +739,6 @@ static idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len  				return IDEVICE_E_UNKNOWN_ERROR;  		}  	} -  	return IDEVICE_E_SUCCESS;  } @@ -638,57 +753,52 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t  	}  	if (connection->type == CONNECTION_USBMUXD) { -		int conn_error = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); +		int conn_error = usbmuxd_recv_timeout((int)(uintptr_t)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 error; -	} else if (connection->type == CONNECTION_NETWORK) { -		int res = socket_receive_timeout((int)(long)connection->data, data, len, 0, timeout); -		if (res < 0) { -			debug_info("ERROR: socket_receive_timeout failed: %d (%s)", res, strerror(-res)); -			return (res == -EAGAIN ? IDEVICE_E_NOT_ENOUGH_DATA : IDEVICE_E_UNKNOWN_ERROR); +	} +	if (connection->type == CONNECTION_NETWORK) { +		int res = socket_receive_timeout((int)(uintptr_t)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));  		} -		*recv_bytes = (uint32_t)res; -		return IDEVICE_E_SUCCESS; -	} else { -		debug_info("Unknown connection type %d", connection->type); +		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) || len == 0) { +	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; -		int do_select = 1; -		while (received < len) { -#ifdef HAVE_OPENSSL -			do_select = (SSL_pending(connection->ssl_data->session) == 0); -#endif -			if (do_select) { -				int conn_error = socket_check_fd((int)(long)connection->data, FDM_READ, timeout); -				idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, received); - -				switch (error) { -					case IDEVICE_E_SUCCESS: -						break; -					case IDEVICE_E_UNKNOWN_ERROR: -					default: -						debug_info("ERROR: socket_check_fd returned %d (%s)", conn_error, strerror(-conn_error)); -						return error; -				} -			} +		if (connection->ssl_recv_timeout != (unsigned int)-1) { +			debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); +		} -#ifdef HAVE_OPENSSL +		// 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) { +#if defined(HAVE_OPENSSL)  			int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received);  			if (r > 0) {  				received += r; @@ -696,23 +806,35 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_receive_timeout(idevice_  				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;  			} -#else +#elif defined(HAVE_GNUTLS)  			ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received);  			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 < len) { -			*recv_bytes = 0; -			return IDEVICE_E_SSL_ERROR; +			*recv_bytes = received; +			return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status;  		}  		*recv_bytes = received; @@ -731,38 +853,49 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti  	}  	if (connection->type == CONNECTION_USBMUXD) { -		int res = usbmuxd_recv((int)(long)connection->data, data, len, recv_bytes); +		int res = usbmuxd_recv((int)(uintptr_t)connection->data, data, len, recv_bytes);  		if (res < 0) {  			debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res));  			return IDEVICE_E_UNKNOWN_ERROR;  		}  		return IDEVICE_E_SUCCESS; -	} else if (connection->type == CONNECTION_NETWORK) { -		int res = socket_receive((int)(long)connection->data, data, len); +	} +	if (connection->type == CONNECTION_NETWORK) { +		int res = socket_receive((int)(uintptr_t)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; -	} else { -		debug_info("Unknown connection type %d", connection->type);  	} + +	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; @@ -774,26 +907,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 if (connection->type == CONNECTION_NETWORK) { -		*fd = (int)(long)connection->data; -		result = IDEVICE_E_SUCCESS; -	} else { -		debug_info("Unknown connection type %d", connection->type); +		*fd = (int)(uintptr_t)connection->data; +		return IDEVICE_E_SUCCESS;  	} -	return result; +	if (connection->type == CONNECTION_NETWORK) { +		*fd = (int)(uintptr_t)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; @@ -802,78 +935,94 @@ 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 +unsigned int idevice_get_device_version(idevice_t device) +{ +	if (!device) { +		return 0; +	} +	if (!device->version) { +		lockdownd_client_t lockdown = NULL; +		lockdownd_client_new(device, &lockdown, NULL); +		// we don't handle any errors here. We should have the product version cached now. +		lockdownd_client_free(lockdown); +	} +	return device->version; +} + +#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. @@ -883,14 +1032,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);  	} @@ -909,10 +1058,65 @@ 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; +#endif +	switch (oper) { +	case (BIO_CB_READ|BIO_CB_RETURN): +		if (argp) { +			bytes = internal_ssl_read(conn, (char *)argp, len); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +			*processed = (size_t)(bytes < 0) ? 0 : bytes; +#endif +			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); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +		*processed = (size_t)(bytes < 0) ? 0 : bytes; +#endif +		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; @@ -947,7 +1151,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.   */ @@ -978,9 +1182,26 @@ 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; @@ -988,13 +1209,13 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne  	idevice_error_t ret = IDEVICE_E_SSL_ERROR;  	plist_t pair_record = NULL; -	userpref_read_pair_record(connection->device->udid, &pair_record); -	if (!pair_record) { -		debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s.", connection->device->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 }; @@ -1004,12 +1225,11 @@ 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(TLS_method());  	if (ssl_ctx == NULL) { @@ -1018,32 +1238,62 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne  		return ret;  	} -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#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)) { +	if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) {  #ifdef SSL_OP_NO_TLSv1_1 -		long opts = SSL_CTX_get_options(ssl_ctx); -		opts |= SSL_OP_NO_TLSv1_1; +		SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); +#endif  #ifdef SSL_OP_NO_TLSv1_2 -		opts |= SSL_OP_NO_TLSv1_2; +		SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2);  #endif  #ifdef SSL_OP_NO_TLSv1_3 -		opts |= SSL_OP_NO_TLSv1_3; -#endif -		SSL_CTX_set_options(ssl_ctx, opts); +		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)) { +	if (connection->device->version < IDEVICE_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; @@ -1056,6 +1306,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); @@ -1064,6 +1324,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); @@ -1084,7 +1345,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne  		if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) {  			break;  		} -#ifdef WIN32 +#ifdef _WIN32  		Sleep(100);  #else  		struct timespec ts = { 0, 100000000 }; @@ -1105,7 +1366,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne  	}  	/* 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... */ @@ -1163,16 +1424,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);  } -LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass) +idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass)  {  	if (!connection)  		return IDEVICE_E_INVALID_ARG; @@ -1184,7 +1521,7 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_disable_bypass_ssl(idevi  	// some services require plain text communication after SSL handshake  	// sending out SSL_shutdown will cause bytes  	if (!sslBypass) { -#ifdef HAVE_OPENSSL +#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) { @@ -1193,14 +1530,16 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_disable_bypass_ssl(idevi  				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\n", ssl_error); +					debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error);  				}  			}  		} -#else +#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  	} @@ -1212,3 +1551,28 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_disable_bypass_ssl(idevi  	return IDEVICE_E_SUCCESS;  } + +const char* idevice_strerror(idevice_error_t err) +{ +	switch (err) { +		case IDEVICE_E_SUCCESS: +			return "Success"; +		case IDEVICE_E_INVALID_ARG: +			return "Invalid argument"; +		case IDEVICE_E_UNKNOWN_ERROR: +			return "Unknown Error"; +		case IDEVICE_E_NO_DEVICE: +			return "No device"; +		case IDEVICE_E_NOT_ENOUGH_DATA: +			return "Not enough data"; +		case IDEVICE_E_CONNREFUSED: +			return "Connection refused"; +		case IDEVICE_E_SSL_ERROR: +			return "SSL error"; +		case IDEVICE_E_TIMEOUT: +			return "Timeout"; +		default: +			break; +	} +	return "Unknown Error"; +} diff --git a/src/idevice.h b/src/idevice.h index 8709c9a..e05338e 100644 --- a/src/idevice.h +++ b/src/idevice.h @@ -26,39 +26,57 @@  #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" -#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; @@ -68,6 +86,8 @@ struct idevice_connection_private {  	enum idevice_connection_type type;  	void *data;  	ssl_data_t ssl_data; +	unsigned int ssl_recv_timeout; +	idevice_error_t status;  };  struct idevice_private { @@ -76,6 +96,7 @@ struct idevice_private {  	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 9a6f1ab..bb6ef01 100644 --- a/src/installation_proxy.c +++ b/src/installation_proxy.c @@ -26,7 +26,11 @@  #include <string.h>  #include <stdlib.h>  #include <inttypes.h> + +#ifndef _MSC_VER  #include <unistd.h> +#endif +  #include <plist/plist.h>  #include "installation_proxy.h" @@ -232,7 +236,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)); @@ -249,14 +253,14 @@ LIBIMOBILEDEVICE_API instproxy_error_t instproxy_client_new(idevice_t device, lo  	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; +	int32_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; @@ -282,9 +286,6 @@ 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 occurred. @@ -367,17 +368,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); @@ -531,7 +529,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; @@ -572,7 +570,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; @@ -609,7 +607,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; @@ -656,7 +654,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; @@ -666,14 +664,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; @@ -683,14 +681,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; @@ -700,14 +698,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; @@ -723,7 +721,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; @@ -733,14 +731,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; @@ -750,14 +748,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; @@ -767,14 +765,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; @@ -811,7 +809,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; @@ -849,7 +847,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"); @@ -861,7 +859,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) { @@ -873,7 +871,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; @@ -910,7 +908,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"); @@ -922,12 +920,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; @@ -940,7 +938,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); @@ -961,7 +959,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; @@ -982,16 +980,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; @@ -1004,7 +1002,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); @@ -1015,7 +1013,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 66dd5d0..5bdbb71 100644 --- a/src/installation_proxy.h +++ b/src/installation_proxy.h @@ -23,9 +23,10 @@  #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; diff --git a/src/libimobiledevice-1.0.pc.in b/src/libimobiledevice-1.0.pc.in index c37654b..f00c392 100644 --- a/src/libimobiledevice-1.0.pc.in +++ b/src/libimobiledevice-1.0.pc.in @@ -9,4 +9,4 @@ Version: @PACKAGE_VERSION@  Libs: -L${libdir} -limobiledevice-1.0  Cflags: -I${includedir}  Requires: libplist-2.0 >= @LIBPLIST_VERSION@ -Requires.private: libusbmuxd-2.0 >= @LIBUSBMUXD_VERSION@ @ssl_requires@ +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..c457cb2 --- /dev/null +++ b/src/lockdown-cu.c @@ -0,0 +1,1197 @@ +/* + * 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> + +#ifndef _MSC_VER +#include <unistd.h> +#endif + +#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 = IDEVICE_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[] = "sendone01234"; // guaranteed to be random by fair dice troll +        if (client->device->version >= IDEVICE_DEVICE_VERSION(11,2,0)) { +#if defined(HAVE_OPENSSL) +		RAND_bytes(cu_nonce, sizeof(cu_nonce)-1); +#elif defined(HAVE_GCRYPT) +		gcry_create_nonce(cu_nonce, sizeof(cu_nonce)-1); +#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, client->device->version); + +	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 49f757c..32389c9 100644 --- a/src/lockdown.c +++ b/src/lockdown.c @@ -32,18 +32,22 @@  #define __USE_GNU 1  #include <stdio.h>  #include <ctype.h> + +#ifndef _MSC_VER  #include <unistd.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 +#ifdef _WIN32  #include <windows.h>  #define sleep(x) Sleep(x*1000)  #endif @@ -152,7 +156,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; @@ -163,53 +167,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 occurred"); -				} +	/* 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; @@ -219,9 +210,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; @@ -242,7 +230,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; @@ -311,12 +299,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; @@ -324,7 +313,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; @@ -340,7 +329,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) @@ -350,7 +339,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; @@ -358,7 +347,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; @@ -366,7 +355,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; @@ -411,7 +400,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; @@ -465,7 +454,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; @@ -513,7 +502,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; @@ -560,7 +549,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; @@ -606,7 +595,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; @@ -623,7 +612,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; @@ -632,6 +621,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new(idevice_t device, lo  		.port = 0xf27e,  		.ssl_enabled = 0  	}; +	char *type = NULL;  	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) { @@ -643,21 +633,70 @@ 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; +	int is_lockdownd = 0; +	if (lockdownd_query_type(client_loc, &type) != LOCKDOWN_E_SUCCESS) { +		debug_info("QueryType failed in the lockdownd client."); +	} else if (!strcmp("com.apple.mobile.lockdown", type)) { +		is_lockdownd = 1; +	} else { +		debug_info("QueryType request returned \"%s\"", type); +	} +	free(type); +  	*client = client_loc; +	if (is_lockdownd && device->version == 0) { +		plist_t p_version = NULL; +		if (lockdownd_get_value(client_loc, 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) { +				device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]); +			} +			free(s_version); +		} +		plist_free(p_version); +	} +	if (is_lockdownd && 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); +	} +  	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; @@ -666,7 +705,6 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevi  	lockdownd_client_t client_loc = NULL;  	plist_t pair_record = NULL;  	char *host_id = NULL; -	char *type = NULL;  	ret = lockdownd_client_new(device, &client_loc, label);  	if (LOCKDOWN_E_SUCCESS != ret) { @@ -675,29 +713,12 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_client_new_with_handshake(idevi  	}  	/* perform handshake */ -	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)) { -		debug_info("Warning QueryType request returned \"%s\".", type); -	} -	free(type); - -	if (device->version == 0) { -		plist_t p_version = NULL; -		if (lockdownd_get_value(client_loc, 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) { -				device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); -			} -			free(s_version); -		} -		plist_free(p_version); +	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;  	} - -	userpref_read_pair_record(client_loc->udid, &pair_record);  	if (pair_record) {  		pair_record_get_host_id(pair_record, &host_id);  	} @@ -707,13 +728,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 < DEVICE_VERSION(7,0,0)) { +	if (device->version < IDEVICE_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); @@ -730,7 +753,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); @@ -806,7 +842,7 @@ static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t  	/* generate keys and certificates into pair record */  	userpref_error_t uret = USERPREF_E_SUCCESS; -	uret = pair_record_generate_keys_and_certs(*pair_record, public_key); +	uret = pair_record_generate_keys_and_certs(*pair_record, public_key, client->device->version);  	switch(uret) {  		case USERPREF_E_INVALID_ARG:  			ret = LOCKDOWN_E_INVALID_ARG; @@ -816,6 +852,7 @@ static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t  			break;  		case USERPREF_E_SSL_ERROR:  			ret = LOCKDOWN_E_SSL_ERROR; +			break;  		default:  			break;  	} @@ -894,9 +931,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;  			}  		}  	} @@ -959,7 +1003,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 */ @@ -977,7 +1021,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 { @@ -1019,7 +1063,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(); @@ -1032,22 +1076,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; @@ -1077,7 +1121,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; @@ -1111,7 +1155,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; @@ -1226,9 +1270,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;  		} @@ -1347,17 +1399,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; @@ -1396,7 +1448,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; @@ -1443,7 +1495,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; @@ -1498,7 +1550,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; @@ -1510,7 +1562,7 @@ 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) {  		free(service->identifier); @@ -1520,7 +1572,7 @@ LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_service_descriptor_free(lockdow  	return LOCKDOWN_E_SUCCESS;  } -LIBIMOBILEDEVICE_API const char* lockdownd_strerror(lockdownd_error_t err) +const char* lockdownd_strerror(lockdownd_error_t err)  {  	switch (err) {  		case LOCKDOWN_E_SUCCESS: 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 d790a05..3fdca4d 100644 --- a/src/misagent.c +++ b/src/misagent.c @@ -24,9 +24,13 @@  #endif  #include <string.h>  #include <stdlib.h> +#include <stdio.h> + +#ifndef _MSC_VER  #include <unistd.h> +#endif +  #include <plist/plist.h> -#include <stdio.h>  #include "misagent.h"  #include "property_list_service.h" @@ -85,12 +89,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)); @@ -106,14 +109,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; @@ -128,7 +131,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; @@ -165,7 +168,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; @@ -205,7 +208,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; @@ -245,7 +248,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; @@ -282,7 +285,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 9ccfd85..6677882 100644 --- a/src/mobile_image_mounter.c +++ b/src/mobile_image_mounter.c @@ -24,7 +24,11 @@  #endif  #include <string.h>  #include <stdlib.h> + +#ifndef _MSC_VER  #include <unistd.h> +#endif +  #include <plist/plist.h>  #include "mobile_image_mounter.h" @@ -78,7 +82,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)); @@ -95,14 +99,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; @@ -115,7 +119,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; @@ -181,7 +185,7 @@ static mobile_image_mounter_error_t process_result(plist_t result, const char *e  	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) +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 unsigned char *signature, unsigned int 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; @@ -192,7 +196,7 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_im  	plist_t dict = plist_new_dict();  	plist_dict_set_item(dict, "Command", plist_new_string("ReceiveBytes"));  	if (signature && signature_size != 0) -		plist_dict_set_item(dict, "ImageSignature", plist_new_data(signature, signature_size)); +		plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));  	plist_dict_set_item(dict, "ImageSize", plist_new_uint(image_size));  	plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); @@ -241,6 +245,7 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_upload_im  	free(buf);  	if (tx < image_size) {  		debug_info("Error: failed to upload image"); +		res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;  		goto leave_unlock;  	}  	debug_info("image uploaded"); @@ -260,7 +265,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_with_options(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t options, plist_t *result)  {  	if (!client || !image_path || !image_type || !result) {  		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; @@ -271,8 +276,11 @@ LIBIMOBILEDEVICE_API mobile_image_mounter_error_t mobile_image_mounter_mount_ima  	plist_dict_set_item(dict, "Command", plist_new_string("MountImage"));  	plist_dict_set_item(dict, "ImagePath", plist_new_string(image_path));  	if (signature && signature_size != 0) -		plist_dict_set_item(dict, "ImageSignature", plist_new_data(signature, signature_size)); +		plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));  	plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); +	if (PLIST_IS_DICT(options)) { +		plist_dict_merge(&dict, options); +	}  	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));  	plist_free(dict); @@ -292,7 +300,57 @@ 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_mount_image(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t *result) +{ +	return mobile_image_mounter_mount_image_with_options(client, image_path, signature, signature_size, image_type, NULL, result); +} + +mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path) +{ +	if (!client || !mount_path) { +		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; +	} +	mobile_image_mounter_lock(client); + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Command", plist_new_string("UnmountImage")); +	plist_dict_set_item(dict, "MountPath", plist_new_string(mount_path)); +	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); +	plist_free(dict); + +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error sending XML plist to device!", __func__); +		goto leave_unlock; +	} + +	plist_t result = NULL; +	res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error receiving response from device!", __func__); +	} else { +		plist_t p_error = plist_dict_get_item(result, "Error"); +		if (p_error) { +			plist_t p_detailed = plist_dict_get_item(result, "DetailedError"); +			const char* detailederr = (p_detailed) ? plist_get_string_ptr(p_detailed, NULL) : ""; +			const char* errstr = plist_get_string_ptr(p_error, NULL); +			if (errstr && !strcmp(errstr, "UnknownCommand")) { +				res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; +			} else if (errstr && !strcmp(errstr, "DeviceLocked")) { +				res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED; +			} else if (strstr(detailederr, "no matching entry")) { +				res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; +			} else { +				res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; +			} +		} +	} + +leave_unlock: +	mobile_image_mounter_unlock(client); +	return res; +} + +mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)  {  	if (!client) {  		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; @@ -324,3 +382,215 @@ leave_unlock:  	mobile_image_mounter_unlock(client);  	return res;  } + +mobile_image_mounter_error_t mobile_image_mounter_query_developer_mode_status(mobile_image_mounter_client_t client, plist_t *result) +{ +	if (!client || !result) { +		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; +	} +	mobile_image_mounter_lock(client); + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Command", plist_new_string("QueryDeveloperModeStatus")); +	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); +	plist_free(dict); + +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error sending XML plist to device!", __func__); +		goto leave_unlock; +	} + +	res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result)); +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error receiving response from device!", __func__); +	} + +leave_unlock: +	mobile_image_mounter_unlock(client); +	return res; +} + +mobile_image_mounter_error_t mobile_image_mounter_query_nonce(mobile_image_mounter_client_t client, const char* image_type, unsigned char** nonce, unsigned int* nonce_size) +{ +	if (!client || !nonce || !nonce_size) { +		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; +	} +	mobile_image_mounter_lock(client); + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Command", plist_new_string("QueryNonce")); +	if (image_type) { +		plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); +	} +	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); +	plist_free(dict); + +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error sending XML plist to device!", __func__); +		goto leave_unlock; +	} + +	plist_t result = NULL; +	res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error receiving response from device!", __func__); +	} else { +		plist_t p_nonce = plist_dict_get_item(result, "PersonalizationNonce"); +		if (!p_nonce) { +			res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; +		} else { +			uint64_t nonce_size_ = 0; +			plist_get_data_val(p_nonce, (char**)nonce, &nonce_size_); +			if (*nonce) { +				*nonce_size = (unsigned int)nonce_size_; +			} else { +				res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; +			} +		} +	} +	plist_free(result); + +leave_unlock: +	mobile_image_mounter_unlock(client); +	return res; +} + +mobile_image_mounter_error_t mobile_image_mounter_query_personalization_identifiers(mobile_image_mounter_client_t client, const char* image_type, plist_t *result) +{ +	if (!client || !result) { +		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; +	} +	mobile_image_mounter_lock(client); + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationIdentifiers")); +	if (image_type) { +		plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); +	} +	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); +	plist_free(dict); + +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error sending XML plist to device!", __func__); +		goto leave_unlock; +	} + +	plist_t _result = NULL; +	res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &_result)); +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error receiving response from device!", __func__); +	} +	*result = plist_copy(plist_dict_get_item(_result, "PersonalizationIdentifiers")); +	if (!*result) { +		debug_info("%s: Response did not contain PersonalizationIdentifiers!", __func__); +		res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; +	} + +leave_unlock: +	mobile_image_mounter_unlock(client); +	return res; +} + +mobile_image_mounter_error_t mobile_image_mounter_query_personalization_manifest(mobile_image_mounter_client_t client, const char* image_type, const unsigned char* signature, unsigned int signature_size, unsigned char** manifest, unsigned int* manifest_size) +{ +	if (!client || !image_type || !signature || !signature_size || !manifest || !manifest_size) { +		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; +	} +	mobile_image_mounter_lock(client); + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationManifest")); +	plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); +	plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); +	plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size)); + +	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); +	plist_free(dict); + +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error sending XML plist to device!", __func__); +		goto leave_unlock; +	} + +	plist_t result = NULL; +	res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error receiving response from device!", __func__); +	} else { +		plist_t p_manifest = plist_dict_get_item(result, "ImageSignature"); +		if (!p_manifest) { +			res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; +		} else { +			uint64_t manifest_size_ = 0; +			plist_get_data_val(p_manifest, (char**)manifest, &manifest_size_); +			if (*manifest) { +				*manifest_size = (unsigned int)manifest_size_; +			} else { +				res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; +			} +		} +	} +	plist_free(result); + +leave_unlock: +	mobile_image_mounter_unlock(client); +	return res; +} + +mobile_image_mounter_error_t mobile_image_mounter_roll_personalization_nonce(mobile_image_mounter_client_t client) +{ +	if (!client) { +		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; +	} +	mobile_image_mounter_lock(client); + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Command", plist_new_string("RollPersonalizationNonce")); +	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); +	plist_free(dict); + +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error sending XML plist to device!", __func__); +		goto leave_unlock; +	} + +	plist_t result = NULL; +	res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error receiving response from device!", __func__); +	} +	plist_free(result); + +leave_unlock: +	mobile_image_mounter_unlock(client); +	return res; +} + +mobile_image_mounter_error_t mobile_image_mounter_roll_cryptex_nonce(mobile_image_mounter_client_t client) +{ +	if (!client) { +		return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; +	} +	mobile_image_mounter_lock(client); + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Command", plist_new_string("RollCryptexNonce")); +	mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); +	plist_free(dict); + +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error sending XML plist to device!", __func__); +		goto leave_unlock; +	} + +	plist_t result = NULL; +	res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); +	if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { +		debug_info("%s: Error receiving response from device!", __func__); +	} +	plist_free(result); + +leave_unlock: +	mobile_image_mounter_unlock(client); +	return res; +} 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 2de4333..fce5f16 100644 --- a/src/mobileactivation.c +++ b/src/mobileactivation.c @@ -54,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; @@ -74,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; @@ -109,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;  	} @@ -118,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) @@ -179,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; @@ -201,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; @@ -221,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; @@ -243,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; @@ -267,7 +264,7 @@ LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation  	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; @@ -280,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; @@ -303,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 cde96b5..36986a4 100644 --- a/src/mobilebackup.c +++ b/src/mobilebackup.c @@ -26,6 +26,7 @@  #include <plist/plist.h>  #include <string.h>  #include <stdlib.h> +#include <stdio.h>  #include "mobilebackup.h"  #include "device_link_service.h" @@ -34,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. @@ -68,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; @@ -95,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; @@ -115,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; @@ -123,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; @@ -240,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; @@ -279,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); @@ -300,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; @@ -346,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); @@ -359,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) { @@ -414,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 edda70f..04ec479 100644 --- a/src/mobilebackup.h +++ b/src/mobilebackup.h @@ -23,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 6c6556d..a8d673f 100644 --- a/src/mobilebackup2.c +++ b/src/mobilebackup2.c @@ -33,7 +33,7 @@  #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. @@ -68,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) @@ -96,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; @@ -116,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; @@ -214,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; @@ -240,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; @@ -265,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; @@ -330,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; @@ -361,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 025b6bf..e232b97 100644 --- a/src/mobilebackup2.h +++ b/src/mobilebackup2.h @@ -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 163a41e..9b81a49 100644 --- a/src/mobilesync.c +++ b/src/mobilesync.c @@ -34,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___" @@ -71,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) @@ -101,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; @@ -118,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; @@ -126,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) { @@ -259,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; @@ -344,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; @@ -421,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; @@ -473,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;  	} @@ -490,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; @@ -524,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; @@ -591,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; @@ -619,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; @@ -688,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; @@ -714,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) { @@ -731,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); @@ -745,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; @@ -782,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 e17e2fe..c7e4660 100644 --- a/src/notification_proxy.c +++ b/src/notification_proxy.c @@ -24,14 +24,19 @@  #endif  #include <string.h>  #include <stdlib.h> + +#ifndef _MSC_VER  #include <unistd.h> +#endif +  #include <plist/plist.h>  #include "notification_proxy.h"  #include "property_list_service.h"  #include "common/debug.h" -#ifdef WIN32 +#ifdef _WIN32 +#include <windows.h>  #define sleep(x) Sleep(x*1000)  #endif @@ -89,7 +94,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)); @@ -107,14 +112,14 @@ LIBIMOBILEDEVICE_API np_error_t np_client_new(idevice_t device, lockdownd_servic  	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; +	int32_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; @@ -168,7 +173,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; @@ -204,7 +209,7 @@ static np_error_t internal_np_observe_notification(np_client_t client, const cha  	return res;  } -LIBIMOBILEDEVICE_API np_error_t np_observe_notification( np_client_t client, const char *notification ) +np_error_t np_observe_notification( np_client_t client, const char *notification )  {  	if (!client || !notification) {  		return NP_E_INVALID_ARG; @@ -215,7 +220,7 @@ LIBIMOBILEDEVICE_API np_error_t np_observe_notification( np_client_t client, con  	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; @@ -346,7 +351,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; diff --git a/src/notification_proxy.h b/src/notification_proxy.h index f641e25..595cb01 100644 --- a/src/notification_proxy.h +++ b/src/notification_proxy.h @@ -22,9 +22,10 @@  #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; diff --git a/src/ostrace.c b/src/ostrace.c new file mode 100644 index 0000000..68eb6bf --- /dev/null +++ b/src/ostrace.c @@ -0,0 +1,436 @@ +/* + * ostrace.c + * com.apple.os_trace_relay service implementation. + * + * Copyright (c) 2020-2025 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 "ostrace.h" +#include "lockdown.h" +#include "common/debug.h" +#include "endianness.h" + +struct ostrace_worker_thread { +	ostrace_client_t client; +	ostrace_activity_cb_t cbfunc; +	void *user_data; +}; + +/** + * Convert a service_error_t value to a ostrace_error_t value. + * Used internally to get correct error codes. + * + * @param err An service_error_t error code + * + * @return A matching ostrace_error_t error code, + *     OSTRACE_E_UNKNOWN_ERROR otherwise. + */ +static ostrace_error_t ostrace_error(service_error_t err) +{ +	switch (err) { +		case SERVICE_E_SUCCESS: +			return OSTRACE_E_SUCCESS; +		case SERVICE_E_INVALID_ARG: +			return OSTRACE_E_INVALID_ARG; +		case SERVICE_E_MUX_ERROR: +			return OSTRACE_E_MUX_ERROR; +		case SERVICE_E_SSL_ERROR: +			return OSTRACE_E_SSL_ERROR; +		case SERVICE_E_NOT_ENOUGH_DATA: +			return OSTRACE_E_NOT_ENOUGH_DATA; +		case SERVICE_E_TIMEOUT: +			return OSTRACE_E_TIMEOUT; +		default: +			break; +	} +	return OSTRACE_E_UNKNOWN_ERROR; +} + +ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client) +{ +	*client = NULL; + +	if (!device || !service || service->port == 0 || !client || *client) { +		debug_info("Incorrect parameter passed to ostrace_client_new."); +		return OSTRACE_E_INVALID_ARG; +	} + +	debug_info("Creating ostrace_client, port = %d.", service->port); + +	service_client_t parent = NULL; +	ostrace_error_t ret = ostrace_error(service_client_new(device, service, &parent)); +	if (ret != OSTRACE_E_SUCCESS) { +		debug_info("Creating base service client failed. Error: %i", ret); +		return ret; +	} + +	ostrace_client_t client_loc = (ostrace_client_t) malloc(sizeof(struct ostrace_client_private)); +	client_loc->parent = parent; +	client_loc->worker = THREAD_T_NULL; + +	*client = client_loc; + +	debug_info("ostrace_client successfully created."); +	return 0; +} + +ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label) +{ +	ostrace_error_t err = OSTRACE_E_UNKNOWN_ERROR; +	service_client_factory_start_service(device, OSTRACE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(ostrace_client_new), &err); +	return err; +} + +ostrace_error_t ostrace_client_free(ostrace_client_t client) +{ +	if (!client) +		return OSTRACE_E_INVALID_ARG; +	ostrace_stop_activity(client); +	ostrace_error_t err = ostrace_error(service_client_free(client->parent)); +	free(client); + +	return err; +} + +static ostrace_error_t ostrace_send_plist(ostrace_client_t client, plist_t plist) +{ +	ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; +	uint32_t blen = 0; +	char* bin = NULL; +	uint32_t sent = 0; +	uint32_t swapped_len = 0; + +	if (!client || !plist) { +		return OSTRACE_E_INVALID_ARG; +	} + +	plist_to_bin(plist, &bin, &blen); +	swapped_len = htobe32(blen); + +	res = ostrace_error(service_send(client->parent, (char*)&swapped_len, 4, &sent)); +	if (res == OSTRACE_E_SUCCESS) { +		res = ostrace_error(service_send(client->parent, bin, blen, &sent)); +	} +	free(bin); +	return res; +} + +static ostrace_error_t ostrace_receive_plist(ostrace_client_t client, plist_t *plist) +{ +	ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; +	uint8_t msgtype = 0; +	uint32_t received = 0; +	res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received)); +	if (res != OSTRACE_E_SUCCESS) { +		debug_info("Failed to read message type from service"); +		return res; +	} +	uint32_t rlen = 0; +	res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received)); +	if (res != OSTRACE_E_SUCCESS) { +		debug_info("Failed to read message size from service"); +		return res; +	} + +	if (msgtype == 1) { +		rlen = be32toh(rlen); +	} else if (msgtype == 2) { +		rlen = le32toh(rlen); +	} else { +		debug_info("Unexpected message type %d", msgtype); +		return OSTRACE_E_UNKNOWN_ERROR; +	} +	debug_info("got length %d", rlen); + +	char* buf = (char*)malloc(rlen); +	res = ostrace_error(service_receive(client->parent, buf, rlen, &received)); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} + +	plist_t reply = NULL; +	plist_err_t perr = plist_from_memory(buf, received, &reply, NULL); +	free(buf); +	if (perr != PLIST_ERR_SUCCESS) { +		return OSTRACE_E_UNKNOWN_ERROR; +	} +	*plist = reply; +	return OSTRACE_E_SUCCESS;	 +} + +static ostrace_error_t _ostrace_check_result(plist_t reply) +{ +	ostrace_error_t res = OSTRACE_E_REQUEST_FAILED; +	if (!reply) { +		return res; +	} +	plist_t p_status = plist_dict_get_item(reply, "Status"); +	if (!p_status) { +		return res; +	} +	const char* status = plist_get_string_ptr(p_status, NULL); +	if (!status) { +		return res; +	} +	if (!strcmp(status, "RequestSuccessful")) { +		res = OSTRACE_E_SUCCESS; +	} +	return res; +} + +void *ostrace_worker(void *arg) +{ +	ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; +	struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)arg; + +	if (!oswt) +		return NULL; + +	uint8_t msgtype = 0; +	uint32_t received = 0; + +	debug_info("Running"); + +	while (oswt->client->parent) { +		res = ostrace_error(service_receive_with_timeout(oswt->client->parent, (char*)&msgtype, 1, &received, 100)); +		if (res == OSTRACE_E_TIMEOUT) { +			continue; +		} +		if (res != OSTRACE_E_SUCCESS) { +			debug_info("Failed to read message type from service"); +			break; +		} +		uint32_t rlen = 0; +		res = ostrace_error(service_receive(oswt->client->parent, (char*)&rlen, 4, &received)); +		if (res != OSTRACE_E_SUCCESS) { +			debug_info("Failed to read message size from service"); +			break; +		} + +		if (msgtype == 1) { +			rlen = be32toh(rlen); +		} else if (msgtype == 2) { +			rlen = le32toh(rlen); +		} else { +			debug_info("Unexpected message type %d", msgtype); +			break; +		} + +		debug_info("got length %d", rlen); + +		void* buf = malloc(rlen); +		res = ostrace_error(service_receive(oswt->client->parent, (char*)buf, rlen, &received)); +		if (res != OSTRACE_E_SUCCESS) { +			debug_info("Failed to receive %d bytes, error %d", rlen, res); +			break; +		} +		if (received < rlen) { +			debug_info("Failed to receive all data, got %d/%d", received, rlen); +			break; +		} +		oswt->cbfunc(buf, received, oswt->user_data); +	} + +	if (oswt) { +		free(oswt); +	} + +	debug_info("Exiting"); + +	return NULL; +} + +ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data) +{ +	if (!client || !callback) +		return OSTRACE_E_INVALID_ARG; + +	ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; + +	if (client->worker) { +		debug_info("Another ostrace activity thread appears to be running already."); +		return res; +	} + +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Pid", plist_new_uint(0x0FFFFFFFF)); +	plist_dict_set_item(dict, "MessageFilter", plist_new_uint(0xFFFF)); +	plist_dict_set_item(dict, "StreamFlags", plist_new_uint(0x3C)); +	if (options) { +		plist_dict_merge(&dict, options); +	} +	plist_dict_set_item(dict, "Request", plist_new_string("StartActivity")); + +	res = ostrace_send_plist(client, dict); +	plist_free(dict); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} +	 +	dict = NULL; +	res = ostrace_receive_plist(client, &dict); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} +	res = _ostrace_check_result(dict); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} + +	/* start worker thread */ +	struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)malloc(sizeof(struct ostrace_worker_thread)); +	if (oswt) { +		oswt->client = client; +		oswt->cbfunc = callback; +		oswt->user_data = user_data; + +		if (thread_new(&client->worker, ostrace_worker, oswt) == 0) { +			res = OSTRACE_E_SUCCESS; +		} +	} + +	return res; +} + +ostrace_error_t ostrace_stop_activity(ostrace_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 OSTRACE_E_SUCCESS; +} + +ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list) +{ +	ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; +	plist_t dict = plist_new_dict(); +	plist_dict_set_item(dict, "Request", plist_new_string("PidList")); + +	if (!client || !list) { +		return OSTRACE_E_INVALID_ARG; +	} + +	res = ostrace_send_plist(client, dict); +	plist_free(dict); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} + +	plist_t reply = NULL; +	res = ostrace_receive_plist(client, &reply); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} +	res = _ostrace_check_result(reply); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} + +	plist_t payload = plist_dict_get_item(reply, "Payload"); +	if (!payload) { +		return OSTRACE_E_REQUEST_FAILED; +	} +	*list = plist_copy(payload); +	plist_free(reply); + +	return OSTRACE_E_SUCCESS; +} + +ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data) +{ +	ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; +	if (!client || !callback) { +		return OSTRACE_E_INVALID_ARG; +	} +	plist_t dict = plist_new_dict(); +	if (options) { +		plist_dict_merge(&dict, options); +	} +	plist_dict_set_item(dict, "Request", plist_new_string("CreateArchive")); + +	res = ostrace_send_plist(client, dict); +	plist_free(dict); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} + +	plist_t reply = NULL; +	res = ostrace_receive_plist(client, &reply); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} + +	res = _ostrace_check_result(reply); +	if (res != OSTRACE_E_SUCCESS) { +		return res; +	} + +	debug_info("Receiving archive...\n"); +	while (1) { +		uint8_t msgtype = 0; +		uint32_t received = 0; +		res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received)); +		if (res != OSTRACE_E_SUCCESS) { +			debug_info("Could not read message type from service: %d", res); +			break; +		} +		if (msgtype != 3) { +			debug_info("Unexpected packet type %d", msgtype); +			return OSTRACE_E_REQUEST_FAILED; +		} +		uint32_t rlen = 0; +		res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received)); +		if (res != OSTRACE_E_SUCCESS) { +			debug_info("Failed to read message size from service"); +			break; +		} + +		rlen = le32toh(rlen); +		debug_info("got length %d", rlen); + +		unsigned char* buf = (unsigned char*)malloc(rlen); +		res = ostrace_error(service_receive(client->parent, (char*)buf, rlen, &received)); +		if (res != OSTRACE_E_SUCCESS) { +			debug_info("Could not read data from service: %d", res); +			break; +		} +		if (callback(buf, received, user_data) < 0) { +			debug_info("Aborted through callback"); +			return OSTRACE_E_REQUEST_FAILED; +		} +	} +	debug_info("Done.\n"); + +	return OSTRACE_E_SUCCESS; +} + diff --git a/src/ostrace.h b/src/ostrace.h new file mode 100644 index 0000000..dcc3e8d --- /dev/null +++ b/src/ostrace.h @@ -0,0 +1,37 @@ +/* + * ostrace.h + * com.apple.os_trace_relay service header file. + * + * Copyright (c) 2020-2025 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 _OSTRACE_H +#define _OSTRACE_H + +#include "idevice.h" +#include "libimobiledevice/ostrace.h" +#include "service.h" +#include <libimobiledevice-glue/thread.h> + +struct ostrace_client_private { +	service_client_t parent; +	THREAD_T worker; +}; + +void *ostrace_worker(void *arg); + +#endif diff --git a/src/preboard.c b/src/preboard.c index b975f0e..c3eff02 100644 --- a/src/preboard.c +++ b/src/preboard.c @@ -62,7 +62,7 @@ static preboard_error_t preboard_error(property_list_service_error_t err)  	return PREBOARD_E_UNKNOWN_ERROR;  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_client_new(idevice_t device, lockdownd_service_descriptor_t service, preboard_client_t * client) +preboard_error_t preboard_client_new(idevice_t device, lockdownd_service_descriptor_t service, preboard_client_t * client)  {  	*client = NULL; @@ -90,14 +90,14 @@ LIBIMOBILEDEVICE_API preboard_error_t preboard_client_new(idevice_t device, lock  	return 0;  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_client_start_service(idevice_t device, preboard_client_t * client, const char* label) +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;  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_client_free(preboard_client_t client) +preboard_error_t preboard_client_free(preboard_client_t client)  {  	if (!client)  		return PREBOARD_E_INVALID_ARG; @@ -116,7 +116,7 @@ LIBIMOBILEDEVICE_API preboard_error_t preboard_client_free(preboard_client_t cli  	return err;  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_send(preboard_client_t client, plist_t plist) +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)); @@ -127,7 +127,7 @@ LIBIMOBILEDEVICE_API preboard_error_t preboard_send(preboard_client_t client, pl  	return res;  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_receive_with_timeout(preboard_client_t client, plist_t * plist, uint32_t timeout_ms) +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; @@ -141,7 +141,7 @@ LIBIMOBILEDEVICE_API preboard_error_t preboard_receive_with_timeout(preboard_cli  	return res;  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_receive(preboard_client_t client, plist_t * plist) +preboard_error_t preboard_receive(preboard_client_t client, plist_t * plist)  {  	return preboard_receive_with_timeout(client, plist, 5000);  } @@ -162,7 +162,8 @@ static void* preboard_receive_status_loop_thread(void* arg)  		preboard_error_t perr = preboard_receive_with_timeout(data->client, &pl, 1000);  		if (perr == PREBOARD_E_TIMEOUT) {  			continue; -		} else if (perr == PREBOARD_E_SUCCESS) { +		} +		if (perr == PREBOARD_E_SUCCESS) {  			data->cbfunc(pl, data->user_data);  		}  		plist_free(pl); @@ -208,7 +209,7 @@ static preboard_error_t preboard_receive_status_loop_with_callback(preboard_clie  	return res;  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_create_stashbag(preboard_client_t client, plist_t manifest, preboard_status_cb_t status_cb, void *user_data) +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; @@ -231,7 +232,7 @@ LIBIMOBILEDEVICE_API preboard_error_t preboard_create_stashbag(preboard_client_t  	return preboard_receive_status_loop_with_callback(client, status_cb, user_data);  } -LIBIMOBILEDEVICE_API preboard_error_t preboard_commit_stashbag(preboard_client_t client, plist_t manifest, preboard_status_cb_t status_cb, void *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; diff --git a/src/preboard.h b/src/preboard.h index c5143a9..f8164eb 100644 --- a/src/preboard.h +++ b/src/preboard.h @@ -22,9 +22,10 @@  #ifndef __PREBOARD_H  #define __PREBOARD_H +#include "idevice.h"  #include "libimobiledevice/preboard.h"  #include "property_list_service.h" -#include "common/thread.h" +#include <libimobiledevice-glue/thread.h>  struct preboard_client_private {  	property_list_service_client_t parent; diff --git a/src/property_list_service.c b/src/property_list_service.c index 7b5c738..2fca4e7 100644 --- a/src/property_list_service.c +++ b/src/property_list_service.c @@ -58,7 +58,7 @@ static property_list_service_error_t service_to_property_list_service_error(serv  	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; @@ -78,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; @@ -152,12 +152,12 @@ static property_list_service_error_t internal_plist_send(property_list_service_c  	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);  } @@ -262,27 +262,34 @@ static property_list_service_error_t internal_plist_receive_timeout(property_lis  	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, 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 4e9d65a..d13a28a 100644 --- a/src/restore.c +++ b/src/restore.c @@ -111,7 +111,7 @@ static restored_error_t restored_error(property_list_service_error_t err)          return RESTORE_E_UNKNOWN_ERROR;  } -LIBIMOBILEDEVICE_API restored_error_t restored_client_free(restored_client_t client) +restored_error_t restored_client_free(restored_client_t client)  {  	if (!client)  		return RESTORE_E_INVALID_ARG; @@ -139,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) @@ -149,7 +149,7 @@ 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; @@ -157,7 +157,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_receive(restored_client_t client,  	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; @@ -165,7 +165,7 @@ LIBIMOBILEDEVICE_API restored_error_t restored_send(restored_client_t client, pl  	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; @@ -224,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; @@ -266,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; @@ -337,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; @@ -369,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; @@ -393,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 ccb7c4b..5df5122 100644 --- a/src/sbservices.c +++ b/src/sbservices.c @@ -24,7 +24,11 @@  #endif  #include <string.h>  #include <stdlib.h> + +#ifndef _MSC_VER  #include <unistd.h> +#endif +  #include <plist/plist.h>  #include "sbservices.h" @@ -79,7 +83,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)); @@ -95,14 +99,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; @@ -115,7 +119,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; @@ -155,7 +159,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; @@ -172,7 +176,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); @@ -181,7 +188,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; @@ -218,7 +225,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; @@ -256,7 +263,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 11d6506..c3cc9ba 100644 --- a/src/screenshotr.c +++ b/src/screenshotr.c @@ -65,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) @@ -93,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; @@ -110,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; @@ -142,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 88132d2..9474021 100644 --- a/src/service.c +++ b/src/service.c @@ -56,7 +56,7 @@ static service_error_t idevice_to_service_error(idevice_error_t err)  	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; @@ -80,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; @@ -91,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;  	} @@ -119,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; @@ -132,7 +132,7 @@ 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;  	uint32_t bytes = 0; @@ -153,7 +153,7 @@ LIBIMOBILEDEVICE_API service_error_t service_send(service_client_t client, const  	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;  	uint32_t bytes = 0; @@ -174,27 +174,34 @@ LIBIMOBILEDEVICE_API service_error_t service_receive_with_timeout(service_client  	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, 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);  } -LIBIMOBILEDEVICE_API service_error_t service_disable_bypass_ssl(service_client_t client, uint8_t sslBypass) +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_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 c137297..9f4296e 100644 --- a/src/syslog_relay.c +++ b/src/syslog_relay.c @@ -67,7 +67,7 @@ static syslog_relay_error_t syslog_relay_error(service_error_t err)  	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; @@ -95,14 +95,14 @@ 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; @@ -113,12 +113,12 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_client_free(syslog_relay_  	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; @@ -154,16 +154,15 @@ void *syslog_relay_worker(void *arg)  		ret = syslog_relay_receive_with_timeout(srwt->client, &c, 1, &bytes, 100);  		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 (srwt->is_raw) {  			srwt->cbfunc(c, srwt->user_data); -		} else { -			if (c != 0) { -				srwt->cbfunc(c, srwt->user_data); -			} +		} else if (c != 0) { +			srwt->cbfunc(c, srwt->user_data);  		}  	} @@ -176,7 +175,7 @@ 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; @@ -204,7 +203,7 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_start_capture(syslog_rela  	return res;  } -LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_start_capture_raw(syslog_relay_client_t client, syslog_relay_receive_cb_t callback, void* user_data) +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; @@ -232,7 +231,7 @@ LIBIMOBILEDEVICE_API syslog_relay_error_t syslog_relay_start_capture_raw(syslog_  	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 */ diff --git a/src/syslog_relay.h b/src/syslog_relay.h index 3e48fa4..d5263e2 100644 --- a/src/syslog_relay.h +++ b/src/syslog_relay.h @@ -22,9 +22,10 @@  #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; diff --git a/src/webinspector.c b/src/webinspector.c index 3360597..f960fcc 100644 --- a/src/webinspector.c +++ b/src/webinspector.c @@ -62,7 +62,7 @@ static webinspector_error_t webinspector_error(property_list_service_error_t err  	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; @@ -89,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; @@ -107,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; @@ -164,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" | 
