diff options
| author | 2009-05-18 22:29:39 +0200 | |
|---|---|---|
| committer | 2009-05-18 18:47:20 -0700 | |
| commit | 8eaac0513bfb238edec22d46320669f5c9c76542 (patch) | |
| tree | 2db842339bf525d67017525bcbd3f4d35181e5ca /src/usbmux.c | |
| parent | dca1758c4f9602fc240c6a7c9ae45839e154d15f (diff) | |
| download | libimobiledevice-8eaac0513bfb238edec22d46320669f5c9c76542.tar.gz libimobiledevice-8eaac0513bfb238edec22d46320669f5c9c76542.tar.bz2 | |
Make use of usbmuxd and remove libusb dependencies
Signed-off-by: Matt Colyer <matt@colyer.name>
Diffstat (limited to 'src/usbmux.c')
| -rw-r--r-- | src/usbmux.c | 410 |
1 files changed, 0 insertions, 410 deletions
diff --git a/src/usbmux.c b/src/usbmux.c deleted file mode 100644 index 7d74b4b..0000000 --- a/src/usbmux.c +++ /dev/null | |||
| @@ -1,410 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * usbmux.c | ||
| 3 | * Interprets the usb multiplexing protocol used by the iPhone. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <sys/types.h> | ||
| 23 | #include <arpa/inet.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <stdlib.h> | ||
| 26 | #include <string.h> | ||
| 27 | |||
| 28 | #include "usbmux.h" | ||
| 29 | #include "utils.h" | ||
| 30 | |||
| 31 | static iphone_umux_client_t *connlist = NULL; | ||
| 32 | static int clients = 0; | ||
| 33 | |||
| 34 | /** Creates a USBMux packet for the given set of ports. | ||
| 35 | * | ||
| 36 | * @param s_port The source port for the connection. | ||
| 37 | * @param d_port The destination port for the connection. | ||
| 38 | * | ||
| 39 | * @return A USBMux packet | ||
| 40 | */ | ||
| 41 | usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port) | ||
| 42 | { | ||
| 43 | usbmux_tcp_header *conn = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); | ||
| 44 | conn->type = htonl(6); | ||
| 45 | conn->length = 28; | ||
| 46 | conn->sport = htons(s_port); | ||
| 47 | conn->dport = htons(d_port); | ||
| 48 | conn->scnt = 0; | ||
| 49 | conn->ocnt = 0; | ||
| 50 | conn->offset = 0x50; | ||
| 51 | conn->window = htons(0x0200); | ||
| 52 | conn->nullnull = 0x0000; | ||
| 53 | conn->length16 = 28; | ||
| 54 | return conn; | ||
| 55 | } | ||
| 56 | |||
| 57 | /** Creates a USBMux header containing version information | ||
| 58 | * | ||
| 59 | * @return A USBMux header | ||
| 60 | */ | ||
| 61 | usbmux_version_header *version_header(void) | ||
| 62 | { | ||
| 63 | usbmux_version_header *version = (usbmux_version_header *) malloc(sizeof(usbmux_version_header)); | ||
| 64 | version->type = 0; | ||
| 65 | version->length = htonl(20); | ||
| 66 | version->major = htonl(1); | ||
| 67 | version->minor = 0; | ||
| 68 | version->allnull = 0; | ||
| 69 | return version; | ||
| 70 | } | ||
| 71 | |||
| 72 | |||
| 73 | // Maintenance functions. | ||
| 74 | |||
| 75 | /** Removes a connection from the list of connections made. | ||
| 76 | * The list of connections is necessary for buffering. | ||
| 77 | * | ||
| 78 | * @param connection The connection to delete from the tracking list. | ||
| 79 | */ | ||
| 80 | static void delete_connection(iphone_umux_client_t connection) | ||
| 81 | { | ||
| 82 | iphone_umux_client_t *newlist = (iphone_umux_client_t *) malloc(sizeof(iphone_umux_client_t) * (clients - 1)); | ||
| 83 | int i = 0, j = 0; | ||
| 84 | for (i = 0; i < clients; i++) { | ||
| 85 | if (connlist[i] == connection) | ||
| 86 | continue; | ||
| 87 | else { | ||
| 88 | newlist[j] = connlist[i]; | ||
| 89 | j++; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | free(connlist); | ||
| 93 | connlist = newlist; | ||
| 94 | clients--; | ||
| 95 | if (connection->recv_buffer) | ||
| 96 | free(connection->recv_buffer); | ||
| 97 | if (connection->header) | ||
| 98 | free(connection->header); | ||
| 99 | connection->r_len = 0; | ||
| 100 | free(connection); | ||
| 101 | } | ||
| 102 | |||
| 103 | /** Adds a connection to the list of connections made. | ||
| 104 | * The connection list is necessary for buffering. | ||
| 105 | * | ||
| 106 | * @param connection The connection to add to the global list of connections. | ||
| 107 | */ | ||
| 108 | |||
| 109 | static void add_connection(iphone_umux_client_t connection) | ||
| 110 | { | ||
| 111 | iphone_umux_client_t *newlist = | ||
| 112 | (iphone_umux_client_t *) realloc(connlist, sizeof(iphone_umux_client_t) * (clients + 1)); | ||
| 113 | newlist[clients] = connection; | ||
| 114 | connlist = newlist; | ||
| 115 | clients++; | ||
| 116 | } | ||
| 117 | |||
| 118 | /** Initializes a connection on phone, with source port s_port and destination port d_port | ||
| 119 | * | ||
| 120 | * @param device The iPhone to initialize a connection on. | ||
| 121 | * @param src_port The source port | ||
| 122 | * @param dst_port The destination port -- 0xf27e for lockdownd. | ||
| 123 | * @param client A mux TCP header for the connection which is used for tracking and data transfer. | ||
| 124 | * @return IPHONE_E_SUCCESS on success, an error code otherwise. | ||
| 125 | */ | ||
| 126 | iphone_error_t iphone_mux_new_client(iphone_device_t device, uint16_t src_port, uint16_t dst_port, | ||
| 127 | iphone_umux_client_t * client) | ||
| 128 | { | ||
| 129 | if (!device || !src_port || !dst_port) | ||
| 130 | return IPHONE_E_INVALID_ARG; | ||
| 131 | |||
| 132 | int bytes = 0; | ||
| 133 | // Initialize connection stuff | ||
| 134 | iphone_umux_client_t new_connection = (iphone_umux_client_t) malloc(sizeof(struct iphone_umux_client_int)); | ||
| 135 | new_connection->header = new_mux_packet(src_port, dst_port); | ||
| 136 | |||
| 137 | // blargg | ||
| 138 | if (new_connection && new_connection->header) { | ||
| 139 | new_connection->header->tcp_flags = 0x02; | ||
| 140 | new_connection->header->length = htonl(new_connection->header->length); | ||
| 141 | new_connection->header->length16 = htons(new_connection->header->length16); | ||
| 142 | |||
| 143 | if (send_to_phone(device, (char *) new_connection->header, sizeof(usbmux_tcp_header)) >= 0) { | ||
| 144 | usbmux_tcp_header *response; | ||
| 145 | response = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); | ||
| 146 | bytes = recv_from_phone(device, (char *) response, sizeof(*response), 3500); | ||
| 147 | if (response->tcp_flags != 0x12) { | ||
| 148 | free(response); | ||
| 149 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 150 | } else { | ||
| 151 | free(response); | ||
| 152 | |||
| 153 | log_debug_msg("mux_connect: connection success\n"); | ||
| 154 | new_connection->header->tcp_flags = 0x10; | ||
| 155 | new_connection->header->scnt = 1; | ||
| 156 | new_connection->header->ocnt = 1; | ||
| 157 | new_connection->phone = device; | ||
| 158 | new_connection->recv_buffer = NULL; | ||
| 159 | new_connection->r_len = 0; | ||
| 160 | add_connection(new_connection); | ||
| 161 | *client = new_connection; | ||
| 162 | return IPHONE_E_SUCCESS; | ||
| 163 | } | ||
| 164 | } else { | ||
| 165 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | // if we get to this point it's probably bad | ||
| 169 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 170 | } | ||
| 171 | |||
| 172 | /** Cleans up the given USBMux connection. | ||
| 173 | * @note Once a connection is closed it may not be used again. | ||
| 174 | * | ||
| 175 | * @param connection The connection to close. | ||
| 176 | * | ||
| 177 | * @return IPHONE_E_SUCCESS on success. | ||
| 178 | */ | ||
| 179 | iphone_error_t iphone_mux_free_client(iphone_umux_client_t client) | ||
| 180 | { | ||
| 181 | if (!client || !client->phone) | ||
| 182 | return IPHONE_E_INVALID_ARG; | ||
| 183 | |||
| 184 | client->header->tcp_flags = 0x04; | ||
| 185 | client->header->length = htonl(0x1C); | ||
| 186 | client->header->scnt = htonl(client->header->scnt); | ||
| 187 | client->header->ocnt = htonl(client->header->ocnt); | ||
| 188 | client->header->window = 0; | ||
| 189 | client->header->length16 = htons(0x1C); | ||
| 190 | int bytes = 0; | ||
| 191 | |||
| 192 | bytes = usb_bulk_write(client->phone->device, BULKOUT, (char *) client->header, sizeof(usbmux_tcp_header), 800); | ||
| 193 | if (bytes < 0) | ||
| 194 | log_debug_msg("iphone_muxèfree_client(): when writing, libusb gave me the error: %s\n", usb_strerror()); | ||
| 195 | |||
| 196 | bytes = usb_bulk_read(client->phone->device, BULKIN, (char *) client->header, sizeof(usbmux_tcp_header), 800); | ||
| 197 | if (bytes < 0) | ||
| 198 | log_debug_msg("get_iPhone(): when reading, libusb gave me the error: %s\n", usb_strerror()); | ||
| 199 | |||
| 200 | delete_connection(client); | ||
| 201 | |||
| 202 | return IPHONE_E_SUCCESS; | ||
| 203 | } | ||
| 204 | |||
| 205 | |||
| 206 | /** Sends the given data over the selected connection. | ||
| 207 | * | ||
| 208 | * @param phone The iPhone to send to. | ||
| 209 | * @param client The client we're sending data on. | ||
| 210 | * @param data A pointer to the data to send. | ||
| 211 | * @param datalen How much data we're sending. | ||
| 212 | * @param sent_bytes The number of bytes sent, minus the header (28) | ||
| 213 | * | ||
| 214 | * @return IPHONE_E_SUCCESS on success. | ||
| 215 | */ | ||
| 216 | |||
| 217 | iphone_error_t iphone_mux_send(iphone_umux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes) | ||
| 218 | { | ||
| 219 | if (!client->phone || !client || !data || datalen == 0 || !sent_bytes) | ||
| 220 | return IPHONE_E_INVALID_ARG; | ||
| 221 | // client->scnt and client->ocnt should already be in host notation... | ||
| 222 | // we don't need to change them juuuust yet. | ||
| 223 | *sent_bytes = 0; | ||
| 224 | log_debug_msg("mux_send(): client wants to send %i bytes\n", datalen); | ||
| 225 | char *buffer = (char *) malloc(sizeof(usbmux_tcp_header) + datalen + 2); // allow 2 bytes of safety padding | ||
| 226 | // Set the length and pre-emptively htonl/htons it | ||
| 227 | client->header->length = htonl(sizeof(usbmux_tcp_header) + datalen); | ||
| 228 | client->header->length16 = htons(sizeof(usbmux_tcp_header) + datalen); | ||
| 229 | |||
| 230 | // Put scnt and ocnt into big-endian notation | ||
| 231 | client->header->scnt = htonl(client->header->scnt); | ||
| 232 | client->header->ocnt = htonl(client->header->ocnt); | ||
| 233 | // Concatenation of stuff in the buffer. | ||
| 234 | memcpy(buffer, client->header, sizeof(usbmux_tcp_header)); | ||
| 235 | memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen); | ||
| 236 | |||
| 237 | // We have a buffer full of data, we should now send it to the phone. | ||
| 238 | log_debug_msg("actually sending %zi bytes of data at %p\n", sizeof(usbmux_tcp_header) + datalen, buffer); | ||
| 239 | |||
| 240 | |||
| 241 | *sent_bytes = send_to_phone(client->phone, buffer, sizeof(usbmux_tcp_header) + datalen); | ||
| 242 | log_debug_msg("mux_send: sent %i bytes!\n", *sent_bytes); | ||
| 243 | // Now that we've sent it off, we can clean up after our sloppy selves. | ||
| 244 | dump_debug_buffer("packet", buffer, *sent_bytes); | ||
| 245 | if (buffer) | ||
| 246 | free(buffer); | ||
| 247 | // Re-calculate scnt and ocnt | ||
| 248 | client->header->scnt = ntohl(client->header->scnt) + datalen; | ||
| 249 | client->header->ocnt = ntohl(client->header->ocnt); | ||
| 250 | |||
| 251 | // Revert lengths | ||
| 252 | client->header->length = ntohl(client->header->length); | ||
| 253 | client->header->length16 = ntohs(client->header->length16); | ||
| 254 | |||
| 255 | // Now return the bytes. | ||
| 256 | if (*sent_bytes < sizeof(usbmux_tcp_header) + datalen) { | ||
| 257 | *sent_bytes = 0; | ||
| 258 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 259 | } else { | ||
| 260 | *sent_bytes = *sent_bytes - 28; // actual length sent. :/ | ||
| 261 | } | ||
| 262 | |||
| 263 | return IPHONE_E_SUCCESS; | ||
| 264 | } | ||
| 265 | |||
| 266 | /** This is a higher-level USBMuxTCP-like function | ||
| 267 | * | ||
| 268 | * @param connection The connection to receive data on. | ||
| 269 | * @param data Where to put the data we receive. | ||
| 270 | * @param datalen How much data to read. | ||
| 271 | * @param recv_bytes Pointer to a uint32_t that will be set | ||
| 272 | * to the number of bytes received. | ||
| 273 | * @param timeout How many milliseconds to wait for data. | ||
| 274 | * | ||
| 275 | * @return IPHONE_E_SUCCESS on success, or and error value. | ||
| 276 | */ | ||
| 277 | iphone_error_t iphone_mux_recv_timeout(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout) | ||
| 278 | { | ||
| 279 | |||
| 280 | if (!client || !data || datalen == 0 || !recv_bytes) | ||
| 281 | return IPHONE_E_INVALID_ARG; | ||
| 282 | /* | ||
| 283 | * Order of operation: | ||
| 284 | * 1.) Check if the client has a pre-received buffer. | ||
| 285 | * 2.) If so, fill data with the buffer, as much as needed. | ||
| 286 | * a.) Return quickly if the buffer has enough | ||
| 287 | * b.) If the buffer is only part of the datalen, get the rest of datalen (and if we can't, just return) | ||
| 288 | * 3.) If not, receive directly from the phone. | ||
| 289 | * a.) Check incoming packet's ports. If proper, follow proper buffering and receiving operation. | ||
| 290 | * b.) If not, find the client the ports belong to and fill that client's buffer, then return mux_recv with the same args to try again. | ||
| 291 | */ | ||
| 292 | log_debug_msg("mux_recv: datalen == %i\n", datalen); | ||
| 293 | int bytes = 0, i = 0, complex = 0, offset = 0; | ||
| 294 | *recv_bytes = 0; | ||
| 295 | char *buffer = NULL; | ||
| 296 | usbmux_tcp_header *header = NULL; | ||
| 297 | |||
| 298 | if (client->recv_buffer) { | ||
| 299 | if (client->r_len >= datalen) { | ||
| 300 | memcpy(data, client->recv_buffer, datalen); | ||
| 301 | if (client->r_len == datalen) { | ||
| 302 | // reset everything | ||
| 303 | free(client->recv_buffer); | ||
| 304 | client->r_len = 0; | ||
| 305 | client->recv_buffer = NULL; | ||
| 306 | } else { | ||
| 307 | buffer = (char *) malloc(sizeof(char) * (client->r_len - datalen)); | ||
| 308 | memcpy(buffer, client->recv_buffer + datalen, (client->r_len - datalen)); | ||
| 309 | client->r_len -= datalen; | ||
| 310 | free(client->recv_buffer); | ||
| 311 | client->recv_buffer = buffer; | ||
| 312 | } | ||
| 313 | |||
| 314 | // Since we were able to fill the data straight from our buffer, we can just return datalen. See 2a above. | ||
| 315 | *recv_bytes = datalen; | ||
| 316 | return IPHONE_E_SUCCESS; | ||
| 317 | } else { | ||
| 318 | memcpy(data, client->recv_buffer, client->r_len); | ||
| 319 | free(client->recv_buffer); // don't need to deal with anymore, but... | ||
| 320 | client->recv_buffer = NULL; | ||
| 321 | offset = client->r_len; // see #2b, above | ||
| 322 | client->r_len = 0; | ||
| 323 | } | ||
| 324 | } // End of what to do if we have a pre-buffer. See #1 and #2 above. | ||
| 325 | |||
| 326 | buffer = (char *) malloc(sizeof(char) * 131072); // make sure we get enough ;) | ||
| 327 | |||
| 328 | // See #3. | ||
| 329 | bytes = recv_from_phone(client->phone, buffer, 131072, timeout); | ||
| 330 | if (bytes < 28) { | ||
| 331 | free(buffer); | ||
| 332 | log_debug_msg("mux_recv: Did not even get the header.\n"); | ||
| 333 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 334 | } | ||
| 335 | |||
| 336 | header = (usbmux_tcp_header *) buffer; | ||
| 337 | if (header->sport != client->header->dport || header->dport != client->header->sport) { | ||
| 338 | // Ooooops -- we got someone else's packet. | ||
| 339 | // We gotta stick it in their buffer. (Take that any old way you want ;) ) | ||
| 340 | for (i = 0; i < clients; i++) { | ||
| 341 | if (connlist[i]->header->sport == header->dport && connlist[i]->header->dport == header->sport) { | ||
| 342 | // we have a winner. | ||
| 343 | char *nfb = (char *) malloc(sizeof(char) * (connlist[i]->r_len + (bytes - 28))); | ||
| 344 | if (connlist[i]->recv_buffer && connlist[i]->r_len) { | ||
| 345 | memcpy(nfb, connlist[i]->recv_buffer, connlist[i]->r_len); | ||
| 346 | free(connlist[i]->recv_buffer); | ||
| 347 | } | ||
| 348 | connlist[i]->r_len += bytes - 28; | ||
| 349 | //connlist[i]->recv_buffer = (char*)realloc(connlist[i]->recv_buffer, sizeof(char) * client->r_len); // grow their buffer | ||
| 350 | connlist[i]->recv_buffer = nfb; | ||
| 351 | nfb = NULL; // A cookie for you if you can guess what "nfb" means. | ||
| 352 | complex = connlist[i]->r_len - (bytes - 28); | ||
| 353 | memcpy(connlist[i]->recv_buffer + complex, buffer + 28, bytes - 28); // paste into their buffer | ||
| 354 | connlist[i]->header->ocnt += bytes - 28; | ||
| 355 | } | ||
| 356 | } | ||
| 357 | // If it wasn't ours, it's been handled by this point... or forgotten. | ||
| 358 | // Free our buffer and continue. | ||
| 359 | free(buffer); | ||
| 360 | buffer = NULL; | ||
| 361 | return iphone_mux_recv(client, data, datalen, recv_bytes); // recurse back in to try again | ||
| 362 | } | ||
| 363 | // The packet was absolutely meant for us if it hits this point. | ||
| 364 | // The pre-buffer has been taken care of, so, again, if we're at this point we have to read from the phone. | ||
| 365 | |||
| 366 | if ((bytes - 28) > datalen) { | ||
| 367 | // Copy what we need into the data, buffer the rest because we can. | ||
| 368 | memcpy(data + offset, buffer + 28, datalen); // data+offset: see #2b, above | ||
| 369 | complex = client->r_len + ((bytes - 28) - datalen); | ||
| 370 | client->recv_buffer = (char *) realloc(client->recv_buffer, (sizeof(char) * complex)); | ||
| 371 | client->r_len = complex; | ||
| 372 | complex = client->r_len - ((bytes - 28) - datalen); | ||
| 373 | memcpy(client->recv_buffer + complex, buffer + 28 + datalen, (bytes - 28) - datalen); | ||
| 374 | free(buffer); | ||
| 375 | client->header->ocnt += bytes - 28; | ||
| 376 | *recv_bytes = datalen; | ||
| 377 | return IPHONE_E_SUCCESS; | ||
| 378 | } else { | ||
| 379 | // Fill the data with what we have, and just return. | ||
| 380 | memcpy(data + offset, buffer + 28, bytes - 28); // data+offset: see #2b, above | ||
| 381 | client->header->ocnt += bytes - 28; | ||
| 382 | free(buffer); | ||
| 383 | *recv_bytes = bytes - 28; | ||
| 384 | return IPHONE_E_SUCCESS; | ||
| 385 | } | ||
| 386 | |||
| 387 | // If we get to this point, 'tis probably bad. | ||
| 388 | log_debug_msg("mux_recv: Heisenbug: bytes and datalen not matching up\n"); | ||
| 389 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 390 | } | ||
| 391 | |||
| 392 | /** | ||
| 393 | * This function is just like 'iphone_mux_recv_timeout' but you do not need | ||
| 394 | * to specify a timeout. It simply calls iphone_mux_recv_timeout with a | ||
| 395 | * timeout value of 3500 milliseconds. | ||
| 396 | * | ||
| 397 | * @param connection The connection to receive data on. | ||
| 398 | * @param data Where to put the data we receive. | ||
| 399 | * @param datalen How much data to read. | ||
| 400 | * @param recv_bytes Pointer to a uint32_t that will be set | ||
| 401 | * to the number of bytes received. | ||
| 402 | * | ||
| 403 | * @return The return value of iphone_mux_recv_timeout. | ||
| 404 | * | ||
| 405 | * @see iphone_mux_recv_timeout | ||
| 406 | */ | ||
| 407 | iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes) | ||
| 408 | { | ||
| 409 | return iphone_mux_recv_timeout(client, data, datalen, recv_bytes, 3500); | ||
| 410 | } | ||
