| /* |
| * usbmux.c |
| * Interprets the usb multiplexing protocol used by the iPhone. |
| * |
| * Copyright (c) 2008 Zach C. All Rights Reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <sys/types.h> |
| #include <arpa/inet.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "usbmux.h" |
| #include "utils.h" |
| |
| static iphone_umux_client_t *connlist = NULL; |
| static int clients = 0; |
| |
| /** Creates a USBMux packet for the given set of ports. |
| * |
| * @param s_port The source port for the connection. |
| * @param d_port The destination port for the connection. |
| * |
| * @return A USBMux packet |
| */ |
| usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port) |
| { |
| usbmux_tcp_header *conn = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); |
| conn->type = htonl(6); |
| conn->length = 28; |
| conn->sport = htons(s_port); |
| conn->dport = htons(d_port); |
| conn->scnt = 0; |
| conn->ocnt = 0; |
| conn->offset = 0x50; |
| conn->window = htons(0x0200); |
| conn->nullnull = 0x0000; |
| conn->length16 = 28; |
| return conn; |
| } |
| |
| /** Creates a USBMux header containing version information |
| * |
| * @return A USBMux header |
| */ |
| usbmux_version_header *version_header(void) |
| { |
| usbmux_version_header *version = (usbmux_version_header *) malloc(sizeof(usbmux_version_header)); |
| version->type = 0; |
| version->length = htonl(20); |
| version->major = htonl(1); |
| version->minor = 0; |
| version->allnull = 0; |
| return version; |
| } |
| |
| |
| // Maintenance functions. |
| |
| /** Removes a connection from the list of connections made. |
| * The list of connections is necessary for buffering. |
| * |
| * @param connection The connection to delete from the tracking list. |
| */ |
| static void delete_connection(iphone_umux_client_t connection) |
| { |
| iphone_umux_client_t *newlist = (iphone_umux_client_t *) malloc(sizeof(iphone_umux_client_t) * (clients - 1)); |
| int i = 0, j = 0; |
| for (i = 0; i < clients; i++) { |
| if (connlist[i] == connection) |
| continue; |
| else { |
| newlist[j] = connlist[i]; |
| j++; |
| } |
| } |
| free(connlist); |
| connlist = newlist; |
| clients--; |
| if (connection->recv_buffer) |
| free(connection->recv_buffer); |
| if (connection->header) |
| free(connection->header); |
| connection->r_len = 0; |
| free(connection); |
| } |
| |
| /** Adds a connection to the list of connections made. |
| * The connection list is necessary for buffering. |
| * |
| * @param connection The connection to add to the global list of connections. |
| */ |
| |
| static void add_connection(iphone_umux_client_t connection) |
| { |
| iphone_umux_client_t *newlist = |
| (iphone_umux_client_t *) realloc(connlist, sizeof(iphone_umux_client_t) * (clients + 1)); |
| newlist[clients] = connection; |
| connlist = newlist; |
| clients++; |
| } |
| |
| /** Initializes a connection on phone, with source port s_port and destination port d_port |
| * |
| * @param device The iPhone to initialize a connection on. |
| * @param src_port The source port |
| * @param dst_port The destination port -- 0xf27e for lockdownd. |
| * @param client A mux TCP header for the connection which is used for tracking and data transfer. |
| * @return IPHONE_E_SUCCESS on success, an error code otherwise. |
| */ |
| iphone_error_t iphone_mux_new_client(iphone_device_t device, uint16_t src_port, uint16_t dst_port, |
| iphone_umux_client_t * client) |
| { |
| if (!device || !src_port || !dst_port) |
| return IPHONE_E_INVALID_ARG; |
| |
| int bytes = 0; |
| // Initialize connection stuff |
| iphone_umux_client_t new_connection = (iphone_umux_client_t) malloc(sizeof(struct iphone_umux_client_int)); |
| new_connection->header = new_mux_packet(src_port, dst_port); |
| |
| // blargg |
| if (new_connection && new_connection->header) { |
| new_connection->header->tcp_flags = 0x02; |
| new_connection->header->length = htonl(new_connection->header->length); |
| new_connection->header->length16 = htons(new_connection->header->length16); |
| |
| if (send_to_phone(device, (char *) new_connection->header, sizeof(usbmux_tcp_header)) >= 0) { |
| usbmux_tcp_header *response; |
| response = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); |
| bytes = recv_from_phone(device, (char *) response, sizeof(*response), 3500); |
| if (response->tcp_flags != 0x12) { |
| free(response); |
| return IPHONE_E_UNKNOWN_ERROR; |
| } else { |
| free(response); |
| |
| log_debug_msg("mux_connect: connection success\n"); |
| new_connection->header->tcp_flags = 0x10; |
| new_connection->header->scnt = 1; |
| new_connection->header->ocnt = 1; |
| new_connection->phone = device; |
| new_connection->recv_buffer = NULL; |
| new_connection->r_len = 0; |
| add_connection(new_connection); |
| *client = new_connection; |
| return IPHONE_E_SUCCESS; |
| } |
| } else { |
| return IPHONE_E_NOT_ENOUGH_DATA; |
| } |
| } |
| // if we get to this point it's probably bad |
| return IPHONE_E_UNKNOWN_ERROR; |
| } |
| |
| /** Cleans up the given USBMux connection. |
| * @note Once a connection is closed it may not be used again. |
| * |
| * @param connection The connection to close. |
| * |
| * @return IPHONE_E_SUCCESS on success. |
| */ |
| iphone_error_t iphone_mux_free_client(iphone_umux_client_t client) |
| { |
| if (!client || !client->phone) |
| return IPHONE_E_INVALID_ARG; |
| |
| client->header->tcp_flags = 0x04; |
| client->header->length = htonl(0x1C); |
| client->header->scnt = htonl(client->header->scnt); |
| client->header->ocnt = htonl(client->header->ocnt); |
| client->header->window = 0; |
| client->header->length16 = htons(0x1C); |
| int bytes = 0; |
| |
| bytes = usb_bulk_write(client->phone->device, BULKOUT, (char *) client->header, sizeof(usbmux_tcp_header), 800); |
| if (bytes < 0) |
| log_debug_msg("iphone_muxèfree_client(): when writing, libusb gave me the error: %s\n", usb_strerror()); |
| |
| bytes = usb_bulk_read(client->phone->device, BULKIN, (char *) client->header, sizeof(usbmux_tcp_header), 800); |
| if (bytes < 0) |
| log_debug_msg("get_iPhone(): when reading, libusb gave me the error: %s\n", usb_strerror()); |
| |
| delete_connection(client); |
| |
| return IPHONE_E_SUCCESS; |
| } |
| |
| |
| /** Sends the given data over the selected connection. |
| * |
| * @param phone The iPhone to send to. |
| * @param client The client we're sending data on. |
| * @param data A pointer to the data to send. |
| * @param datalen How much data we're sending. |
| * @param sent_bytes The number of bytes sent, minus the header (28) |
| * |
| * @return IPHONE_E_SUCCESS on success. |
| */ |
| |
| iphone_error_t iphone_mux_send(iphone_umux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes) |
| { |
| if (!client->phone || !client || !data || datalen == 0 || !sent_bytes) |
| return IPHONE_E_INVALID_ARG; |
| // client->scnt and client->ocnt should already be in host notation... |
| // we don't need to change them juuuust yet. |
| *sent_bytes = 0; |
| log_debug_msg("mux_send(): client wants to send %i bytes\n", datalen); |
| char *buffer = (char *) malloc(sizeof(usbmux_tcp_header) + datalen + 2); // allow 2 bytes of safety padding |
| // Set the length and pre-emptively htonl/htons it |
| client->header->length = htonl(sizeof(usbmux_tcp_header) + datalen); |
| client->header->length16 = htons(sizeof(usbmux_tcp_header) + datalen); |
| |
| // Put scnt and ocnt into big-endian notation |
| client->header->scnt = htonl(client->header->scnt); |
| client->header->ocnt = htonl(client->header->ocnt); |
| // Concatenation of stuff in the buffer. |
| memcpy(buffer, client->header, sizeof(usbmux_tcp_header)); |
| memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen); |
| |
| // We have a buffer full of data, we should now send it to the phone. |
| log_debug_msg("actually sending %zi bytes of data at %p\n", sizeof(usbmux_tcp_header) + datalen, buffer); |
| |
| |
| *sent_bytes = send_to_phone(client->phone, buffer, sizeof(usbmux_tcp_header) + datalen); |
| log_debug_msg("mux_send: sent %i bytes!\n", *sent_bytes); |
| // Now that we've sent it off, we can clean up after our sloppy selves. |
| dump_debug_buffer("packet", buffer, *sent_bytes); |
| if (buffer) |
| free(buffer); |
| // Re-calculate scnt and ocnt |
| client->header->scnt = ntohl(client->header->scnt) + datalen; |
| client->header->ocnt = ntohl(client->header->ocnt); |
| |
| // Revert lengths |
| client->header->length = ntohl(client->header->length); |
| client->header->length16 = ntohs(client->header->length16); |
| |
| // Now return the bytes. |
| if (*sent_bytes < sizeof(usbmux_tcp_header) + datalen) { |
| *sent_bytes = 0; |
| return IPHONE_E_NOT_ENOUGH_DATA; |
| } else { |
| *sent_bytes = *sent_bytes - 28; // actual length sent. :/ |
| } |
| |
| return IPHONE_E_SUCCESS; |
| } |
| |
| /** This is a higher-level USBMuxTCP-like function |
| * |
| * @param connection The connection to receive data on. |
| * @param data Where to put the data we receive. |
| * @param datalen How much data to read. |
| * @param recv_bytes Pointer to a uint32_t that will be set |
| * to the number of bytes received. |
| * @param timeout How many milliseconds to wait for data. |
| * |
| * @return IPHONE_E_SUCCESS on success, or and error value. |
| */ |
| iphone_error_t iphone_mux_recv_timeout(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout) |
| { |
| |
| if (!client || !data || datalen == 0 || !recv_bytes) |
| return IPHONE_E_INVALID_ARG; |
| /* |
| * Order of operation: |
| * 1.) Check if the client has a pre-received buffer. |
| * 2.) If so, fill data with the buffer, as much as needed. |
| * a.) Return quickly if the buffer has enough |
| * b.) If the buffer is only part of the datalen, get the rest of datalen (and if we can't, just return) |
| * 3.) If not, receive directly from the phone. |
| * a.) Check incoming packet's ports. If proper, follow proper buffering and receiving operation. |
| * 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. |
| */ |
| log_debug_msg("mux_recv: datalen == %i\n", datalen); |
| int bytes = 0, i = 0, complex = 0, offset = 0; |
| *recv_bytes = 0; |
| char *buffer = NULL; |
| usbmux_tcp_header *header = NULL; |
| |
| if (client->recv_buffer) { |
| if (client->r_len >= datalen) { |
| memcpy(data, client->recv_buffer, datalen); |
| if (client->r_len == datalen) { |
| // reset everything |
| free(client->recv_buffer); |
| client->r_len = 0; |
| client->recv_buffer = NULL; |
| } else { |
| buffer = (char *) malloc(sizeof(char) * (client->r_len - datalen)); |
| memcpy(buffer, client->recv_buffer + datalen, (client->r_len - datalen)); |
| client->r_len -= datalen; |
| free(client->recv_buffer); |
| client->recv_buffer = buffer; |
| } |
| |
| // Since we were able to fill the data straight from our buffer, we can just return datalen. See 2a above. |
| *recv_bytes = datalen; |
| return IPHONE_E_SUCCESS; |
| } else { |
| memcpy(data, client->recv_buffer, client->r_len); |
| free(client->recv_buffer); // don't need to deal with anymore, but... |
| client->recv_buffer = NULL; |
| offset = client->r_len; // see #2b, above |
| client->r_len = 0; |
| } |
| } // End of what to do if we have a pre-buffer. See #1 and #2 above. |
| |
| buffer = (char *) malloc(sizeof(char) * 131072); // make sure we get enough ;) |
| |
| // See #3. |
| bytes = recv_from_phone(client->phone, buffer, 131072, timeout); |
| if (bytes < 28) { |
| free(buffer); |
| log_debug_msg("mux_recv: Did not even get the header.\n"); |
| return IPHONE_E_NOT_ENOUGH_DATA; |
| } |
| |
| header = (usbmux_tcp_header *) buffer; |
| if (header->sport != client->header->dport || header->dport != client->header->sport) { |
| // Ooooops -- we got someone else's packet. |
| // We gotta stick it in their buffer. (Take that any old way you want ;) ) |
| for (i = 0; i < clients; i++) { |
| if (connlist[i]->header->sport == header->dport && connlist[i]->header->dport == header->sport) { |
| // we have a winner. |
| char *nfb = (char *) malloc(sizeof(char) * (connlist[i]->r_len + (bytes - 28))); |
| if (connlist[i]->recv_buffer && connlist[i]->r_len) { |
| memcpy(nfb, connlist[i]->recv_buffer, connlist[i]->r_len); |
| free(connlist[i]->recv_buffer); |
| } |
| connlist[i]->r_len += bytes - 28; |
| //connlist[i]->recv_buffer = (char*)realloc(connlist[i]->recv_buffer, sizeof(char) * client->r_len); // grow their buffer |
| connlist[i]->recv_buffer = nfb; |
| nfb = NULL; // A cookie for you if you can guess what "nfb" means. |
| complex = connlist[i]->r_len - (bytes - 28); |
| memcpy(connlist[i]->recv_buffer + complex, buffer + 28, bytes - 28); // paste into their buffer |
| connlist[i]->header->ocnt += bytes - 28; |
| } |
| } |
| // If it wasn't ours, it's been handled by this point... or forgotten. |
| // Free our buffer and continue. |
| free(buffer); |
| buffer = NULL; |
| return iphone_mux_recv(client, data, datalen, recv_bytes); // recurse back in to try again |
| } |
| // The packet was absolutely meant for us if it hits this point. |
| // The pre-buffer has been taken care of, so, again, if we're at this point we have to read from the phone. |
| |
| if ((bytes - 28) > datalen) { |
| // Copy what we need into the data, buffer the rest because we can. |
| memcpy(data + offset, buffer + 28, datalen); // data+offset: see #2b, above |
| complex = client->r_len + ((bytes - 28) - datalen); |
| client->recv_buffer = (char *) realloc(client->recv_buffer, (sizeof(char) * complex)); |
| client->r_len = complex; |
| complex = client->r_len - ((bytes - 28) - datalen); |
| memcpy(client->recv_buffer + complex, buffer + 28 + datalen, (bytes - 28) - datalen); |
| free(buffer); |
| client->header->ocnt += bytes - 28; |
| *recv_bytes = datalen; |
| return IPHONE_E_SUCCESS; |
| } else { |
| // Fill the data with what we have, and just return. |
| memcpy(data + offset, buffer + 28, bytes - 28); // data+offset: see #2b, above |
| client->header->ocnt += bytes - 28; |
| free(buffer); |
| *recv_bytes = bytes - 28; |
| return IPHONE_E_SUCCESS; |
| } |
| |
| // If we get to this point, 'tis probably bad. |
| log_debug_msg("mux_recv: Heisenbug: bytes and datalen not matching up\n"); |
| return IPHONE_E_UNKNOWN_ERROR; |
| } |
| |
| /** |
| * This function is just like 'iphone_mux_recv_timeout' but you do not need |
| * to specify a timeout. It simply calls iphone_mux_recv_timeout with a |
| * timeout value of 3500 milliseconds. |
| * |
| * @param connection The connection to receive data on. |
| * @param data Where to put the data we receive. |
| * @param datalen How much data to read. |
| * @param recv_bytes Pointer to a uint32_t that will be set |
| * to the number of bytes received. |
| * |
| * @return The return value of iphone_mux_recv_timeout. |
| * |
| * @see iphone_mux_recv_timeout |
| */ |
| iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes) |
| { |
| return iphone_mux_recv_timeout(client, data, datalen, recv_bytes, 3500); |
| } |