diff options
| author | 2010-01-28 22:18:41 +0100 | |
|---|---|---|
| committer | 2010-01-29 02:16:00 +0100 | |
| commit | 96101a1231a4ddfeb40fd738a24e108a3a904048 (patch) | |
| tree | 65a8f54354d9acbbba93dac2c8602d07e469482c /src/idevice.c | |
| parent | 45b88ae3956de089fdc35605910f1359a1d3961c (diff) | |
| download | libimobiledevice-96101a1231a4ddfeb40fd738a24e108a3a904048.tar.gz libimobiledevice-96101a1231a4ddfeb40fd738a24e108a3a904048.tar.bz2 | |
Global renames due to project rename to libimobiledevice
Diffstat (limited to 'src/idevice.c')
| -rw-r--r-- | src/idevice.c | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/src/idevice.c b/src/idevice.c new file mode 100644 index 0000000..c5050d5 --- /dev/null +++ b/src/idevice.c | |||
| @@ -0,0 +1,618 @@ | |||
| 1 | /* | ||
| 2 | * idevice.c | ||
| 3 | * Device discovery and communication interface. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. | ||
| 7 | * | ||
| 8 | * This library is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU Lesser General Public | ||
| 10 | * License as published by the Free Software Foundation; either | ||
| 11 | * version 2.1 of the License, or (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This library is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 16 | * Lesser General Public License for more details. | ||
| 17 | * | ||
| 18 | * 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 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <errno.h> | ||
| 26 | #include <arpa/inet.h> | ||
| 27 | |||
| 28 | #include <usbmuxd.h> | ||
| 29 | #include <gnutls/gnutls.h> | ||
| 30 | #include "idevice.h" | ||
| 31 | #include "debug.h" | ||
| 32 | |||
| 33 | static idevice_event_cb_t event_cb = NULL; | ||
| 34 | |||
| 35 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) | ||
| 36 | { | ||
| 37 | idevice_event_t ev; | ||
| 38 | |||
| 39 | ev.event = event->event; | ||
| 40 | ev.uuid = event->device.uuid; | ||
| 41 | ev.conn_type = CONNECTION_USBMUXD; | ||
| 42 | |||
| 43 | if (event_cb) { | ||
| 44 | event_cb(&ev, user_data); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | /** | ||
| 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 | { | ||
| 60 | event_cb = callback; | ||
| 61 | int res = usbmuxd_subscribe(usbmux_event_cb, user_data); | ||
| 62 | if (res != 0) { | ||
| 63 | event_cb = NULL; | ||
| 64 | debug_info("Error %d when subscribing usbmux event callback!", res); | ||
| 65 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 66 | } | ||
| 67 | return IDEVICE_E_SUCCESS; | ||
| 68 | } | ||
| 69 | |||
| 70 | /** | ||
| 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 | { | ||
| 78 | event_cb = NULL; | ||
| 79 | int res = usbmuxd_unsubscribe(); | ||
| 80 | if (res != 0) { | ||
| 81 | debug_info("Error %d when unsubscribing usbmux event callback!", res); | ||
| 82 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 83 | } | ||
| 84 | return IDEVICE_E_SUCCESS; | ||
| 85 | } | ||
| 86 | |||
| 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) | ||
| 97 | { | ||
| 98 | usbmuxd_device_info_t *dev_list; | ||
| 99 | |||
| 100 | *devices = NULL; | ||
| 101 | *count = 0; | ||
| 102 | |||
| 103 | if (usbmuxd_get_device_list(&dev_list) < 0) { | ||
| 104 | debug_info("ERROR: usbmuxd is not running!\n", __func__); | ||
| 105 | return IDEVICE_E_NO_DEVICE; | ||
| 106 | } | ||
| 107 | |||
| 108 | char **newlist = NULL; | ||
| 109 | int i, newcount = 0; | ||
| 110 | |||
| 111 | for (i = 0; dev_list[i].handle > 0; i++) { | ||
| 112 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | ||
| 113 | newlist[newcount++] = strdup(dev_list[i].uuid); | ||
| 114 | *devices = newlist; | ||
| 115 | } | ||
| 116 | usbmuxd_device_list_free(&dev_list); | ||
| 117 | |||
| 118 | *count = newcount; | ||
| 119 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | ||
| 120 | newlist[newcount] = NULL; | ||
| 121 | *devices = newlist; | ||
| 122 | |||
| 123 | return IDEVICE_E_SUCCESS; | ||
| 124 | } | ||
| 125 | |||
| 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) | ||
| 134 | { | ||
| 135 | if (devices) { | ||
| 136 | int i = 0; | ||
| 137 | while (devices[i++]) { | ||
| 138 | free(devices[i]); | ||
| 139 | } | ||
| 140 | free(devices); | ||
| 141 | } | ||
| 142 | return IDEVICE_E_SUCCESS; | ||
| 143 | } | ||
| 144 | |||
| 145 | /** | ||
| 146 | * Creates an idevice_t structure for the device specified by uuid, | ||
| 147 | * if the device is available. | ||
| 148 | * | ||
| 149 | * @note The resulting idevice_t structure has to be freed with | ||
| 150 | * idevice_free() if it is no longer used. | ||
| 151 | * | ||
| 152 | * @param device Upon calling this function, a pointer to a location of type | ||
| 153 | * idevice_t. On successful return, this location will be populated. | ||
| 154 | * @param uuid The UUID to match. | ||
| 155 | * | ||
| 156 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 157 | */ | ||
| 158 | idevice_error_t idevice_new(idevice_t * device, const char *uuid) | ||
| 159 | { | ||
| 160 | usbmuxd_device_info_t muxdev; | ||
| 161 | int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); | ||
| 162 | if (res > 0) { | ||
| 163 | idevice_t phone = (idevice_t) malloc(sizeof(struct idevice_int)); | ||
| 164 | phone->uuid = strdup(muxdev.uuid); | ||
| 165 | phone->conn_type = CONNECTION_USBMUXD; | ||
| 166 | phone->conn_data = (void*)muxdev.handle; | ||
| 167 | *device = phone; | ||
| 168 | return IDEVICE_E_SUCCESS; | ||
| 169 | } | ||
| 170 | /* other connection types could follow here */ | ||
| 171 | |||
| 172 | return IDEVICE_E_NO_DEVICE; | ||
| 173 | } | ||
| 174 | |||
| 175 | /** Cleans up an idevice structure, then frees the structure itself. | ||
| 176 | * This is a library-level function; deals directly with the device to tear | ||
| 177 | * down relations, but otherwise is mostly internal. | ||
| 178 | * | ||
| 179 | * @param device idevice_t to free. | ||
| 180 | */ | ||
| 181 | idevice_error_t idevice_free(idevice_t device) | ||
| 182 | { | ||
| 183 | if (!device) | ||
| 184 | return IDEVICE_E_INVALID_ARG; | ||
| 185 | idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; | ||
| 186 | |||
| 187 | ret = IDEVICE_E_SUCCESS; | ||
| 188 | |||
| 189 | free(device->uuid); | ||
| 190 | |||
| 191 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
| 192 | device->conn_data = 0; | ||
| 193 | } | ||
| 194 | if (device->conn_data) { | ||
| 195 | free(device->conn_data); | ||
| 196 | } | ||
| 197 | free(device); | ||
| 198 | return ret; | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * Set up a connection to the given device. | ||
| 203 | * | ||
| 204 | * @param device The device to connect to. | ||
| 205 | * @param port The destination port to connect to. | ||
| 206 | * @param connection Pointer to an idevice_connection_t that will be filled | ||
| 207 | * with the necessary data of the connection. | ||
| 208 | * | ||
| 209 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 210 | */ | ||
| 211 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) | ||
| 212 | { | ||
| 213 | if (!device) { | ||
| 214 | return IDEVICE_E_INVALID_ARG; | ||
| 215 | } | ||
| 216 | |||
| 217 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
| 218 | int sfd = usbmuxd_connect((uint32_t)(device->conn_data), port); | ||
| 219 | if (sfd < 0) { | ||
| 220 | debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); | ||
| 221 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 222 | } | ||
| 223 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_int)); | ||
| 224 | new_connection->type = CONNECTION_USBMUXD; | ||
| 225 | new_connection->data = (void*)sfd; | ||
| 226 | new_connection->ssl_data = NULL; | ||
| 227 | *connection = new_connection; | ||
| 228 | return IDEVICE_E_SUCCESS; | ||
| 229 | } else { | ||
| 230 | debug_info("Unknown connection type %d", device->conn_type); | ||
| 231 | } | ||
| 232 | |||
| 233 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 234 | } | ||
| 235 | |||
| 236 | /** | ||
| 237 | * Disconnect from the device and clean up the connection structure. | ||
| 238 | * | ||
| 239 | * @param connection The connection to close. | ||
| 240 | * | ||
| 241 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 242 | */ | ||
| 243 | idevice_error_t idevice_disconnect(idevice_connection_t connection) | ||
| 244 | { | ||
| 245 | if (!connection) { | ||
| 246 | return IDEVICE_E_INVALID_ARG; | ||
| 247 | } | ||
| 248 | /* shut down ssl if enabled */ | ||
| 249 | if (connection->ssl_data) { | ||
| 250 | idevice_connection_disable_ssl(connection); | ||
| 251 | } | ||
| 252 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; | ||
| 253 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 254 | usbmuxd_disconnect((int)(connection->data)); | ||
| 255 | result = IDEVICE_E_SUCCESS; | ||
| 256 | } else { | ||
| 257 | debug_info("Unknown connection type %d", connection->type); | ||
| 258 | } | ||
| 259 | free(connection); | ||
| 260 | return result; | ||
| 261 | } | ||
| 262 | |||
| 263 | /** | ||
| 264 | * Internally used function to send raw data over the given connection. | ||
| 265 | */ | ||
| 266 | static idevice_error_t internal_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) | ||
| 267 | { | ||
| 268 | if (!connection || !data) { | ||
| 269 | return IDEVICE_E_INVALID_ARG; | ||
| 270 | } | ||
| 271 | |||
| 272 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 273 | int res = usbmuxd_send((int)(connection->data), data, len, sent_bytes); | ||
| 274 | if (res < 0) { | ||
| 275 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); | ||
| 276 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 277 | } | ||
| 278 | return IDEVICE_E_SUCCESS; | ||
| 279 | } else { | ||
| 280 | debug_info("Unknown connection type %d", connection->type); | ||
| 281 | } | ||
| 282 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 283 | |||
| 284 | } | ||
| 285 | |||
| 286 | /** | ||
| 287 | * Send data to a device via the given connection. | ||
| 288 | * | ||
| 289 | * @param connection The connection to send data over. | ||
| 290 | * @param data Buffer with data to send. | ||
| 291 | * @param len Size of the buffer to send. | ||
| 292 | * @param sent_bytes Pointer to an uint32_t that will be filled | ||
| 293 | * with the number of bytes actually sent. | ||
| 294 | * | ||
| 295 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 296 | */ | ||
| 297 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) | ||
| 298 | { | ||
| 299 | if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { | ||
| 300 | return IDEVICE_E_INVALID_ARG; | ||
| 301 | } | ||
| 302 | |||
| 303 | if (connection->ssl_data) { | ||
| 304 | ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); | ||
| 305 | if ((uint32_t)sent == (uint32_t)len) { | ||
| 306 | *sent_bytes = sent; | ||
| 307 | return IDEVICE_E_SUCCESS; | ||
| 308 | } | ||
| 309 | *sent_bytes = 0; | ||
| 310 | return IDEVICE_E_SSL_ERROR; | ||
| 311 | } | ||
| 312 | return internal_connection_send(connection, data, len, sent_bytes); | ||
| 313 | } | ||
| 314 | |||
| 315 | /** | ||
| 316 | * Internally used function for receiving raw data over the given connection | ||
| 317 | * using a timeout. | ||
| 318 | */ | ||
| 319 | static idevice_error_t internal_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) | ||
| 320 | { | ||
| 321 | if (!connection) { | ||
| 322 | return IDEVICE_E_INVALID_ARG; | ||
| 323 | } | ||
| 324 | |||
| 325 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 326 | int res = usbmuxd_recv_timeout((int)(connection->data), data, len, recv_bytes, timeout); | ||
| 327 | if (res < 0) { | ||
| 328 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res)); | ||
| 329 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 330 | } | ||
| 331 | return IDEVICE_E_SUCCESS; | ||
| 332 | } else { | ||
| 333 | debug_info("Unknown connection type %d", connection->type); | ||
| 334 | } | ||
| 335 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 336 | } | ||
| 337 | |||
| 338 | /** | ||
| 339 | * Receive data from a device via the given connection. | ||
| 340 | * This function will return after the given timeout even if no data has been | ||
| 341 | * received. | ||
| 342 | * | ||
| 343 | * @param connection The connection to receive data from. | ||
| 344 | * @param data Buffer that will be filled with the received data. | ||
| 345 | * This buffer has to be large enough to hold len bytes. | ||
| 346 | * @param len Buffer size or number of bytes to receive. | ||
| 347 | * @param recv_bytes Number of bytes actually received. | ||
| 348 | * @param timeout Timeout in milliseconds after which this function should | ||
| 349 | * return even if no data has been received. | ||
| 350 | * | ||
| 351 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 352 | */ | ||
| 353 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) | ||
| 354 | { | ||
| 355 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | ||
| 356 | return IDEVICE_E_INVALID_ARG; | ||
| 357 | } | ||
| 358 | |||
| 359 | if (connection->ssl_data) { | ||
| 360 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | ||
| 361 | if (received > 0) { | ||
| 362 | *recv_bytes = received; | ||
| 363 | return IDEVICE_E_SUCCESS; | ||
| 364 | } | ||
| 365 | *recv_bytes = 0; | ||
| 366 | return IDEVICE_E_SSL_ERROR; | ||
| 367 | } | ||
| 368 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); | ||
| 369 | } | ||
| 370 | |||
| 371 | /** | ||
| 372 | * Internally used function for receiving raw data over the given connection. | ||
| 373 | */ | ||
| 374 | static idevice_error_t internal_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) | ||
| 375 | { | ||
| 376 | if (!connection) { | ||
| 377 | return IDEVICE_E_INVALID_ARG; | ||
| 378 | } | ||
| 379 | |||
| 380 | if (connection->type == CONNECTION_USBMUXD) { | ||
| 381 | int res = usbmuxd_recv((int)(connection->data), data, len, recv_bytes); | ||
| 382 | if (res < 0) { | ||
| 383 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); | ||
| 384 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 385 | } | ||
| 386 | |||
| 387 | return IDEVICE_E_SUCCESS; | ||
| 388 | } else { | ||
| 389 | debug_info("Unknown connection type %d", connection->type); | ||
| 390 | } | ||
| 391 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 392 | } | ||
| 393 | |||
| 394 | /** | ||
| 395 | * Receive data from a device via the given connection. | ||
| 396 | * This function is like idevice_connection_receive_timeout, but with a | ||
| 397 | * predefined reasonable timeout. | ||
| 398 | * | ||
| 399 | * @param connection The connection to receive data from. | ||
| 400 | * @param data Buffer that will be filled with the received data. | ||
| 401 | * This buffer has to be large enough to hold len bytes. | ||
| 402 | * @param len Buffer size or number of bytes to receive. | ||
| 403 | * @param recv_bytes Number of bytes actually received. | ||
| 404 | * | ||
| 405 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 406 | */ | ||
| 407 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) | ||
| 408 | { | ||
| 409 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | ||
| 410 | return IDEVICE_E_INVALID_ARG; | ||
| 411 | } | ||
| 412 | |||
| 413 | if (connection->ssl_data) { | ||
| 414 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | ||
| 415 | if (received > 0) { | ||
| 416 | *recv_bytes = received; | ||
| 417 | return IDEVICE_E_SUCCESS; | ||
| 418 | } | ||
| 419 | *recv_bytes = 0; | ||
| 420 | return IDEVICE_E_SSL_ERROR; | ||
| 421 | } | ||
| 422 | return internal_connection_receive(connection, data, len, recv_bytes); | ||
| 423 | } | ||
| 424 | |||
| 425 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) | ||
| 426 | { | ||
| 427 | if (!device) | ||
| 428 | return IDEVICE_E_INVALID_ARG; | ||
| 429 | |||
| 430 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
| 431 | *handle = (uint32_t)device->conn_data; | ||
| 432 | return IDEVICE_E_SUCCESS; | ||
| 433 | } else { | ||
| 434 | debug_info("Unknown connection type %d", device->conn_type); | ||
| 435 | } | ||
| 436 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 437 | } | ||
| 438 | |||
| 439 | idevice_error_t idevice_get_uuid(idevice_t device, char **uuid) | ||
| 440 | { | ||
| 441 | if (!device) | ||
| 442 | return IDEVICE_E_INVALID_ARG; | ||
| 443 | |||
| 444 | *uuid = strdup(device->uuid); | ||
| 445 | return IDEVICE_E_SUCCESS; | ||
| 446 | } | ||
| 447 | |||
| 448 | /** | ||
| 449 | * Internally used gnutls callback function for receiving encrypted data. | ||
| 450 | */ | ||
| 451 | static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) | ||
| 452 | { | ||
| 453 | int bytes = 0, pos_start_fill = 0; | ||
| 454 | size_t tbytes = 0; | ||
| 455 | int this_len = length; | ||
| 456 | idevice_error_t res; | ||
| 457 | idevice_connection_t connection = (idevice_connection_t)transport; | ||
| 458 | char *recv_buffer; | ||
| 459 | |||
| 460 | debug_info("pre-read client wants %zi bytes", length); | ||
| 461 | |||
| 462 | recv_buffer = (char *) malloc(sizeof(char) * this_len); | ||
| 463 | |||
| 464 | /* repeat until we have the full data or an error occurs */ | ||
| 465 | do { | ||
| 466 | if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { | ||
| 467 | debug_info("ERROR: idevice_connection_receive returned %d", res); | ||
| 468 | return res; | ||
| 469 | } | ||
| 470 | debug_info("post-read we got %i bytes", bytes); | ||
| 471 | |||
| 472 | // increase read count | ||
| 473 | tbytes += bytes; | ||
| 474 | |||
| 475 | // fill the buffer with what we got right now | ||
| 476 | memcpy(buffer + pos_start_fill, recv_buffer, bytes); | ||
| 477 | pos_start_fill += bytes; | ||
| 478 | |||
| 479 | if (tbytes >= length) { | ||
| 480 | break; | ||
| 481 | } | ||
| 482 | |||
| 483 | this_len = length - tbytes; | ||
| 484 | debug_info("re-read trying to read missing %i bytes", this_len); | ||
| 485 | } while (tbytes < length); | ||
| 486 | |||
| 487 | if (recv_buffer) { | ||
| 488 | free(recv_buffer); | ||
| 489 | } | ||
| 490 | return tbytes; | ||
| 491 | } | ||
| 492 | |||
| 493 | /** | ||
| 494 | * Internally used gnutls callback function for sending encrypted data. | ||
| 495 | */ | ||
| 496 | static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) | ||
| 497 | { | ||
| 498 | uint32_t bytes = 0; | ||
| 499 | idevice_connection_t connection = (idevice_connection_t)transport; | ||
| 500 | debug_info("pre-send length = %zi", length); | ||
| 501 | internal_connection_send(connection, buffer, length, &bytes); | ||
| 502 | debug_info("post-send sent %i bytes", bytes); | ||
| 503 | return bytes; | ||
| 504 | } | ||
| 505 | |||
| 506 | /** | ||
| 507 | * Internally used function for cleaning up SSL stuff. | ||
| 508 | */ | ||
| 509 | static void internal_ssl_cleanup(ssl_data_t ssl_data) | ||
| 510 | { | ||
| 511 | if (!ssl_data) | ||
| 512 | return; | ||
| 513 | |||
| 514 | if (ssl_data->session) { | ||
| 515 | gnutls_deinit(ssl_data->session); | ||
| 516 | } | ||
| 517 | if (ssl_data->certificate) { | ||
| 518 | gnutls_certificate_free_credentials(ssl_data->certificate); | ||
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | /** | ||
| 523 | * Enables SSL for the given connection. | ||
| 524 | * | ||
| 525 | * @param connection The connection to enable SSL for. | ||
| 526 | * | ||
| 527 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
| 528 | * is NULL or connection->ssl_data is non-NULL, or IDEVICE_E_SSL_ERROR when | ||
| 529 | * SSL initialization, setup, or handshake fails. | ||
| 530 | */ | ||
| 531 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | ||
| 532 | { | ||
| 533 | if (!connection || connection->ssl_data) | ||
| 534 | return IDEVICE_E_INVALID_ARG; | ||
| 535 | |||
| 536 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; | ||
| 537 | uint32_t return_me = 0; | ||
| 538 | |||
| 539 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_int)); | ||
| 540 | |||
| 541 | // Set up GnuTLS... | ||
| 542 | debug_info("enabling SSL mode"); | ||
| 543 | errno = 0; | ||
| 544 | gnutls_global_init(); | ||
| 545 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); | ||
| 546 | gnutls_certificate_set_x509_trust_file(ssl_data_loc->certificate, "hostcert.pem", GNUTLS_X509_FMT_PEM); | ||
| 547 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); | ||
| 548 | { | ||
| 549 | int protocol_priority[16] = { GNUTLS_SSL3, 0 }; | ||
| 550 | int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 }; | ||
| 551 | int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; | ||
| 552 | int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; | ||
| 553 | int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; | ||
| 554 | |||
| 555 | gnutls_cipher_set_priority(ssl_data_loc->session, cipher_priority); | ||
| 556 | gnutls_compression_set_priority(ssl_data_loc->session, comp_priority); | ||
| 557 | gnutls_kx_set_priority(ssl_data_loc->session, kx_priority); | ||
| 558 | gnutls_protocol_set_priority(ssl_data_loc->session, protocol_priority); | ||
| 559 | gnutls_mac_set_priority(ssl_data_loc->session, mac_priority); | ||
| 560 | } | ||
| 561 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); // this part is killing me. | ||
| 562 | |||
| 563 | debug_info("GnuTLS step 1..."); | ||
| 564 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); | ||
| 565 | debug_info("GnuTLS step 2..."); | ||
| 566 | gnutls_transport_set_push_function(ssl_data_loc->session, (gnutls_push_func) & internal_ssl_write); | ||
| 567 | debug_info("GnuTLS step 3..."); | ||
| 568 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); | ||
| 569 | debug_info("GnuTLS step 4 -- now handshaking..."); | ||
| 570 | if (errno) | ||
| 571 | debug_info("WARN: errno says %s before handshake!", strerror(errno)); | ||
| 572 | return_me = gnutls_handshake(ssl_data_loc->session); | ||
| 573 | debug_info("GnuTLS handshake done..."); | ||
| 574 | |||
| 575 | if (return_me != GNUTLS_E_SUCCESS) { | ||
| 576 | internal_ssl_cleanup(ssl_data_loc); | ||
| 577 | free(ssl_data_loc); | ||
| 578 | debug_info("GnuTLS reported something wrong."); | ||
| 579 | gnutls_perror(return_me); | ||
| 580 | debug_info("oh.. errno says %s", strerror(errno)); | ||
| 581 | } else { | ||
| 582 | connection->ssl_data = ssl_data_loc; | ||
| 583 | ret = IDEVICE_E_SUCCESS; | ||
| 584 | debug_info("SSL mode enabled"); | ||
| 585 | } | ||
| 586 | return ret; | ||
| 587 | } | ||
| 588 | |||
| 589 | /** | ||
| 590 | * Disable SSL for the given connection. | ||
| 591 | * | ||
| 592 | * @param connection The connection to disable SSL for. | ||
| 593 | * | ||
| 594 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
| 595 | * is NULL. This function also returns IDEVICE_E_SUCCESS when SSL is not | ||
| 596 | * enabled and does no further error checking on cleanup. | ||
| 597 | */ | ||
| 598 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | ||
| 599 | { | ||
| 600 | if (!connection) | ||
| 601 | return IDEVICE_E_INVALID_ARG; | ||
| 602 | if (!connection->ssl_data) { | ||
| 603 | /* ignore if ssl is not enabled */ | ||
| 604 | return IDEVICE_E_SUCCESS; | ||
| 605 | } | ||
| 606 | |||
| 607 | if (connection->ssl_data->session) { | ||
| 608 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | ||
| 609 | } | ||
| 610 | internal_ssl_cleanup(connection->ssl_data); | ||
| 611 | free(connection->ssl_data); | ||
| 612 | connection->ssl_data = NULL; | ||
| 613 | |||
| 614 | debug_info("SSL mode disabled"); | ||
| 615 | |||
| 616 | return IDEVICE_E_SUCCESS; | ||
| 617 | } | ||
| 618 | |||
