diff options
Diffstat (limited to 'src/idevice.c')
-rw-r--r-- | src/idevice.c | 1434 |
1 files changed, 1155 insertions, 279 deletions
diff --git a/src/idevice.c b/src/idevice.c index 5a9d49b..b9bbb1f 100644 --- a/src/idevice.c +++ b/src/idevice.c | |||
@@ -1,98 +1,387 @@ | |||
1 | /* | 1 | /* |
2 | * idevice.c | 2 | * idevice.c |
3 | * Device discovery and communication interface. | 3 | * Device discovery and communication interface. |
4 | * | 4 | * |
5 | * Copyright (c) 2009-2021 Nikias Bassen. All Rights Reserved. | ||
6 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 7 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
6 | * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. | ||
7 | * | 8 | * |
8 | * This library is free software; you can redistribute it and/or | 9 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public | 10 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either | 11 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. | 12 | * version 2.1 of the License, or (at your option) any later version. |
12 | * | 13 | * |
13 | * This library is distributed in the hope that it will be useful, | 14 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. | 17 | * Lesser General Public License for more details. |
17 | * | 18 | * |
18 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, write to the Free Software | 20 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ | 22 | */ |
22 | 23 | ||
24 | #ifdef HAVE_CONFIG_H | ||
25 | #include <config.h> | ||
26 | #endif | ||
27 | |||
23 | #include <stdlib.h> | 28 | #include <stdlib.h> |
24 | #include <string.h> | 29 | #include <string.h> |
25 | #include <errno.h> | 30 | #include <errno.h> |
31 | #include <time.h> | ||
32 | |||
33 | #ifdef WIN32 | ||
34 | #include <winsock2.h> | ||
35 | #include <ws2tcpip.h> | ||
36 | #include <windows.h> | ||
37 | #else | ||
38 | #include <sys/socket.h> | ||
39 | #include <netinet/in.h> | ||
40 | #endif | ||
26 | 41 | ||
27 | #include <usbmuxd.h> | 42 | #include <usbmuxd.h> |
43 | |||
44 | #if defined(HAVE_OPENSSL) | ||
45 | #include <openssl/err.h> | ||
46 | #include <openssl/rsa.h> | ||
47 | #include <openssl/ssl.h> | ||
48 | #elif defined(HAVE_GNUTLS) | ||
28 | #include <gnutls/gnutls.h> | 49 | #include <gnutls/gnutls.h> |
50 | #elif defined(HAVE_MBEDTLS) | ||
51 | #include <mbedtls/rsa.h> | ||
52 | #include <mbedtls/ssl.h> | ||
53 | #include <mbedtls/entropy.h> | ||
54 | #include <mbedtls/ctr_drbg.h> | ||
55 | #include <mbedtls/debug.h> | ||
56 | #else | ||
57 | #error No supported TLS/SSL library enabled | ||
58 | #endif | ||
59 | |||
60 | #include <libimobiledevice-glue/socket.h> | ||
61 | #include <libimobiledevice-glue/thread.h> | ||
62 | |||
29 | #include "idevice.h" | 63 | #include "idevice.h" |
30 | #include "userpref.h" | 64 | #include "lockdown.h" |
31 | #include "debug.h" | 65 | #include "common/userpref.h" |
66 | #include "common/debug.h" | ||
67 | |||
68 | #ifndef ECONNREFUSED | ||
69 | #define ECONNREFUSED 107 | ||
70 | #endif | ||
71 | #ifndef ETIMEDOUT | ||
72 | #define ETIMEDOUT 138 | ||
73 | #endif | ||
74 | |||
75 | |||
76 | #ifdef HAVE_OPENSSL | ||
77 | |||
78 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || \ | ||
79 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20020000L)) | ||
80 | #define TLS_method TLSv1_method | ||
81 | #endif | ||
82 | |||
83 | #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) | ||
84 | static void SSL_COMP_free_compression_methods(void) | ||
85 | { | ||
86 | sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); | ||
87 | } | ||
88 | #endif | ||
89 | |||
90 | static void openssl_remove_thread_state(void) | ||
91 | { | ||
92 | /* ERR_remove_thread_state() is available since OpenSSL 1.0.0-beta1, but | ||
93 | * deprecated in OpenSSL 1.1.0 */ | ||
94 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
95 | #if OPENSSL_VERSION_NUMBER >= 0x10000001L | ||
96 | ERR_remove_thread_state(NULL); | ||
97 | #else | ||
98 | ERR_remove_state(0); | ||
99 | #endif | ||
100 | #endif | ||
101 | } | ||
102 | |||
103 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
104 | static mutex_t *mutex_buf = NULL; | ||
105 | static void locking_function(int mode, int n, const char* file, int line) | ||
106 | { | ||
107 | if (mode & CRYPTO_LOCK) | ||
108 | mutex_lock(&mutex_buf[n]); | ||
109 | else | ||
110 | mutex_unlock(&mutex_buf[n]); | ||
111 | } | ||
112 | |||
113 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
114 | static unsigned long id_function(void) | ||
115 | { | ||
116 | return ((unsigned long)THREAD_ID); | ||
117 | } | ||
118 | #else | ||
119 | static void id_function(CRYPTO_THREADID *thread) | ||
120 | { | ||
121 | CRYPTO_THREADID_set_numeric(thread, (unsigned long)THREAD_ID); | ||
122 | } | ||
123 | #endif | ||
124 | #endif | ||
125 | #endif /* HAVE_OPENSSL */ | ||
126 | |||
127 | static void internal_idevice_init(void) | ||
128 | { | ||
129 | #if defined(HAVE_OPENSSL) | ||
130 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
131 | int i; | ||
132 | SSL_library_init(); | ||
133 | |||
134 | mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); | ||
135 | if (!mutex_buf) | ||
136 | return; | ||
137 | for (i = 0; i < CRYPTO_num_locks(); i++) | ||
138 | mutex_init(&mutex_buf[i]); | ||
139 | |||
140 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
141 | CRYPTO_set_id_callback(id_function); | ||
142 | #else | ||
143 | CRYPTO_THREADID_set_callback(id_function); | ||
144 | #endif | ||
145 | CRYPTO_set_locking_callback(locking_function); | ||
146 | #endif | ||
147 | #elif defined(HAVE_GNUTLS) | ||
148 | gnutls_global_init(); | ||
149 | #elif defined(HAVE_MBEDTLS) | ||
150 | // NO-OP | ||
151 | #endif | ||
152 | } | ||
153 | |||
154 | static void internal_idevice_deinit(void) | ||
155 | { | ||
156 | #if defined(HAVE_OPENSSL) | ||
157 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
158 | int i; | ||
159 | if (mutex_buf) { | ||
160 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
161 | CRYPTO_set_id_callback(NULL); | ||
162 | #else | ||
163 | CRYPTO_THREADID_set_callback(NULL); | ||
164 | #endif | ||
165 | CRYPTO_set_locking_callback(NULL); | ||
166 | for (i = 0; i < CRYPTO_num_locks(); i++) | ||
167 | mutex_destroy(&mutex_buf[i]); | ||
168 | free(mutex_buf); | ||
169 | mutex_buf = NULL; | ||
170 | } | ||
171 | |||
172 | EVP_cleanup(); | ||
173 | CRYPTO_cleanup_all_ex_data(); | ||
174 | SSL_COMP_free_compression_methods(); | ||
175 | openssl_remove_thread_state(); | ||
176 | #endif | ||
177 | #elif defined(HAVE_GNUTLS) | ||
178 | gnutls_global_deinit(); | ||
179 | #elif defined(HAVE_MBEDTLS) | ||
180 | // NO-OP | ||
181 | #endif | ||
182 | } | ||
183 | |||
184 | static thread_once_t init_once = THREAD_ONCE_INIT; | ||
185 | static thread_once_t deinit_once = THREAD_ONCE_INIT; | ||
186 | |||
187 | #ifndef HAVE_ATTRIBUTE_CONSTRUCTOR | ||
188 | #if defined(__llvm__) || defined(__GNUC__) | ||
189 | #define HAVE_ATTRIBUTE_CONSTRUCTOR | ||
190 | #endif | ||
191 | #endif | ||
192 | |||
193 | #ifdef HAVE_ATTRIBUTE_CONSTRUCTOR | ||
194 | static void __attribute__((constructor)) libimobiledevice_initialize(void) | ||
195 | { | ||
196 | thread_once(&init_once, internal_idevice_init); | ||
197 | } | ||
198 | |||
199 | static void __attribute__((destructor)) libimobiledevice_deinitialize(void) | ||
200 | { | ||
201 | thread_once(&deinit_once, internal_idevice_deinit); | ||
202 | } | ||
203 | #elif defined(WIN32) | ||
204 | BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) | ||
205 | { | ||
206 | switch (dwReason) { | ||
207 | case DLL_PROCESS_ATTACH: | ||
208 | thread_once(&init_once, internal_idevice_init); | ||
209 | break; | ||
210 | case DLL_PROCESS_DETACH: | ||
211 | thread_once(&deinit_once, internal_idevice_deinit); | ||
212 | break; | ||
213 | default: | ||
214 | break; | ||
215 | } | ||
216 | return 1; | ||
217 | } | ||
218 | #else | ||
219 | #warning No compiler support for constructor/destructor attributes, some features might not be available. | ||
220 | #endif | ||
221 | |||
222 | const char* libimobiledevice_version() | ||
223 | { | ||
224 | #ifndef PACKAGE_VERSION | ||
225 | #error PACKAGE_VERSION is not defined! | ||
226 | #endif | ||
227 | return PACKAGE_VERSION; | ||
228 | } | ||
229 | |||
230 | struct idevice_subscription_context { | ||
231 | idevice_event_cb_t callback; | ||
232 | void *user_data; | ||
233 | usbmuxd_subscription_context_t ctx; | ||
234 | }; | ||
32 | 235 | ||
33 | static idevice_event_cb_t event_cb = NULL; | 236 | static idevice_subscription_context_t event_ctx = NULL; |
34 | 237 | ||
35 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) | 238 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) |
36 | { | 239 | { |
240 | idevice_subscription_context_t context = (idevice_subscription_context_t)user_data; | ||
37 | idevice_event_t ev; | 241 | idevice_event_t ev; |
38 | 242 | ||
39 | ev.event = event->event; | 243 | ev.event = event->event; |
40 | ev.uuid = event->device.uuid; | 244 | ev.udid = event->device.udid; |
41 | ev.conn_type = CONNECTION_USBMUXD; | 245 | ev.conn_type = 0; |
246 | if (event->device.conn_type == CONNECTION_TYPE_USB) { | ||
247 | ev.conn_type = CONNECTION_USBMUXD; | ||
248 | } else if (event->device.conn_type == CONNECTION_TYPE_NETWORK) { | ||
249 | ev.conn_type = CONNECTION_NETWORK; | ||
250 | } else { | ||
251 | debug_info("Unknown connection type %d", event->device.conn_type); | ||
252 | } | ||
42 | 253 | ||
43 | if (event_cb) { | 254 | if (context->callback) { |
44 | event_cb(&ev, user_data); | 255 | context->callback(&ev, context->user_data); |
45 | } | 256 | } |
46 | } | 257 | } |
47 | 258 | ||
48 | /** | 259 | idevice_error_t idevice_events_subscribe(idevice_subscription_context_t *context, idevice_event_cb_t callback, void *user_data) |
49 | * Register a callback function that will be called when device add/remove | ||
50 | * events occur. | ||
51 | * | ||
52 | * @param callback Callback function to call. | ||
53 | * @param user_data Application-specific data passed as parameter | ||
54 | * to the registered callback function. | ||
55 | * | ||
56 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
57 | */ | ||
58 | idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) | ||
59 | { | 260 | { |
60 | event_cb = callback; | 261 | if (!context || !callback) { |
61 | int res = usbmuxd_subscribe(usbmux_event_cb, user_data); | 262 | return IDEVICE_E_INVALID_ARG; |
62 | if (res != 0) { | 263 | } |
63 | event_cb = NULL; | 264 | *context = malloc(sizeof(struct idevice_subscription_context)); |
64 | debug_info("Error %d when subscribing usbmux event callback!", res); | 265 | if (!*context) { |
266 | debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__); | ||
267 | return IDEVICE_E_UNKNOWN_ERROR; | ||
268 | } | ||
269 | (*context)->callback = callback; | ||
270 | (*context)->user_data = user_data; | ||
271 | int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context); | ||
272 | if (res != 0) { | ||
273 | free(*context); | ||
274 | *context = NULL; | ||
275 | debug_info("ERROR: usbmuxd_subscribe() returned %d!", res); | ||
65 | return IDEVICE_E_UNKNOWN_ERROR; | 276 | return IDEVICE_E_UNKNOWN_ERROR; |
66 | } | 277 | } |
67 | return IDEVICE_E_SUCCESS; | 278 | return IDEVICE_E_SUCCESS; |
68 | } | 279 | } |
69 | 280 | ||
70 | /** | 281 | idevice_error_t idevice_events_unsubscribe(idevice_subscription_context_t context) |
71 | * Release the event callback function that has been registered with | ||
72 | * idevice_event_subscribe(). | ||
73 | * | ||
74 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
75 | */ | ||
76 | idevice_error_t idevice_event_unsubscribe() | ||
77 | { | 282 | { |
78 | event_cb = NULL; | 283 | if (!context) { |
79 | int res = usbmuxd_unsubscribe(); | 284 | return IDEVICE_E_INVALID_ARG; |
285 | } | ||
286 | int res = usbmuxd_events_unsubscribe(context->ctx); | ||
80 | if (res != 0) { | 287 | if (res != 0) { |
81 | debug_info("Error %d when unsubscribing usbmux event callback!", res); | 288 | debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res); |
82 | return IDEVICE_E_UNKNOWN_ERROR; | 289 | return IDEVICE_E_UNKNOWN_ERROR; |
83 | } | 290 | } |
291 | if (context == event_ctx) { | ||
292 | event_ctx = NULL; | ||
293 | } | ||
294 | free(context); | ||
295 | return IDEVICE_E_SUCCESS; | ||
296 | } | ||
297 | |||
298 | idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) | ||
299 | { | ||
300 | if (event_ctx) { | ||
301 | idevice_events_unsubscribe(event_ctx); | ||
302 | } | ||
303 | return idevice_events_subscribe(&event_ctx, callback, user_data); | ||
304 | } | ||
305 | |||
306 | idevice_error_t idevice_event_unsubscribe(void) | ||
307 | { | ||
308 | if (!event_ctx) { | ||
309 | return IDEVICE_E_SUCCESS; | ||
310 | } | ||
311 | event_ctx->callback = NULL; | ||
312 | return idevice_events_unsubscribe(event_ctx); | ||
313 | } | ||
314 | |||
315 | idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count) | ||
316 | { | ||
317 | usbmuxd_device_info_t *dev_list; | ||
318 | |||
319 | *devices = NULL; | ||
320 | *count = 0; | ||
321 | |||
322 | if (usbmuxd_get_device_list(&dev_list) < 0) { | ||
323 | debug_info("ERROR: usbmuxd is not running!", __func__); | ||
324 | return IDEVICE_E_NO_DEVICE; | ||
325 | } | ||
326 | |||
327 | idevice_info_t *newlist = NULL; | ||
328 | int i, newcount = 0; | ||
329 | |||
330 | for (i = 0; dev_list[i].handle > 0; i++) { | ||
331 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
332 | newlist[newcount] = malloc(sizeof(struct idevice_info)); | ||
333 | newlist[newcount]->udid = strdup(dev_list[i].udid); | ||
334 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { | ||
335 | newlist[newcount]->conn_type = CONNECTION_USBMUXD; | ||
336 | newlist[newcount]->conn_data = NULL; | ||
337 | } else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) { | ||
338 | newlist[newcount]->conn_type = CONNECTION_NETWORK; | ||
339 | struct sockaddr* saddr = (struct sockaddr*)(dev_list[i].conn_data); | ||
340 | size_t addrlen = 0; | ||
341 | switch (saddr->sa_family) { | ||
342 | case AF_INET: | ||
343 | addrlen = sizeof(struct sockaddr_in); | ||
344 | break; | ||
345 | #ifdef AF_INET6 | ||
346 | case AF_INET6: | ||
347 | addrlen = sizeof(struct sockaddr_in6); | ||
348 | break; | ||
349 | #endif | ||
350 | default: | ||
351 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
352 | continue; | ||
353 | } | ||
354 | newlist[newcount]->conn_data = malloc(addrlen); | ||
355 | memcpy(newlist[newcount]->conn_data, dev_list[i].conn_data, addrlen); | ||
356 | } | ||
357 | newcount++; | ||
358 | *devices = newlist; | ||
359 | } | ||
360 | usbmuxd_device_list_free(&dev_list); | ||
361 | |||
362 | *count = newcount; | ||
363 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
364 | newlist[newcount] = NULL; | ||
365 | *devices = newlist; | ||
366 | |||
367 | return IDEVICE_E_SUCCESS; | ||
368 | } | ||
369 | |||
370 | idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices) | ||
371 | { | ||
372 | if (devices) { | ||
373 | int i = 0; | ||
374 | while (devices[i]) { | ||
375 | free(devices[i]->udid); | ||
376 | free(devices[i]->conn_data); | ||
377 | free(devices[i]); | ||
378 | i++; | ||
379 | } | ||
380 | free(devices); | ||
381 | } | ||
84 | return IDEVICE_E_SUCCESS; | 382 | return IDEVICE_E_SUCCESS; |
85 | } | 383 | } |
86 | 384 | ||
87 | /** | ||
88 | * Get a list of currently available devices. | ||
89 | * | ||
90 | * @param devices List of uuids of devices that are currently available. | ||
91 | * This list is terminated by a NULL pointer. | ||
92 | * @param count Number of devices found. | ||
93 | * | ||
94 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
95 | */ | ||
96 | idevice_error_t idevice_get_device_list(char ***devices, int *count) | 385 | idevice_error_t idevice_get_device_list(char ***devices, int *count) |
97 | { | 386 | { |
98 | usbmuxd_device_info_t *dev_list; | 387 | usbmuxd_device_info_t *dev_list; |
@@ -101,7 +390,7 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
101 | *count = 0; | 390 | *count = 0; |
102 | 391 | ||
103 | if (usbmuxd_get_device_list(&dev_list) < 0) { | 392 | if (usbmuxd_get_device_list(&dev_list) < 0) { |
104 | debug_info("ERROR: usbmuxd is not running!\n", __func__); | 393 | debug_info("ERROR: usbmuxd is not running!", __func__); |
105 | return IDEVICE_E_NO_DEVICE; | 394 | return IDEVICE_E_NO_DEVICE; |
106 | } | 395 | } |
107 | 396 | ||
@@ -109,9 +398,11 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
109 | int i, newcount = 0; | 398 | int i, newcount = 0; |
110 | 399 | ||
111 | for (i = 0; dev_list[i].handle > 0; i++) { | 400 | for (i = 0; dev_list[i].handle > 0; i++) { |
112 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | 401 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { |
113 | newlist[newcount++] = strdup(dev_list[i].uuid); | 402 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); |
114 | *devices = newlist; | 403 | newlist[newcount++] = strdup(dev_list[i].udid); |
404 | *devices = newlist; | ||
405 | } | ||
115 | } | 406 | } |
116 | usbmuxd_device_list_free(&dev_list); | 407 | usbmuxd_device_list_free(&dev_list); |
117 | 408 | ||
@@ -123,62 +414,101 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
123 | return IDEVICE_E_SUCCESS; | 414 | return IDEVICE_E_SUCCESS; |
124 | } | 415 | } |
125 | 416 | ||
126 | /** | ||
127 | * Free a list of device uuids. | ||
128 | * | ||
129 | * @param devices List of uuids to free. | ||
130 | * | ||
131 | * @return Always returnes IDEVICE_E_SUCCESS. | ||
132 | */ | ||
133 | idevice_error_t idevice_device_list_free(char **devices) | 417 | idevice_error_t idevice_device_list_free(char **devices) |
134 | { | 418 | { |
135 | if (devices) { | 419 | if (devices) { |
136 | int i = 0; | 420 | int i = 0; |
137 | while (devices[i++]) { | 421 | while (devices[i]) { |
138 | free(devices[i]); | 422 | free(devices[i]); |
423 | i++; | ||
139 | } | 424 | } |
140 | free(devices); | 425 | free(devices); |
141 | } | 426 | } |
142 | return IDEVICE_E_SUCCESS; | 427 | return IDEVICE_E_SUCCESS; |
143 | } | 428 | } |
144 | 429 | ||
145 | /** | 430 | void idevice_set_debug_level(int level) |
146 | * Creates an idevice_t structure for the device specified by uuid, | 431 | { |
147 | * if the device is available. | 432 | internal_set_debug_level(level); |
148 | * | 433 | } |
149 | * @note The resulting idevice_t structure has to be freed with | 434 | |
150 | * idevice_free() if it is no longer used. | 435 | static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev) |
151 | * | 436 | { |
152 | * @param device Upon calling this function, a pointer to a location of type | 437 | if (!muxdev) |
153 | * idevice_t. On successful return, this location will be populated. | 438 | return NULL; |
154 | * @param uuid The UUID to match. | 439 | |
155 | * | 440 | idevice_t device = (idevice_t)malloc(sizeof(struct idevice_private)); |
156 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | 441 | if (!device) |
157 | */ | 442 | return NULL; |
158 | idevice_error_t idevice_new(idevice_t * device, const char *uuid) | 443 | |
444 | device->udid = strdup(muxdev->udid); | ||
445 | device->mux_id = muxdev->handle; | ||
446 | device->version = 0; | ||
447 | device->device_class = 0; | ||
448 | switch (muxdev->conn_type) { | ||
449 | case CONNECTION_TYPE_USB: | ||
450 | device->conn_type = CONNECTION_USBMUXD; | ||
451 | device->conn_data = NULL; | ||
452 | break; | ||
453 | case CONNECTION_TYPE_NETWORK: | ||
454 | device->conn_type = CONNECTION_NETWORK; | ||
455 | struct sockaddr* saddr = (struct sockaddr*)(muxdev->conn_data); | ||
456 | size_t addrlen = 0; | ||
457 | switch (saddr->sa_family) { | ||
458 | case AF_INET: | ||
459 | addrlen = sizeof(struct sockaddr_in); | ||
460 | break; | ||
461 | #ifdef AF_INET6 | ||
462 | case AF_INET6: | ||
463 | addrlen = sizeof(struct sockaddr_in6); | ||
464 | break; | ||
465 | #endif | ||
466 | default: | ||
467 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
468 | free(device->udid); | ||
469 | free(device); | ||
470 | return NULL; | ||
471 | } | ||
472 | device->conn_data = malloc(addrlen); | ||
473 | memcpy(device->conn_data, muxdev->conn_data, addrlen); | ||
474 | break; | ||
475 | default: | ||
476 | device->conn_type = 0; | ||
477 | device->conn_data = NULL; | ||
478 | break; | ||
479 | } | ||
480 | return device; | ||
481 | } | ||
482 | |||
483 | idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options) | ||
159 | { | 484 | { |
160 | usbmuxd_device_info_t muxdev; | 485 | usbmuxd_device_info_t muxdev; |
161 | int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); | 486 | int usbmux_options = 0; |
487 | if (options & IDEVICE_LOOKUP_USBMUX) { | ||
488 | usbmux_options |= DEVICE_LOOKUP_USBMUX; | ||
489 | } | ||
490 | if (options & IDEVICE_LOOKUP_NETWORK) { | ||
491 | usbmux_options |= DEVICE_LOOKUP_NETWORK; | ||
492 | } | ||
493 | if (options & IDEVICE_LOOKUP_PREFER_NETWORK) { | ||
494 | usbmux_options |= DEVICE_LOOKUP_PREFER_NETWORK; | ||
495 | } | ||
496 | int res = usbmuxd_get_device(udid, &muxdev, usbmux_options); | ||
162 | if (res > 0) { | 497 | if (res > 0) { |
163 | idevice_t phone = (idevice_t) malloc(sizeof(struct idevice_private)); | 498 | *device = idevice_from_mux_device(&muxdev); |
164 | phone->uuid = strdup(muxdev.uuid); | 499 | if (!*device) { |
165 | phone->conn_type = CONNECTION_USBMUXD; | 500 | return IDEVICE_E_UNKNOWN_ERROR; |
166 | phone->conn_data = (void*)(long)muxdev.handle; | 501 | } |
167 | *device = phone; | ||
168 | return IDEVICE_E_SUCCESS; | 502 | return IDEVICE_E_SUCCESS; |
169 | } | 503 | } |
170 | /* other connection types could follow here */ | ||
171 | |||
172 | return IDEVICE_E_NO_DEVICE; | 504 | return IDEVICE_E_NO_DEVICE; |
173 | } | 505 | } |
174 | 506 | ||
175 | /** | 507 | idevice_error_t idevice_new(idevice_t * device, const char *udid) |
176 | * Cleans up an idevice structure, then frees the structure itself. | 508 | { |
177 | * This is a library-level function; deals directly with the device to tear | 509 | return idevice_new_with_options(device, udid, 0); |
178 | * down relations, but otherwise is mostly internal. | 510 | } |
179 | * | 511 | |
180 | * @param device idevice_t to free. | ||
181 | */ | ||
182 | idevice_error_t idevice_free(idevice_t device) | 512 | idevice_error_t idevice_free(idevice_t device) |
183 | { | 513 | { |
184 | if (!device) | 514 | if (!device) |
@@ -187,11 +517,8 @@ idevice_error_t idevice_free(idevice_t device) | |||
187 | 517 | ||
188 | ret = IDEVICE_E_SUCCESS; | 518 | ret = IDEVICE_E_SUCCESS; |
189 | 519 | ||
190 | free(device->uuid); | 520 | free(device->udid); |
191 | 521 | ||
192 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
193 | device->conn_data = 0; | ||
194 | } | ||
195 | if (device->conn_data) { | 522 | if (device->conn_data) { |
196 | free(device->conn_data); | 523 | free(device->conn_data); |
197 | } | 524 | } |
@@ -199,16 +526,6 @@ idevice_error_t idevice_free(idevice_t device) | |||
199 | return ret; | 526 | return ret; |
200 | } | 527 | } |
201 | 528 | ||
202 | /** | ||
203 | * Set up a connection to the given device. | ||
204 | * | ||
205 | * @param device The device to connect to. | ||
206 | * @param port The destination port to connect to. | ||
207 | * @param connection Pointer to an idevice_connection_t that will be filled | ||
208 | * with the necessary data of the connection. | ||
209 | * | ||
210 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
211 | */ | ||
212 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) | 529 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) |
213 | { | 530 | { |
214 | if (!device) { | 531 | if (!device) { |
@@ -216,31 +533,80 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect | |||
216 | } | 533 | } |
217 | 534 | ||
218 | if (device->conn_type == CONNECTION_USBMUXD) { | 535 | if (device->conn_type == CONNECTION_USBMUXD) { |
219 | int sfd = usbmuxd_connect((uint32_t)(long)device->conn_data, port); | 536 | int sfd = usbmuxd_connect(device->mux_id, port); |
220 | if (sfd < 0) { | 537 | if (sfd < 0) { |
221 | debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); | 538 | debug_info("ERROR: Connecting to usbmux device failed: %d (%s)", sfd, strerror(-sfd)); |
539 | switch (-sfd) { | ||
540 | case ECONNREFUSED: | ||
541 | return IDEVICE_E_CONNREFUSED; | ||
542 | case ENODEV: | ||
543 | return IDEVICE_E_NO_DEVICE; | ||
544 | default: | ||
545 | break; | ||
546 | } | ||
222 | return IDEVICE_E_UNKNOWN_ERROR; | 547 | return IDEVICE_E_UNKNOWN_ERROR; |
223 | } | 548 | } |
224 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | 549 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); |
225 | new_connection->type = CONNECTION_USBMUXD; | 550 | new_connection->type = CONNECTION_USBMUXD; |
226 | new_connection->data = (void*)(long)sfd; | 551 | new_connection->data = (void*)(long)sfd; |
227 | new_connection->ssl_data = NULL; | 552 | new_connection->ssl_data = NULL; |
553 | new_connection->device = device; | ||
554 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
555 | new_connection->status = IDEVICE_E_SUCCESS; | ||
228 | *connection = new_connection; | 556 | *connection = new_connection; |
229 | return IDEVICE_E_SUCCESS; | 557 | return IDEVICE_E_SUCCESS; |
230 | } else { | 558 | } |
231 | debug_info("Unknown connection type %d", device->conn_type); | 559 | if (device->conn_type == CONNECTION_NETWORK) { |
560 | struct sockaddr* saddr = (struct sockaddr*)(device->conn_data); | ||
561 | switch (saddr->sa_family) { | ||
562 | case AF_INET: | ||
563 | #ifdef AF_INET6 | ||
564 | case AF_INET6: | ||
565 | #endif | ||
566 | break; | ||
567 | default: | ||
568 | debug_info("Unsupported address family 0x%02x", saddr->sa_family); | ||
569 | return IDEVICE_E_UNKNOWN_ERROR; | ||
570 | } | ||
571 | |||
572 | char addrtxt[48]; | ||
573 | addrtxt[0] = '\0'; | ||
574 | |||
575 | if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) { | ||
576 | debug_info("Failed to convert network address: %d (%s)", errno, strerror(errno)); | ||
577 | } | ||
578 | |||
579 | debug_info("Connecting to %s port %d...", addrtxt, port); | ||
580 | |||
581 | int sfd = socket_connect_addr(saddr, port); | ||
582 | if (sfd < 0) { | ||
583 | int result = errno; | ||
584 | debug_info("ERROR: Connecting to network device failed: %d (%s)", result, strerror(result)); | ||
585 | switch (result) { | ||
586 | case ECONNREFUSED: | ||
587 | return IDEVICE_E_CONNREFUSED; | ||
588 | default: | ||
589 | break; | ||
590 | } | ||
591 | return IDEVICE_E_NO_DEVICE; | ||
592 | } | ||
593 | |||
594 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | ||
595 | new_connection->type = CONNECTION_NETWORK; | ||
596 | new_connection->data = (void*)(long)sfd; | ||
597 | new_connection->ssl_data = NULL; | ||
598 | new_connection->device = device; | ||
599 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
600 | |||
601 | *connection = new_connection; | ||
602 | |||
603 | return IDEVICE_E_SUCCESS; | ||
232 | } | 604 | } |
233 | 605 | ||
606 | debug_info("Unknown connection type %d", device->conn_type); | ||
234 | return IDEVICE_E_UNKNOWN_ERROR; | 607 | return IDEVICE_E_UNKNOWN_ERROR; |
235 | } | 608 | } |
236 | 609 | ||
237 | /** | ||
238 | * Disconnect from the device and clean up the connection structure. | ||
239 | * | ||
240 | * @param connection The connection to close. | ||
241 | * | ||
242 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
243 | */ | ||
244 | idevice_error_t idevice_disconnect(idevice_connection_t connection) | 610 | idevice_error_t idevice_disconnect(idevice_connection_t connection) |
245 | { | 611 | { |
246 | if (!connection) { | 612 | if (!connection) { |
@@ -253,11 +619,19 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection) | |||
253 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; | 619 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; |
254 | if (connection->type == CONNECTION_USBMUXD) { | 620 | if (connection->type == CONNECTION_USBMUXD) { |
255 | usbmuxd_disconnect((int)(long)connection->data); | 621 | usbmuxd_disconnect((int)(long)connection->data); |
622 | connection->data = NULL; | ||
623 | result = IDEVICE_E_SUCCESS; | ||
624 | } else if (connection->type == CONNECTION_NETWORK) { | ||
625 | socket_close((int)(long)connection->data); | ||
626 | connection->data = NULL; | ||
256 | result = IDEVICE_E_SUCCESS; | 627 | result = IDEVICE_E_SUCCESS; |
257 | } else { | 628 | } else { |
258 | debug_info("Unknown connection type %d", connection->type); | 629 | debug_info("Unknown connection type %d", connection->type); |
259 | } | 630 | } |
631 | |||
260 | free(connection); | 632 | free(connection); |
633 | connection = NULL; | ||
634 | |||
261 | return result; | 635 | return result; |
262 | } | 636 | } |
263 | 637 | ||
@@ -271,46 +645,111 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection, | |||
271 | } | 645 | } |
272 | 646 | ||
273 | if (connection->type == CONNECTION_USBMUXD) { | 647 | if (connection->type == CONNECTION_USBMUXD) { |
274 | int res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); | 648 | int res; |
649 | do { | ||
650 | res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); | ||
651 | } while (res == -EAGAIN); | ||
275 | if (res < 0) { | 652 | if (res < 0) { |
276 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); | 653 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); |
277 | return IDEVICE_E_UNKNOWN_ERROR; | 654 | return IDEVICE_E_UNKNOWN_ERROR; |
278 | } | 655 | } |
279 | return IDEVICE_E_SUCCESS; | 656 | return IDEVICE_E_SUCCESS; |
280 | } else { | ||
281 | debug_info("Unknown connection type %d", connection->type); | ||
282 | } | 657 | } |
658 | if (connection->type == CONNECTION_NETWORK) { | ||
659 | int s = socket_send((int)(long)connection->data, (void*)data, len); | ||
660 | if (s < 0) { | ||
661 | *sent_bytes = 0; | ||
662 | return IDEVICE_E_UNKNOWN_ERROR; | ||
663 | } | ||
664 | *sent_bytes = s; | ||
665 | return IDEVICE_E_SUCCESS; | ||
666 | } | ||
667 | |||
668 | debug_info("Unknown connection type %d", connection->type); | ||
283 | return IDEVICE_E_UNKNOWN_ERROR; | 669 | return IDEVICE_E_UNKNOWN_ERROR; |
284 | 670 | ||
285 | } | 671 | } |
286 | 672 | ||
287 | /** | ||
288 | * Send data to a device via the given connection. | ||
289 | * | ||
290 | * @param connection The connection to send data over. | ||
291 | * @param data Buffer with data to send. | ||
292 | * @param len Size of the buffer to send. | ||
293 | * @param sent_bytes Pointer to an uint32_t that will be filled | ||
294 | * with the number of bytes actually sent. | ||
295 | * | ||
296 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
297 | */ | ||
298 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) | 673 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) |
299 | { | 674 | { |
300 | if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { | 675 | if (!connection || !data |
676 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
677 | || (connection->ssl_data && !connection->ssl_data->session) | ||
678 | #endif | ||
679 | ) { | ||
301 | return IDEVICE_E_INVALID_ARG; | 680 | return IDEVICE_E_INVALID_ARG; |
302 | } | 681 | } |
303 | 682 | ||
304 | if (connection->ssl_data) { | 683 | if (connection->ssl_data) { |
305 | ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); | 684 | connection->status = IDEVICE_E_SUCCESS; |
306 | if ((uint32_t)sent == (uint32_t)len) { | 685 | uint32_t sent = 0; |
307 | *sent_bytes = sent; | 686 | while (sent < len) { |
308 | return IDEVICE_E_SUCCESS; | 687 | #if defined(HAVE_OPENSSL) |
688 | int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent)); | ||
689 | if (s <= 0) { | ||
690 | int sslerr = SSL_get_error(connection->ssl_data->session, s); | ||
691 | if (sslerr == SSL_ERROR_WANT_WRITE) { | ||
692 | continue; | ||
693 | } | ||
694 | break; | ||
695 | } | ||
696 | #elif defined(HAVE_GNUTLS) | ||
697 | ssize_t s = gnutls_record_send(connection->ssl_data->session, (void*)(data+sent), (size_t)(len-sent)); | ||
698 | #elif defined(HAVE_MBEDTLS) | ||
699 | int s = mbedtls_ssl_write(&connection->ssl_data->ctx, (const unsigned char*)(data+sent), (size_t)(len-sent)); | ||
700 | #endif | ||
701 | if (s < 0) { | ||
702 | break; | ||
703 | } | ||
704 | sent += s; | ||
705 | } | ||
706 | debug_info("SSL_write %d, sent %d", len, sent); | ||
707 | if (sent < len) { | ||
708 | *sent_bytes = 0; | ||
709 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; | ||
710 | } | ||
711 | *sent_bytes = sent; | ||
712 | return IDEVICE_E_SUCCESS; | ||
713 | } | ||
714 | uint32_t sent = 0; | ||
715 | while (sent < len) { | ||
716 | uint32_t bytes = 0; | ||
717 | int s = internal_connection_send(connection, data+sent, len-sent, &bytes); | ||
718 | if (s < 0) { | ||
719 | break; | ||
720 | } | ||
721 | sent += bytes; | ||
722 | } | ||
723 | debug_info("internal_connection_send %d, sent %d", len, sent); | ||
724 | if (sent < len) { | ||
725 | *sent_bytes = sent; | ||
726 | if (sent == 0) { | ||
727 | return IDEVICE_E_UNKNOWN_ERROR; | ||
728 | } | ||
729 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
730 | } | ||
731 | *sent_bytes = sent; | ||
732 | return IDEVICE_E_SUCCESS; | ||
733 | } | ||
734 | |||
735 | static inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received) | ||
736 | { | ||
737 | if (conn_error < 0) { | ||
738 | switch (conn_error) { | ||
739 | case -EAGAIN: | ||
740 | if (len) { | ||
741 | debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error)); | ||
742 | } else { | ||
743 | debug_info("ERROR: received partial data (%s)", strerror(-conn_error)); | ||
744 | } | ||
745 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
746 | case -ETIMEDOUT: | ||
747 | return IDEVICE_E_TIMEOUT; | ||
748 | default: | ||
749 | return IDEVICE_E_UNKNOWN_ERROR; | ||
309 | } | 750 | } |
310 | *sent_bytes = 0; | ||
311 | return IDEVICE_E_SSL_ERROR; | ||
312 | } | 751 | } |
313 | return internal_connection_send(connection, data, len, sent_bytes); | 752 | return IDEVICE_E_SUCCESS; |
314 | } | 753 | } |
315 | 754 | ||
316 | /** | 755 | /** |
@@ -324,47 +763,92 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t | |||
324 | } | 763 | } |
325 | 764 | ||
326 | if (connection->type == CONNECTION_USBMUXD) { | 765 | if (connection->type == CONNECTION_USBMUXD) { |
327 | int res = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); | 766 | int conn_error = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); |
328 | if (res < 0) { | 767 | idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes); |
329 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res)); | 768 | if (error == IDEVICE_E_UNKNOWN_ERROR) { |
330 | return IDEVICE_E_UNKNOWN_ERROR; | 769 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error)); |
331 | } | 770 | } |
332 | return IDEVICE_E_SUCCESS; | 771 | return error; |
333 | } else { | ||
334 | debug_info("Unknown connection type %d", connection->type); | ||
335 | } | 772 | } |
773 | if (connection->type == CONNECTION_NETWORK) { | ||
774 | int res = socket_receive_timeout((int)(long)connection->data, data, len, 0, timeout); | ||
775 | idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0); | ||
776 | if (error == IDEVICE_E_SUCCESS) { | ||
777 | *recv_bytes = (uint32_t)res; | ||
778 | } else if (error == IDEVICE_E_UNKNOWN_ERROR) { | ||
779 | debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res)); | ||
780 | } | ||
781 | return error; | ||
782 | } | ||
783 | |||
784 | debug_info("Unknown connection type %d", connection->type); | ||
336 | return IDEVICE_E_UNKNOWN_ERROR; | 785 | return IDEVICE_E_UNKNOWN_ERROR; |
337 | } | 786 | } |
338 | 787 | ||
339 | /** | ||
340 | * Receive data from a device via the given connection. | ||
341 | * This function will return after the given timeout even if no data has been | ||
342 | * received. | ||
343 | * | ||
344 | * @param connection The connection to receive data from. | ||
345 | * @param data Buffer that will be filled with the received data. | ||
346 | * This buffer has to be large enough to hold len bytes. | ||
347 | * @param len Buffer size or number of bytes to receive. | ||
348 | * @param recv_bytes Number of bytes actually received. | ||
349 | * @param timeout Timeout in milliseconds after which this function should | ||
350 | * return even if no data has been received. | ||
351 | * | ||
352 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
353 | */ | ||
354 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) | 788 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) |
355 | { | 789 | { |
356 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 790 | if (!connection |
791 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
792 | || (connection->ssl_data && !connection->ssl_data->session) | ||
793 | #endif | ||
794 | || len == 0 | ||
795 | ) { | ||
357 | return IDEVICE_E_INVALID_ARG; | 796 | return IDEVICE_E_INVALID_ARG; |
358 | } | 797 | } |
359 | 798 | ||
360 | if (connection->ssl_data) { | 799 | if (connection->ssl_data) { |
361 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 800 | uint32_t received = 0; |
362 | if (received > 0) { | 801 | |
802 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
803 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
804 | } | ||
805 | |||
806 | // this should be reset after the SSL_read call on all codepaths, as | ||
807 | // the supplied timeout should only apply to the current read. | ||
808 | connection->ssl_recv_timeout = timeout; | ||
809 | connection->status = IDEVICE_E_SUCCESS; | ||
810 | while (received < len) { | ||
811 | #if defined(HAVE_OPENSSL) | ||
812 | int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received); | ||
813 | if (r > 0) { | ||
814 | received += r; | ||
815 | } else { | ||
816 | int sslerr = SSL_get_error(connection->ssl_data->session, r); | ||
817 | if (sslerr == SSL_ERROR_WANT_READ) { | ||
818 | continue; | ||
819 | } else if (sslerr == SSL_ERROR_ZERO_RETURN) { | ||
820 | if (connection->status == IDEVICE_E_TIMEOUT) { | ||
821 | SSL_set_shutdown(connection->ssl_data->session, 0); | ||
822 | } | ||
823 | } | ||
824 | break; | ||
825 | } | ||
826 | #elif defined(HAVE_GNUTLS) | ||
827 | ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received); | ||
828 | if (r > 0) { | ||
829 | received += r; | ||
830 | } else { | ||
831 | break; | ||
832 | } | ||
833 | #elif defined(HAVE_MBEDTLS) | ||
834 | int r = mbedtls_ssl_read(&connection->ssl_data->ctx, (void*)(data+received), (size_t)len-received); | ||
835 | if (r > 0) { | ||
836 | received += r; | ||
837 | } else { | ||
838 | break; | ||
839 | } | ||
840 | #endif | ||
841 | } | ||
842 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
843 | |||
844 | debug_info("SSL_read %d, received %d", len, received); | ||
845 | if (received < len) { | ||
363 | *recv_bytes = received; | 846 | *recv_bytes = received; |
364 | return IDEVICE_E_SUCCESS; | 847 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; |
365 | } | 848 | } |
366 | *recv_bytes = 0; | 849 | |
367 | return IDEVICE_E_SSL_ERROR; | 850 | *recv_bytes = received; |
851 | return IDEVICE_E_SUCCESS; | ||
368 | } | 852 | } |
369 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); | 853 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); |
370 | } | 854 | } |
@@ -384,35 +868,45 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti | |||
384 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); | 868 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); |
385 | return IDEVICE_E_UNKNOWN_ERROR; | 869 | return IDEVICE_E_UNKNOWN_ERROR; |
386 | } | 870 | } |
387 | |||
388 | return IDEVICE_E_SUCCESS; | 871 | return IDEVICE_E_SUCCESS; |
389 | } else { | ||
390 | debug_info("Unknown connection type %d", connection->type); | ||
391 | } | 872 | } |
873 | if (connection->type == CONNECTION_NETWORK) { | ||
874 | int res = socket_receive((int)(long)connection->data, data, len); | ||
875 | if (res < 0) { | ||
876 | debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res)); | ||
877 | return IDEVICE_E_UNKNOWN_ERROR; | ||
878 | } | ||
879 | *recv_bytes = (uint32_t)res; | ||
880 | return IDEVICE_E_SUCCESS; | ||
881 | } | ||
882 | |||
883 | debug_info("Unknown connection type %d", connection->type); | ||
392 | return IDEVICE_E_UNKNOWN_ERROR; | 884 | return IDEVICE_E_UNKNOWN_ERROR; |
393 | } | 885 | } |
394 | 886 | ||
395 | /** | ||
396 | * Receive data from a device via the given connection. | ||
397 | * This function is like idevice_connection_receive_timeout, but with a | ||
398 | * predefined reasonable timeout. | ||
399 | * | ||
400 | * @param connection The connection to receive data from. | ||
401 | * @param data Buffer that will be filled with the received data. | ||
402 | * This buffer has to be large enough to hold len bytes. | ||
403 | * @param len Buffer size or number of bytes to receive. | ||
404 | * @param recv_bytes Number of bytes actually received. | ||
405 | * | ||
406 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
407 | */ | ||
408 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) | 887 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) |
409 | { | 888 | { |
410 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 889 | if (!connection |
890 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
891 | || (connection->ssl_data && !connection->ssl_data->session) | ||
892 | #endif | ||
893 | ) { | ||
411 | return IDEVICE_E_INVALID_ARG; | 894 | return IDEVICE_E_INVALID_ARG; |
412 | } | 895 | } |
413 | 896 | ||
414 | if (connection->ssl_data) { | 897 | if (connection->ssl_data) { |
898 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
899 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
900 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
901 | } | ||
902 | #if defined(HAVE_OPENSSL) | ||
903 | int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len); | ||
904 | debug_info("SSL_read %d, received %d", len, received); | ||
905 | #elif defined(HAVE_GNUTLS) | ||
415 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 906 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); |
907 | #elif defined(HAVE_MBEDTLS) | ||
908 | int received = mbedtls_ssl_read(&connection->ssl_data->ctx, (unsigned char*)data, (size_t)len); | ||
909 | #endif | ||
416 | if (received > 0) { | 910 | if (received > 0) { |
417 | *recv_bytes = received; | 911 | *recv_bytes = received; |
418 | return IDEVICE_E_SUCCESS; | 912 | return IDEVICE_E_SUCCESS; |
@@ -423,89 +917,105 @@ idevice_error_t idevice_connection_receive(idevice_connection_t connection, char | |||
423 | return internal_connection_receive(connection, data, len, recv_bytes); | 917 | return internal_connection_receive(connection, data, len, recv_bytes); |
424 | } | 918 | } |
425 | 919 | ||
426 | /** | 920 | idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *fd) |
427 | * Gets the handle of the device. Depends on the connection type. | ||
428 | */ | ||
429 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) | ||
430 | { | 921 | { |
431 | if (!device) | 922 | if (!connection || !fd) { |
432 | return IDEVICE_E_INVALID_ARG; | 923 | return IDEVICE_E_INVALID_ARG; |
924 | } | ||
433 | 925 | ||
434 | if (device->conn_type == CONNECTION_USBMUXD) { | 926 | if (connection->type == CONNECTION_USBMUXD) { |
435 | *handle = (uint32_t)(long)device->conn_data; | 927 | *fd = (int)(long)connection->data; |
928 | return IDEVICE_E_SUCCESS; | ||
929 | } | ||
930 | if (connection->type == CONNECTION_NETWORK) { | ||
931 | *fd = (int)(long)connection->data; | ||
436 | return IDEVICE_E_SUCCESS; | 932 | return IDEVICE_E_SUCCESS; |
437 | } else { | ||
438 | debug_info("Unknown connection type %d", device->conn_type); | ||
439 | } | 933 | } |
934 | |||
935 | debug_info("Unknown connection type %d", connection->type); | ||
440 | return IDEVICE_E_UNKNOWN_ERROR; | 936 | return IDEVICE_E_UNKNOWN_ERROR; |
441 | } | 937 | } |
442 | 938 | ||
443 | /** | 939 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) |
444 | * Gets the unique id for the device. | 940 | { |
445 | */ | 941 | if (!device || !handle) |
446 | idevice_error_t idevice_get_uuid(idevice_t device, char **uuid) | 942 | return IDEVICE_E_INVALID_ARG; |
943 | |||
944 | *handle = device->mux_id; | ||
945 | return IDEVICE_E_SUCCESS; | ||
946 | } | ||
947 | |||
948 | idevice_error_t idevice_get_udid(idevice_t device, char **udid) | ||
447 | { | 949 | { |
448 | if (!device || !uuid) | 950 | if (!device || !udid) |
449 | return IDEVICE_E_INVALID_ARG; | 951 | return IDEVICE_E_INVALID_ARG; |
450 | 952 | ||
451 | *uuid = strdup(device->uuid); | 953 | if (device->udid) { |
954 | *udid = strdup(device->udid); | ||
955 | } | ||
452 | return IDEVICE_E_SUCCESS; | 956 | return IDEVICE_E_SUCCESS; |
453 | } | 957 | } |
454 | 958 | ||
959 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
960 | typedef ssize_t ssl_cb_ret_type_t; | ||
961 | #elif defined(HAVE_MBEDTLS) | ||
962 | typedef int ssl_cb_ret_type_t; | ||
963 | #endif | ||
964 | |||
455 | /** | 965 | /** |
456 | * Internally used gnutls callback function for receiving encrypted data. | 966 | * Internally used SSL callback function for receiving encrypted data. |
457 | */ | 967 | */ |
458 | static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 968 | static ssl_cb_ret_type_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length) |
459 | { | 969 | { |
460 | int bytes = 0, pos_start_fill = 0; | 970 | uint32_t bytes = 0; |
461 | size_t tbytes = 0; | 971 | uint32_t pos = 0; |
462 | int this_len = length; | ||
463 | idevice_error_t res; | 972 | idevice_error_t res; |
464 | idevice_connection_t connection = (idevice_connection_t)transport; | 973 | unsigned int timeout = connection->ssl_recv_timeout; |
465 | char *recv_buffer; | ||
466 | |||
467 | debug_info("pre-read client wants %zi bytes", length); | ||
468 | 974 | ||
469 | recv_buffer = (char *) malloc(sizeof(char) * this_len); | 975 | debug_info("pre-read length = %zi bytes", length); |
470 | 976 | ||
471 | /* repeat until we have the full data or an error occurs */ | 977 | /* repeat until we have the full data or an error occurs */ |
472 | do { | 978 | do { |
473 | if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { | 979 | bytes = 0; |
474 | debug_info("ERROR: idevice_connection_receive returned %d", res); | 980 | if (timeout == (unsigned int)-1) { |
475 | return res; | 981 | res = internal_connection_receive(connection, buffer + pos, (uint32_t)length - pos, &bytes); |
982 | } else { | ||
983 | res = internal_connection_receive_timeout(connection, buffer + pos, (uint32_t)length - pos, &bytes, (unsigned int)timeout); | ||
476 | } | 984 | } |
477 | debug_info("post-read we got %i bytes", bytes); | 985 | if (res != IDEVICE_E_SUCCESS) { |
986 | if (res != IDEVICE_E_TIMEOUT) { | ||
987 | debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res); | ||
988 | } | ||
989 | connection->status = res; | ||
990 | return -1; | ||
991 | } | ||
992 | debug_info("read %i bytes", bytes); | ||
478 | 993 | ||
479 | /* increase read count */ | 994 | /* increase read count */ |
480 | tbytes += bytes; | 995 | pos += bytes; |
481 | 996 | if (pos < (uint32_t)length) { | |
482 | /* fill the buffer with what we got right now */ | 997 | debug_info("re-read trying to read missing %i bytes", (uint32_t)length - pos); |
483 | memcpy(buffer + pos_start_fill, recv_buffer, bytes); | ||
484 | pos_start_fill += bytes; | ||
485 | |||
486 | if (tbytes >= length) { | ||
487 | break; | ||
488 | } | 998 | } |
999 | } while (pos < (uint32_t)length); | ||
489 | 1000 | ||
490 | this_len = length - tbytes; | 1001 | debug_info("post-read received %i bytes", pos); |
491 | debug_info("re-read trying to read missing %i bytes", this_len); | ||
492 | } while (tbytes < length); | ||
493 | 1002 | ||
494 | if (recv_buffer) { | 1003 | return pos; |
495 | free(recv_buffer); | ||
496 | } | ||
497 | return tbytes; | ||
498 | } | 1004 | } |
499 | 1005 | ||
500 | /** | 1006 | /** |
501 | * Internally used gnutls callback function for sending encrypted data. | 1007 | * Internally used SSL callback function for sending encrypted data. |
502 | */ | 1008 | */ |
503 | static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 1009 | static ssl_cb_ret_type_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length) |
504 | { | 1010 | { |
505 | uint32_t bytes = 0; | 1011 | uint32_t bytes = 0; |
506 | idevice_connection_t connection = (idevice_connection_t)transport; | 1012 | idevice_error_t res; |
507 | debug_info("pre-send length = %zi", length); | 1013 | debug_info("pre-send length = %zi bytes", length); |
508 | internal_connection_send(connection, buffer, length, &bytes); | 1014 | if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) { |
1015 | debug_info("ERROR: internal_connection_send returned %d", res); | ||
1016 | connection->status = res; | ||
1017 | return -1; | ||
1018 | } | ||
509 | debug_info("post-send sent %i bytes", bytes); | 1019 | debug_info("post-send sent %i bytes", bytes); |
510 | return bytes; | 1020 | return bytes; |
511 | } | 1021 | } |
@@ -518,6 +1028,14 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
518 | if (!ssl_data) | 1028 | if (!ssl_data) |
519 | return; | 1029 | return; |
520 | 1030 | ||
1031 | #if defined(HAVE_OPENSSL) | ||
1032 | if (ssl_data->session) { | ||
1033 | SSL_free(ssl_data->session); | ||
1034 | } | ||
1035 | if (ssl_data->ctx) { | ||
1036 | SSL_CTX_free(ssl_data->ctx); | ||
1037 | } | ||
1038 | #elif defined(HAVE_GNUTLS) | ||
521 | if (ssl_data->session) { | 1039 | if (ssl_data->session) { |
522 | gnutls_deinit(ssl_data->session); | 1040 | gnutls_deinit(ssl_data->session); |
523 | } | 1041 | } |
@@ -536,20 +1054,118 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
536 | if (ssl_data->host_privkey) { | 1054 | if (ssl_data->host_privkey) { |
537 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); | 1055 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); |
538 | } | 1056 | } |
1057 | #elif defined(HAVE_MBEDTLS) | ||
1058 | mbedtls_pk_free(&ssl_data->root_privkey); | ||
1059 | mbedtls_x509_crt_free(&ssl_data->certificate); | ||
1060 | mbedtls_entropy_free(&ssl_data->entropy); | ||
1061 | mbedtls_ctr_drbg_free(&ssl_data->ctr_drbg); | ||
1062 | mbedtls_ssl_config_free(&ssl_data->config); | ||
1063 | mbedtls_ssl_free(&ssl_data->ctx); | ||
1064 | #endif | ||
1065 | } | ||
1066 | |||
1067 | #ifdef HAVE_OPENSSL | ||
1068 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1069 | 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) | ||
1070 | #else | ||
1071 | static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue) | ||
1072 | #endif | ||
1073 | { | ||
1074 | ssize_t bytes = 0; | ||
1075 | idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b); | ||
1076 | #if OPENSSL_VERSION_NUMBER < 0x30000000L | ||
1077 | size_t len = (size_t)argi; | ||
1078 | size_t *processed = (size_t*)&bytes; | ||
1079 | #endif | ||
1080 | switch (oper) { | ||
1081 | case (BIO_CB_READ|BIO_CB_RETURN): | ||
1082 | if (argp) { | ||
1083 | bytes = internal_ssl_read(conn, (char *)argp, len); | ||
1084 | *processed = bytes; | ||
1085 | return (long)bytes; | ||
1086 | } | ||
1087 | return 0; | ||
1088 | case (BIO_CB_PUTS|BIO_CB_RETURN): | ||
1089 | len = strlen(argp); | ||
1090 | // fallthrough | ||
1091 | case (BIO_CB_WRITE|BIO_CB_RETURN): | ||
1092 | bytes = internal_ssl_write(conn, argp, len); | ||
1093 | *processed = bytes; | ||
1094 | return (long)bytes; | ||
1095 | default: | ||
1096 | return retvalue; | ||
1097 | } | ||
1098 | } | ||
1099 | |||
1100 | static BIO *ssl_idevice_bio_new(idevice_connection_t conn) | ||
1101 | { | ||
1102 | BIO *b = BIO_new(BIO_s_null()); | ||
1103 | if (!b) return NULL; | ||
1104 | BIO_set_callback_arg(b, (char *)conn); | ||
1105 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1106 | BIO_set_callback_ex(b, ssl_idevice_bio_callback); | ||
1107 | #else | ||
1108 | BIO_set_callback(b, ssl_idevice_bio_callback); | ||
1109 | #endif | ||
1110 | return b; | ||
1111 | } | ||
1112 | |||
1113 | static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx) | ||
1114 | { | ||
1115 | return 1; | ||
1116 | } | ||
1117 | |||
1118 | #ifndef STRIP_DEBUG_CODE | ||
1119 | static const char *ssl_error_to_string(int e) | ||
1120 | { | ||
1121 | switch(e) { | ||
1122 | case SSL_ERROR_NONE: | ||
1123 | return "SSL_ERROR_NONE"; | ||
1124 | case SSL_ERROR_SSL: | ||
1125 | return ERR_error_string(ERR_get_error(), NULL); | ||
1126 | case SSL_ERROR_WANT_READ: | ||
1127 | return "SSL_ERROR_WANT_READ"; | ||
1128 | case SSL_ERROR_WANT_WRITE: | ||
1129 | return "SSL_ERROR_WANT_WRITE"; | ||
1130 | case SSL_ERROR_WANT_X509_LOOKUP: | ||
1131 | return "SSL_ERROR_WANT_X509_LOOKUP"; | ||
1132 | case SSL_ERROR_SYSCALL: | ||
1133 | return "SSL_ERROR_SYSCALL"; | ||
1134 | case SSL_ERROR_ZERO_RETURN: | ||
1135 | return "SSL_ERROR_ZERO_RETURN"; | ||
1136 | case SSL_ERROR_WANT_CONNECT: | ||
1137 | return "SSL_ERROR_WANT_CONNECT"; | ||
1138 | case SSL_ERROR_WANT_ACCEPT: | ||
1139 | return "SSL_ERROR_WANT_ACCEPT"; | ||
1140 | default: | ||
1141 | return "UNKOWN_ERROR_VALUE"; | ||
1142 | } | ||
539 | } | 1143 | } |
1144 | #endif | ||
1145 | #endif | ||
540 | 1146 | ||
1147 | #if defined(HAVE_GNUTLS) | ||
541 | /** | 1148 | /** |
542 | * Internally used gnutls callback function that gets called during handshake. | 1149 | * Internally used gnutls callback function that gets called during handshake. |
543 | */ | 1150 | */ |
544 | static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) | 1151 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
1152 | static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st) | ||
1153 | #else | ||
1154 | static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) | ||
1155 | #endif | ||
545 | { | 1156 | { |
546 | int res = -1; | 1157 | int res = -1; |
547 | gnutls_certificate_type_t type = gnutls_certificate_type_get (session); | 1158 | gnutls_certificate_type_t type = gnutls_certificate_type_get(session); |
548 | if (type == GNUTLS_CRT_X509) { | 1159 | if (type == GNUTLS_CRT_X509) { |
549 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr (session); | 1160 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr(session); |
550 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { | 1161 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { |
551 | debug_info("Passing certificate"); | 1162 | debug_info("Passing certificate"); |
1163 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 | ||
1164 | st->cert_type = type; | ||
1165 | st->key_type = GNUTLS_PRIVKEY_X509; | ||
1166 | #else | ||
552 | st->type = type; | 1167 | st->type = type; |
1168 | #endif | ||
553 | st->ncerts = 1; | 1169 | st->ncerts = 1; |
554 | st->cert.x509 = &ssl_data->host_cert; | 1170 | st->cert.x509 = &ssl_data->host_cert; |
555 | st->key.x509 = ssl_data->host_privkey; | 1171 | st->key.x509 = ssl_data->host_privkey; |
@@ -559,46 +1175,204 @@ static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_ | |||
559 | } | 1175 | } |
560 | return res; | 1176 | return res; |
561 | } | 1177 | } |
1178 | #elif defined(HAVE_MBEDTLS) | ||
1179 | static void _mbedtls_log_cb(void* ctx, int level, const char* filename, int line, const char* message) | ||
1180 | { | ||
1181 | fprintf(stderr, "[mbedtls][%d] %s:%d => %s", level, filename, line, message); | ||
1182 | } | ||
1183 | |||
1184 | static int cert_verify_cb(void* ctx, mbedtls_x509_crt* cert, int depth, uint32_t *flags) | ||
1185 | { | ||
1186 | *flags = 0; | ||
1187 | return 0; | ||
1188 | } | ||
1189 | |||
1190 | static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len) | ||
1191 | { | ||
1192 | memset(buf, 4, len); | ||
1193 | return 0; | ||
1194 | } | ||
1195 | #endif | ||
562 | 1196 | ||
563 | /** | ||
564 | * Enables SSL for the given connection. | ||
565 | * | ||
566 | * @param connection The connection to enable SSL for. | ||
567 | * | ||
568 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
569 | * is NULL or connection->ssl_data is non-NULL, or IDEVICE_E_SSL_ERROR when | ||
570 | * SSL initialization, setup, or handshake fails. | ||
571 | */ | ||
572 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | 1197 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) |
573 | { | 1198 | { |
574 | if (!connection || connection->ssl_data) | 1199 | if (!connection || connection->ssl_data) |
575 | return IDEVICE_E_INVALID_ARG; | 1200 | return IDEVICE_E_INVALID_ARG; |
576 | 1201 | ||
577 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; | 1202 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; |
578 | uint32_t return_me = 0; | 1203 | plist_t pair_record = NULL; |
1204 | |||
1205 | userpref_error_t uerr = userpref_read_pair_record(connection->device->udid, &pair_record); | ||
1206 | if (uerr != USERPREF_E_SUCCESS) { | ||
1207 | debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s (%d)", connection->device->udid, uerr); | ||
1208 | return ret; | ||
1209 | } | ||
1210 | |||
1211 | #if defined(HAVE_OPENSSL) | ||
1212 | key_data_t root_cert = { NULL, 0 }; | ||
1213 | key_data_t root_privkey = { NULL, 0 }; | ||
1214 | |||
1215 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
1216 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
579 | 1217 | ||
1218 | if (pair_record) | ||
1219 | plist_free(pair_record); | ||
1220 | |||
1221 | BIO *ssl_bio = ssl_idevice_bio_new(connection); | ||
1222 | if (!ssl_bio) { | ||
1223 | debug_info("ERROR: Could not create SSL bio."); | ||
1224 | return ret; | ||
1225 | } | ||
1226 | |||
1227 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); | ||
1228 | if (ssl_ctx == NULL) { | ||
1229 | debug_info("ERROR: Could not create SSL context."); | ||
1230 | BIO_free(ssl_bio); | ||
1231 | return ret; | ||
1232 | } | ||
1233 | |||
1234 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || \ | ||
1235 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3060000fL)) | ||
1236 | SSL_CTX_set_security_level(ssl_ctx, 0); | ||
1237 | #endif | ||
1238 | |||
1239 | #if OPENSSL_VERSION_NUMBER < 0x10100002L || \ | ||
1240 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL)) | ||
1241 | /* force use of TLSv1 for older devices */ | ||
1242 | if (connection->device->version < DEVICE_VERSION(10,0,0)) { | ||
1243 | #ifdef SSL_OP_NO_TLSv1_1 | ||
1244 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); | ||
1245 | #endif | ||
1246 | #ifdef SSL_OP_NO_TLSv1_2 | ||
1247 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); | ||
1248 | #endif | ||
1249 | #ifdef SSL_OP_NO_TLSv1_3 | ||
1250 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); | ||
1251 | #endif | ||
1252 | } | ||
1253 | #else | ||
1254 | SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); | ||
1255 | if (connection->device->version < DEVICE_VERSION(10,0,0)) { | ||
1256 | SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION); | ||
1257 | if (connection->device->version == 0) { | ||
1258 | /* | ||
1259 | iOS 1 doesn't understand TLS1_VERSION, it can only speak SSL3_VERSION. | ||
1260 | However, modern OpenSSL is usually compiled without SSLv3 support. | ||
1261 | So if we set min_proto_version to SSL3_VERSION on an OpenSSL instance which doesn't support it, | ||
1262 | it will just ignore min_proto_version altogether and fall back to an even higher version. | ||
1263 | To avoid accidentally breaking iOS 2.0+, we set min version to 0 instead. | ||
1264 | Here is what documentation says: | ||
1265 | Setting the minimum or maximum version to 0, | ||
1266 | will enable protocol versions down to the lowest version, | ||
1267 | or up to the highest version supported by the library, respectively. | ||
1268 | */ | ||
1269 | SSL_CTX_set_min_proto_version(ssl_ctx, 0); | ||
1270 | } | ||
1271 | } | ||
1272 | #endif | ||
1273 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1274 | #if defined(SSL_OP_IGNORE_UNEXPECTED_EOF) | ||
1275 | /* | ||
1276 | * For OpenSSL 3 and later, mark close_notify alerts as optional. | ||
1277 | * For prior versions of OpenSSL we check for SSL_ERROR_SYSCALL when | ||
1278 | * reading instead (this error changes to SSL_ERROR_SSL in OpenSSL 3). | ||
1279 | */ | ||
1280 | SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); | ||
1281 | #endif | ||
1282 | #if defined(SSL_OP_LEGACY_SERVER_CONNECT) | ||
1283 | /* | ||
1284 | * Without setting SSL_OP_LEGACY_SERVER_CONNECT, OpenSSL 3 fails with | ||
1285 | * error "unsafe legacy renegotiation disabled" when talking to iOS 5 | ||
1286 | */ | ||
1287 | SSL_CTX_set_options(ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT); | ||
1288 | #endif | ||
1289 | #endif | ||
1290 | |||
1291 | BIO* membp; | ||
1292 | X509* rootCert = NULL; | ||
1293 | membp = BIO_new_mem_buf(root_cert.data, root_cert.size); | ||
1294 | PEM_read_bio_X509(membp, &rootCert, NULL, NULL); | ||
1295 | BIO_free(membp); | ||
1296 | if (SSL_CTX_use_certificate(ssl_ctx, rootCert) != 1) { | ||
1297 | debug_info("WARNING: Could not load RootCertificate"); | ||
1298 | } | ||
1299 | X509_free(rootCert); | ||
1300 | free(root_cert.data); | ||
1301 | |||
1302 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1303 | EVP_PKEY* rootPrivKey = NULL; | ||
1304 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
1305 | PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
1306 | BIO_free(membp); | ||
1307 | if (SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
1308 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
1309 | } | ||
1310 | EVP_PKEY_free(rootPrivKey); | ||
1311 | #else | ||
1312 | RSA* rootPrivKey = NULL; | ||
1313 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
1314 | PEM_read_bio_RSAPrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
1315 | BIO_free(membp); | ||
1316 | if (SSL_CTX_use_RSAPrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
1317 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
1318 | } | ||
1319 | RSA_free(rootPrivKey); | ||
1320 | #endif | ||
1321 | free(root_privkey.data); | ||
1322 | |||
1323 | SSL *ssl = SSL_new(ssl_ctx); | ||
1324 | if (!ssl) { | ||
1325 | debug_info("ERROR: Could not create SSL object"); | ||
1326 | BIO_free(ssl_bio); | ||
1327 | SSL_CTX_free(ssl_ctx); | ||
1328 | return ret; | ||
1329 | } | ||
1330 | SSL_set_connect_state(ssl); | ||
1331 | SSL_set_verify(ssl, 0, ssl_verify_callback); | ||
1332 | SSL_set_bio(ssl, ssl_bio, ssl_bio); | ||
1333 | |||
1334 | debug_info("Performing SSL handshake"); | ||
1335 | int ssl_error = 0; | ||
1336 | do { | ||
1337 | ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl)); | ||
1338 | if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) { | ||
1339 | break; | ||
1340 | } | ||
1341 | #ifdef WIN32 | ||
1342 | Sleep(100); | ||
1343 | #else | ||
1344 | struct timespec ts = { 0, 100000000 }; | ||
1345 | nanosleep(&ts, NULL); | ||
1346 | #endif | ||
1347 | } while (1); | ||
1348 | if (ssl_error != 0) { | ||
1349 | debug_info("ERROR during SSL handshake: %s", ssl_error_to_string(ssl_error)); | ||
1350 | SSL_free(ssl); | ||
1351 | SSL_CTX_free(ssl_ctx); | ||
1352 | } else { | ||
1353 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
1354 | ssl_data_loc->session = ssl; | ||
1355 | ssl_data_loc->ctx = ssl_ctx; | ||
1356 | connection->ssl_data = ssl_data_loc; | ||
1357 | ret = IDEVICE_E_SUCCESS; | ||
1358 | debug_info("SSL mode enabled, %s, cipher: %s", SSL_get_version(ssl), SSL_get_cipher(ssl)); | ||
1359 | } | ||
1360 | /* required for proper multi-thread clean up to prevent leaks */ | ||
1361 | openssl_remove_thread_state(); | ||
1362 | #elif defined(HAVE_GNUTLS) | ||
580 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | 1363 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); |
581 | 1364 | ||
582 | /* Set up GnuTLS... */ | 1365 | /* Set up GnuTLS... */ |
583 | debug_info("enabling SSL mode"); | 1366 | debug_info("enabling SSL mode"); |
584 | errno = 0; | 1367 | errno = 0; |
585 | gnutls_global_init(); | ||
586 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); | 1368 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); |
587 | gnutls_certificate_client_set_retrieve_function (ssl_data_loc->certificate, internal_cert_callback); | 1369 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
1370 | gnutls_certificate_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
1371 | #else | ||
1372 | gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
1373 | #endif | ||
588 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); | 1374 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); |
589 | { | 1375 | gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-TLS1.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL); |
590 | int protocol_priority[16] = { GNUTLS_SSL3, 0 }; | ||
591 | int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 }; | ||
592 | int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; | ||
593 | int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; | ||
594 | int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; | ||
595 | |||
596 | gnutls_cipher_set_priority(ssl_data_loc->session, cipher_priority); | ||
597 | gnutls_compression_set_priority(ssl_data_loc->session, comp_priority); | ||
598 | gnutls_kx_set_priority(ssl_data_loc->session, kx_priority); | ||
599 | gnutls_protocol_set_priority(ssl_data_loc->session, protocol_priority); | ||
600 | gnutls_mac_set_priority(ssl_data_loc->session, mac_priority); | ||
601 | } | ||
602 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); | 1376 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); |
603 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); | 1377 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); |
604 | 1378 | ||
@@ -607,10 +1381,13 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
607 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); | 1381 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); |
608 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); | 1382 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); |
609 | 1383 | ||
610 | userpref_error_t uerr = userpref_get_keys_and_certs(ssl_data_loc->root_privkey, ssl_data_loc->root_cert, ssl_data_loc->host_privkey, ssl_data_loc->host_cert); | 1384 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, ssl_data_loc->root_cert); |
611 | if (uerr != USERPREF_E_SUCCESS) { | 1385 | pair_record_import_crt_with_name(pair_record, USERPREF_HOST_CERTIFICATE_KEY, ssl_data_loc->host_cert); |
612 | debug_info("Error %d when loading keys and certificates! %d", uerr); | 1386 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, ssl_data_loc->root_privkey); |
613 | } | 1387 | pair_record_import_key_with_name(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, ssl_data_loc->host_privkey); |
1388 | |||
1389 | if (pair_record) | ||
1390 | plist_free(pair_record); | ||
614 | 1391 | ||
615 | debug_info("GnuTLS step 1..."); | 1392 | debug_info("GnuTLS step 1..."); |
616 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); | 1393 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); |
@@ -619,46 +1396,146 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
619 | debug_info("GnuTLS step 3..."); | 1396 | debug_info("GnuTLS step 3..."); |
620 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); | 1397 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); |
621 | debug_info("GnuTLS step 4 -- now handshaking..."); | 1398 | debug_info("GnuTLS step 4 -- now handshaking..."); |
622 | if (errno) | 1399 | if (errno) { |
623 | debug_info("WARN: errno says %s before handshake!", strerror(errno)); | 1400 | debug_info("WARNING: errno says %s before handshake!", strerror(errno)); |
624 | return_me = gnutls_handshake(ssl_data_loc->session); | 1401 | } |
1402 | |||
1403 | int return_me = 0; | ||
1404 | do { | ||
1405 | return_me = gnutls_handshake(ssl_data_loc->session); | ||
1406 | } while(return_me == GNUTLS_E_AGAIN || return_me == GNUTLS_E_INTERRUPTED); | ||
1407 | |||
625 | debug_info("GnuTLS handshake done..."); | 1408 | debug_info("GnuTLS handshake done..."); |
626 | 1409 | ||
627 | if (return_me != GNUTLS_E_SUCCESS) { | 1410 | if (return_me != GNUTLS_E_SUCCESS) { |
628 | internal_ssl_cleanup(ssl_data_loc); | 1411 | internal_ssl_cleanup(ssl_data_loc); |
629 | free(ssl_data_loc); | 1412 | free(ssl_data_loc); |
630 | debug_info("GnuTLS reported something wrong."); | 1413 | debug_info("GnuTLS reported something wrong: %s", gnutls_strerror(return_me)); |
631 | gnutls_perror(return_me); | ||
632 | debug_info("oh.. errno says %s", strerror(errno)); | 1414 | debug_info("oh.. errno says %s", strerror(errno)); |
633 | } else { | 1415 | } else { |
634 | connection->ssl_data = ssl_data_loc; | 1416 | connection->ssl_data = ssl_data_loc; |
635 | ret = IDEVICE_E_SUCCESS; | 1417 | ret = IDEVICE_E_SUCCESS; |
636 | debug_info("SSL mode enabled"); | 1418 | debug_info("SSL mode enabled"); |
637 | } | 1419 | } |
1420 | #elif defined(HAVE_MBEDTLS) | ||
1421 | key_data_t root_cert = { NULL, 0 }; | ||
1422 | key_data_t root_privkey = { NULL, 0 }; | ||
1423 | |||
1424 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
1425 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
1426 | |||
1427 | plist_free(pair_record); | ||
1428 | |||
1429 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
1430 | |||
1431 | mbedtls_ssl_init(&ssl_data_loc->ctx); | ||
1432 | mbedtls_ssl_config_init(&ssl_data_loc->config); | ||
1433 | mbedtls_entropy_init(&ssl_data_loc->entropy); | ||
1434 | mbedtls_ctr_drbg_init(&ssl_data_loc->ctr_drbg); | ||
1435 | |||
1436 | int r = mbedtls_ctr_drbg_seed(&ssl_data_loc->ctr_drbg, mbedtls_entropy_func, &ssl_data_loc->entropy, NULL, 0); | ||
1437 | if (r != 0) { | ||
1438 | debug_info("ERROR: [mbedtls] mbedtls_ctr_drbg_seed failed: %d", r); | ||
1439 | return ret; | ||
1440 | } | ||
1441 | |||
1442 | if (mbedtls_ssl_config_defaults(&ssl_data_loc->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { | ||
1443 | debug_info("ERROR: [mbedtls] Failed to set config defaults"); | ||
1444 | return ret; | ||
1445 | } | ||
1446 | |||
1447 | mbedtls_ssl_conf_rng(&ssl_data_loc->config, mbedtls_ctr_drbg_random, &ssl_data_loc->ctr_drbg); | ||
1448 | |||
1449 | mbedtls_ssl_conf_dbg(&ssl_data_loc->config, _mbedtls_log_cb, NULL); | ||
1450 | |||
1451 | mbedtls_ssl_conf_verify(&ssl_data_loc->config, cert_verify_cb, NULL); | ||
1452 | |||
1453 | mbedtls_ssl_setup(&ssl_data_loc->ctx, &ssl_data_loc->config); | ||
1454 | |||
1455 | mbedtls_ssl_set_bio(&ssl_data_loc->ctx, connection, (mbedtls_ssl_send_t*)&internal_ssl_write, (mbedtls_ssl_recv_t*)&internal_ssl_read, NULL); | ||
1456 | |||
1457 | mbedtls_x509_crt_init(&ssl_data_loc->certificate); | ||
1458 | |||
1459 | int crterr = mbedtls_x509_crt_parse(&ssl_data_loc->certificate, root_cert.data, root_cert.size); | ||
1460 | if (crterr < 0) { | ||
1461 | debug_info("ERROR: [mbedtls] parsing root cert failed: %d", crterr); | ||
1462 | return ret; | ||
1463 | } | ||
1464 | |||
1465 | mbedtls_ssl_conf_ca_chain(&ssl_data_loc->config, &ssl_data_loc->certificate, NULL); | ||
1466 | |||
1467 | mbedtls_pk_init(&ssl_data_loc->root_privkey); | ||
1468 | |||
1469 | #if MBEDTLS_VERSION_NUMBER >= 0x03000000 | ||
1470 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0, &_mbedtls_f_rng, NULL); | ||
1471 | #else | ||
1472 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0); | ||
1473 | #endif | ||
1474 | if (pkerr < 0) { | ||
1475 | debug_info("ERROR: [mbedtls] parsing private key failed: %d (size=%d)", pkerr, root_privkey.size); | ||
1476 | return ret; | ||
1477 | } | ||
1478 | |||
1479 | mbedtls_ssl_conf_own_cert(&ssl_data_loc->config, &ssl_data_loc->certificate, &ssl_data_loc->root_privkey); | ||
1480 | |||
1481 | int return_me = 0; | ||
1482 | do { | ||
1483 | return_me = mbedtls_ssl_handshake(&ssl_data_loc->ctx); | ||
1484 | } while (return_me == MBEDTLS_ERR_SSL_WANT_READ || return_me == MBEDTLS_ERR_SSL_WANT_WRITE); | ||
1485 | |||
1486 | if (return_me != 0) { | ||
1487 | debug_info("ERROR during SSL handshake: %d", return_me); | ||
1488 | internal_ssl_cleanup(ssl_data_loc); | ||
1489 | free(ssl_data_loc); | ||
1490 | } else { | ||
1491 | connection->ssl_data = ssl_data_loc; | ||
1492 | ret = IDEVICE_E_SUCCESS; | ||
1493 | debug_info("SSL mode enabled, %s, cipher: %s", mbedtls_ssl_get_version(&ssl_data_loc->ctx), mbedtls_ssl_get_ciphersuite(&ssl_data_loc->ctx)); | ||
1494 | debug_info("SSL mode enabled"); | ||
1495 | } | ||
1496 | #endif | ||
638 | return ret; | 1497 | return ret; |
639 | } | 1498 | } |
640 | 1499 | ||
641 | /** | ||
642 | * Disable SSL for the given connection. | ||
643 | * | ||
644 | * @param connection The connection to disable SSL for. | ||
645 | * | ||
646 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
647 | * is NULL. This function also returns IDEVICE_E_SUCCESS when SSL is not | ||
648 | * enabled and does no further error checking on cleanup. | ||
649 | */ | ||
650 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | 1500 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) |
651 | { | 1501 | { |
1502 | return idevice_connection_disable_bypass_ssl(connection, 0); | ||
1503 | } | ||
1504 | |||
1505 | idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass) | ||
1506 | { | ||
652 | if (!connection) | 1507 | if (!connection) |
653 | return IDEVICE_E_INVALID_ARG; | 1508 | return IDEVICE_E_INVALID_ARG; |
654 | if (!connection->ssl_data) { | 1509 | if (!connection->ssl_data) { |
655 | /* ignore if ssl is not enabled */ | 1510 | /* ignore if ssl is not enabled */ |
656 | return IDEVICE_E_SUCCESS; | 1511 | return IDEVICE_E_SUCCESS; |
657 | } | 1512 | } |
658 | 1513 | ||
659 | if (connection->ssl_data->session) { | 1514 | // some services require plain text communication after SSL handshake |
660 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | 1515 | // sending out SSL_shutdown will cause bytes |
1516 | if (!sslBypass) { | ||
1517 | #if defined(HAVE_OPENSSL) | ||
1518 | if (connection->ssl_data->session) { | ||
1519 | /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */ | ||
1520 | if (SSL_shutdown(connection->ssl_data->session) == 0) { | ||
1521 | /* Only try bidirectional shutdown if we know it can complete */ | ||
1522 | int ssl_error; | ||
1523 | if ((ssl_error = SSL_get_error(connection->ssl_data->session, 0)) == SSL_ERROR_NONE) { | ||
1524 | SSL_shutdown(connection->ssl_data->session); | ||
1525 | } else { | ||
1526 | debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error); | ||
1527 | } | ||
1528 | } | ||
1529 | } | ||
1530 | #elif defined(HAVE_GNUTLS) | ||
1531 | if (connection->ssl_data->session) { | ||
1532 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | ||
1533 | } | ||
1534 | #elif defined(HAVE_MBEDTLS) | ||
1535 | mbedtls_ssl_close_notify(&connection->ssl_data->ctx); | ||
1536 | #endif | ||
661 | } | 1537 | } |
1538 | |||
662 | internal_ssl_cleanup(connection->ssl_data); | 1539 | internal_ssl_cleanup(connection->ssl_data); |
663 | free(connection->ssl_data); | 1540 | free(connection->ssl_data); |
664 | connection->ssl_data = NULL; | 1541 | connection->ssl_data = NULL; |
@@ -667,4 +1544,3 @@ idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | |||
667 | 1544 | ||
668 | return IDEVICE_E_SUCCESS; | 1545 | return IDEVICE_E_SUCCESS; |
669 | } | 1546 | } |
670 | |||