diff options
Diffstat (limited to 'src/usbmux.c')
| -rw-r--r-- | src/usbmux.c | 251 |
1 files changed, 186 insertions, 65 deletions
diff --git a/src/usbmux.c b/src/usbmux.c index bdeea09..043f8af 100644 --- a/src/usbmux.c +++ b/src/usbmux.c | |||
| @@ -29,6 +29,9 @@ | |||
| 29 | 29 | ||
| 30 | extern int debug; | 30 | extern int debug; |
| 31 | 31 | ||
| 32 | static usbmux_connection **connlist = NULL; | ||
| 33 | static int connections = 0; | ||
| 34 | |||
| 32 | usbmux_tcp_header *new_mux_packet(uint16 s_port, uint16 d_port) { | 35 | usbmux_tcp_header *new_mux_packet(uint16 s_port, uint16 d_port) { |
| 33 | usbmux_tcp_header *conn = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header)); | 36 | usbmux_tcp_header *conn = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header)); |
| 34 | conn->type = htonl(6); | 37 | conn->type = htonl(6); |
| @@ -54,6 +57,47 @@ usbmux_version_header *version_header() { | |||
| 54 | return version; | 57 | return version; |
| 55 | } | 58 | } |
| 56 | 59 | ||
| 60 | |||
| 61 | // Maintenance functions. | ||
| 62 | |||
| 63 | /* delete_connection(connection) | ||
| 64 | * connection: the connection to delete from the tracking list. | ||
| 65 | * Removes a connection from the list of connections made. | ||
| 66 | * The list of connections is necessary for buffering. | ||
| 67 | */ | ||
| 68 | |||
| 69 | void delete_connection(usbmux_connection *connection) { | ||
| 70 | usbmux_connection **newlist = (usbmux_connection**)malloc(sizeof(usbmux_connection*) * (connections - 1)); | ||
| 71 | int i = 0, j = 0; | ||
| 72 | for (i = 0; i < connections; i++) { | ||
| 73 | if (connlist[i] == connection) continue; | ||
| 74 | else { | ||
| 75 | newlist[j] = connlist[i]; | ||
| 76 | j++; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | free(connlist); | ||
| 80 | connlist = newlist; | ||
| 81 | connections--; | ||
| 82 | if (connection->recv_buffer) free(connection->recv_buffer); | ||
| 83 | if (connection->header) free(connection->header); | ||
| 84 | connection->r_len = 0; | ||
| 85 | free(connection); | ||
| 86 | } | ||
| 87 | |||
| 88 | /* add_connection(connection) | ||
| 89 | * connection: the connection to add to the global list of connections. | ||
| 90 | * Adds a connection to the list of connections made. | ||
| 91 | * The connection list is necessary for buffering. | ||
| 92 | */ | ||
| 93 | |||
| 94 | void add_connection(usbmux_connection *connection) { | ||
| 95 | usbmux_connection **newlist = (usbmux_connection**)realloc(connlist, sizeof(usbmux_connection*) * (connections+1)); | ||
| 96 | newlist[connections] = connection; | ||
| 97 | connlist = newlist; | ||
| 98 | connections++; | ||
| 99 | } | ||
| 100 | |||
| 57 | /* mux_connect(phone, s_port, d_port) | 101 | /* mux_connect(phone, s_port, d_port) |
| 58 | * This is a higher-level USBMuxTCP-type function. | 102 | * This is a higher-level USBMuxTCP-type function. |
| 59 | * phone: the iPhone to initialize a connection on. | 103 | * phone: the iPhone to initialize a connection on. |
| @@ -64,27 +108,33 @@ usbmux_version_header *version_header() { | |||
| 64 | * Returns a mux TCP header for the connection which is used for tracking and data transfer. | 108 | * Returns a mux TCP header for the connection which is used for tracking and data transfer. |
| 65 | */ | 109 | */ |
| 66 | 110 | ||
| 67 | usbmux_tcp_header *mux_connect(iPhone *phone, uint16 s_port, uint16 d_port) { | 111 | usbmux_connection *mux_connect(iPhone *phone, uint16 s_port, uint16 d_port) { |
| 68 | if (!phone || !s_port || !d_port) return NULL; | 112 | if (!phone || !s_port || !d_port) return NULL; |
| 69 | int bytes = 0; | 113 | int bytes = 0; |
| 70 | // Initialize connection stuff | 114 | // Initialize connection stuff |
| 71 | usbmux_tcp_header *new_connection; | 115 | usbmux_connection *new_connection = (usbmux_connection*)malloc(sizeof(usbmux_connection)); |
| 72 | new_connection = new_mux_packet(s_port, d_port); | 116 | new_connection->header = new_mux_packet(s_port, d_port); |
| 73 | usbmux_tcp_header *response; | 117 | usbmux_tcp_header *response; |
| 74 | response = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header)); | 118 | response = (usbmux_tcp_header*)malloc(sizeof(usbmux_tcp_header)); |
| 75 | // blargg | 119 | // blargg |
| 76 | if (new_connection) { | 120 | if (new_connection && new_connection->header) { |
| 77 | new_connection->tcp_flags = 0x02; | 121 | new_connection->header->tcp_flags = 0x02; |
| 78 | new_connection->length = htonl(new_connection->length); | 122 | new_connection->header->length = htonl(new_connection->header->length); |
| 79 | new_connection->length16 = htons(new_connection->length16); | 123 | new_connection->header->length16 = htons(new_connection->header->length16); |
| 80 | 124 | ||
| 81 | if (send_to_phone(phone, (char*)new_connection, sizeof(*new_connection)) >= 0) { | 125 | if (send_to_phone(phone, (char*)new_connection->header, sizeof(usbmux_tcp_header)) >= 0) { |
| 82 | bytes = recv_from_phone(phone, (char*)response, sizeof(*response)); | 126 | bytes = recv_from_phone(phone, (char*)response, sizeof(*response)); |
| 83 | if (response->tcp_flags != 0x12) return NULL; | 127 | if (response->tcp_flags != 0x12) return NULL; |
| 84 | else { | 128 | else { |
| 85 | new_connection->tcp_flags = 0x10; | 129 | if (debug) printf("mux_connect: connection success\n"); |
| 86 | new_connection->scnt = 1; | 130 | new_connection->header->tcp_flags = 0x10; |
| 87 | new_connection->ocnt = 1; | 131 | new_connection->header->scnt = 1; |
| 132 | new_connection->header->ocnt = 1; | ||
| 133 | add_connection(new_connection); | ||
| 134 | new_connection->phone = phone; | ||
| 135 | new_connection->recv_buffer = NULL; | ||
| 136 | new_connection->r_len = 0; | ||
| 137 | add_connection(new_connection); | ||
| 88 | return new_connection; | 138 | return new_connection; |
| 89 | } | 139 | } |
| 90 | } else { | 140 | } else { |
| @@ -103,23 +153,24 @@ usbmux_tcp_header *mux_connect(iPhone *phone, uint16 s_port, uint16 d_port) { | |||
| 103 | * | 153 | * |
| 104 | * Doesn't return anything; WILL FREE THE CONNECTION'S MEMORY!!! | 154 | * Doesn't return anything; WILL FREE THE CONNECTION'S MEMORY!!! |
| 105 | */ | 155 | */ |
| 106 | void mux_close_connection(iPhone *phone, usbmux_tcp_header *connection) { | 156 | |
| 107 | if (!phone || !connection) return; | 157 | void mux_close_connection(usbmux_connection *connection) { |
| 158 | if (!connection || !connection->phone) return; | ||
| 108 | 159 | ||
| 109 | connection->tcp_flags = 0x04; | 160 | connection->header->tcp_flags = 0x04; |
| 110 | connection->scnt = htonl(connection->scnt); | 161 | connection->header->scnt = htonl(connection->header->scnt); |
| 111 | connection->ocnt = htonl(connection->ocnt); | 162 | connection->header->ocnt = htonl(connection->header->ocnt); |
| 112 | int bytes = 0; | 163 | int bytes = 0; |
| 113 | 164 | ||
| 114 | bytes = usb_bulk_write(phone->device, BULKOUT, (char*)connection, sizeof(*connection), 800); | 165 | bytes = usb_bulk_write(connection->phone->device, BULKOUT, (char*)connection->header, sizeof(usbmux_tcp_header), 800); |
| 115 | if(debug && bytes < 0) | 166 | if(debug && bytes < 0) |
| 116 | printf("mux_close_connection(): when writing, libusb gave me the error: %s\n", usb_strerror()); | 167 | printf("mux_close_connection(): when writing, libusb gave me the error: %s\n", usb_strerror()); |
| 117 | 168 | ||
| 118 | bytes = usb_bulk_read(phone->device, BULKIN, (char*)connection, sizeof(*connection), 800); | 169 | bytes = usb_bulk_read(connection->phone->device, BULKIN, (char*)connection->header, sizeof(usbmux_tcp_header), 800); |
| 119 | if(debug && bytes < 0) | 170 | if(debug && bytes < 0) |
| 120 | printf("get_iPhone(): when reading, libusb gave me the error: %s\n", usb_strerror()); | 171 | printf("get_iPhone(): when reading, libusb gave me the error: %s\n", usb_strerror()); |
| 121 | 172 | ||
| 122 | free(connection); | 173 | delete_connection(connection); |
| 123 | } | 174 | } |
| 124 | 175 | ||
| 125 | /* mux_send(phone, connection, data, datalen) | 176 | /* mux_send(phone, connection, data, datalen) |
| @@ -131,40 +182,46 @@ void mux_close_connection(iPhone *phone, usbmux_tcp_header *connection) { | |||
| 131 | * | 182 | * |
| 132 | * Returns number of bytes sent, minus the header (28), or -1 on error. | 183 | * Returns number of bytes sent, minus the header (28), or -1 on error. |
| 133 | */ | 184 | */ |
| 134 | int mux_send(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen) { | 185 | int mux_send(usbmux_connection *connection, const char *data, uint32 datalen) { |
| 135 | if (!phone || !connection || !data || datalen == 0) return -1; | 186 | if (!connection->phone || !connection || !data || datalen == 0) return -1; |
| 136 | // connection->scnt and connection->ocnt should already be in host notation... | 187 | // connection->scnt and connection->ocnt should already be in host notation... |
| 137 | // we don't need to change them juuuust yet. | 188 | // we don't need to change them juuuust yet. |
| 138 | int bytes = 0; | 189 | int bytes = 0; |
| 139 | if (debug) printf("mux_send(): client wants to send %i bytes\n", datalen); | 190 | if (debug) printf("mux_send(): client wants to send %i bytes\n", datalen); |
| 140 | char *buffer = (char*)malloc(sizeof(*connection) + datalen + 2); // allow 2 bytes of safety padding | 191 | char *buffer = (char*)malloc(sizeof(usbmux_tcp_header) + datalen + 2); // allow 2 bytes of safety padding |
| 141 | // Set the length and pre-emptively htonl/htons it | 192 | // Set the length and pre-emptively htonl/htons it |
| 142 | connection->length = htonl(sizeof(*connection) + datalen); | 193 | connection->header->length = htonl(sizeof(usbmux_tcp_header) + datalen); |
| 143 | connection->length16 = htons(sizeof(*connection) + datalen); | 194 | connection->header->length16 = htons(sizeof(usbmux_tcp_header) + datalen); |
| 144 | 195 | ||
| 145 | // Put scnt and ocnt into big-endian notation | 196 | // Put scnt and ocnt into big-endian notation |
| 146 | connection->scnt = htonl(connection->scnt); | 197 | connection->header->scnt = htonl(connection->header->scnt); |
| 147 | connection->ocnt = htonl(connection->ocnt); | 198 | connection->header->ocnt = htonl(connection->header->ocnt); |
| 148 | // Concatenation of stuff in the buffer. | 199 | // Concatenation of stuff in the buffer. |
| 149 | memcpy(buffer, connection, sizeof(*connection)); | 200 | memcpy(buffer, connection->header, sizeof(usbmux_tcp_header)); |
| 150 | memcpy(buffer+sizeof(*connection)/*+sizeof(datalen)*/, data, datalen); | 201 | memcpy(buffer+sizeof(usbmux_tcp_header), data, datalen); |
| 151 | 202 | ||
| 152 | // We have a buffer full of data, we should now send it to the phone. | 203 | // We have a buffer full of data, we should now send it to the phone. |
| 153 | if (debug) printf("actually sending %i bytes of data at %x\n", sizeof(*connection)+datalen, buffer); | 204 | if (debug) printf("actually sending %i bytes of data at %x\n", sizeof(usbmux_tcp_header)+datalen, buffer); |
| 154 | 205 | ||
| 155 | 206 | ||
| 156 | bytes = send_to_phone(phone, buffer, sizeof(*connection)+datalen); | 207 | bytes = send_to_phone(connection->phone, buffer, sizeof(usbmux_tcp_header)+datalen); |
| 157 | 208 | if (debug) printf("mux_send: sent %i bytes!\n", bytes); | |
| 158 | // Now that we've sent it off, we can clean up after our sloppy selves. | 209 | // Now that we've sent it off, we can clean up after our sloppy selves. |
| 159 | free(buffer); | 210 | if (debug) { |
| 211 | FILE *packet = fopen("packet", "a+"); | ||
| 212 | fwrite(buffer, 1, bytes, packet); | ||
| 213 | fclose(packet); | ||
| 214 | printf("\n"); | ||
| 215 | } | ||
| 160 | 216 | ||
| 217 | if (buffer) free(buffer); | ||
| 161 | // Re-calculate scnt and ocnt | 218 | // Re-calculate scnt and ocnt |
| 162 | connection->scnt = ntohl(connection->scnt) + datalen; | 219 | connection->header->scnt = ntohl(connection->header->scnt) + datalen; |
| 163 | connection->ocnt = ntohl(connection->ocnt); | 220 | connection->header->ocnt = ntohl(connection->header->ocnt); |
| 164 | 221 | ||
| 165 | // Revert lengths | 222 | // Revert lengths |
| 166 | connection->length = ntohl(connection->length); | 223 | connection->header->length = ntohl(connection->header->length); |
| 167 | connection->length16 = ntohs(connection->length16); | 224 | connection->header->length16 = ntohs(connection->header->length16); |
| 168 | 225 | ||
| 169 | // Now return the bytes. | 226 | // Now return the bytes. |
| 170 | if (bytes < sizeof(*connection)+datalen) { | 227 | if (bytes < sizeof(*connection)+datalen) { |
| @@ -186,39 +243,103 @@ int mux_send(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 da | |||
| 186 | * Returns: how many bytes were read, or -1 if something bad happens. | 243 | * Returns: how many bytes were read, or -1 if something bad happens. |
| 187 | */ | 244 | */ |
| 188 | 245 | ||
| 189 | int mux_recv(iPhone *phone, usbmux_tcp_header *connection, char *data, uint32 datalen) { | 246 | int mux_recv(usbmux_connection *connection, char *data, uint32 datalen) { |
| 190 | char *buffer = (char*)malloc(sizeof(*connection) + sizeof(datalen) + datalen); | 247 | /* |
| 191 | int bytes = 0, my_datalen = 0; | 248 | * Order of operation: |
| 249 | * 1.) Check if the connection has a pre-received buffer. | ||
| 250 | * 2.) If so, fill data with the buffer, as much as needed. | ||
| 251 | * a.) Return quickly if the buffer has enough | ||
| 252 | * b.) If the buffer is only part of the datalen, get the rest of datalen (and if we can't, just return) | ||
| 253 | * 3.) If not, receive directly from the phone. | ||
| 254 | * a.) Check incoming packet's ports. If proper, follow proper buffering and receiving operation. | ||
| 255 | * b.) If not, find the connection the ports belong to and fill that connection's buffer, then return mux_recv with the same args to try again. | ||
| 256 | */ | ||
| 192 | if (debug) printf("mux_recv: datalen == %i\n", datalen); | 257 | if (debug) printf("mux_recv: datalen == %i\n", datalen); |
| 193 | bytes = recv_from_phone(phone, buffer, sizeof(*connection) + datalen); | 258 | int bytes = 0, i = 0, complex = 0, offset = 0; |
| 194 | if (debug) printf("mux_recv: bytes == %i\n", bytes); | 259 | char *buffer = NULL; |
| 195 | if (bytes < datalen) { | 260 | usbmux_tcp_header *header = NULL; |
| 196 | if (bytes < 28) { | 261 | |
| 197 | // if they didn't do that annoying thing, something else mighta happened. | 262 | if (connection->recv_buffer) { |
| 198 | if (debug) printf("mux_recv: bytes too low anyway!\n"); | 263 | if (connection->r_len >= datalen) { |
| 199 | free(buffer); | 264 | memcpy(data, connection->recv_buffer, datalen); |
| 200 | return -1; | 265 | if (connection->r_len == datalen) { |
| 201 | } else if (bytes == 28) { // no data... | 266 | // reset everything |
| 202 | free(buffer); | 267 | free(connection->recv_buffer); |
| 203 | return 0; | 268 | connection->r_len = 0; |
| 204 | } else { // bytes > 28 | 269 | connection->recv_buffer = NULL; |
| 205 | my_datalen = ntohl(buffer[4]) - 28; | 270 | } else { |
| 206 | connection->ocnt += my_datalen; | 271 | buffer = (char*)malloc(sizeof(char) * (connection->r_len - datalen)); |
| 207 | memcpy(data, buffer+28, bytes - 28); | 272 | memcpy(buffer, connection->recv_buffer+datalen, (connection->r_len - datalen)); |
| 208 | free(buffer); | 273 | connection->r_len -= datalen; |
| 209 | if (debug) printf("mux_recv: bytes received: %i\n", bytes - 28); | 274 | free(connection->recv_buffer); |
| 210 | return bytes - 28; | 275 | connection->recv_buffer = buffer; |
| 276 | } | ||
| 277 | |||
| 278 | // Since we were able to fill the data straight from our buffer, we can just return datalen. See 2a above. | ||
| 279 | return datalen; | ||
| 280 | } else { | ||
| 281 | memcpy(data, connection->recv_buffer, connection->r_len); | ||
| 282 | free(connection->recv_buffer); // don't need to deal with anymore, but... | ||
| 283 | offset = connection->r_len; // see #2b, above | ||
| 284 | connection->r_len = 0; | ||
| 211 | } | 285 | } |
| 212 | } else {// all's good, they didn't do anything bonky. | 286 | } // End of what to do if we have a pre-buffer. See #1 and #2 above. |
| 213 | my_datalen = ntohl(buffer[4]) - 28; | 287 | |
| 214 | connection->ocnt += my_datalen; | 288 | buffer = (char*)malloc(sizeof(char) * 131072); // make sure we get enough ;) |
| 215 | if (bytes == (datalen+28)) memcpy(data, buffer+28, datalen); | 289 | |
| 216 | else if (bytes == datalen) memcpy(data, buffer+28, datalen-28); | 290 | // See #3. |
| 291 | bytes = recv_from_phone(connection->phone, buffer, 131072); | ||
| 292 | if (bytes < 28) { | ||
| 293 | free(buffer); | ||
| 294 | if (debug) printf("mux_recv: Did not even get the header.\n"); | ||
| 295 | return -1; | ||
| 296 | } | ||
| 297 | |||
| 298 | header = (usbmux_tcp_header*)buffer; | ||
| 299 | if (header->sport != connection->header->dport || header->dport != connection->header->sport) { | ||
| 300 | // Ooooops -- we got someone else's packet. | ||
| 301 | // We gotta stick it in their buffer. (Take that any old way you want ;) ) | ||
| 302 | for (i = 0; i < connections; i++) { | ||
| 303 | if (connlist[i]->header->sport == header->dport && connlist[i]->header->dport == header->sport) { | ||
| 304 | // we have a winner. | ||
| 305 | connlist[i]->r_len += bytes - 28; | ||
| 306 | connlist[i]->recv_buffer = (char*)realloc(connlist[i]->recv_buffer, sizeof(char) * connection->r_len); // grow their buffer | ||
| 307 | complex = connlist[i]->r_len - (bytes - 28); | ||
| 308 | memcpy(connlist[i]->recv_buffer+complex, buffer+28, bytes-28); // paste into their buffer | ||
| 309 | connlist[i]->header->ocnt += bytes-28; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | // If it wasn't ours, it's been handled by this point... or forgotten. | ||
| 313 | // Free our buffer and continue. | ||
| 314 | free(buffer); | ||
| 315 | buffer = NULL; | ||
| 316 | return mux_recv(connection, data, datalen); // recurse back in to try again | ||
| 317 | } | ||
| 318 | |||
| 319 | // The packet was absolutely meant for us if it hits this point. | ||
| 320 | // The pre-buffer has been taken care of, so, again, if we're at this point we have to read from the phone. | ||
| 321 | |||
| 322 | if ((bytes-28) > datalen) { | ||
| 323 | // Copy what we need into the data, buffer the rest because we can. | ||
| 324 | memcpy(data+offset, buffer+28, datalen); // data+offset: see #2b, above | ||
| 325 | complex = connection->r_len + (bytes-28) - datalen; | ||
| 326 | connection->recv_buffer = (char*)realloc(connection->recv_buffer, (sizeof(char) * complex)); | ||
| 327 | connection->r_len = complex; | ||
| 328 | complex = connection->r_len - (bytes-28) - datalen; | ||
| 329 | memcpy(connection->recv_buffer+complex, buffer+28+datalen, (bytes-28) - datalen); | ||
| 330 | free(buffer); | ||
| 331 | connection->header->ocnt += bytes-28; | ||
| 332 | return datalen; | ||
| 333 | } else { | ||
| 334 | // Fill the data with what we have, and just return. | ||
| 335 | memcpy(data+offset, buffer+28, bytes-28); // data+offset: see #2b, above | ||
| 336 | connection->header->ocnt += bytes-28; | ||
| 217 | free(buffer); | 337 | free(buffer); |
| 218 | if (debug) printf("mux_recv: bytes received: %i\n", bytes - 28); | 338 | return (bytes-28); |
| 219 | return bytes - 28; | ||
| 220 | } | 339 | } |
| 221 | 340 | ||
| 222 | return bytes; | 341 | // If we get to this point, 'tis probably bad. |
| 342 | if (debug) printf("mux_recv: Heisenbug: bytes and datalen not matching up\n"); | ||
| 343 | return -1; | ||
| 223 | } | 344 | } |
| 224 | 345 | ||
