diff options
Diffstat (limited to 'src/idevice.c')
| -rw-r--r-- | src/idevice.c | 537 | 
1 files changed, 366 insertions, 171 deletions
diff --git a/src/idevice.c b/src/idevice.c index 6a03c5e..0af27fd 100644 --- a/src/idevice.c +++ b/src/idevice.c @@ -30,6 +30,15 @@  #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>  #if defined(HAVE_OPENSSL) @@ -52,17 +61,18 @@  #include <libimobiledevice-glue/thread.h>  #include "idevice.h" +#include "lockdown.h"  #include "common/userpref.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 || \ @@ -114,32 +124,32 @@ static void id_function(CRYPTO_THREADID *thread)  #endif  #endif /* HAVE_OPENSSL */ -static void internal_idevice_init(void) -{ -#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); +// 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 -	CRYPTO_THREADID_set_callback(id_function); -#endif -	CRYPTO_set_locking_callback(locking_function); +    #define INITIALIZER(f) \ +        static void f(void) __attribute__((__constructor__)); \ +        static void f(void)  #endif -#elif defined(HAVE_GNUTLS) -	gnutls_global_init(); -#elif defined(HAVE_MBEDTLS) -	// NO-OP -#endif -}  static void internal_idevice_deinit(void)  { @@ -171,48 +181,53 @@ static void internal_idevice_deinit(void)  #endif  } -static thread_once_t init_once = THREAD_ONCE_INIT; -static thread_once_t deinit_once = THREAD_ONCE_INIT; +INITIALIZER(internal_idevice_init) +{ +#if defined(HAVE_OPENSSL) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +	int i; +	SSL_library_init(); -#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR -  #if defined(__llvm__) || defined(__GNUC__) -    #define HAVE_ATTRIBUTE_CONSTRUCTOR -  #endif -#endif +	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]); -#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR -static void __attribute__((constructor)) libimobiledevice_initialize(void) -{ -	thread_once(&init_once, internal_idevice_init); +#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 +#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) -{ -	thread_once(&deinit_once, internal_idevice_deinit); -} -#elif defined(WIN32) -BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) +const char* libimobiledevice_version()  { -	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; -} -#else -#warning No compiler support for constructor/destructor attributes, some features might not be available. +#ifndef PACKAGE_VERSION +#error PACKAGE_VERSION is not defined!  #endif +	return PACKAGE_VERSION; +} -static idevice_event_cb_t event_cb = NULL; +struct idevice_subscription_context { +	idevice_event_cb_t callback; +	void *user_data; +	usbmuxd_subscription_context_t ctx; +}; + +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; @@ -226,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; @@ -278,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);  		} @@ -295,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; @@ -310,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; @@ -342,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; @@ -355,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);  } @@ -380,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; @@ -392,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; @@ -416,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; @@ -438,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; @@ -447,40 +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]; @@ -494,13 +570,20 @@ 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; @@ -508,14 +591,13 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connect(idevice_t device, uint16_t  		*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; @@ -526,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 { @@ -555,29 +637,30 @@ 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  #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) @@ -617,24 +700,26 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_send(idevice_connection_  		}  		*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 inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received) @@ -668,14 +753,15 @@ 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 (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; @@ -683,13 +769,13 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t  			debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res));  		}  		return error; -	} 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_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  #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) @@ -720,6 +806,10 @@ 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;  			} @@ -763,27 +853,28 @@ 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  #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) @@ -816,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; @@ -844,15 +935,31 @@ 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;  } +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) @@ -962,18 +1069,36 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data)  }  #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): -		return argp ? (long)internal_ssl_read(conn, (char *)argp, len) : 0; +		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): -		return (long)internal_ssl_write(conn, argp, len); +		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;  	} @@ -984,7 +1109,11 @@ 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;  } @@ -1072,7 +1201,7 @@ static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len)  }  #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; @@ -1109,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; @@ -1147,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); @@ -1155,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); @@ -1175,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 }; @@ -1334,12 +1504,12 @@ LIBIMOBILEDEVICE_API idevice_error_t idevice_connection_enable_ssl(idevice_conne  	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; @@ -1381,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"; +}  | 
