summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile26
-rw-r--r--iphone.c1089
-rw-r--r--iphone.h79
-rw-r--r--iproxy.c329
-rw-r--r--sock_stuff.c277
-rw-r--r--sock_stuff.h27
-rw-r--r--testclient.c148
-rw-r--r--usbmuxd.c795
-rw-r--r--usbmuxd.h44
9 files changed, 2814 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..04a36b3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
1TARGET=usbmuxd
2CFLAGS=-Wall
3LDFLAGS=-lpthread -lusb -lrt
4
5objects = sock_stuff.o usbmuxd.o iphone.o
6
7all: $(TARGET)
8
9%.o: %.c %.h
10 $(CC) -o $@ $(CFLAGS) -c $<
11
12$(TARGET): $(objects)
13 $(CC) -o $@ $(LDFLAGS) $^
14
15clean:
16 rm -f *.o $(TARGET)
17
18realclean: clean
19 rm -f *~
20
21testclient: testclient.c sock_stuff.o
22 $(CC) $(LDFLAGS) -o testclient $(CFLAGS) $< sock_stuff.o
23
24iproxy: iproxy.c sock_stuff.o
25 $(CC) -lpthread -o iproxy $(CFLAGS) $< sock_stuff.o
26
diff --git a/iphone.c b/iphone.c
new file mode 100644
index 0000000..9035be9
--- /dev/null
+++ b/iphone.c
@@ -0,0 +1,1089 @@
1/*
2 * Copyright (c) 2008 Jing Su. All Rights Reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18#include <stdint.h>
19#include <stdarg.h>
20#include <stdlib.h>
21#include <string.h>
22#include <usb.h>
23#include <stdio.h>
24#include <arpa/inet.h>
25#include <errno.h>
26#include <pthread.h>
27#include "iphone.h"
28
29#define BULKIN 0x85
30#define BULKOUT 0x04
31#define HEADERLEN 28
32
33typedef uint16_t uint16;
34typedef uint32_t uint32;
35typedef uint8_t uint8;
36
37static const uint8 TCP_FIN = 1;
38static const uint8 TCP_SYN = 1 << 1;
39static const uint8 TCP_RST = 1 << 2;
40static const uint8 TCP_PSH = 1 << 3;
41static const uint8 TCP_ACK = 1 << 4;
42static const uint8 TCP_URG = 1 << 5;
43
44// I have trouble figuring out how to properly manage the windowing to
45// the iPhone. It keeps sending back 512 and seems to drop off a cliff
46// when the phone gets overwhelmed. In addition, the phone likes to
47// panic and send out RESETS before the window hits zero. Also, waiting
48// for responses seems to not be a winning strategy.
49//
50// Since I'm not sure how in the hell to interpret the window sizes that
51// the phone is sending back to us, I've figured out some magic number
52// constants which seem to work okay.
53static const uint32 WINDOW_MAX = 5 * 1024;
54static const uint32 WINDOW_INCREMENT = 512;
55
56
57struct iphone_device_int {
58 char *buffer;
59 struct usb_dev_handle *device;
60 struct usb_device *__device;
61};
62
63typedef struct {
64 uint32 type, length, major, minor, allnull;
65} usbmux_version_header;
66
67typedef struct {
68 uint32 type, length;
69 uint16 sport, dport;
70 uint32 scnt, ocnt;
71 uint8 offset, tcp_flags;
72 uint16 window, nullnull, length16;
73} usbmux_tcp_header;
74
75struct iphone_umux_client_int {
76 usbmux_tcp_header *header;
77 iphone_device_t phone;
78
79 char *recv_buffer;
80 int r_len;
81 pthread_cond_t wait;
82
83 // this contains a conditional variable which usb-writers can wait
84 // on while waiting for window updates from the phone.
85 pthread_cond_t wr_wait;
86 // I'm going to do something really cheesy here. We are going to
87 // just record the most recent scnt that we are expecting to hear
88 // back on. We will actually halt progress by limiting the number
89 // of outstanding un-acked bulk sends that we have beamed out.
90 uint32 wr_pending_scnt;
91 long wr_window;
92
93 pthread_mutex_t mutex;
94
95 // this variable is not protected by the mutex. This will always
96 // be E_SUCCESS, unless an error of some kind breaks this stream.
97 // this will then be set to the error that caused the broken stream.
98 // no further operations other than free_client will be allowed.
99 iphone_error_t error;
100};
101
102
103typedef struct {
104 char* buffer;
105 int leftover;
106 int capacity;
107} receivebuf_t;
108
109
110static pthread_mutex_t iphonemutex = PTHREAD_MUTEX_INITIALIZER;
111static iphone_umux_client_t *connlist = NULL;
112static int clients = 0;
113static receivebuf_t usbReceive = {NULL, 0, 0};
114
115
116/**
117 */
118int toto_debug = 0;
119void log_debug_msg(const char *format, ...)
120{
121#ifndef STRIP_DEBUG_CODE
122
123 va_list args;
124 /* run the real fprintf */
125 va_start(args, format);
126
127 if (toto_debug)
128 fprintf(stderr, format, args);
129
130 va_end(args);
131
132#endif
133}
134
135
136/** Creates a USBMux header containing version information
137 *
138 * @return A USBMux header
139 */
140usbmux_version_header *version_header()
141{
142 usbmux_version_header *version = (usbmux_version_header *) malloc(sizeof(usbmux_version_header));
143 version->type = 0;
144 version->length = htonl(20);
145 version->major = htonl(1);
146 version->minor = 0;
147 version->allnull = 0;
148 return version;
149}
150
151/**
152 * This function sets the configuration of the given device to 3
153 * and claims the interface 1. If usb_set_configuration fails, it detaches
154 * the kernel driver that blocks the device, and retries configuration.
155 *
156 * @param phone which device to configure
157 */
158static iphone_error_t iphone_config_usb_device(iphone_device_t phone)
159{
160 int ret;
161 int bytes;
162 char buf[512];
163
164 log_debug_msg("setting configuration...\n");
165 ret = usb_set_configuration(phone->device, 3);
166 if (ret != 0) {
167 log_debug_msg("Hm, usb_set_configuration returned %d: %s\n", ret, strerror(-ret));
168#if LIBUSB_HAS_GET_DRIVER_NP
169 log_debug_msg("trying to fix:\n");
170 log_debug_msg("-> detaching kernel driver... ");
171 ret = usb_detach_kernel_driver_np(phone->device, phone->__device->config->interface->altsetting->bInterfaceNumber);
172 if (ret != 0) {
173 log_debug_msg("usb_detach_kernel_driver_np returned %d: %s\n", ret, strerror(-ret));
174 } else {
175 log_debug_msg("done.\n");
176 log_debug_msg("setting configuration again... ");
177 ret = usb_set_configuration(phone->device, 3);
178 if (ret != 0) {
179 log_debug_msg("Error: usb_set_configuration returned %d: %s\n", ret, strerror(-ret));
180 log_debug_msg("--> trying to continue anyway...\n");
181 } else {
182 log_debug_msg("done.\n");
183 }
184 }
185#else
186 log_debug_msg("--> trying to continue anyway...\n");
187#endif
188 } else {
189 log_debug_msg("done.\n");
190 }
191
192 log_debug_msg("claiming interface... ");
193 ret = usb_claim_interface(phone->device, 1);
194 if (ret != 0) {
195 log_debug_msg("Error: usb_claim_interface returned %d: %s\n", ret, strerror(-ret));
196 return IPHONE_E_NO_DEVICE;
197 } else {
198 log_debug_msg("done.\n");
199 }
200
201 do {
202 bytes = usb_bulk_read(phone->device, BULKIN, buf, 512, 800);
203 } while (bytes > 0);
204
205 return IPHONE_E_SUCCESS;
206}
207
208/**
209 * Given a USB bus and device number, returns a device handle to the iPhone on
210 * that bus. To aid compatibility with future devices, this function does not
211 * check the vendor and device IDs! To do that, you should use
212 * iphone_get_device() or a system-specific API (e.g. HAL).
213 *
214 * @param bus_n The USB bus number.
215 * @param dev_n The USB device number.
216 * @param device A pointer to a iphone_device_t, which must be set to NULL upon
217 * calling iphone_get_specific_device, which will be filled with a device
218 * descriptor on return.
219 * @return IPHONE_E_SUCCESS if ok, otherwise an error code.
220 */
221iphone_error_t iphone_get_specific_device(int bus_n, int dev_n, iphone_device_t * device)
222{
223 struct usb_bus *bus, *busses;
224 struct usb_device *dev;
225 usbmux_version_header *version;
226 int bytes = 0;
227
228 //check we can actually write in device
229 if (!device || (device && *device))
230 return IPHONE_E_INVALID_ARG;
231
232 iphone_device_t phone = (iphone_device_t) malloc(sizeof(struct iphone_device_int));
233
234 // Initialize the struct
235 phone->device = NULL;
236 phone->__device = NULL;
237 phone->buffer = NULL;
238
239 // Initialize libusb
240 usb_init();
241 usb_find_busses();
242 usb_find_devices();
243 busses = usb_get_busses();
244
245 // Set the device configuration
246 for (bus = busses; bus; bus = bus->next)
247 if (bus->location == bus_n)
248 for (dev = bus->devices; dev != NULL; dev = dev->next)
249 if (dev->devnum == dev_n) {
250 phone->__device = dev;
251 phone->device = usb_open(phone->__device);
252 iphone_config_usb_device(phone);
253 goto found;
254 }
255
256 iphone_free_device(phone);
257
258 log_debug_msg("iphone_get_specific_device: iPhone not found\n");
259 return IPHONE_E_NO_DEVICE;
260
261 found:
262 // Send the version command to the phone
263 version = version_header();
264 bytes = usb_bulk_write(phone->device, BULKOUT, (char *) version, sizeof(*version), 800);
265 if (bytes < 20) {
266 log_debug_msg("get_iPhone(): libusb did NOT send enough!\n");
267 if (bytes < 0) {
268 log_debug_msg("get_iPhone(): libusb gave me the error %d: %s (%s)\n",
269 bytes, usb_strerror(), strerror(-bytes));
270 }
271 }
272 // Read the phone's response
273 bytes = usb_bulk_read(phone->device, BULKIN, (char *) version, sizeof(*version), 800);
274
275 // Check for bad response
276 if (bytes < 20) {
277 free(version);
278 iphone_free_device(phone);
279 log_debug_msg("get_iPhone(): Invalid version message -- header too short.\n");
280 if (bytes < 0)
281 log_debug_msg("get_iPhone(): libusb error message %d: %s (%s)\n", bytes, usb_strerror(), strerror(-bytes));
282 return IPHONE_E_NOT_ENOUGH_DATA;
283 }
284 // Check for correct version
285 if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) {
286 // We're all ready to roll.
287 fprintf(stderr, "get_iPhone() success\n");
288 free(version);
289 *device = phone;
290 return IPHONE_E_SUCCESS;
291 } else {
292 // Bad header
293 iphone_free_device(phone);
294 free(version);
295 log_debug_msg("get_iPhone(): Received a bad header/invalid version number.");
296 return IPHONE_E_BAD_HEADER;
297 }
298
299 // If it got to this point it's gotta be bad
300 log_debug_msg("get_iPhone(): Unknown error.\n");
301 iphone_free_device(phone);
302 free(version);
303 return IPHONE_E_UNKNOWN_ERROR; // if it got to this point it's gotta be bad
304}
305
306
307/**
308 * Scans all USB busses and devices for a known AFC-compatible device and
309 * returns a handle to the first such device it finds. Known devices include
310 * those with vendor ID 0x05ac and product ID between 0x1290 and 0x1293
311 * inclusive.
312 *
313 * This function is convenient, but on systems where higher-level abstractions
314 * (such as HAL) are available it may be preferable to use
315 * iphone_get_specific_device instead, because it can deal with multiple
316 * connected devices as well as devices not known to libiphone.
317 *
318 * @param device Upon calling this function, a pointer to a location of type
319 * iphone_device_t, which must have the value NULL. On return, this location
320 * will be filled with a handle to the device.
321 * @return IPHONE_E_SUCCESS if ok, otherwise an error code.
322 */
323iphone_error_t iphone_get_device(iphone_device_t * device)
324{
325 struct usb_bus *bus;
326 struct usb_device *dev;
327
328 pthread_mutex_init(&iphonemutex, NULL);
329
330 usb_init();
331 usb_find_busses();
332 usb_find_devices();
333
334 for (bus = usb_get_busses(); bus != NULL; bus = bus->next)
335 for (dev = bus->devices; dev != NULL; dev = dev->next)
336 if (dev->descriptor.idVendor == 0x05ac
337 && dev->descriptor.idProduct >= 0x1290 && dev->descriptor.idProduct <= 0x1293)
338 return iphone_get_specific_device(bus->location, dev->devnum, device);
339
340 return IPHONE_E_NO_DEVICE;
341}
342
343/** Cleans up an iPhone structure, then frees the structure itself.
344 * This is a library-level function; deals directly with the iPhone to tear
345 * down relations, but otherwise is mostly internal.
346 *
347 * @param phone A pointer to an iPhone structure.
348 */
349iphone_error_t iphone_free_device(iphone_device_t device)
350{
351 char buf[512];
352 int bytes;
353
354 if (!device)
355 return IPHONE_E_INVALID_ARG;
356 iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR;
357
358 do {
359 bytes = usb_bulk_read(device->device, BULKIN, buf, 512, 800);
360 } while (bytes > 0);
361
362 if (device->buffer) {
363 free(device->buffer);
364 }
365 if (device->device) {
366 usb_release_interface(device->device, 1);
367 usb_close(device->device);
368 ret = IPHONE_E_SUCCESS;
369 }
370 free(device);
371
372 pthread_mutex_destroy(&iphonemutex);
373
374 return ret;
375}
376
377
378
379/** Sends data to the phone
380 * This is a low-level (i.e. directly to phone) function.
381 *
382 * @param phone The iPhone to send data to
383 * @param data The data to send to the iPhone
384 * @param datalen The length of the data
385 * @return The number of bytes sent, or -ERRNO on error
386 */
387int send_to_phone(iphone_device_t phone, char *data, int datalen)
388{
389 if (!phone)
390 return -1;
391
392 int timeout = 1000;
393 int retrycount = 0;
394 int bytes = 0;
395 do {
396 if (retrycount > 3) {
397 fprintf(stderr, "EPIC FAIL! aborting on retry count overload.\n");
398 return -1;
399 }
400
401 bytes = usb_bulk_write(phone->device, BULKOUT, data, datalen, timeout);
402 if (bytes == -ETIMEDOUT) {
403 // timed out waiting for write.
404 fprintf(stderr, "usb_bulk_write timeout error.\n");
405 return bytes;
406 }
407 else if (bytes < 0) {
408 fprintf(stderr, "usb_bulk_write failed with error. err:%d (%s)(%s)\n",
409 bytes, usb_strerror(), strerror(-bytes));
410 return -1;
411 }
412 else if (bytes == 0) {
413 fprintf(stderr, "usb_bulk_write sent nothing. retrying.\n");
414 timeout = timeout * 4;
415 retrycount++;
416 continue;
417 }
418 else if (bytes < datalen) {
419 fprintf(stderr, "usb_bulk_write failed to send full dataload. %d of %d\n", bytes, datalen);
420 timeout = timeout * 4;
421 retrycount++;
422 data += bytes;
423 datalen -= bytes;
424 continue;
425 }
426 }
427 while(0); // fall out
428
429 return bytes;
430}
431
432/**
433 */
434int recv_from_phone_timeout(iphone_device_t phone, char *data, int datalen, int timeoutmillis)
435{
436 if (!phone)
437 return -1;
438 int bytes = 0;
439
440 if (!phone)
441 return -1;
442 log_debug_msg("recv_from_phone(): attempting to receive %i bytes\n", datalen);
443
444 bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, timeoutmillis);
445 if (bytes < 0) {
446 // there are some things which are errors, others which are no problem.
447 // it's not documented in libUSB, but it seems that the error returns are
448 // just negated ERRNO values.
449 if (bytes == -ETIMEDOUT) {
450 // ignore this. it just means timeout reached before we
451 // picked up any data. no problem.
452 }
453 else {
454 fprintf(stderr, "recv_from_phone(): libusb gave me the error %d: %s (%s)\n", bytes, usb_strerror(),
455 strerror(-bytes));
456 log_debug_msg("recv_from_phone(): libusb gave me the error %d: %s (%s)\n", bytes, usb_strerror(),
457 strerror(-bytes));
458 }
459 return -1;
460 }
461
462 return bytes;
463}
464
465/** This function is a low-level (i.e. direct to iPhone) function.
466 *
467 * @param phone The iPhone to receive data from
468 * @param data Where to put data read
469 * @param datalen How much data to read in
470 *
471 * @return How many bytes were read in, or -1 on error.
472 */
473int recv_from_phone(iphone_device_t phone, char *data, int datalen) {
474 return recv_from_phone_timeout(phone, data, datalen, 100);
475}
476
477
478/** Creates a USBMux packet for the given set of ports.
479 *
480 * @param s_port The source port for the connection.
481 * @param d_port The destination port for the connection.
482 *
483 * @return A USBMux packet
484 */
485usbmux_tcp_header *new_mux_packet(uint16 s_port, uint16 d_port)
486{
487 usbmux_tcp_header *conn = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header));
488 conn->type = htonl(6);
489 conn->length = HEADERLEN;
490 conn->sport = htons(s_port);
491 conn->dport = htons(d_port);
492 conn->scnt = 0;
493 conn->ocnt = 0;
494 conn->offset = 0x50;
495 conn->window = htons(0x0200);
496 conn->nullnull = 0x0000;
497 conn->length16 = HEADERLEN;
498 return conn;
499}
500
501
502/** Removes a connection from the list of connections made.
503 * The list of connections is necessary for buffering.
504 *
505 * @param connection The connection to delete from the tracking list.
506 */
507static void delete_connection(iphone_umux_client_t connection)
508{
509 pthread_mutex_lock(&iphonemutex);
510
511 // update the global list of connections
512 iphone_umux_client_t *newlist = (iphone_umux_client_t *) malloc(sizeof(iphone_umux_client_t) * (clients - 1));
513 int i = 0, j = 0;
514 for (i = 0; i < clients; i++) {
515 if (connlist[i] == connection)
516 continue;
517 else {
518 newlist[j] = connlist[i];
519 j++;
520 }
521 }
522 free(connlist);
523 connlist = newlist;
524 clients--;
525
526 // free up this connection
527 pthread_mutex_lock(&connection->mutex);
528 if (connection->recv_buffer)
529 free(connection->recv_buffer);
530 if (connection->header)
531 free(connection->header);
532 connection->r_len = 0;
533 pthread_mutex_unlock(&connection->mutex);
534 pthread_mutex_destroy(&connection->mutex);
535 free(connection);
536
537 pthread_mutex_unlock(&iphonemutex);
538}
539
540/** Adds a connection to the list of connections made.
541 * The connection list is necessary for buffering.
542 *
543 * @param connection The connection to add to the global list of connections.
544 */
545
546static void add_connection(iphone_umux_client_t connection)
547{
548 pthread_mutex_lock(&iphonemutex);
549 iphone_umux_client_t *newlist =
550 (iphone_umux_client_t *) realloc(connlist, sizeof(iphone_umux_client_t) * (clients + 1));
551 newlist[clients] = connection;
552 connlist = newlist;
553 clients++;
554 pthread_mutex_unlock(&iphonemutex);
555}
556
557/**
558 * Get a source port number that is not used by one of our connections
559 * This is needed for us to make sure we are not sending on another
560 * connection.
561 */
562static uint16_t get_free_port()
563{
564 int i;
565 uint16_t newport = 30000;
566 int cnt = 0;
567
568 pthread_mutex_lock(&iphonemutex);
569 while (1) {
570 cnt = 0;
571 for (i = 0; i < clients; i++) {
572 if (ntohs(connlist[i]->header->sport) == newport) {
573 cnt++;
574 }
575 }
576 if (cnt == 0) {
577 // newport is not used in our list of connections!
578 break;
579 } else {
580 newport++;
581 if (newport < 30000) {
582 // if all ports from 30000 to 65535 are in use,
583 // the value wraps (16-bit overflow)
584 // return 0, no port is available.
585 // This should not happen, but just in case ;)
586 newport = 0;
587 break;
588 }
589 }
590 }
591 pthread_mutex_unlock(&iphonemutex);
592
593 return newport;
594}
595
596/** Initializes a connection on phone, with source port s_port and destination port d_port
597 *
598 * @param device The iPhone to initialize a connection on.
599 * @param src_port The source port
600 * @param dst_port The destination port -- 0xf27e for lockdownd.
601 * @param client A mux TCP header for the connection which is used for tracking and data transfer.
602 * @return IPHONE_E_SUCCESS on success, an error code otherwise.
603 */
604iphone_error_t iphone_mux_new_client(iphone_device_t device, uint16_t src_port, uint16_t dst_port,
605 iphone_umux_client_t * client)
606{
607 if (!device || !dst_port)
608 return IPHONE_E_INVALID_ARG;
609
610 src_port = get_free_port();
611
612 if (!src_port) {
613 // this is a special case, if we get 0, this is not good, so
614 return -EISCONN; // TODO: error code suitable?
615 }
616
617 // Initialize connection stuff
618 iphone_umux_client_t new_connection = (iphone_umux_client_t) malloc(sizeof(struct iphone_umux_client_int));
619 new_connection->header = new_mux_packet(src_port, dst_port);
620
621 // send TCP syn
622 if (new_connection && new_connection->header) {
623 new_connection->header->tcp_flags = TCP_SYN;
624 new_connection->header->length = htonl(new_connection->header->length);
625 new_connection->header->length16 = htons(new_connection->header->length16);
626 new_connection->header->scnt = 0;
627 new_connection->header->ocnt = 0;
628 new_connection->phone = device;
629 new_connection->recv_buffer = NULL;
630 new_connection->r_len = 0;
631 pthread_cond_init(&new_connection->wait, NULL);
632 pthread_mutex_init(&new_connection->mutex, NULL);
633 pthread_cond_init(&new_connection->wr_wait, NULL);
634 new_connection->wr_pending_scnt = 0;
635 new_connection->wr_window = 0;
636 add_connection(new_connection);
637 new_connection->error = IPHONE_E_SUCCESS;
638 if (send_to_phone(device, (char *) new_connection->header, sizeof(usbmux_tcp_header)) >= 0) {
639 *client = new_connection;
640 return IPHONE_E_SUCCESS;
641 } else {
642 delete_connection(new_connection);
643 return IPHONE_E_NOT_ENOUGH_DATA;
644 }
645 }
646 // if we get to this point it's probably bad
647 return IPHONE_E_UNKNOWN_ERROR;
648}
649
650/** Cleans up the given USBMux connection.
651 * @note Once a connection is closed it may not be used again.
652 *
653 * @param connection The connection to close.
654 *
655 * @return IPHONE_E_SUCCESS on success.
656 */
657iphone_error_t iphone_mux_free_client(iphone_umux_client_t client)
658{
659 if (!client || !client->phone)
660 return IPHONE_E_INVALID_ARG;
661
662 pthread_mutex_lock(&client->mutex);
663 client->header->tcp_flags = TCP_FIN;
664 client->header->length = htonl(0x1C);
665 client->header->scnt = htonl(client->header->scnt);
666 client->header->ocnt = htonl(client->header->ocnt);
667 client->header->window = 0;
668 client->header->length16 = htons(0x1C);
669 int bytes = 0;
670
671 bytes = usb_bulk_write(client->phone->device, BULKOUT, (char *) client->header, sizeof(usbmux_tcp_header), 800);
672 if (bytes < 0)
673 log_debug_msg("iphone_mux_free_client(): when writing, libusb gave me the error: %s\n", usb_strerror());
674
675 bytes = usb_bulk_read(client->phone->device, BULKIN, (char *) client->header, sizeof(usbmux_tcp_header), 800);
676 if (bytes < 0)
677 log_debug_msg("get_iPhone(): when reading, libusb gave me the error: %s\n", usb_strerror());
678
679 pthread_mutex_unlock(&client->mutex);
680 // make sure we don't have any last-minute laggards waiting on this.
681 // I put it after the mutex unlock because we have cases where the
682 // conditional wait is dependent on re-grabbing that mutex.
683 pthread_cond_broadcast(&client->wait);
684 pthread_cond_destroy(&client->wait);
685 pthread_cond_broadcast(&client->wr_wait);
686 pthread_cond_destroy(&client->wr_wait);
687
688 delete_connection(client);
689
690 return IPHONE_E_SUCCESS;
691}
692
693
694/** Sends the given data over the selected connection.
695 *
696 * @param phone The iPhone to send to.
697 * @param client The client we're sending data on.
698 * @param data A pointer to the data to send.
699 * @param datalen How much data we're sending.
700 * @param sent_bytes The number of bytes sent, minus the header (28)
701 *
702 * @return IPHONE_E_SUCCESS on success.
703 */
704iphone_error_t iphone_mux_send(iphone_umux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes)
705{
706 if (!client->phone || !client || !sent_bytes)
707 return IPHONE_E_INVALID_ARG;
708
709 if (client->error != IPHONE_E_SUCCESS) {
710 return client->error;
711 }
712
713 *sent_bytes = 0;
714 pthread_mutex_lock(&client->mutex);
715
716 int sendresult = 0;
717 uint32 blocksize = 0;
718 if (client->wr_window <= 0) {
719 struct timespec ts;
720 clock_gettime(CLOCK_REALTIME, &ts);
721 //ts.tv_sec += 1;
722 ts.tv_nsec += 750 * 1000;
723 if (pthread_cond_timedwait(&client->wait, &client->mutex, &ts) == ETIMEDOUT) {
724 // timd out. optimistically grow the window and try to make progress
725 client->wr_window += WINDOW_INCREMENT;
726 }
727 }
728
729 blocksize = sizeof(usbmux_tcp_header) + datalen;
730
731 // client->scnt and client->ocnt should already be in host notation...
732 // we don't need to change them juuuust yet.
733 char *buffer = (char *) malloc(blocksize + 2); // allow 2 bytes of safety padding
734 // Set the length and pre-emptively htonl/htons it
735 client->header->length = htonl(blocksize);
736 client->header->length16 = htons(blocksize);
737
738 // Put scnt and ocnt into big-endian notation
739 client->header->scnt = htonl(client->header->scnt);
740 client->header->ocnt = htonl(client->header->ocnt);
741 // Concatenation of stuff in the buffer.
742 memcpy(buffer, client->header, sizeof(usbmux_tcp_header));
743 memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen);
744
745 sendresult = send_to_phone(client->phone, buffer, blocksize);
746 // Now that we've sent it off, we can clean up after our sloppy selves.
747 if (buffer)
748 free(buffer);
749
750 // update counts ONLY if the send succeeded.
751 if (sendresult == blocksize) {
752 // Re-calculate scnt and ocnt
753 client->header->scnt = ntohl(client->header->scnt) + datalen;
754 client->header->ocnt = ntohl(client->header->ocnt);
755 // Revert lengths
756 client->header->length = ntohl(client->header->length);
757 client->header->length16 = ntohs(client->header->length16);
758
759 client->wr_window -= blocksize;
760 }
761
762
763 pthread_mutex_unlock(&client->mutex);
764
765
766 if (sendresult == -ETIMEDOUT || sendresult == 0) {
767 // no problem for now...
768 *sent_bytes = 0;
769 return IPHONE_E_TIMEOUT;
770 }
771 else if (sendresult < 0) {
772 return IPHONE_E_UNKNOWN_ERROR;
773 }
774 else if (sendresult == blocksize) {
775 // actual number of data bytes sent.
776 *sent_bytes = sendresult - HEADERLEN;
777 return IPHONE_E_SUCCESS;
778 }
779 else {
780 fprintf(stderr, "usbsend managed to dump a packet that is not full size. %d of %d\n",
781 sendresult, blocksize);
782 return IPHONE_E_UNKNOWN_ERROR;
783 }
784}
785
786/** append the packet's DATA to the receive buffer for the client.
787 *
788 * this has a few other corner-case functions:
789 * 1. this will properly handle the handshake syn+ack.
790 * 2. for all receives, this will appropriately update the ocnt.
791 *
792 * @return number of bytes consumed (header + data)
793 */
794uint32 append_receive_buffer(iphone_umux_client_t client, char* packet)
795{
796 if (client == NULL || packet == NULL) return 0;
797
798 usbmux_tcp_header *header = (usbmux_tcp_header *) packet;
799 char* data = &packet[HEADERLEN];
800 uint32 packetlen = ntohl(header->length);
801 uint32 datalen = packetlen-HEADERLEN;
802
803 int dobroadcast = 0;
804
805 pthread_mutex_lock(&client->mutex);
806
807 // we need to handle a few corner case tasks and book-keeping which
808 // falls on our responsibility because we are the ones reading in
809 // feedback.
810 if (client->header->scnt == 0 && client->header->ocnt == 0 ) {
811 fprintf(stdout, "client is still waiting for handshake.\n");
812 if (header->tcp_flags == (TCP_SYN | TCP_ACK)) {
813 fprintf(stdout, "yes, got syn+ack ; replying with ack.\n");
814 client->header->tcp_flags = TCP_ACK;
815 client->header->length = htonl(sizeof(usbmux_tcp_header));
816 client->header->length16 = htons(sizeof(usbmux_tcp_header));
817 client->header->scnt = htonl(client->header->scnt + 1);
818 client->header->ocnt = header->ocnt;
819 // push it to USB
820 // TODO: need to check for error in the send here.... :(
821 if (send_to_phone(client->phone, (char *)client->header, sizeof(usbmux_tcp_header)) <= 0) {
822 fprintf(stdout, "%s: error when pushing to usb...\n", __func__);
823 }
824 // need to revert some of the fields back to host notation.
825 client->header->scnt = ntohl(client->header->scnt);
826 client->header->ocnt = ntohl(client->header->ocnt);
827 client->header->length = ntohl(client->header->length);
828 client->header->length16 = ntohs(client->header->length16);
829 }
830 else {
831 client->error = IPHONE_E_ECONNABORTED;
832 // woah... this connection failed us.
833 // TODO: somehow signal that this stream is a no-go.
834 fprintf(stderr, "WOAH! client failed to get proper syn+ack.\n");
835 }
836 }
837
838 // update TCP counters and windows.
839 //
840 // save the window that we're getting from the USB device.
841 // apparently the window is bigger than just the 512 that's typically
842 // advertised. iTunes apparently shifts this value by 8 to get a much
843 // larger number.
844 if (header->tcp_flags & TCP_RST) {
845 client->error = IPHONE_E_ECONNRESET;
846 fprintf(stderr, "peer sent connection reset. setting error: %d\n", client->error);
847 }
848
849 // the packet's ocnt tells us how much of our data the device has received.
850 if (header->tcp_flags & TCP_ACK) {
851
852 // this is a hacky magic number condition. it seems that once the window
853 // reported by the phone starts to drop below this number, we quickly fall
854 // into connection reset problems. Once we see the reported window size
855 // start falling off, cut off and wait for solid acks to come back.
856 if (ntohs(header->window) < 256)
857 client->wr_window = 0;
858
859 // check what just got acked.
860 if (ntohl(header->ocnt) < client->header->scnt) {
861 // we got some kind of ack, but it hasn't caught up with the
862 // pending that have been sent.
863 pthread_cond_broadcast(&client->wr_wait);
864 }
865 else if (ntohl(header->ocnt) > /*client->wr_pending_scnt*/ client->header->scnt) {
866 fprintf(stderr, "WTF?! acks overtook pending outstanding. %u,%u\n",
867 ntohl(header->ocnt), client->wr_pending_scnt);
868 }
869 else {
870 // reset the window
871 client->wr_window = WINDOW_MAX;
872 pthread_cond_broadcast(&client->wr_wait);
873 }
874 }
875
876 // the packet's scnt will be our new ocnt.
877 client->header->ocnt = ntohl(header->scnt);
878
879 // ensure there is enough space, either by first malloc or realloc
880 if (datalen > 0) {
881 if (client->r_len == 0) dobroadcast = 1;
882
883 if (client->recv_buffer == NULL) {
884 client->recv_buffer = malloc(datalen);
885 client->r_len = 0;
886 }
887 else {
888 client->recv_buffer = realloc(client->recv_buffer, client->r_len + datalen);
889 }
890
891 memcpy(&client->recv_buffer[client->r_len], data, datalen);
892 client->r_len += datalen;
893 }
894
895 pthread_mutex_unlock(&client->mutex);
896
897 // I put this outside the mutex unlock just so that when the threads
898 // wake, we don't have to do another round of unlock+try to grab.
899 if (dobroadcast)
900 pthread_cond_broadcast(&client->wait);
901
902
903 return packetlen;
904}
905
906/** NOTE! THERE IS NO MUTEX LOCK IN THIS FUNCTION!
907 because we're only called from one location, pullbulk, where the lock
908 is already held.
909 */
910iphone_umux_client_t find_client(usbmux_tcp_header* recv_header)
911{
912 // remember, as we're looking for the client, the receive header is
913 // coming from the USB into our client. This means that when we check
914 // the src/dst ports, we need to reverse them.
915 iphone_umux_client_t retval = NULL;
916
917 // just for debugging check, I'm going to convert the numbers to host-endian.
918 uint16 hsport = ntohs(recv_header->sport);
919 uint16 hdport = ntohs(recv_header->dport);
920
921 pthread_mutex_lock(&iphonemutex);
922 int i;
923 for (i = 0; i < clients; i++) {
924 uint16 csport = ntohs(connlist[i]->header->sport);
925 uint16 cdport = ntohs(connlist[i]->header->dport);
926
927 if (hsport == cdport && hdport == csport) {
928 retval = connlist[i];
929 break;
930 }
931 }
932 pthread_mutex_unlock(&iphonemutex);
933
934 return retval;
935}
936
937/** pull in a big USB bulk packet and distribute it to queues appropriately.
938 */
939void iphone_mux_pullbulk(iphone_device_t phone)
940{
941 static const int DEFAULT_CAPACITY = 128*1024;
942 if (usbReceive.buffer == NULL) {
943 usbReceive.capacity = DEFAULT_CAPACITY;
944 usbReceive.buffer = malloc(usbReceive.capacity);
945 usbReceive.leftover = 0;
946 }
947
948 // start the cursor off just ahead of the leftover.
949 char* cursor = &usbReceive.buffer[usbReceive.leftover];
950 // pull in content, note that the amount we can pull is capacity minus leftover
951 int readlen = recv_from_phone_timeout(phone, cursor, usbReceive.capacity - usbReceive.leftover, 5000);
952 if (readlen < 0) {
953 //fprintf(stderr, "recv_from_phone_timeout gave us an error.\n");
954 readlen = 0;
955 }
956 if (readlen > 0) {
957 //fprintf(stdout, "recv_from_phone_timeout pulled an extra %d bytes\n", readlen);
958 }
959
960 // the amount of content we have to work with is the remainder plus
961 // what we managed to read
962 usbReceive.leftover += readlen;
963
964 // reset the cursor to the front of that buffer and work through
965 // trying to decode packets out of them.
966 cursor = usbReceive.buffer;
967 while (1) {
968 // check if there's even sufficient data to decode a header
969 if (usbReceive.leftover < HEADERLEN) break;
970 usbmux_tcp_header *header = (usbmux_tcp_header *) cursor;
971
972 // now that we have a header, check if there is sufficient data
973 // to construct a full packet, including its data
974 uint32 packetlen = ntohl(header->length);
975 if (usbReceive.leftover < packetlen) {
976 break;
977 }
978
979 // ok... find the client this packet will get stuffed to.
980 iphone_umux_client_t client = find_client(header);
981 if (client == NULL) {
982 fprintf(stderr, "WARNING: client for packet cannot be found. dropping packet.\n");
983 }
984 else {
985 // stuff the data
986 append_receive_buffer(client, cursor);
987 }
988
989 // move the cursor and account for the consumption
990 cursor += packetlen;
991 usbReceive.leftover -= packetlen;
992 }
993
994 // now, we need to manage any leftovers.
995 // I'm going to manage the leftovers by alloc'ing a new block and copying
996 // the leftovers to it. This is just to prevent problems with memory
997 // moves where there may be overlap. Besides, the leftovers should be
998 // small enough that this copy is minimal in overhead.
999 //
1000 // if there are no leftovers, we just leave the datastructure as is,
1001 // and re-use the block next time.
1002 if (usbReceive.leftover > 0 && cursor != usbReceive.buffer) {
1003 char* newbuff = malloc(DEFAULT_CAPACITY);
1004 memcpy(newbuff, cursor, usbReceive.leftover);
1005 free(usbReceive.buffer);
1006 usbReceive.buffer = newbuff;
1007 usbReceive.capacity = DEFAULT_CAPACITY;
1008 }
1009}
1010
1011/**
1012 * return the error code stored in iphone_umux_client_t structure,
1013 * e.g. non-zero when an usb read error occurs.
1014 *
1015 * @param client the umux client
1016 *
1017 * @return IPHONE_E_* error codes.
1018 */
1019iphone_error_t iphone_mux_get_error(iphone_umux_client_t client)
1020{
1021 if (!client) {
1022 return 0;
1023 }
1024
1025 return client->error;
1026}
1027
1028/** This is a higher-level USBMuxTCP-like function
1029 *
1030 * @param connection The connection to receive data on.
1031 * @param data Where to put the data we receive.
1032 * @param datalen How much data to read.
1033 *
1034 * @return IPHONE_E_SUCCESS or error code if failure.
1035 */
1036iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes)
1037{
1038 return iphone_mux_recv_timeout(client, data, datalen, recv_bytes, 0);
1039}
1040
1041/**
1042 @param timeout
1043 */
1044iphone_error_t iphone_mux_recv_timeout(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout)
1045{
1046
1047 if (!client || !data || datalen == 0 || !recv_bytes)
1048 return IPHONE_E_INVALID_ARG;
1049
1050 if (client->error != IPHONE_E_SUCCESS) return client->error;
1051
1052 pthread_mutex_lock(&client->mutex);
1053
1054 if (timeout > 0 && (client->recv_buffer == NULL ||client->r_len == 0)) {
1055 struct timespec ts;
1056 clock_gettime(CLOCK_REALTIME, &ts);
1057 ts.tv_sec += timeout/1000;
1058 ts.tv_nsec += (timeout-((int)(timeout/1000))*1000)*1000; //millis * 1000;
1059 pthread_cond_timedwait(&client->wait, &client->mutex, &ts);
1060 }
1061
1062 *recv_bytes = 0;
1063 if (client->recv_buffer != NULL && client->r_len > 0) {
1064 uint32_t foolen = datalen;
1065 if (foolen > client->r_len) foolen = client->r_len;
1066 memcpy(data, client->recv_buffer, foolen);
1067 *recv_bytes = foolen;
1068
1069 // preserve any left-over unread amounts.
1070 int remainder = client->r_len - foolen;
1071 if (remainder > 0) {
1072 char* newbuf = malloc(remainder);
1073 memcpy(newbuf, client->recv_buffer + foolen, remainder);
1074 client->r_len = remainder;
1075 free(client->recv_buffer);
1076 client->recv_buffer = newbuf;
1077 }
1078 else {
1079 free(client->recv_buffer);
1080 client->recv_buffer = NULL;
1081 client->r_len = 0;
1082 }
1083 }
1084
1085 pthread_mutex_unlock(&client->mutex);
1086
1087
1088 return IPHONE_E_SUCCESS;
1089}
diff --git a/iphone.h b/iphone.h
new file mode 100644
index 0000000..e132cd5
--- /dev/null
+++ b/iphone.h
@@ -0,0 +1,79 @@
1/*
2 * Copyright (c) 2008 Jing Su. All Rights Reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#ifndef __IPHONE_H__
20#define __IPHONE_H__
21
22#include <stdint.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25
26//general errors
27#define IPHONE_E_SUCCESS 0
28#define IPHONE_E_INVALID_ARG -1
29#define IPHONE_E_UNKNOWN_ERROR -2
30#define IPHONE_E_NO_DEVICE -3
31#define IPHONE_E_TIMEOUT -4
32#define IPHONE_E_NOT_ENOUGH_DATA -5
33#define IPHONE_E_BAD_HEADER -6
34
35//lockdownd specific error
36#define IPHONE_E_INVALID_CONF -7
37#define IPHONE_E_PAIRING_FAILED -8
38#define IPHONE_E_SSL_ERROR -9
39#define IPHONE_E_PLIST_ERROR -10
40#define IPHONE_E_DICT_ERROR -11
41
42//afc specific error
43#define IPHONE_E_NO_SUCH_FILE -12
44
45//general TCP-style errors and conditions
46#define IPHONE_E_ECONNABORTED -ECONNABORTED
47#define IPHONE_E_ECONNRESET -ECONNRESET
48#define IPHONE_E_ENOTCONN -ENOTCONN
49#define IPHONE_E_ESHUTDOWN -ESHUTDOWN
50#define IPHONE_E_ETIMEDOUT -ETIMEDOUT
51#define IPHONE_E_ECONNREFUSED -ECONNREFUSED
52
53
54typedef int16_t iphone_error_t;
55
56struct iphone_device_int;
57typedef struct iphone_device_int *iphone_device_t;
58
59struct iphone_umux_client_int;
60typedef struct iphone_umux_client_int *iphone_umux_client_t;
61
62iphone_error_t iphone_get_device ( iphone_device_t *device );
63iphone_error_t iphone_get_specific_device(int bus_n, int dev_n, iphone_device_t * device);
64iphone_error_t iphone_free_device ( iphone_device_t device );
65
66
67iphone_error_t iphone_mux_new_client ( iphone_device_t device, uint16_t src_port, uint16_t dst_port, iphone_umux_client_t *client );
68iphone_error_t iphone_mux_free_client ( iphone_umux_client_t client );
69
70iphone_error_t iphone_mux_send(iphone_umux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes);
71
72iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes);
73iphone_error_t iphone_mux_recv_timeout(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout);
74
75void iphone_mux_pullbulk(iphone_device_t phone);
76
77iphone_error_t iphone_mux_get_error(iphone_umux_client_t client);
78
79#endif
diff --git a/iproxy.c b/iproxy.c
new file mode 100644
index 0000000..df3d689
--- /dev/null
+++ b/iproxy.c
@@ -0,0 +1,329 @@
1/*
2 * iproxy -- proxy that enables tcp service access to iPhone/iPod
3 * via USB cable
4 * TODO: improve code...
5 *
6 * Copyright (c) 2009 Nikias Bassen. All Rights Reserved.
7 * Based upon iTunnel source code, Copyright (c) 2008 Jing Su.
8 * http://www.cs.toronto.edu/~jingsu/itunnel/
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24#include <stdio.h>
25#include <stdlib.h>
26#include <fcntl.h>
27#include <stddef.h>
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <unistd.h>
31#include <errno.h>
32#include <arpa/inet.h>
33#include <pthread.h>
34#include "usbmuxd.h"
35#include "sock_stuff.h"
36
37#define SOCKET_FILE "/var/run/usbmuxd"
38
39volatile int stop_ctos = 0;
40volatile int stop_stoc = 0;
41
42static uint16_t listen_port = 0;
43static uint16_t device_port = 0;
44
45pthread_mutex_t smutex = PTHREAD_MUTEX_INITIALIZER;
46
47struct client_data {
48 int fd;
49 int sfd;
50};
51
52int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t *result)
53{
54 struct usbmux_result res;
55 int recv_len;
56 int i;
57 uint32_t rrr[5];
58
59 if (!result) {
60 return -EINVAL;
61 }
62
63 if ((recv_len = recv_buf(sfd, &res, sizeof(res))) <= 0) {
64 perror("recv");
65 return -errno;
66 } else {
67 memcpy(&rrr, &res, recv_len);
68 for (i = 0; i < recv_len/4; i++) {
69 fprintf(stderr, "%08x ", rrr[i]);
70 }
71 fprintf(stderr, "\n");
72 if ((recv_len == sizeof(res))
73 && (res.header.length == recv_len)
74 && (res.header.reserved == 0)
75 && (res.header.type == usbmux_result)
76 ) {
77 *result = res.result;
78 if (res.header.tag == tag) {
79 return 1;
80 } else {
81 return 0;
82 }
83 }
84 }
85
86 return -1;
87}
88
89void *run_stoc_loop(void *arg)
90{
91 struct client_data *cdata = (struct client_data*)arg;
92 int recv_len;
93 int sent;
94 char buffer[131072];
95
96 printf("%s: fd = %d\n", __func__, cdata->fd);
97
98 while (!stop_stoc && cdata->fd>0 && cdata->sfd>0) {
99 recv_len = recv_buf_timeout(cdata->sfd, buffer, sizeof(buffer), 0, 5000);
100 if (recv_len <= 0) {
101 if (recv_len == 0) {
102 // try again
103 continue;
104 } else {
105 fprintf(stderr, "recv failed: %s\n", strerror(errno));
106 break;
107 }
108 } else {
109 printf("received %d bytes from server\n", recv_len);
110 // send to socket
111 sent = send_buf(cdata->fd, buffer, recv_len);
112 if (sent < recv_len) {
113 if (sent <= 0) {
114 fprintf(stderr, "send failed: %s\n", strerror(errno));
115 break;
116 } else {
117 fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
118 }
119 } else {
120 // sending succeeded, receive from device
121 printf("pushed %d bytes to client\n", sent);
122 }
123 }
124 }
125 close(cdata->fd);
126 cdata->fd = -1;
127 stop_ctos = 1;
128
129 return NULL;
130}
131
132void *run_ctos_loop(void *arg)
133{
134 struct client_data *cdata = (struct client_data*)arg;
135 int recv_len;
136 int sent;
137 char buffer[131072];
138 pthread_t stoc = 0;
139
140 printf("%s: fd = %d\n", __func__, cdata->fd);
141
142 stop_stoc = 0;
143 pthread_create(&stoc, NULL, run_stoc_loop, cdata);
144
145 while (!stop_ctos && cdata->fd>0 && cdata->sfd>0) {
146 recv_len = recv_buf_timeout(cdata->fd, buffer, sizeof(buffer), 0, 5000);
147 if (recv_len <= 0) {
148 if (recv_len == 0) {
149 // try again
150 continue;
151 } else {
152 fprintf(stderr, "recv failed: %s\n", strerror(errno));
153 break;
154 }
155 } else {
156 printf("pulled %d bytes from client\n", recv_len);
157 // send to local socket
158 sent = send_buf(cdata->sfd, buffer, recv_len);
159 if (sent < recv_len) {
160 if (sent <= 0) {
161 fprintf(stderr, "send failed: %s\n", strerror(errno));
162 break;
163 } else {
164 fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
165 }
166 } else {
167 // sending succeeded, receive from device
168 printf("sent %d bytes to server\n", sent);
169 }
170 }
171 }
172 close(cdata->fd);
173 cdata->fd = -1;
174 stop_stoc = 1;
175
176 pthread_join(stoc, NULL);
177
178 return NULL;
179}
180
181int main(int argc, char **argv)
182{
183 int recv_len = 0;
184 int hello_done;
185 int connected;
186 uint32_t pktlen;
187 unsigned char *buf;
188 struct usbmux_header hello;
189 struct usbmux_dev_info device_info;
190 int sfd = -1;
191
192 if (argc != 3) {
193 printf("usage: %s LOCAL_PORT DEVICE_PORT\n", argv[0]);
194 return 0;
195 }
196
197 listen_port = atoi(argv[1]);
198 device_port = atoi(argv[2]);
199
200 if (!listen_port) {
201 fprintf(stderr, "Invalid listen_port specified!\n");
202 return -EINVAL;
203 }
204
205 if (!device_port) {
206 fprintf(stderr, "Invalid device_port specified!\n");
207 return -EINVAL;
208 }
209
210 sfd = connect_unix_socket(SOCKET_FILE);
211 if (sfd < 0) {
212 printf("error opening socket, terminating.\n");
213 return -1;
214 }
215
216 // send hello
217 hello.length = sizeof(struct usbmux_header);
218 hello.reserved = 0;
219 hello.type = usbmux_hello;
220 hello.tag = 2;
221
222 hello_done = 0;
223 connected = 0;
224
225 fprintf(stdout, "sending Hello packet\n");
226 if (send(sfd, &hello, hello.length, 0) == hello.length) {
227 uint32_t res = -1;
228 // get response
229 if (usbmuxd_get_result(sfd, hello.tag, &res) && (res==0)) {
230 fprintf(stdout, "Got Hello Response!\n");
231 hello_done = 1;
232 } else {
233 fprintf(stderr, "Did not get Hello response (with result=0)...\n");
234 close(sfd);
235 return -1;
236 }
237
238 device_info.device_id = 0;
239
240 if (hello_done) {
241 // get all devices
242 while (1) {
243 if (recv_buf_timeout(sfd, &pktlen, 4, MSG_PEEK, 1000) == 4) {
244 buf = (unsigned char*)malloc(pktlen);
245 if (!buf) {
246 exit(-ENOMEM);
247 }
248 recv_len = recv_buf(sfd, buf, pktlen);
249 if (recv_len < pktlen) {
250 fprintf(stdout, "received less data than specified in header!\n");
251 }
252 fprintf(stdout, "Received device data\n");
253 //log_debug_buffer(stdout, (char*)buf, pktlen);
254 memcpy(&device_info, buf + sizeof(struct usbmux_header), sizeof(device_info));
255 free(buf);
256 } else {
257 // we _should_ have all of them now.
258 // or perhaps an error occured.
259 break;
260 }
261 }
262 }
263
264 if (device_info.device_id > 0) {
265 struct usbmux_connect_request c_req;
266
267 fprintf(stdout, "Requesting connecion to device %d port %d\n", device_info.device_id, device_port);
268
269 // try to connect to last device found
270 c_req.header.length = sizeof(c_req);
271 c_req.header.reserved = 0;
272 c_req.header.type = usbmux_connect;
273 c_req.header.tag = 3;
274 c_req.device_id = device_info.device_id;
275 c_req.port = htons(device_port);
276 c_req.reserved = 0;
277
278 if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) {
279 perror("send");
280 } else {
281 // read ACK
282 res = -1;
283 fprintf(stdout, "Reading connect result...\n");
284 if (usbmuxd_get_result(sfd, c_req.header.tag, &res)) {
285 if (res == 0) {
286 fprintf(stdout, "Connect success!\n");
287 connected = 1;
288 } else {
289 fprintf(stderr, "Connect failed, Error code=%d\n", res);
290 }
291 }
292 }
293 }
294
295 if (connected) {
296 int mysock = create_socket(listen_port);
297 if (mysock < 0) {
298 fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
299 } else {
300 pthread_t ctos;
301 struct sockaddr_in c_addr;
302 socklen_t len = sizeof(struct sockaddr_in);
303 struct client_data cdata;
304 int c_sock;
305 while (1) {
306 printf("waiting for connection\n");
307 c_sock = accept(mysock, (struct sockaddr*)&c_addr, &len);
308 if (c_sock) {
309 printf("accepted connection, fd = %d\n", c_sock);
310 cdata.fd = c_sock;
311 cdata.sfd = sfd;
312 stop_ctos = 0;
313 pthread_create(&ctos, NULL, run_ctos_loop, &cdata);
314 pthread_join(ctos, NULL);
315 } else {
316 break;
317 }
318 }
319 close(c_sock);
320 close(mysock);
321 }
322 } else {
323 fprintf(stderr, "No attached device found?!\n");
324 }
325 }
326 close(sfd);
327
328 return 0;
329}
diff --git a/sock_stuff.c b/sock_stuff.c
new file mode 100644
index 0000000..1a23bc1
--- /dev/null
+++ b/sock_stuff.c
@@ -0,0 +1,277 @@
1#include <stdio.h>
2#include <stddef.h>
3#include <unistd.h>
4#include <errno.h>
5#include <sys/time.h>
6#include <sys/stat.h>
7#include <sys/socket.h>
8#include <sys/un.h>
9#include <netinet/in.h>
10#include <netdb.h>
11#include <arpa/inet.h>
12#include "sock_stuff.h"
13
14#define RECV_TIMEOUT 10000
15
16int create_unix_socket (const char *filename)
17{
18 struct sockaddr_un name;
19 int sock;
20 size_t size;
21
22 // remove if still present
23 unlink(filename);
24
25 /* Create the socket. */
26 sock = socket (PF_LOCAL, SOCK_STREAM, 0);
27 if (sock < 0) {
28 perror ("socket");
29 return -1;
30 }
31
32 /* Bind a name to the socket. */
33 name.sun_family = AF_LOCAL;
34 strncpy (name.sun_path, filename, sizeof (name.sun_path));
35 name.sun_path[sizeof (name.sun_path) - 1] = '\0';
36
37 /* The size of the address is
38 the offset of the start of the filename,
39 plus its length,
40 plus one for the terminating null byte.
41 Alternatively you can just do:
42 size = SUN_LEN (&name);
43 */
44 size = (offsetof (struct sockaddr_un, sun_path)
45 + strlen (name.sun_path) + 1);
46
47 if (bind (sock, (struct sockaddr *) &name, size) < 0) {
48 perror("bind");
49 close(sock);
50 return -1;
51 }
52
53 if (listen(sock, 10) < 0) {
54 perror("listen");
55 close(sock);
56 return -1;
57 }
58
59 return sock;
60}
61
62int connect_unix_socket(const char *filename)
63{
64 struct sockaddr_un name;
65 int sfd = -1;
66 size_t size;
67 struct stat fst;
68
69 // check if socket file exists...
70 if (stat(filename, &fst) != 0) {
71 fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename, strerror(errno));
72 return -1;
73 }
74
75 // ... and if it is a unix domain socket
76 if (!S_ISSOCK(fst.st_mode)) {
77 fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__, filename);
78 return -1;
79 }
80
81 // make a new socket
82 if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
83 fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno));
84 return -1;
85 }
86
87 // and connect to 'filename'
88 name.sun_family = AF_LOCAL;
89 strncpy(name.sun_path, filename, sizeof(name.sun_path));
90 name.sun_path[sizeof(name.sun_path) - 1] = 0;
91
92 size = (offsetof (struct sockaddr_un, sun_path)
93 + strlen (name.sun_path) + 1);
94
95 if (connect(sfd, (struct sockaddr*)&name, size) < 0) {
96 close(sfd);
97 fprintf(stderr, "%s: connect: %s\n", __func__, strerror(errno));
98 return -1;
99 }
100
101 return sfd;
102}
103
104int create_socket(uint16_t port)
105{
106 int sfd = -1;
107 int yes = 1;
108 struct sockaddr_in saddr;
109
110 if ( 0 > ( sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) ) ) {
111 perror("socket()");
112 return -1;
113 }
114
115 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
116 perror("setsockopt()");
117 close(sfd);
118 return -1;
119 }
120
121 memset((void *)&saddr, 0, sizeof(saddr));
122 saddr.sin_family = AF_INET;
123 saddr.sin_addr.s_addr = htonl(INADDR_ANY);
124 saddr.sin_port = htons(port);
125
126 if(0 > bind(sfd, (struct sockaddr *)&saddr , sizeof(saddr))) {
127 perror("bind()");
128 close(sfd);
129 return -1;
130 }
131
132 if (listen(sfd, 1) == -1) {
133 perror("listen()");
134 close(sfd);
135 return -1;
136 }
137
138 return sfd;
139}
140
141int connect_socket(const char *addr, uint16_t port)
142{
143 int sfd = -1;
144 int yes = 1;
145 struct hostent *hp;
146 struct sockaddr_in saddr;
147
148 if (!addr) {
149 errno = EINVAL;
150 return -1;
151 }
152
153 if ((hp = gethostbyname(addr)) == NULL) {
154 fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr);
155 return -1;
156 }
157
158 if (!hp->h_addr) {
159 fprintf(stderr, "%s: gethostbyname returned NULL address!\n", __func__);
160 return -1;
161 }
162
163 if ( 0 > ( sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) ) ) {
164 perror("socket()");
165 return -1;
166 }
167
168 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
169 perror("setsockopt()");
170 close(sfd);
171 return -1;
172 }
173
174 memset((void *)&saddr, 0, sizeof(saddr));
175 saddr.sin_family = AF_INET;
176 saddr.sin_addr.s_addr = (uint32_t)hp->h_addr;
177 saddr.sin_port = htons(port);
178
179 if (connect(sfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {
180 perror("connect");
181 close(sfd);
182 return -2;
183 }
184
185 return sfd;
186}
187
188int check_fd(int fd, fd_mode fdm, unsigned int timeout)
189{
190 fd_set fds;
191 int sret;
192 int eagain;
193 struct timeval to;
194
195 if (fd <= 0) {
196 fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd);
197 return -1;
198 }
199
200 FD_ZERO(&fds);
201 FD_SET(fd, &fds);
202
203 to.tv_sec = (time_t)(timeout/1000);
204 to.tv_usec = (time_t)((timeout-(to.tv_sec*1000))*1000);
205
206 sret = -1;
207
208 do {
209 eagain = 0;
210 switch(fdm) {
211 case fdread:
212 sret = select(fd+1,&fds,NULL,NULL,&to);
213 break;
214 case fdwrite:
215 sret = select(fd+1,NULL,&fds,NULL,&to);
216 break;
217 case fdexcept:
218 sret = select(fd+1,NULL,NULL,&fds,&to);
219 break;
220 }
221
222 if (sret < 0) {
223 switch(errno) {
224 case EINTR:
225 // interrupt signal in select
226 fprintf(stderr, "%s: EINTR\n", __func__);
227 eagain = 1;
228 break;
229 case EAGAIN:
230 fprintf(stderr, "%s: EAGAIN\n", __func__);
231 break;
232 default:
233 fprintf(stderr, "%s: select failed: %s\n", __func__, strerror(errno));
234 return -1;
235 }
236 }
237 } while (eagain);
238
239 return sret;
240}
241
242int recv_buf(int fd, void *data, size_t length)
243{
244 return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT);
245}
246
247int peek_buf(int fd, void *data, size_t length)
248{
249 return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT);
250}
251
252int recv_buf_timeout(int fd, void *data, size_t length, int flags, unsigned int timeout)
253{
254 int res;
255 int result;
256
257 // check if data is available
258 res = check_fd(fd, fdread, timeout);
259 if (res <= 0) {
260 return res;
261 }
262
263 // if we get here, there _is_ data available
264 result = recv(fd, data, length, flags);
265 if (res > 0 && result == 0) {
266 // but this is an error condition
267 fprintf(stderr, "%s: fd=%d\n", __func__, fd);
268 return -1;
269 }
270 return result;
271}
272
273int send_buf(int fd, void *data, size_t length)
274{
275 return send(fd, data, length, 0);
276}
277
diff --git a/sock_stuff.h b/sock_stuff.h
new file mode 100644
index 0000000..01082d1
--- /dev/null
+++ b/sock_stuff.h
@@ -0,0 +1,27 @@
1#ifndef __SOCK_STUFF_H
2#define __SOCK_STUFF_H
3
4#include <stdint.h>
5
6enum fd_mode
7{
8 fdread,
9 fdwrite,
10 fdexcept
11};
12typedef enum fd_mode fd_mode;
13
14int create_unix_socket(const char *filename);
15int connect_unix_socket(const char *filename);
16int create_socket(uint16_t port);
17int connect_socket(const char *addr, uint16_t port);
18int check_fd(int fd, fd_mode fdm, unsigned int timeout);
19
20int recv_buf(int fd, void *data, size_t size);
21int peek_buf(int fd, void *data, size_t size);
22int recv_buf_timeout(int fd, void *data, size_t size, int flags, unsigned int timeout);
23
24int send_buf(int fd, void *data, size_t size);
25
26#endif /* __SOCK_STUFF_H */
27
diff --git a/testclient.c b/testclient.c
new file mode 100644
index 0000000..fafbf23
--- /dev/null
+++ b/testclient.c
@@ -0,0 +1,148 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <fcntl.h>
4#include <stddef.h>
5#include <sys/socket.h>
6#include <sys/un.h>
7#include <unistd.h>
8#include <errno.h>
9#include <arpa/inet.h>
10#include "usbmuxd.h"
11#include "sock_stuff.h"
12
13#define SOCKET_FILE "/var/run/usbmuxd"
14
15int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t *result)
16{
17 struct usbmux_result res;
18 int recv_len;
19
20 if (!result) {
21 return -EINVAL;
22 }
23
24 if ((recv_len = recv_buf(sfd, &res, sizeof(res))) <= 0) {
25 perror("recv");
26 return -errno;
27 } else {
28 if ((recv_len == sizeof(res))
29 && (res.header.length == recv_len)
30 && (res.header.reserved == 0)
31 && (res.header.type == usbmux_result)
32 ) {
33 *result = res.result;
34 if (res.header.tag == tag) {
35 return 1;
36 } else {
37 return 0;
38 }
39 }
40 }
41
42 return -1;
43}
44
45int main(int argc, char **argv)
46{
47 int sfd;
48 int recv_len = 0;
49 int hello_done;
50 int connected;
51 uint32_t pktlen;
52 unsigned char *buf;
53 struct usbmux_header hello;
54 struct usbmux_dev_info device_info;
55
56 sfd = connect_unix_socket(SOCKET_FILE);
57 if (sfd < 0) {
58 printf("error opening socket, terminating.\n");
59 return -1;
60 }
61
62 // send hello
63 hello.length = sizeof(struct usbmux_header);
64 hello.reserved = 0;
65 hello.type = usbmux_hello;
66 hello.tag = 2;
67
68 hello_done = 0;
69 connected = 0;
70
71 fprintf(stdout, "sending Hello packet\n");
72 if (send(sfd, &hello, hello.length, 0) == hello.length) {
73 uint32_t res = -1;
74 // get response
75 if (usbmuxd_get_result(sfd, hello.tag, &res) && (res==0)) {
76 fprintf(stdout, "Got Hello Response!\n");
77 hello_done = 1;
78 } else {
79 fprintf(stderr, "Did not get Hello response (with result=0)...\n");
80 close(sfd);
81 return -1;
82 }
83
84 device_info.device_id = 0;
85
86 if (hello_done) {
87 // get all devices
88 while (1) {
89 if (recv_buf_timeout(sfd, &pktlen, 4, MSG_PEEK, 1000) == 4) {
90 buf = (unsigned char*)malloc(pktlen);
91 if (!buf) {
92 exit(-ENOMEM);
93 }
94 recv_len = recv_buf(sfd, buf, pktlen);
95 if (recv_len < pktlen) {
96 fprintf(stdout, "received less data than specified in header!\n");
97 }
98 fprintf(stdout, "got device data:\n");
99 //log_debug_buffer(stdout, (char*)buf, pktlen);
100 memcpy(&device_info, buf + sizeof(struct usbmux_header), sizeof(device_info));
101 free(buf);
102 } else {
103 // we _should_ have all of them now.
104 // or perhaps an error occured.
105 break;
106 }
107 }
108 }
109
110 if (device_info.device_id > 0) {
111 struct usbmux_connect_request c_req;
112
113 // try to connect to last device found
114 c_req.header.length = sizeof(c_req);
115 c_req.header.reserved = 0;
116 c_req.header.type = usbmux_connect;
117 c_req.header.tag = 3;
118 c_req.device_id = device_info.device_id;
119 c_req.port = htons(22);
120 c_req.reserved = 0;
121
122 if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) {
123 perror("send");
124 } else {
125 // read ACK
126 res = -1;
127 if (usbmuxd_get_result(sfd, c_req.header.tag, &res)) {
128 if (res == 0) {
129 fprintf(stdout, "Connect success!\n");
130 connected = 1;
131 } else {
132 fprintf(stderr, "Connect failed, Error code=%d\n", res);
133 }
134 }
135 }
136 }
137
138 if (connected) {
139
140
141 // do communication now.
142 sleep(10);
143 }
144 }
145 close(sfd);
146
147 return 0;
148}
diff --git a/usbmuxd.c b/usbmuxd.c
new file mode 100644
index 0000000..37a7f9e
--- /dev/null
+++ b/usbmuxd.c
@@ -0,0 +1,795 @@
1/*
2 * usbmuxd -- daemon for communication with iPhone/iPod via USB
3 *
4 * Copyright (c) 2009 Nikias Bassen. All Rights Reserved.
5 * Based upon iTunnel source code, Copyright (c) 2008 Jing Su.
6 * http://www.cs.toronto.edu/~jingsu/itunnel/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program 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
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22#include <stddef.h>
23#include <stdio.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <sys/stat.h>
30#include <arpa/inet.h>
31#include <unistd.h>
32#include <signal.h>
33#include <pthread.h>
34#include <stdint.h>
35#include <usb.h>
36
37#include "usbmuxd.h"
38#include "sock_stuff.h"
39
40#include "iphone.h"
41
42#define SOCKET_FILE "/var/run/usbmuxd"
43
44#define DEFAULT_TIMEOUT 4000
45#define DEFAULT_CHILDREN_CAPACITY 10
46
47static int quit_flag = 0;
48static int fsock = -1;
49
50struct client_data {
51 volatile int dead;
52 int socket;
53 int tag;
54 pthread_t thread;
55 pthread_t handler;
56 pthread_t reader;
57 int reader_quit;
58 int reader_dead;
59 int handler_dead;
60 iphone_umux_client_t muxclient;
61};
62
63struct device_use_info {
64 uint32_t device_id;
65 iphone_device_t phone;
66 int use_count;
67};
68
69static struct device_use_info **device_use_list = NULL;
70static int device_use_count = 0;
71static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER;
72
73static void print_buffer(const char *data, const int length)
74{
75 int i;
76 int j;
77 unsigned char c;
78
79 for(i=0; i<length; i+=16) {
80 printf("%04x: ", i);
81 for (j=0;j<16;j++) {
82 if (i+j >= length) {
83 printf(" ");
84 continue;
85 }
86 printf("%02hhx ", *(data+i+j));
87 }
88 printf(" | ");
89 for(j=0;j<16;j++) {
90 if (i+j >= length)
91 break;
92 c = *(data+i+j);
93 if ((c < 32) || (c > 127)) {
94 printf(".");
95 continue;
96 }
97 printf("%c", c);
98 }
99 printf("\n");
100 }
101 printf("\n");
102}
103
104static int usbmuxd_get_request(int fd, void *data, size_t len)
105{
106 uint32_t pktlen;
107 int recv_len;
108
109 if (peek_buf(fd, &pktlen, sizeof(pktlen)) < sizeof(pktlen)) {
110 return -errno;
111 }
112
113 if (len < pktlen) {
114 // target buffer is to small to hold this packet! fix it!
115 fprintf(stderr, "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.\n", __func__, pktlen, len);
116 pktlen = len;
117 }
118
119 recv_len = recv_buf(fd, data, pktlen);
120 if (recv_len < pktlen) {
121 fprintf(stderr, "%s: Uh-oh, we got less than the packet's size, %d instead of %d...\n", __func__, recv_len, pktlen);
122 }
123
124 return recv_len;
125}
126
127static int usbmuxd_send_result(int fd, uint32_t tag, uint32_t result_code)
128{
129 struct usbmux_result res;
130
131 res.header.length = sizeof(res);
132 res.header.reserved = 0;
133 res.header.type = usbmux_result;
134 res.header.tag = tag;
135 res.result = result_code;
136
137 fprintf(stderr, "%s: tag=%d result=%d\n", __func__, res.header.tag, res.result);
138
139 return send_buf(fd, &res, sizeof(res));
140}
141
142/**
143 *
144 */
145static void *usbmuxd_client_reader_thread(void *arg)
146{
147 struct client_data *cdata;
148
149 char rbuffer[512];
150 uint32_t rbuffersize = 512;
151 uint32_t rlen;
152 iphone_error_t err;
153 char *cursor;
154 ssize_t len;
155 int result;
156
157 if (!arg) {
158 fprintf(stderr, "%s: invalid client_data supplied!\n", __func__);
159 cdata->reader_dead = 1;
160 return NULL;
161 }
162
163 cdata = (struct client_data*)arg;
164
165 cdata->reader_dead = 0;
166
167 fprintf(stdout, "%s: started\n", __func__);
168
169 while (!quit_flag && !cdata->reader_quit) {
170 result = check_fd(cdata->socket, fdwrite, DEFAULT_TIMEOUT);
171 if (result <= 0) {
172 if (result < 0) {
173 fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno));
174 }
175 continue;
176 }
177
178 rlen = 0;
179 err = iphone_mux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize, &rlen, DEFAULT_TIMEOUT);
180 if (err != 0) {
181 fprintf(stderr, "%s: encountered USB read error: %d\n", __func__, err);
182 break;
183 }
184
185 cursor = rbuffer;
186 while (rlen > 0) {
187 //printf("%s: \n", __func__);
188 //print_buffer(cursor, rlen);
189 //if ((rlen > 4) && !cursor[3]) {
190 len = send_buf(cdata->socket, cursor, rlen);
191 /*} else if (cursor[0] == 1) {
192 fprintf(stderr, "%s: Error message received: %s\n", __func__, cursor+1);
193 // we got an error message and no data. don't send it.
194 // TODO parse the error code and put it in the right place!
195 len = rlen;
196 }*/
197 // calculate remainder
198 rlen -= len;
199 // advance cursor
200 cursor += len;
201 }
202 fsync(cdata->socket);
203 }
204
205 fprintf(stdout, "%s: terminated\n", __func__);
206
207 cdata->reader_dead = 1;
208
209 return NULL;
210}
211
212static int usbmuxd_handleConnectResult(struct client_data *cdata)
213{
214 int result;
215 char buffer[512];
216 char err_type[64];
217 int err_code;
218 ssize_t maxlen = 512;
219 uint32_t rlen;
220 iphone_error_t err;
221
222 // trigger connection attempt if ready to write to client
223 result = check_fd(cdata->socket, fdwrite, DEFAULT_TIMEOUT);
224 if (result <= 0) {
225 if (result < 0) {
226 fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno));
227 return result;
228 }
229 } else {
230 result = 0;
231 err = iphone_mux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen, DEFAULT_TIMEOUT);
232 if (err != 0) {
233 fprintf(stderr, "%s: encountered USB read error: %d\n", __func__, err);
234 usbmuxd_send_result(cdata->socket, cdata->tag, err);
235 } else {
236 if (rlen > 0) {
237 //print_buffer(buffer, rlen);
238 if ((buffer[0] == 1) && (rlen > 20) && !memcmp(buffer+1, "handleConnectResult:", 20)) {
239 // hm... we got an error message!
240 buffer[rlen] = 0;
241 fprintf(stderr, "%s: %s\n", __func__, buffer+22);
242
243 if (sscanf(buffer+22, "%s - %d\n", err_type, &err_code) == 2) {
244 usbmuxd_send_result(cdata->socket, cdata->tag, err_code);
245 } else {
246 usbmuxd_send_result(cdata->socket, cdata->tag, ENODATA);
247 }
248 return -2;
249 } else {
250 // send success result
251 usbmuxd_send_result(cdata->socket, cdata->tag, 0);
252 // and the server greeting message
253 send_buf(cdata->socket, buffer, rlen);
254 }
255 } else {
256 // no server greeting? this seems to be ok. send success.
257 usbmuxd_send_result(cdata->socket, cdata->tag, 0);
258 return 0;
259 }
260 }
261 //fsync(cdata->socket);
262 }
263 return 0;
264}
265
266/**
267 * This thread handles the communication between the connected iPhone/iPod
268 * and the client that created the connection.
269 */
270static void *usbmuxd_client_handler_thread(void *arg)
271{
272 struct client_data *cdata;
273 int result;
274 char *cursor;
275 char buffer[1024];
276 ssize_t len;
277 ssize_t maxlen = sizeof(buffer);
278 uint32_t wlen;
279 iphone_error_t err;
280
281 if (!arg) {
282 fprintf(stderr, "%s: invalid client_data provided!\n", __func__);
283 return NULL;
284 }
285
286 cdata = (struct client_data*)arg;
287
288 fprintf(stdout, "%s: started\n", __func__);
289
290 if (usbmuxd_handleConnectResult(cdata)) {
291 goto leave;
292 }
293
294 // starting mux reader thread
295 cdata->reader_quit = 0;
296 cdata->reader_dead = 0;
297 if (pthread_create(&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) {
298 fprintf(stderr, "%s: could not start client_reader thread\n", __func__);
299 cdata->reader = 0;
300 }
301
302 while (!quit_flag && !cdata->reader_dead) {
303 result = check_fd(cdata->socket, fdread, DEFAULT_TIMEOUT);
304 if (result <= 0) {
305 if (result < 0) {
306 fprintf(stderr, "%s: Error: checkfd: %s\n", __func__, strerror(errno));
307 }
308 continue;
309 }
310
311 // check_fd told us there's data available, so read from client
312 // and push to USB device.
313 len = recv(cdata->socket, buffer, maxlen, 0);
314 if (len == 0) {
315 break;
316 }
317 if (len < 0) {
318 fprintf(stderr, "%s: Error: recv: %s\n", __func__, strerror(errno));
319 break;
320 }
321
322 cursor = buffer;
323 do {
324 wlen = 0;
325 err = iphone_mux_send(cdata->muxclient, cursor, len, &wlen);
326 if (err == IPHONE_E_TIMEOUT) {
327 // some kind of timeout... just be patient and retry.
328 } else if (err != IPHONE_E_SUCCESS) {
329 fprintf(stderr, "%s: USB write error: %d\n", __func__, err);
330 len = -1;
331 break;
332 }
333
334 // calculate remainder.
335 len -= wlen;
336 // advance cursor appropiately.
337 cursor += wlen;
338 } while ((len > 0) && !quit_flag);
339 if (len < 0) {
340 break;
341 }
342 }
343
344leave:
345 // cleanup
346 fprintf(stdout, "%s: terminating\n", __func__);
347 if (cdata->reader != 0) {
348 cdata->reader_quit = 1;
349 pthread_join(cdata->reader, NULL);
350 }
351
352 cdata->handler_dead = 1;
353
354 fprintf(stdout, "%s: terminated\n", __func__);
355 return NULL;
356}
357
358/**
359 * This thread is started when a new connection is accepted.
360 * It performs the handshake, then waits for the connect packet and
361 * on success it starts the usbmuxd_client_handler thread.
362 */
363static void *usbmuxd_client_init_thread(void *arg)
364{
365 struct client_data *cdata;
366 struct usbmux_header hello;
367 struct usbmux_dev_info_request dev_info_req;
368 struct usbmux_connect_request c_req;
369
370 struct usb_bus *bus;
371 struct usb_device *dev;
372
373 int recv_len;
374 int found = 0;
375 int res;
376 int i;
377 int sent_result;
378 iphone_error_t err;
379
380 iphone_device_t phone;
381 struct device_use_info *cur_dev = NULL;
382
383 if (!arg) {
384 fprintf(stderr, "%s: invalid client_data provided!\n", __func__);
385 return NULL;
386 }
387
388 cdata = (struct client_data*)arg;
389 cdata->dead = 0;
390
391 fprintf(stdout, "%s: started (fd=%d)\n", __func__, cdata->socket);
392
393 if ((recv_len = usbmuxd_get_request(cdata->socket, &hello, sizeof(hello))) <= 0) {
394 fprintf(stderr, "%s: No Hello packet received, error %s\n", __func__, strerror(errno));
395 goto leave;
396 }
397
398 if ((recv_len == 16) && (hello.length == 16)
399 && (hello.reserved == 0) && (hello.type == usbmux_hello)) {
400 // send success response
401 usbmuxd_send_result(cdata->socket, hello.tag, 0);
402 } else {
403 // send error response and exit
404 fprintf(stderr, "%s: Invalid Hello packet received.\n", __func__);
405 // TODO is this required?!
406 usbmuxd_send_result(cdata->socket, hello.tag, EINVAL);
407 goto leave;
408 }
409
410 // gather data about all iPhones/iPods attached
411 usb_init();
412 usb_find_busses();
413 usb_find_devices();
414
415 for (bus = usb_get_busses(); bus; bus = bus->next) {
416 for (dev = bus->devices; dev; dev = dev->next) {
417 if (dev->descriptor.idVendor == 0x05ac
418 && dev->descriptor.idProduct >= 0x1290
419 && dev->descriptor.idProduct <= 0x1293)
420 {
421 fprintf(stdout, "%s: Found device on bus %d, id %d\n", __func__, bus->location, dev->devnum);
422 found++;
423
424 // construct packet
425 memset(&dev_info_req, 0, sizeof(dev_info_req));
426 dev_info_req.header.length = sizeof(dev_info_req);
427 dev_info_req.header.type = usbmux_device_info;
428 dev_info_req.dev_info.device_id = dev->devnum;
429 dev_info_req.dev_info.product_id = dev->descriptor.idProduct;
430 if (dev->descriptor.iSerialNumber) {
431 usb_dev_handle *udev;
432 //pthread_mutex_lock(&usbmux_mutex);
433 udev = usb_open(dev);
434 if (udev) {
435 usb_get_string_simple(udev, dev->descriptor.iSerialNumber, dev_info_req.dev_info.serial_number, sizeof(dev_info_req.dev_info.serial_number)+1);
436 usb_close(udev);
437 }
438 //pthread_mutex_unlock(&usbmux_mutex);
439 }
440
441 print_buffer((char*)&dev_info_req, sizeof(dev_info_req));
442
443 // send it
444 if (send_buf(cdata->socket, &dev_info_req, sizeof(dev_info_req)) <= 0) {
445 fprintf(stderr, "%s: Error: Could not send device info: %s\n", __func__, strerror(errno));
446 found--;
447 }
448 }
449 }
450 }
451
452 // now wait for connect request
453 if (found <= 0) {
454 fprintf(stderr, "%s: No attached iPhone/iPod devices found.\n", __func__);
455 goto leave;
456 }
457
458 memset(&c_req, 0, sizeof(c_req));
459 if ((recv_len = usbmuxd_get_request(cdata->socket, &c_req, sizeof(c_req))) <= 0) {
460 fprintf(stderr, "%s: Did not receive any connect request.\n", __func__);
461 goto leave;
462 }
463
464 if (c_req.header.type != usbmux_connect) {
465 fprintf(stderr, "%s: Unexpected packet of type %d received.\n", __func__, c_req.header.type);
466 goto leave;
467 }
468
469 fprintf(stdout, "%s: Setting up connection to usb device #%d on port %d\n", __func__, c_req.device_id, ntohs(c_req.port));
470
471 // find the device, and open usb connection
472 phone = NULL;
473 cur_dev = NULL;
474 // first check if we already have an open connection
475 if (device_use_list) {
476 pthread_mutex_lock(&usbmux_mutex);
477 for (i = 0; i < device_use_count; i++) {
478 if (device_use_list[i]) {
479 if (device_use_list[i]->device_id == c_req.device_id) {
480 device_use_list[i]->use_count++;
481 cur_dev = device_use_list[i];
482 phone = cur_dev->phone;
483 break;
484 }
485 }
486 }
487 pthread_mutex_unlock(&usbmux_mutex);
488 }
489 if (!phone) {
490 // if not found, make a new connection
491 if (iphone_get_specific_device(0, c_req.device_id, &phone) != IPHONE_E_SUCCESS) {
492 fprintf(stderr, "%s: device_id %d could not be opened\n", __func__, c_req.device_id);
493 usbmuxd_send_result(cdata->socket, c_req.header.tag, ENODEV);
494 goto leave;
495 }
496 // add to device list
497 cur_dev = (struct device_use_info*)malloc(sizeof(struct device_use_info));
498 memset(cur_dev, 0, sizeof(struct device_use_info));
499 cur_dev->use_count = 1;
500 cur_dev->device_id = c_req.device_id;
501 cur_dev->phone = phone;
502
503 pthread_mutex_lock(&usbmux_mutex);
504 device_use_list = (struct device_use_info**)realloc(device_use_list, sizeof(struct device_use_info*) * (device_use_count+1));
505 if (device_use_list) {
506 device_use_list[device_use_count] = cur_dev;
507 device_use_count++;
508 }
509 pthread_mutex_unlock(&usbmux_mutex);
510 } else {
511 fprintf(stdout, "%s: reusing usb connection device_id %d\n", __func__, c_req.device_id);
512 }
513
514 // setup connection to iPhone/iPod
515// pthread_mutex_lock(&usbmux_mutex);
516 res = iphone_mux_new_client(cur_dev->phone, 0, ntohs(c_req.port), &(cdata->muxclient));
517// pthread_mutex_unlock(&usbmux_mutex);
518
519 if (res != 0) {
520 usbmuxd_send_result(cdata->socket, c_req.header.tag, res);
521 fprintf(stderr, "%s: mux_new_client returned %d, aborting.\n", __func__, res);
522 goto leave;
523 }
524
525 // start connection handler thread
526 cdata->handler_dead = 0;
527 cdata->tag = c_req.header.tag;
528 if (pthread_create(&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0) {
529 fprintf(stderr, "%s: could not create usbmuxd_client_handler_thread!\n", __func__);
530 cdata->handler = 0;
531 goto leave;
532 }
533
534 sent_result = 0;
535
536 // start reading data from the connected device
537 while (!quit_flag && !cdata->handler_dead) {
538 iphone_mux_pullbulk(cur_dev->phone);
539 err = iphone_mux_get_error(cdata->muxclient);
540 if (err != IPHONE_E_SUCCESS) {
541 break;
542 /*} else if (!sent_result) {
543 usbmuxd_send_result(cdata->socket, c_req.header.tag, 0);
544 sent_result = 1;*/
545 }
546 }
547
548 if (!sent_result) {
549 //fprintf(stderr, "Sending error message %d tag %d\n", err, c_req.header.tag);
550 err = iphone_mux_get_error(cdata->muxclient);
551 //usbmuxd_send_result(cdata->socket, c_req.header.tag, err);
552 }
553
554 fprintf(stdout, "%s: terminating\n", __func__);
555
556 // wait for handler thread to finish its work
557 if (cdata->handler != 0) {
558 pthread_join(cdata->handler, NULL);
559 }
560
561 // time to clean up
562 if (cdata && cdata->muxclient) { // should be non-NULL
563 iphone_mux_free_client(cdata->muxclient);
564 }
565
566leave:
567 // this has to be freed only if it's not in use anymore as it closes
568 // the USB connection
569 if (cur_dev) {
570 if (cur_dev->use_count > 1) {
571 cur_dev->use_count--;
572 } else {
573 iphone_free_device(cur_dev->phone);
574 cur_dev->use_count = 0;
575 free(cur_dev);
576 cur_dev = NULL;
577 pthread_mutex_lock(&usbmux_mutex);
578 if (device_use_count > 1) {
579 struct device_use_info **newlist;
580 int j;
581
582 newlist = (struct device_use_info**)malloc(sizeof(struct device_use_info*) * device_use_count-1);
583 for (i = 0; i < device_use_count; i++) {
584 if (device_use_list[i] != NULL) {
585 newlist[j++] = device_use_list[i];
586 }
587 }
588 free(device_use_list);
589 device_use_list = newlist;
590 } else {
591 free(device_use_list);
592 device_use_list = NULL;
593 }
594 pthread_mutex_unlock(&usbmux_mutex);
595 }
596 }
597
598 cdata->dead = 1;
599
600 fprintf(stdout, "%s: terminated\n", __func__);
601
602 return NULL;
603}
604
605/**
606 * make this program run detached from the current console
607 */
608static int daemonize()
609{
610 // TODO still to be implemented, also logging is missing!
611 return 0;
612}
613
614/**
615 * signal handler function for cleaning up stuff
616 */
617static void clean_exit(int sig)
618{
619 if (sig == SIGINT) {
620 fprintf(stdout, "CTRL+C pressed\n");
621 }
622 quit_flag = 1;
623}
624
625/**
626 * thread function that performs accept() and starts the required child
627 * threads to perform the rest of the communication stuff.
628 */
629static void *usbmuxd_accept_thread(void *arg)
630{
631 struct sockaddr_un c_addr;
632 socklen_t len = sizeof(struct sockaddr_un);
633 struct client_data *cdata;
634 struct client_data **children = NULL;
635 int children_capacity = DEFAULT_CHILDREN_CAPACITY;
636 int i = 0;
637 int result = 0;
638 int cnt;
639
640 // Reserve space for 10 clients which should be enough. If not, the
641 // buffer gets enlarged later.
642 children = (struct client_data**)malloc(sizeof(struct client_data*) * children_capacity);
643 if (!children) {
644 fprintf(stderr, "%s: Out of memory when allocating memory for child threads. Terminating.\n", __func__);
645 exit(EXIT_FAILURE);
646 }
647 memset(children, 0, sizeof(struct client_data*) * children_capacity);
648
649 fprintf(stdout, "%s: waiting for connection\n", __func__);
650 while (!quit_flag) {
651 // Check the file descriptor before accepting a connection.
652 // If no connection attempt is made, just repeat...
653 result = check_fd(fsock, fdread, DEFAULT_TIMEOUT);
654 if (result <= 0) {
655 if (result == 0) {
656 // cleanup
657 for (i = 0; i < children_capacity; i++) {
658 if (children[i]) {
659 if (children[i]->dead != 0) {
660 pthread_join(children[i]->thread, NULL);
661 fprintf(stdout, "%s: reclaimed client thread (fd=%d)\n", __func__, children[i]->socket);
662 free(children[i]);
663 children[i] = NULL;
664 cnt++;
665 } else {
666 cnt = 0;
667 }
668 } else {
669 cnt++;
670 }
671 }
672
673 if ((children_capacity > DEFAULT_CHILDREN_CAPACITY)
674 && ((children_capacity - cnt) <= DEFAULT_CHILDREN_CAPACITY)) {
675 children_capacity = DEFAULT_CHILDREN_CAPACITY;
676 children = realloc(children, sizeof(struct client_data*) * children_capacity);
677 }
678 continue;
679 } else {
680 fprintf(stderr, "select error: %s\n", strerror(errno));
681 continue;
682 }
683 }
684
685 cdata = (struct client_data*)malloc(sizeof(struct client_data));
686 memset(cdata, 0, sizeof(struct client_data));
687 if (!cdata) {
688 quit_flag = 1;
689 fprintf(stderr, "%s: Error: Out of memory! Terminating.\n", __func__);
690 break;
691 }
692
693 cdata->socket = accept(fsock, (struct sockaddr*)&c_addr, &len);
694 if (cdata->socket < 0) {
695 free(cdata);
696 if (errno == EINTR) {
697 continue;
698 } else {
699 fprintf(stderr, "%s: Error in accept: %s\n", __func__, strerror(errno));
700 continue;
701 }
702 }
703
704 fprintf(stdout, "%s: new client connected (fd=%d)\n", __func__, cdata->socket);
705
706 // create client thread:
707 if (pthread_create(&cdata->thread, NULL, usbmuxd_client_init_thread, cdata) == 0) {
708 for (i = 0; i < children_capacity; i++) {
709 if (children[i] == NULL) break;
710 }
711 if (i == children_capacity) {
712 // enlarge buffer
713 children_capacity++;
714 children = realloc(children, sizeof(struct client_data*) * children_capacity);
715 if (!children) {
716 fprintf(stderr, "%s: Out of memory when enlarging child thread buffer\n", __func__);
717 }
718 }
719 children[i] = cdata;
720 } else {
721 fprintf(stderr, "%s: Failed to create client_init_thread.\n", __func__);
722 close(cdata->socket);
723 free(cdata);
724 cdata = NULL;
725 }
726 }
727
728 fprintf(stdout, "%s: terminating\n", __func__);
729
730 // preparing for shutdown: wait for child threads to terminate (if any)
731 fprintf(stdout, "%s: waiting for child threads to terminate...\n", __func__);
732 for (i = 0; i < children_capacity; i++) {
733 if (children[i] != NULL) {
734 pthread_join(children[i]->thread, NULL);
735 free(children[i]);
736 }
737 }
738
739 // delete the children set.
740 free(children);
741 children = NULL;
742
743 fprintf(stdout, "%s: terminated.\n", __func__);
744
745 return NULL;
746}
747
748int main(int argc, char **argv)
749{
750 int foreground = 1;
751 pthread_t acceptor;
752
753 fprintf(stdout, "usbmuxd: starting\n");
754
755 // TODO: Parameter checking.
756
757 fsock = create_unix_socket(SOCKET_FILE);
758 if (fsock < 0) {
759 fprintf(stderr, "Could not create socket, exiting\n");
760 return -1;
761 }
762
763 chmod(SOCKET_FILE, 0666);
764
765 if (!foreground) {
766 if (daemonize() < 0) {
767 exit(EXIT_FAILURE);
768 }
769 }
770
771 // signal(SIGHUP, reload_conf); // none yet
772 signal(SIGINT, clean_exit);
773 signal(SIGQUIT, clean_exit);
774 signal(SIGTERM, clean_exit);
775 signal(SIGPIPE, SIG_IGN);
776
777 if (pthread_create(&acceptor, NULL, usbmuxd_accept_thread, NULL) != 0) {
778 fprintf(stderr, "Failed to create server thread.\n");
779 close(fsock);
780 return -1;
781 }
782
783 // Relax here. Just wait for the accept thread to terminate.
784 pthread_join(acceptor, NULL);
785
786 fprintf(stdout, "usbmuxd: terminating\n");
787 if (fsock >= 0) {
788 close(fsock);
789 }
790
791 unlink(SOCKET_FILE);
792
793 return 0;
794}
795
diff --git a/usbmuxd.h b/usbmuxd.h
new file mode 100644
index 0000000..fcbee52
--- /dev/null
+++ b/usbmuxd.h
@@ -0,0 +1,44 @@
1#ifndef __USBMUXD_H
2#define __USBMUXD_H
3
4#include <stdint.h>
5
6struct usbmux_header {
7 uint32_t length; // length of message, including header
8 uint32_t reserved; // always zero
9 uint32_t type; // message type
10 uint32_t tag; // responses to this query will echo back this tag
11};
12
13struct usbmux_result {
14 struct usbmux_header header;
15 uint32_t result;
16};
17
18struct usbmux_connect_request {
19 struct usbmux_header header;
20 uint32_t device_id;
21 uint16_t port; // TCP port number
22 uint16_t reserved; // set to zero
23};
24
25struct usbmux_dev_info {
26 uint32_t device_id;
27 uint16_t product_id;
28 char serial_number[40];
29};
30
31struct usbmux_dev_info_request {
32 struct usbmux_header header;
33 struct usbmux_dev_info dev_info;
34 unsigned char padding[222];
35};
36
37enum {
38 usbmux_result = 1,
39 usbmux_connect = 2,
40 usbmux_hello = 3,
41 usbmux_device_info = 4,
42};
43
44#endif