summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am32
-rw-r--r--src/client.c1058
-rw-r--r--src/client.h47
-rw-r--r--src/conf.c535
-rw-r--r--src/conf.h40
-rw-r--r--src/device.c1037
-rw-r--r--src/device.h56
-rw-r--r--src/log.c101
-rw-r--r--src/log.h42
-rw-r--r--src/main.c920
-rw-r--r--src/preflight.c406
-rw-r--r--src/preflight.h28
-rw-r--r--src/usb.c1084
-rw-r--r--src/usb.h73
-rw-r--r--src/usbmuxd-proto.h95
-rw-r--r--src/utils.c131
-rw-r--r--src/utils.h49
17 files changed, 5734 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..8a96e46
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,32 @@
1AM_CPPFLAGS = \
2 -I$(top_srcdir)/include \
3 -I$(top_srcdir)
4
5AM_CFLAGS = \
6 $(GLOBAL_CFLAGS) \
7 $(libplist_CFLAGS) \
8 $(libusb_CFLAGS) \
9 $(limd_glue_CFLAGS) \
10 $(libimobiledevice_CFLAGS)
11
12AM_LDFLAGS = \
13 $(libplist_LIBS) \
14 $(libusb_LIBS) \
15 $(limd_glue_LIBS) \
16 $(libimobiledevice_LIBS) \
17 $(libpthread_LIBS)
18
19sbin_PROGRAMS = usbmuxd
20
21usbmuxd_CFLAGS = $(AM_CFLAGS)
22usbmuxd_LDFLAGS = $(AM_LDFLAGS) -no-undefined
23usbmuxd_SOURCES = \
24 client.c client.h \
25 device.c device.h \
26 preflight.c preflight.h \
27 log.c log.h \
28 usbmuxd-proto.h \
29 usb.c usb.h \
30 utils.c utils.h \
31 conf.c conf.h \
32 main.c
diff --git a/src/client.c b/src/client.c
new file mode 100644
index 0000000..dbbdd5f
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,1058 @@
1/*
2 * client.c
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#define _GNU_SOURCE 1
26
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include <errno.h>
31#include <unistd.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <netinet/in.h>
35#include <netinet/tcp.h>
36#include <sys/un.h>
37#include <arpa/inet.h>
38#include <fcntl.h>
39
40#include <plist/plist.h>
41#include <libimobiledevice-glue/collection.h>
42#include <libimobiledevice-glue/thread.h>
43
44#include "log.h"
45#include "usb.h"
46#include "client.h"
47#include "device.h"
48#include "conf.h"
49
50#define CMD_BUF_SIZE 0x10000
51#define REPLY_BUF_SIZE 0x10000
52
53enum client_state {
54 CLIENT_COMMAND, // waiting for command
55 CLIENT_LISTEN, // listening for devices
56 CLIENT_CONNECTING1, // issued connection request
57 CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
58 CLIENT_CONNECTED, // connected
59 CLIENT_DEAD
60};
61
62struct mux_client {
63 int fd;
64 unsigned char *ob_buf;
65 uint32_t ob_size;
66 uint32_t ob_capacity;
67 unsigned char *ib_buf;
68 uint32_t ib_size;
69 uint32_t ib_capacity;
70 short events, devents;
71 uint32_t connect_tag;
72 int connect_device;
73 enum client_state state;
74 uint32_t proto_version;
75 uint32_t number;
76 plist_t info;
77};
78
79static struct collection client_list;
80mutex_t client_list_mutex;
81static uint32_t client_number = 0;
82
83#ifdef SO_PEERCRED
84static char* _get_process_name_by_pid(const int pid)
85{
86 char* name = (char*)calloc(1024, sizeof(char));
87 if(name) {
88 sprintf(name, "/proc/%d/cmdline", pid);
89 FILE* f = fopen(name, "r");
90 if(f) {
91 size_t size;
92 size = fread(name, sizeof(char), 1024, f);
93 if(size > 0) {
94 if('\n' == name[size-1])
95 name[size-1]='\0';
96 }
97 fclose(f);
98 }
99 }
100 return name;
101}
102#endif
103
104/**
105 * Receive raw data from the client socket.
106 *
107 * @param client Client to read from.
108 * @param buffer Buffer to store incoming data.
109 * @param len Max number of bytes to read.
110 * @return Same as recv() system call. Number of bytes read; when < 0 errno will be set.
111 */
112int client_read(struct mux_client *client, void *buffer, uint32_t len)
113{
114 usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
115 if(client->state != CLIENT_CONNECTED) {
116 usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
117 return -1;
118 }
119 return recv(client->fd, buffer, len, 0);
120}
121
122/**
123 * Send raw data to the client socket.
124 *
125 * @param client Client to send to.
126 * @param buffer The data to send.
127 * @param len Number of bytes to write.
128 * @return Same as system call send(). Number of bytes written; when < 0 errno will be set.
129 */
130int client_write(struct mux_client *client, void *buffer, uint32_t len)
131{
132 int sret = -1;
133
134 usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
135 if(client->state != CLIENT_CONNECTED) {
136 usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
137 return -1;
138 }
139
140 sret = send(client->fd, buffer, len, 0);
141 if (sret < 0) {
142 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
143 usbmuxd_log(LL_DEBUG, "client_write: fd %d not ready for writing", client->fd);
144 sret = 0;
145 } else {
146 usbmuxd_log(LL_ERROR, "ERROR: client_write: sending to fd %d failed: %s", client->fd, strerror(errno));
147 }
148 }
149 return sret;
150}
151
152/**
153 * Set event mask to use for ppoll()ing the client socket.
154 * Typically POLLOUT and/or POLLIN. Note that this overrides
155 * the current mask, that is, it is not ORing the argument
156 * into the current mask.
157 *
158 * @param client The client to set the event mask on.
159 * @param events The event mask to sert.
160 * @return 0 on success, -1 on error.
161 */
162int client_set_events(struct mux_client *client, short events)
163{
164 if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
165 usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
166 return -1;
167 }
168 client->devents = events;
169 if(client->state == CLIENT_CONNECTED)
170 client->events = events;
171 return 0;
172}
173
174/**
175 * Wait for an inbound connection on the usbmuxd socket
176 * and create a new mux_client instance for it, and store
177 * the client in the client list.
178 *
179 * @param listenfd the socket fd to accept() on.
180 * @return The connection fd for the client, or < 0 for error
181 * in which case errno will be set.
182 */
183int client_accept(int listenfd)
184{
185 struct sockaddr_un addr;
186 int cfd;
187 socklen_t len = sizeof(struct sockaddr_un);
188 cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
189 if (cfd < 0) {
190 usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
191 return cfd;
192 }
193
194 int flags = fcntl(cfd, F_GETFL, 0);
195 if (flags < 0) {
196 usbmuxd_log(LL_ERROR, "ERROR: Could not get socket flags!");
197 } else {
198 if (fcntl(cfd, F_SETFL, flags | O_NONBLOCK) < 0) {
199 usbmuxd_log(LL_ERROR, "ERROR: Could not set socket to non-blocking mode");
200 }
201 }
202
203 int bufsize = 0x20000;
204 if (setsockopt(cfd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(int)) == -1) {
205 usbmuxd_log(LL_WARNING, "Could not set send buffer for client socket");
206 }
207 if (setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(int)) == -1) {
208 usbmuxd_log(LL_WARNING, "Could not set receive buffer for client socket");
209 }
210
211 int yes = 1;
212 setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(int));
213
214 struct mux_client *client;
215 client = malloc(sizeof(struct mux_client));
216 memset(client, 0, sizeof(struct mux_client));
217
218 client->fd = cfd;
219 client->ob_buf = malloc(REPLY_BUF_SIZE);
220 client->ob_size = 0;
221 client->ob_capacity = REPLY_BUF_SIZE;
222 client->ib_buf = malloc(CMD_BUF_SIZE);
223 client->ib_size = 0;
224 client->ib_capacity = CMD_BUF_SIZE;
225 client->state = CLIENT_COMMAND;
226 client->events = POLLIN;
227 client->info = NULL;
228
229 mutex_lock(&client_list_mutex);
230 client->number = client_number++;
231 collection_add(&client_list, client);
232 mutex_unlock(&client_list_mutex);
233
234#ifdef SO_PEERCRED
235 if (log_level >= LL_INFO) {
236 struct ucred cr;
237 len = sizeof(struct ucred);
238 getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
239
240 if (getpid() == cr.pid) {
241 usbmuxd_log(LL_INFO, "Client %d accepted: %s[%d]", client->fd, PACKAGE_NAME, cr.pid);
242 } else {
243 char* process_name = _get_process_name_by_pid(cr.pid);
244 usbmuxd_log(LL_INFO, "Client %d accepted: %s[%d]", client->fd, process_name, cr.pid);
245 free(process_name);
246 }
247 }
248#else
249 usbmuxd_log(LL_INFO, "Client %d accepted", client->fd);
250#endif
251 return client->fd;
252}
253
254void client_close(struct mux_client *client)
255{
256 int found = 0;
257 mutex_lock(&client_list_mutex);
258 FOREACH(struct mux_client *lc, &client_list) {
259 if (client == lc) {
260 found = 1;
261 break;
262 }
263 } ENDFOREACH
264 if (!found) {
265 // in case we get called again but client was already freed
266 usbmuxd_log(LL_DEBUG, "%s: ignoring for non-existing client %p", __func__, client);
267 mutex_unlock(&client_list_mutex);
268 return;
269 }
270#ifdef SO_PEERCRED
271 if (log_level >= LL_INFO) {
272 struct ucred cr;
273 socklen_t len = sizeof(struct ucred);
274 getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
275
276 if (getpid() == cr.pid) {
277 usbmuxd_log(LL_INFO, "Client %d is going to be disconnected: %s[%d]", client->fd, PACKAGE_NAME, cr.pid);
278 } else {
279 char* process_name = _get_process_name_by_pid(cr.pid);
280 usbmuxd_log(LL_INFO, "Client %d is going to be disconnected: %s[%d]", client->fd, process_name, cr.pid);
281 free(process_name);
282 }
283 }
284#else
285 usbmuxd_log(LL_INFO, "Client %d is going to be disconnected", client->fd);
286#endif
287 if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
288 usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
289 client->state = CLIENT_DEAD;
290 device_abort_connect(client->connect_device, client);
291 }
292 close(client->fd);
293 free(client->ob_buf);
294 free(client->ib_buf);
295 plist_free(client->info);
296
297 collection_remove(&client_list, client);
298 mutex_unlock(&client_list_mutex);
299 free(client);
300}
301
302void client_get_fds(struct fdlist *list)
303{
304 mutex_lock(&client_list_mutex);
305 FOREACH(struct mux_client *client, &client_list) {
306 fdlist_add(list, FD_CLIENT, client->fd, client->events);
307 } ENDFOREACH
308 mutex_unlock(&client_list_mutex);
309}
310
311static int output_buffer_add_message(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length)
312{
313 struct usbmuxd_header hdr;
314 hdr.version = client->proto_version;
315 hdr.length = sizeof(hdr) + payload_length;
316 hdr.message = msg;
317 hdr.tag = tag;
318 usbmuxd_log(LL_DEBUG, "Client %d output buffer got tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
319
320 uint32_t available = client->ob_capacity - client->ob_size;
321 /* the output buffer _should_ be large enough, but just in case */
322 if(available < hdr.length) {
323 unsigned char* new_buf;
324 uint32_t new_size = ((client->ob_capacity + hdr.length + 4096) / 4096) * 4096;
325 usbmuxd_log(LL_DEBUG, "%s: Enlarging client %d output buffer %d -> %d", __func__, client->fd, client->ob_capacity, new_size);
326 new_buf = realloc(client->ob_buf, new_size);
327 if (!new_buf) {
328 usbmuxd_log(LL_FATAL, "%s: Failed to realloc.", __func__);
329 return -1;
330 }
331 client->ob_buf = new_buf;
332 client->ob_capacity = new_size;
333 }
334 memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
335 if(payload && payload_length)
336 memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
337 client->ob_size += hdr.length;
338 client->events |= POLLOUT;
339 return hdr.length;
340}
341
342static int send_plist(struct mux_client *client, uint32_t tag, plist_t plist)
343{
344 int res = -1;
345 char *xml = NULL;
346 uint32_t xmlsize = 0;
347 plist_to_xml(plist, &xml, &xmlsize);
348 if (xml) {
349 res = output_buffer_add_message(client, tag, MESSAGE_PLIST, xml, xmlsize);
350 free(xml);
351 } else {
352 usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__);
353 }
354 return res;
355}
356
357static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
358{
359 int res = -1;
360 if (client->proto_version == 1) {
361 /* XML plist packet */
362 plist_t dict = plist_new_dict();
363 plist_dict_set_item(dict, "MessageType", plist_new_string("Result"));
364 plist_dict_set_item(dict, "Number", plist_new_uint(result));
365 res = send_plist(client, tag, dict);
366 plist_free(dict);
367 } else {
368 /* binary packet */
369 res = output_buffer_add_message(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
370 }
371 return res;
372}
373
374int client_notify_connect(struct mux_client *client, enum usbmuxd_result result)
375{
376 usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
377 if(client->state == CLIENT_DEAD)
378 return -1;
379 if(client->state != CLIENT_CONNECTING1) {
380 usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
381 return -1;
382 }
383 if(send_result(client, client->connect_tag, result) < 0)
384 return -1;
385 if(result == RESULT_OK) {
386 client->state = CLIENT_CONNECTING2;
387 client->events = POLLOUT; // wait for the result packet to go through
388 // no longer need this
389 free(client->ib_buf);
390 client->ib_buf = NULL;
391 } else {
392 client->state = CLIENT_COMMAND;
393 }
394 return 0;
395}
396
397static plist_t create_device_attached_plist(struct device_info *dev)
398{
399 plist_t dict = plist_new_dict();
400 plist_dict_set_item(dict, "MessageType", plist_new_string("Attached"));
401 plist_dict_set_item(dict, "DeviceID", plist_new_uint(dev->id));
402 plist_t props = plist_new_dict();
403 plist_dict_set_item(props, "ConnectionSpeed", plist_new_uint(dev->speed));
404 plist_dict_set_item(props, "ConnectionType", plist_new_string("USB"));
405 plist_dict_set_item(props, "DeviceID", plist_new_uint(dev->id));
406 plist_dict_set_item(props, "LocationID", plist_new_uint(dev->location));
407 plist_dict_set_item(props, "ProductID", plist_new_uint(dev->pid));
408 plist_dict_set_item(props, "SerialNumber", plist_new_string(dev->serial));
409 plist_dict_set_item(dict, "Properties", props);
410 return dict;
411}
412
413static int send_device_list(struct mux_client *client, uint32_t tag)
414{
415 int res = -1;
416 plist_t dict = plist_new_dict();
417 plist_t devices = plist_new_array();
418
419 struct device_info *devs = NULL;
420 struct device_info *dev;
421 int i;
422
423 int count = device_get_list(0, &devs);
424 dev = devs;
425 for (i = 0; devs && i < count; i++) {
426 plist_t device = create_device_attached_plist(dev++);
427 if (device) {
428 plist_array_append_item(devices, device);
429 }
430 }
431 if (devs)
432 free(devs);
433
434 plist_dict_set_item(dict, "DeviceList", devices);
435 res = send_plist(client, tag, dict);
436 plist_free(dict);
437 return res;
438}
439
440static int send_listener_list(struct mux_client *client, uint32_t tag)
441{
442 int res = -1;
443
444 plist_t dict = plist_new_dict();
445 plist_t listeners = plist_new_array();
446
447 mutex_lock(&client_list_mutex);
448 FOREACH(struct mux_client *lc, &client_list) {
449 if (lc->state == CLIENT_LISTEN) {
450 plist_t n = NULL;
451 plist_t l = plist_new_dict();
452 plist_dict_set_item(l, "Blacklisted", plist_new_bool(0));
453 n = NULL;
454 if (lc->info) {
455 n = plist_dict_get_item(lc->info, "BundleID");
456 }
457 if (n) {
458 plist_dict_set_item(l, "BundleID", plist_copy(n));
459 }
460 plist_dict_set_item(l, "ConnType", plist_new_uint(0));
461
462 n = NULL;
463 char *progname = NULL;
464 if (lc->info) {
465 n = plist_dict_get_item(lc->info, "ProgName");
466 }
467 if (n) {
468 plist_get_string_val(n, &progname);
469 }
470 if (!progname) {
471 progname = strdup("unknown");
472 }
473 char *idstring = malloc(strlen(progname) + 12);
474 sprintf(idstring, "%u-%s", client->number, progname);
475
476 plist_dict_set_item(l, "ID String", plist_new_string(idstring));
477 free(idstring);
478 plist_dict_set_item(l, "ProgName", plist_new_string(progname));
479 free(progname);
480
481 n = NULL;
482 uint64_t version = 0;
483 if (lc->info) {
484 n = plist_dict_get_item(lc->info, "kLibUSBMuxVersion");
485 }
486 if (n) {
487 plist_get_uint_val(n, &version);
488 }
489 plist_dict_set_item(l, "kLibUSBMuxVersion", plist_new_uint(version));
490
491 plist_array_append_item(listeners, l);
492 }
493 } ENDFOREACH
494 mutex_unlock(&client_list_mutex);
495
496 plist_dict_set_item(dict, "ListenerList", listeners);
497 res = send_plist(client, tag, dict);
498 plist_free(dict);
499
500 return res;
501}
502
503static int send_system_buid(struct mux_client *client, uint32_t tag)
504{
505 int res = -1;
506 char* buid = NULL;
507
508 config_get_system_buid(&buid);
509
510 plist_t dict = plist_new_dict();
511 plist_dict_set_item(dict, "BUID", plist_new_string(buid));
512 free(buid);
513 res = send_plist(client, tag, dict);
514 plist_free(dict);
515 return res;
516}
517
518static int send_pair_record(struct mux_client *client, uint32_t tag, const char* record_id)
519{
520 int res = -1;
521 char* record_data = NULL;
522 uint64_t record_size = 0;
523
524 if (!record_id) {
525 return send_result(client, tag, EINVAL);
526 }
527
528 config_get_device_record(record_id, &record_data, &record_size);
529
530 if (record_data) {
531 plist_t dict = plist_new_dict();
532 plist_dict_set_item(dict, "PairRecordData", plist_new_data(record_data, record_size));
533 free(record_data);
534 res = send_plist(client, tag, dict);
535 plist_free(dict);
536 } else {
537 res = send_result(client, tag, ENOENT);
538 }
539 return res;
540}
541
542static int send_device_add(struct mux_client *client, struct device_info *dev)
543{
544 int res = -1;
545 if (client->proto_version == 1) {
546 /* XML plist packet */
547 plist_t dict = create_device_attached_plist(dev);
548 res = send_plist(client, 0, dict);
549 plist_free(dict);
550 } else {
551 /* binary packet */
552 struct usbmuxd_device_record dmsg;
553 memset(&dmsg, 0, sizeof(dmsg));
554 dmsg.device_id = dev->id;
555 strncpy(dmsg.serial_number, dev->serial, 256);
556 dmsg.serial_number[255] = 0;
557 dmsg.location = dev->location;
558 dmsg.product_id = dev->pid;
559 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
560 }
561 return res;
562}
563
564static int send_device_remove(struct mux_client *client, uint32_t device_id)
565{
566 int res = -1;
567 if (client->proto_version == 1) {
568 /* XML plist packet */
569 plist_t dict = plist_new_dict();
570 plist_dict_set_item(dict, "MessageType", plist_new_string("Detached"));
571 plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id));
572 res = send_plist(client, 0, dict);
573 plist_free(dict);
574 } else {
575 /* binary packet */
576 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t));
577 }
578 return res;
579}
580
581static int send_device_paired(struct mux_client *client, uint32_t device_id)
582{
583 int res = -1;
584 if (client->proto_version == 1) {
585 /* XML plist packet */
586 plist_t dict = plist_new_dict();
587 plist_dict_set_item(dict, "MessageType", plist_new_string("Paired"));
588 plist_dict_set_item(dict, "DeviceID", plist_new_uint(device_id));
589 res = send_plist(client, 0, dict);
590 plist_free(dict);
591 }
592 else {
593 /* binary packet */
594 res = output_buffer_add_message(client, 0, MESSAGE_DEVICE_PAIRED, &device_id, sizeof(uint32_t));
595 }
596 return res;
597}
598
599static int start_listen(struct mux_client *client)
600{
601 struct device_info *devs = NULL;
602 struct device_info *dev;
603 int count, i;
604
605 client->state = CLIENT_LISTEN;
606
607 count = device_get_list(0, &devs);
608 dev = devs;
609 for(i=0; devs && i < count; i++) {
610 if(send_device_add(client, dev++) < 0) {
611 free(devs);
612 return -1;
613 }
614 }
615 if (devs)
616 free(devs);
617
618 return count;
619}
620
621static char* plist_dict_get_string_val(plist_t dict, const char* key)
622{
623 if (!dict || plist_get_node_type(dict) != PLIST_DICT)
624 return NULL;
625 plist_t item = plist_dict_get_item(dict, key);
626 if (!item || plist_get_node_type(item) != PLIST_STRING)
627 return NULL;
628 char *str = NULL;
629 plist_get_string_val(item, &str);
630 return str;
631}
632
633static void update_client_info(struct mux_client *client, plist_t dict)
634{
635 plist_t node = NULL;
636 plist_t info = plist_new_dict();
637
638 node = plist_dict_get_item(dict, "BundleID");
639 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
640 plist_dict_set_item(info, "BundleID", plist_copy(node));
641 }
642
643 node = plist_dict_get_item(dict, "ClientVersionString");
644 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
645 plist_dict_set_item(info, "ClientVersionString", plist_copy(node));
646 }
647
648 node = plist_dict_get_item(dict, "ProgName");
649 if (node && (plist_get_node_type(node) == PLIST_STRING)) {
650 plist_dict_set_item(info, "ProgName", plist_copy(node));
651 }
652
653 node = plist_dict_get_item(dict, "kLibUSBMuxVersion");
654 if (node && (plist_get_node_type(node) == PLIST_UINT)) {
655 plist_dict_set_item(info, "kLibUSBMuxVersion", plist_copy(node));
656 }
657 plist_free(client->info);
658 client->info = info;
659}
660
661static int handle_command(struct mux_client *client, struct usbmuxd_header *hdr)
662{
663 int res;
664 usbmuxd_log(LL_DEBUG, "Client %d command len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag);
665
666 if(client->state != CLIENT_COMMAND) {
667 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state, got %d but want %d", client->fd, client->state, CLIENT_COMMAND);
668 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
669 return -1;
670 client_close(client);
671 return -1;
672 }
673
674 if((hdr->version != 0) && (hdr->version != 1)) {
675 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected 0 or 1, got %d", client->fd, hdr->version);
676 send_result(client, hdr->tag, RESULT_BADVERSION);
677 return 0;
678 }
679
680 struct usbmuxd_connect_request *ch;
681 char *payload;
682 uint32_t payload_size;
683
684 switch(hdr->message) {
685 case MESSAGE_PLIST:
686 client->proto_version = 1;
687 payload = (char*)(hdr) + sizeof(struct usbmuxd_header);
688 payload_size = hdr->length - sizeof(struct usbmuxd_header);
689 plist_t dict = NULL;
690 plist_from_xml(payload, payload_size, &dict);
691 if (!dict) {
692 usbmuxd_log(LL_ERROR, "Could not parse plist from payload!");
693 return -1;
694 } else {
695 char *message = NULL;
696 plist_t node = plist_dict_get_item(dict, "MessageType");
697 if (!node || plist_get_node_type(node) != PLIST_STRING) {
698 usbmuxd_log(LL_ERROR, "Could not read valid MessageType node from plist!");
699 plist_free(dict);
700 return -1;
701 }
702 plist_get_string_val(node, &message);
703 if (!message) {
704 usbmuxd_log(LL_ERROR, "Could not extract MessageType from plist!");
705 plist_free(dict);
706 return -1;
707 }
708 update_client_info(client, dict);
709 if (!strcmp(message, "Listen")) {
710 free(message);
711 plist_free(dict);
712 if (send_result(client, hdr->tag, 0) < 0)
713 return -1;
714 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
715 return start_listen(client);
716 } else if (!strcmp(message, "Connect")) {
717 uint64_t val;
718 uint16_t portnum = 0;
719 uint32_t device_id = 0;
720 free(message);
721 // get device id
722 node = plist_dict_get_item(dict, "DeviceID");
723 if (!node) {
724 usbmuxd_log(LL_ERROR, "Received connect request without device_id!");
725 plist_free(dict);
726 if (send_result(client, hdr->tag, RESULT_BADDEV) < 0)
727 return -1;
728 return 0;
729 }
730 val = 0;
731 plist_get_uint_val(node, &val);
732 device_id = (uint32_t)val;
733
734 // get port number
735 node = plist_dict_get_item(dict, "PortNumber");
736 if (!node) {
737 usbmuxd_log(LL_ERROR, "Received connect request without port number!");
738 plist_free(dict);
739 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
740 return -1;
741 return 0;
742 }
743 val = 0;
744 plist_get_uint_val(node, &val);
745 portnum = (uint16_t)val;
746 plist_free(dict);
747
748 usbmuxd_log(LL_DEBUG, "Client %d requesting connection to device %d port %d", client->fd, device_id, ntohs(portnum));
749 res = device_start_connect(device_id, ntohs(portnum), client);
750 if(res < 0) {
751 if (send_result(client, hdr->tag, -res) < 0)
752 return -1;
753 } else {
754 client->connect_tag = hdr->tag;
755 client->connect_device = device_id;
756 client->state = CLIENT_CONNECTING1;
757 }
758 return 0;
759 } else if (!strcmp(message, "ListDevices")) {
760 free(message);
761 plist_free(dict);
762 if (send_device_list(client, hdr->tag) < 0)
763 return -1;
764 return 0;
765 } else if (!strcmp(message, "ListListeners")) {
766 free(message);
767 plist_free(dict);
768 if (send_listener_list(client, hdr->tag) < 0)
769 return -1;
770 return 0;
771 } else if (!strcmp(message, "ReadBUID")) {
772 free(message);
773 plist_free(dict);
774 if (send_system_buid(client, hdr->tag) < 0)
775 return -1;
776 return 0;
777 } else if (!strcmp(message, "ReadPairRecord")) {
778 free(message);
779 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
780 plist_free(dict);
781
782 res = send_pair_record(client, hdr->tag, record_id);
783 if (record_id)
784 free(record_id);
785 if (res < 0)
786 return -1;
787 return 0;
788 } else if (!strcmp(message, "SavePairRecord")) {
789 uint32_t rval = RESULT_OK;
790 free(message);
791 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
792 char* record_data = NULL;
793 uint64_t record_size = 0;
794 plist_t rdata = plist_dict_get_item(dict, "PairRecordData");
795 if (rdata && plist_get_node_type(rdata) == PLIST_DATA) {
796 plist_get_data_val(rdata, &record_data, &record_size);
797 }
798
799 if (record_id && record_data) {
800 res = config_set_device_record(record_id, record_data, record_size);
801 if (res < 0) {
802 rval = -res;
803 } else {
804 plist_t p_dev_id = plist_dict_get_item(dict, "DeviceID");
805 uint32_t dev_id = 0;
806 if (p_dev_id && plist_get_node_type(p_dev_id) == PLIST_UINT) {
807 uint64_t u_dev_id = 0;
808 plist_get_uint_val(p_dev_id, &u_dev_id);
809 dev_id = (uint32_t)u_dev_id;
810 }
811 if (dev_id > 0) {
812 struct device_info *devs = NULL;
813 struct device_info *dev;
814 int i;
815 int count = device_get_list(1, &devs);
816 int found = 0;
817 dev = devs;
818 for (i = 0; devs && i < count; i++, dev++) {
819 if ((uint32_t)dev->id == dev_id && (strcmp(dev->serial, record_id) == 0)) {
820 found++;
821 break;
822 }
823 }
824 if (!found) {
825 usbmuxd_log(LL_ERROR, "ERROR: SavePairRecord: DeviceID %d (%s) is not connected\n", dev_id, record_id);
826 } else {
827 client_device_paired(dev_id);
828 }
829 free(devs);
830 }
831 }
832 free(record_id);
833 } else {
834 rval = EINVAL;
835 }
836 free(record_data);
837 plist_free(dict);
838 if (send_result(client, hdr->tag, rval) < 0)
839 return -1;
840 return 0;
841 } else if (!strcmp(message, "DeletePairRecord")) {
842 uint32_t rval = RESULT_OK;
843 free(message);
844 char* record_id = plist_dict_get_string_val(dict, "PairRecordID");
845 plist_free(dict);
846 if (record_id) {
847 res = config_remove_device_record(record_id);
848 if (res < 0) {
849 rval = -res;
850 }
851 free(record_id);
852 } else {
853 rval = EINVAL;
854 }
855 if (send_result(client, hdr->tag, rval) < 0)
856 return -1;
857 return 0;
858 } else {
859 usbmuxd_log(LL_ERROR, "Unexpected command '%s' received!", message);
860 free(message);
861 plist_free(dict);
862 if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
863 return -1;
864 return 0;
865 }
866 }
867 // should not be reached?!
868 return -1;
869 case MESSAGE_LISTEN:
870 if(send_result(client, hdr->tag, 0) < 0)
871 return -1;
872 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
873 return start_listen(client);
874 case MESSAGE_CONNECT:
875 ch = (void*)hdr;
876 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
877 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
878 if(res < 0) {
879 if(send_result(client, hdr->tag, -res) < 0)
880 return -1;
881 } else {
882 client->connect_tag = hdr->tag;
883 client->connect_device = ch->device_id;
884 client->state = CLIENT_CONNECTING1;
885 }
886 return 0;
887 default:
888 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
889 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
890 return -1;
891 return 0;
892 }
893 return -1;
894}
895
896static void output_buffer_process(struct mux_client *client)
897{
898 int res;
899 if(!client->ob_size) {
900 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
901 client->events &= ~POLLOUT;
902 return;
903 }
904 res = send(client->fd, client->ob_buf, client->ob_size, 0);
905 if(res <= 0) {
906 usbmuxd_log(LL_ERROR, "Sending to client fd %d failed: %d %s", client->fd, res, strerror(errno));
907 client_close(client);
908 return;
909 }
910 if((uint32_t)res == client->ob_size) {
911 client->ob_size = 0;
912 client->events &= ~POLLOUT;
913 if(client->state == CLIENT_CONNECTING2) {
914 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
915 client->state = CLIENT_CONNECTED;
916 client->events = client->devents;
917 // no longer need this
918 free(client->ob_buf);
919 client->ob_buf = NULL;
920 }
921 } else {
922 client->ob_size -= res;
923 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
924 }
925}
926static void input_buffer_process(struct mux_client *client)
927{
928 int res;
929 int did_read = 0;
930 if(client->ib_size < sizeof(struct usbmuxd_header)) {
931 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0);
932 if(res <= 0) {
933 if(res < 0)
934 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
935 else
936 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
937 client_close(client);
938 return;
939 }
940 client->ib_size += res;
941 if(client->ib_size < sizeof(struct usbmuxd_header))
942 return;
943 did_read = 1;
944 }
945 struct usbmuxd_header *hdr = (void*)client->ib_buf;
946 if(hdr->length > client->ib_capacity) {
947 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
948 client_close(client);
949 return;
950 }
951 if(hdr->length < sizeof(struct usbmuxd_header)) {
952 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
953 client_close(client);
954 return;
955 }
956 if(client->ib_size < hdr->length) {
957 if(did_read)
958 return; //maybe we would block, so defer to next loop
959 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
960 if(res < 0) {
961 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
962 client_close(client);
963 return;
964 } else if(res == 0) {
965 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
966 client_close(client);
967 return;
968 }
969 client->ib_size += res;
970 if(client->ib_size < hdr->length)
971 return;
972 }
973 handle_command(client, hdr);
974 client->ib_size = 0;
975}
976
977void client_process(int fd, short events)
978{
979 struct mux_client *client = NULL;
980 mutex_lock(&client_list_mutex);
981 FOREACH(struct mux_client *lc, &client_list) {
982 if(lc->fd == fd) {
983 client = lc;
984 break;
985 }
986 } ENDFOREACH
987 mutex_unlock(&client_list_mutex);
988
989 if(!client) {
990 usbmuxd_log(LL_INFO, "client_process: fd %d not found in client list", fd);
991 return;
992 }
993
994 if(client->state == CLIENT_CONNECTED) {
995 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
996 device_client_process(client->connect_device, client, events);
997 } else {
998 if(events & POLLIN) {
999 input_buffer_process(client);
1000 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
1001 output_buffer_process(client);
1002 }
1003 }
1004
1005}
1006
1007void client_device_add(struct device_info *dev)
1008{
1009 mutex_lock(&client_list_mutex);
1010 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
1011 device_set_visible(dev->id);
1012 FOREACH(struct mux_client *client, &client_list) {
1013 if(client->state == CLIENT_LISTEN)
1014 send_device_add(client, dev);
1015 } ENDFOREACH
1016 mutex_unlock(&client_list_mutex);
1017}
1018
1019void client_device_remove(int device_id)
1020{
1021 mutex_lock(&client_list_mutex);
1022 uint32_t id = device_id;
1023 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
1024 FOREACH(struct mux_client *client, &client_list) {
1025 if(client->state == CLIENT_LISTEN)
1026 send_device_remove(client, id);
1027 } ENDFOREACH
1028 mutex_unlock(&client_list_mutex);
1029}
1030
1031void client_device_paired(int device_id)
1032{
1033 mutex_lock(&client_list_mutex);
1034 uint32_t id = device_id;
1035 usbmuxd_log(LL_DEBUG, "client_device_paired: id %d", device_id);
1036 FOREACH(struct mux_client *client, &client_list) {
1037 if (client->state == CLIENT_LISTEN)
1038 send_device_paired(client, id);
1039 } ENDFOREACH
1040 mutex_unlock(&client_list_mutex);
1041}
1042
1043void client_init(void)
1044{
1045 usbmuxd_log(LL_DEBUG, "client_init");
1046 collection_init(&client_list);
1047 mutex_init(&client_list_mutex);
1048}
1049
1050void client_shutdown(void)
1051{
1052 usbmuxd_log(LL_DEBUG, "client_shutdown");
1053 FOREACH(struct mux_client *client, &client_list) {
1054 client_close(client);
1055 } ENDFOREACH
1056 mutex_destroy(&client_list_mutex);
1057 collection_free(&client_list);
1058}
diff --git a/src/client.h b/src/client.h
new file mode 100644
index 0000000..6cac4db
--- /dev/null
+++ b/src/client.h
@@ -0,0 +1,47 @@
1/*
2 * client.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifndef CLIENT_H
22#define CLIENT_H
23
24#include <stdint.h>
25#include "usbmuxd-proto.h"
26
27struct device_info;
28struct mux_client;
29
30int client_read(struct mux_client *client, void *buffer, uint32_t len);
31int client_write(struct mux_client *client, void *buffer, uint32_t len);
32int client_set_events(struct mux_client *client, short events);
33void client_close(struct mux_client *client);
34int client_notify_connect(struct mux_client *client, enum usbmuxd_result result);
35
36void client_device_add(struct device_info *dev);
37void client_device_remove(int device_id);
38void client_device_paired(int device_id);
39
40int client_accept(int fd);
41void client_get_fds(struct fdlist *list);
42void client_process(int fd, short events);
43
44void client_init(void);
45void client_shutdown(void);
46
47#endif
diff --git a/src/conf.c b/src/conf.c
new file mode 100644
index 0000000..2e6c97f
--- /dev/null
+++ b/src/conf.c
@@ -0,0 +1,535 @@
1/*
2 * conf.c
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdio.h>
25#include <stdint.h>
26#include <stdlib.h>
27#include <string.h>
28#include <time.h>
29#ifdef HAVE_SYS_TYPES_H
30#include <sys/types.h>
31#endif
32
33#include <dirent.h>
34#include <libgen.h>
35#include <sys/stat.h>
36#include <errno.h>
37
38#ifdef WIN32
39#include <shlobj.h>
40#endif
41
42#include <libimobiledevice-glue/utils.h>
43#include <plist/plist.h>
44
45#include "conf.h"
46#include "utils.h"
47#include "log.h"
48
49#ifdef WIN32
50#define DIR_SEP '\\'
51#define DIR_SEP_S "\\"
52#else
53#define DIR_SEP '/'
54#define DIR_SEP_S "/"
55#endif
56
57#define CONFIG_SYSTEM_BUID_KEY "SystemBUID"
58#define CONFIG_HOST_ID_KEY "HostID"
59
60#define CONFIG_EXT ".plist"
61
62#ifdef WIN32
63#define CONFIG_DIR "Apple"DIR_SEP_S"Lockdown"
64#else
65#define CONFIG_DIR "lockdown"
66#endif
67
68#define CONFIG_FILE "SystemConfiguration"CONFIG_EXT
69
70static char *__config_dir = NULL;
71
72#ifdef WIN32
73static char *config_utf16_to_utf8(wchar_t *unistr, long len, long *items_read, long *items_written)
74{
75 if (!unistr || (len <= 0)) return NULL;
76 char *outbuf = (char*)malloc(3*(len+1));
77 int p = 0;
78 int i = 0;
79
80 wchar_t wc;
81
82 while (i < len) {
83 wc = unistr[i++];
84 if (wc >= 0x800) {
85 outbuf[p++] = (char)(0xE0 + ((wc >> 12) & 0xF));
86 outbuf[p++] = (char)(0x80 + ((wc >> 6) & 0x3F));
87 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
88 } else if (wc >= 0x80) {
89 outbuf[p++] = (char)(0xC0 + ((wc >> 6) & 0x1F));
90 outbuf[p++] = (char)(0x80 + (wc & 0x3F));
91 } else {
92 outbuf[p++] = (char)(wc & 0x7F);
93 }
94 }
95 if (items_read) {
96 *items_read = i;
97 }
98 if (items_written) {
99 *items_written = p;
100 }
101 outbuf[p] = 0;
102
103 return outbuf;
104}
105#endif
106
107const char *config_get_config_dir()
108{
109 char *base_config_dir = NULL;
110
111 if (__config_dir)
112 return __config_dir;
113
114#ifdef WIN32
115 wchar_t path[MAX_PATH+1];
116 HRESULT hr;
117 LPITEMIDLIST pidl = NULL;
118 BOOL b = FALSE;
119
120 hr = SHGetSpecialFolderLocation (NULL, CSIDL_COMMON_APPDATA, &pidl);
121 if (hr == S_OK) {
122 b = SHGetPathFromIDListW (pidl, path);
123 if (b) {
124 base_config_dir = config_utf16_to_utf8 (path, wcslen(path), NULL, NULL);
125 CoTaskMemFree (pidl);
126 }
127 }
128#else
129#ifdef __APPLE__
130 base_config_dir = strdup("/var/db");
131#else
132 base_config_dir = strdup("/var/lib");
133#endif
134#endif
135 __config_dir = string_concat(base_config_dir, DIR_SEP_S, CONFIG_DIR, NULL);
136
137 if (__config_dir) {
138 int i = strlen(__config_dir)-1;
139 while ((i > 0) && (__config_dir[i] == DIR_SEP)) {
140 __config_dir[i--] = '\0';
141 }
142 }
143
144 free(base_config_dir);
145
146 usbmuxd_log(LL_DEBUG, "Initialized config_dir to %s", __config_dir);
147
148 return __config_dir;
149}
150
151static int __mkdir(const char *dir, int mode)
152{
153#ifdef WIN32
154 return mkdir(dir);
155#else
156 return mkdir(dir, mode);
157#endif
158}
159
160static int mkdir_with_parents(const char *dir, int mode)
161{
162 if (!dir) return -1;
163 if (__mkdir(dir, mode) == 0) {
164 return 0;
165 } else {
166 if (errno == EEXIST) return 0;
167 }
168 int res;
169 char *parent = strdup(dir);
170 char* parentdir = dirname(parent);
171 if (parentdir) {
172 res = mkdir_with_parents(parentdir, mode);
173 } else {
174 res = -1;
175 }
176 free(parent);
177 return res;
178}
179
180/**
181 * Creates a freedesktop compatible configuration directory.
182 */
183static void config_create_config_dir(void)
184{
185 const char *config_path = config_get_config_dir();
186 struct stat st;
187 if (stat(config_path, &st) != 0) {
188 mkdir_with_parents(config_path, 0755);
189 }
190}
191
192static int get_rand(int min, int max)
193{
194 int retval = (rand() % (max - min)) + min;
195 return retval;
196}
197
198static char *config_generate_uuid(int idx)
199{
200 char *uuid = (char *) malloc(sizeof(char) * 37);
201 const char *chars = "ABCDEF0123456789";
202 srand(time(NULL) - idx);
203 int i = 0;
204
205 for (i = 0; i < 36; i++) {
206 if (i == 8 || i == 13 || i == 18 || i == 23) {
207 uuid[i] = '-';
208 continue;
209 } else {
210 uuid[i] = chars[get_rand(0, 16)];
211 }
212 }
213 /* make it a real string */
214 uuid[36] = '\0';
215 return uuid;
216}
217
218/**
219 * Generates a valid BUID for this system (which is actually a UUID).
220 *
221 * @return A null terminated string containing a valid BUID.
222 */
223static char *config_generate_system_buid()
224{
225 return config_generate_uuid(1);
226}
227
228static int internal_set_value(const char *config_file, const char *key, plist_t value)
229{
230 if (!config_file)
231 return 0;
232
233 /* read file into plist */
234 plist_t config = NULL;
235
236 plist_read_from_file(config_file, &config, NULL);
237 if (!config) {
238 config = plist_new_dict();
239 plist_dict_set_item(config, key, value);
240 } else {
241 plist_t n = plist_dict_get_item(config, key);
242 if (n) {
243 plist_dict_remove_item(config, key);
244 }
245 plist_dict_set_item(config, key, value);
246 remove(config_file);
247 }
248
249 /* store in config file */
250 char *value_string = NULL;
251 if (plist_get_node_type(value) == PLIST_STRING) {
252 plist_get_string_val(value, &value_string);
253 usbmuxd_log(LL_DEBUG, "Setting key %s to %s in config file %s", key, value_string, config_file);
254 if (value_string)
255 free(value_string);
256 } else {
257 usbmuxd_log(LL_DEBUG, "Setting key %s in config file %s", key, config_file);
258 }
259
260 int res = (plist_write_to_file(config, config_file, PLIST_FORMAT_XML, 0) == PLIST_ERR_SUCCESS);
261
262 plist_free(config);
263
264 return res;
265}
266
267static int config_set_value(const char *key, plist_t value)
268{
269 const char *config_path = NULL;
270 char *config_file = NULL;
271
272 /* Make sure config directory exists */
273 config_create_config_dir();
274
275 config_path = config_get_config_dir();
276 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
277
278 int result = internal_set_value(config_file, key, value);
279 if (!result) {
280 usbmuxd_log(LL_ERROR, "ERROR: Failed to write to '%s'", config_file);
281 }
282
283 free(config_file);
284
285 return result;
286}
287
288static int internal_get_value(const char* config_file, const char *key, plist_t *value)
289{
290 *value = NULL;
291
292 /* now parse file to get the SystemBUID */
293 plist_t config = NULL;
294 if (plist_read_from_file(config_file, &config, NULL) == PLIST_ERR_SUCCESS) {
295 usbmuxd_log(LL_DEBUG, "Reading key %s from config file %s", key, config_file);
296 plist_t n = plist_dict_get_item(config, key);
297 if (n) {
298 *value = plist_copy(n);
299 n = NULL;
300 }
301 }
302 plist_free(config);
303
304 return 1;
305}
306
307static int config_get_value(const char *key, plist_t *value)
308{
309 const char *config_path = NULL;
310 char *config_file = NULL;
311
312 config_path = config_get_config_dir();
313 config_file = string_concat(config_path, DIR_SEP_S, CONFIG_FILE, NULL);
314
315 int result = internal_get_value(config_file, key, value);
316
317 free(config_file);
318
319 return result;
320}
321
322/**
323 * Store SystemBUID in config file.
324 *
325 * @param system_buid A null terminated string containing a valid SystemBUID.
326 */
327static int config_set_system_buid(const char *system_buid)
328{
329 return config_set_value(CONFIG_SYSTEM_BUID_KEY, plist_new_string(system_buid));
330}
331
332/**
333 * Determines whether a pairing record is present for the given device.
334 *
335 * @param udid The device UDID as given by the device.
336 *
337 * @return 1 if there's a pairing record for the given udid or 0 otherwise.
338 */
339int config_has_device_record(const char *udid)
340{
341 int res = 0;
342 if (!udid) return 0;
343
344 /* ensure config directory exists */
345 config_create_config_dir();
346
347 /* build file path */
348 const char *config_path = config_get_config_dir();
349 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
350
351 struct stat st;
352
353 if ((stat(device_record_file, &st) == 0) && S_ISREG(st.st_mode))
354 res = 1;
355
356 free(device_record_file);
357
358 return res;
359}
360
361/**
362 * Reads the BUID from a previously generated configuration file.
363 *
364 * @param system_buid pointer to a variable that will be set to point to a
365 * newly allocated string containing the BUID.
366 *
367 * @note It is the responsibility of the calling function to free the returned system_buid
368 */
369void config_get_system_buid(char **system_buid)
370{
371 plist_t value = NULL;
372
373 config_get_value(CONFIG_SYSTEM_BUID_KEY, &value);
374
375 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
376 plist_get_string_val(value, system_buid);
377 usbmuxd_log(LL_DEBUG, "Got %s %s", CONFIG_SYSTEM_BUID_KEY, *system_buid);
378 }
379
380 if (value)
381 plist_free(value);
382
383 if (!*system_buid) {
384 /* no config, generate system_buid */
385 usbmuxd_log(LL_DEBUG, "No previous %s found", CONFIG_SYSTEM_BUID_KEY);
386 *system_buid = config_generate_system_buid();
387 if (!config_set_system_buid(*system_buid)) {
388 usbmuxd_log(LL_WARNING, "WARNING: Failed to store SystemBUID, this might be a problem");
389 }
390 }
391
392 usbmuxd_log(LL_DEBUG, "Using %s as %s", *system_buid, CONFIG_SYSTEM_BUID_KEY);
393}
394
395/**
396 * Store a pairing record for the given device identifier.
397 *
398 * @param udid device identifier
399 * @param record_data buffer containing a pairing record
400 * @param record_size size of buffer passed in record_data
401 *
402 * @return 0 on success or a negative errno otherwise.
403 */
404int config_set_device_record(const char *udid, char* record_data, uint64_t record_size)
405{
406 int res = 0;
407
408 if (!udid || !record_data || record_size < 8)
409 return -EINVAL;
410
411 plist_t plist = NULL;
412 if (memcmp(record_data, "bplist00", 8) == 0) {
413 plist_from_bin(record_data, record_size, &plist);
414 } else {
415 plist_from_xml(record_data, record_size, &plist);
416 }
417
418 if (!plist || plist_get_node_type(plist) != PLIST_DICT) {
419 if (plist)
420 plist_free(plist);
421 return -EINVAL;
422 }
423
424 /* ensure config directory exists */
425 config_create_config_dir();
426
427 /* build file path */
428 const char *config_path = config_get_config_dir();
429 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
430
431 remove(device_record_file);
432
433 /* store file */
434 if (!plist_write_to_file(plist, device_record_file, PLIST_FORMAT_XML, 0)) {
435 usbmuxd_log(LL_DEBUG, "Could not open '%s' for writing: %s", device_record_file, strerror(errno));
436 res = -ENOENT;
437 }
438 free(device_record_file);
439 if (plist)
440 plist_free(plist);
441
442 return res;
443}
444
445/**
446 * Retrieve a pairing record for the given device identifier
447 *
448 * @param udid device identifier
449 * @param record_data pointer to a variable that will be set to point to a
450 * newly allocated buffer holding the pairing record
451 * @param record_size pointer to a variable that will be set to the size
452 * of the buffer given in record_data.
453 *
454 * @return 0 on success or a negative errno otherwise.
455 */
456int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size)
457{
458 int res = 0;
459
460 /* ensure config directory exists */
461 config_create_config_dir();
462
463 /* build file path */
464 const char *config_path = config_get_config_dir();
465 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
466
467 /* read file */
468 buffer_read_from_filename(device_record_file, record_data, record_size);
469 if (!*record_data) {
470 usbmuxd_log(LL_ERROR, "ERROR: Failed to read '%s': %s", device_record_file, strerror(errno));
471 res = -ENOENT;
472 }
473 free(device_record_file);
474
475 return res;
476}
477
478/**
479 * Remove the pairing record stored for a device from this host.
480 *
481 * @param udid The udid of the device
482 *
483 * @return 0 on success or a negative errno otherwise.
484 */
485int config_remove_device_record(const char *udid)
486{
487 int res = 0;
488
489 /* build file path */
490 const char *config_path = config_get_config_dir();
491 char *device_record_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
492
493 /* remove file */
494 if (remove(device_record_file) != 0) {
495 res = -errno;
496 usbmuxd_log(LL_DEBUG, "Could not remove %s: %s", device_record_file, strerror(errno));
497 }
498
499 free(device_record_file);
500
501 return res;
502}
503
504static int config_device_record_get_value(const char *udid, const char *key, plist_t *value)
505{
506 const char *config_path = NULL;
507 char *config_file = NULL;
508
509 config_path = config_get_config_dir();
510 config_file = string_concat(config_path, DIR_SEP_S, udid, CONFIG_EXT, NULL);
511
512 int result = internal_get_value(config_file, key, value);
513
514 free(config_file);
515
516 return result;
517}
518
519void config_device_record_get_host_id(const char *udid, char **host_id)
520{
521 plist_t value = NULL;
522
523 config_device_record_get_value(udid, CONFIG_HOST_ID_KEY, &value);
524
525 if (value && (plist_get_node_type(value) == PLIST_STRING)) {
526 plist_get_string_val(value, host_id);
527 }
528
529 if (value)
530 plist_free(value);
531
532 if (!*host_id) {
533 usbmuxd_log(LL_ERROR, "ERROR: Could not get HostID from pairing record for udid %s", udid);
534 }
535}
diff --git a/src/conf.h b/src/conf.h
new file mode 100644
index 0000000..bbfa965
--- /dev/null
+++ b/src/conf.h
@@ -0,0 +1,40 @@
1/*
2 * conf.h
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef CONF_H
21#define CONF_H
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <plist/plist.h>
28
29const char *config_get_config_dir();
30
31void config_get_system_buid(char **system_buid);
32
33int config_has_device_record(const char *udid);
34int config_get_device_record(const char *udid, char **record_data, uint64_t *record_size);
35int config_set_device_record(const char *udid, char* record_data, uint64_t record_size);
36int config_remove_device_record(const char *udid);
37
38void config_device_record_get_host_id(const char *udid, char **host_id);
39
40#endif
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..ce73718
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,1037 @@
1/*
2 * device.c
3 *
4 * Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5 * Copyright (C) 2014 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@xamarin.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#define _DEFAULT_SOURCE
22#define _BSD_SOURCE
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <sys/time.h>
29#include <netinet/in.h>
30#include <netinet/tcp.h>
31#include <stdlib.h>
32#include <string.h>
33#include <stdint.h>
34#include <inttypes.h>
35#include <unistd.h>
36
37#include <libimobiledevice-glue/collection.h>
38#include <libimobiledevice-glue/thread.h>
39
40#include "device.h"
41#include "client.h"
42#include "preflight.h"
43#include "usb.h"
44#include "log.h"
45
46int next_device_id;
47
48#define DEV_MRU 65536
49
50#define CONN_INBUF_SIZE 262144
51#define CONN_OUTBUF_SIZE 65536
52
53#define ACK_TIMEOUT 30
54
55enum mux_protocol {
56 MUX_PROTO_VERSION = 0,
57 MUX_PROTO_CONTROL = 1,
58 MUX_PROTO_SETUP = 2,
59 MUX_PROTO_TCP = IPPROTO_TCP,
60};
61
62enum mux_dev_state {
63 MUXDEV_INIT, // sent version packet
64 MUXDEV_ACTIVE, // received version packet, active
65 MUXDEV_DEAD // dead
66};
67
68enum mux_conn_state {
69 CONN_CONNECTING, // SYN
70 CONN_CONNECTED, // SYN/SYNACK/ACK -> active
71 CONN_REFUSED, // RST received during SYN
72 CONN_DYING, // RST received
73 CONN_DEAD // being freed; used to prevent infinite recursion between client<->device freeing
74};
75
76struct mux_header
77{
78 uint32_t protocol;
79 uint32_t length;
80 uint32_t magic;
81 uint16_t tx_seq;
82 uint16_t rx_seq;
83};
84
85struct version_header
86{
87 uint32_t major;
88 uint32_t minor;
89 uint32_t padding;
90};
91
92struct mux_device;
93
94#define CONN_ACK_PENDING 1
95
96struct mux_connection
97{
98 struct mux_device *dev;
99 struct mux_client *client;
100 enum mux_conn_state state;
101 uint16_t sport, dport;
102 uint32_t tx_seq, tx_ack, tx_acked, tx_win;
103 uint32_t rx_seq, rx_recvd, rx_ack, rx_win;
104 uint32_t max_payload;
105 uint32_t sendable;
106 int flags;
107 unsigned char *ib_buf;
108 uint32_t ib_size;
109 uint32_t ib_capacity;
110 unsigned char *ob_buf;
111 uint32_t ob_capacity;
112 short events;
113 uint64_t last_ack_time;
114};
115
116struct mux_device
117{
118 struct usb_device *usbdev;
119 int id;
120 enum mux_dev_state state;
121 int visible;
122 struct collection connections;
123 uint16_t next_sport;
124 unsigned char *pktbuf;
125 uint32_t pktlen;
126 void *preflight_cb_data;
127 int version;
128 uint16_t rx_seq;
129 uint16_t tx_seq;
130};
131
132static struct collection device_list;
133mutex_t device_list_mutex;
134
135static struct mux_device* get_mux_device_for_id(int device_id)
136{
137 struct mux_device *dev = NULL;
138 mutex_lock(&device_list_mutex);
139 FOREACH(struct mux_device *cdev, &device_list) {
140 if(cdev->id == device_id) {
141 dev = cdev;
142 break;
143 }
144 } ENDFOREACH
145 mutex_unlock(&device_list_mutex);
146
147 return dev;
148}
149
150static struct mux_connection* get_mux_connection(int device_id, struct mux_client *client)
151{
152 struct mux_connection *conn = NULL;
153 FOREACH(struct mux_device *dev, &device_list) {
154 if(dev->id == device_id) {
155 FOREACH(struct mux_connection *lconn, &dev->connections) {
156 if(lconn->client == client) {
157 conn = lconn;
158 break;
159 }
160 } ENDFOREACH
161 break;
162 }
163 } ENDFOREACH
164
165 return conn;
166}
167
168static int get_next_device_id(void)
169{
170 while(1) {
171 int ok = 1;
172 mutex_lock(&device_list_mutex);
173 FOREACH(struct mux_device *dev, &device_list) {
174 if(dev->id == next_device_id) {
175 next_device_id++;
176 ok = 0;
177 break;
178 }
179 } ENDFOREACH
180 mutex_unlock(&device_list_mutex);
181 if(ok)
182 return next_device_id++;
183 }
184}
185
186static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, const void *data, int length)
187{
188 unsigned char *buffer;
189 int hdrlen;
190 int res;
191
192 switch(proto) {
193 case MUX_PROTO_VERSION:
194 hdrlen = sizeof(struct version_header);
195 break;
196 case MUX_PROTO_SETUP:
197 hdrlen = 0;
198 break;
199 case MUX_PROTO_TCP:
200 hdrlen = sizeof(struct tcphdr);
201 break;
202 default:
203 usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length);
204 return -1;
205 }
206 usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length);
207
208 int mux_header_size = ((dev->version < 2) ? 8 : sizeof(struct mux_header));
209
210 int total = mux_header_size + hdrlen + length;
211
212 if(total > USB_MTU) {
213 usbmuxd_log(LL_ERROR, "Tried to send packet larger than USB MTU (hdr %d data %d total %d) to device %d", hdrlen, length, total, dev->id);
214 return -1;
215 }
216
217 buffer = malloc(total);
218 struct mux_header *mhdr = (struct mux_header *)buffer;
219 mhdr->protocol = htonl(proto);
220 mhdr->length = htonl(total);
221 if (dev->version >= 2) {
222 mhdr->magic = htonl(0xfeedface);
223 if (proto == MUX_PROTO_SETUP) {
224 dev->tx_seq = 0;
225 dev->rx_seq = 0xFFFF;
226 }
227 mhdr->tx_seq = htons(dev->tx_seq);
228 mhdr->rx_seq = htons(dev->rx_seq);
229 dev->tx_seq++;
230 }
231 memcpy(buffer + mux_header_size, header, hdrlen);
232 if(data && length)
233 memcpy(buffer + mux_header_size + hdrlen, data, length);
234
235 if((res = usb_send(dev->usbdev, buffer, total)) < 0) {
236 usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res);
237 free(buffer);
238 return res;
239 }
240 return total;
241}
242
243static uint16_t find_sport(struct mux_device *dev)
244{
245 if(collection_count(&dev->connections) >= 65535)
246 return 0; //insanity
247
248 while(1) {
249 int ok = 1;
250 FOREACH(struct mux_connection *conn, &dev->connections) {
251 if(dev->next_sport == conn->sport) {
252 dev->next_sport++;
253 ok = 0;
254 break;
255 }
256 } ENDFOREACH
257 if(ok)
258 return dev->next_sport++;
259 }
260}
261
262static int send_anon_rst(struct mux_device *dev, uint16_t sport, uint16_t dport, uint32_t ack)
263{
264 struct tcphdr th;
265 memset(&th, 0, sizeof(th));
266 th.th_sport = htons(sport);
267 th.th_dport = htons(dport);
268 th.th_ack = htonl(ack);
269 th.th_flags = TH_RST;
270 th.th_off = sizeof(th) / 4;
271
272 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d flags=0x%x", dev->id, sport, dport, th.th_flags);
273
274 int res = send_packet(dev, MUX_PROTO_TCP, &th, NULL, 0);
275 return res;
276}
277
278static int send_tcp(struct mux_connection *conn, uint8_t flags, const unsigned char *data, int length)
279{
280 struct tcphdr th;
281 memset(&th, 0, sizeof(th));
282 th.th_sport = htons(conn->sport);
283 th.th_dport = htons(conn->dport);
284 th.th_seq = htonl(conn->tx_seq);
285 th.th_ack = htonl(conn->tx_ack);
286 th.th_flags = flags;
287 th.th_off = sizeof(th) / 4;
288 th.th_win = htons(conn->tx_win >> 8);
289
290 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
291 conn->dev->id, conn->sport, conn->dport, conn->tx_seq, conn->tx_ack, flags, conn->tx_win, conn->tx_win >> 8, length);
292
293 int res = send_packet(conn->dev, MUX_PROTO_TCP, &th, data, length);
294 if(res >= 0) {
295 conn->tx_acked = conn->tx_ack;
296 conn->last_ack_time = mstime64();
297 conn->flags &= ~CONN_ACK_PENDING;
298 }
299 return res;
300}
301
302static void connection_teardown(struct mux_connection *conn)
303{
304 int res;
305 int size;
306 if(conn->state == CONN_DEAD)
307 return;
308 usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport);
309 if(conn->dev->state != MUXDEV_DEAD && conn->state != CONN_DYING && conn->state != CONN_REFUSED) {
310 res = send_tcp(conn, TH_RST, NULL, 0);
311 if(res < 0)
312 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, conn->sport, conn->dport);
313 }
314 if(conn->client) {
315 if(conn->state == CONN_REFUSED || conn->state == CONN_CONNECTING) {
316 client_notify_connect(conn->client, RESULT_CONNREFUSED);
317 } else {
318 conn->state = CONN_DEAD;
319 if((conn->events & POLLOUT) && conn->ib_size > 0){
320 usbmuxd_log(LL_DEBUG, "%s: flushing buffer to client (%u bytes)", __func__, conn->ib_size);
321 uint64_t tm_last = mstime64();
322 while(1){
323 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
324 if(size < 0) {
325 usbmuxd_log(LL_ERROR, "%s: aborting buffer flush to client after error.", __func__);
326 break;
327 } else if (size == 0) {
328 uint64_t tm_now = mstime64();
329 if (tm_now - tm_last > 1000) {
330 usbmuxd_log(LL_ERROR, "%s: aborting buffer flush to client after unsuccessfully attempting for %dms.", __func__, (int)(tm_now - tm_last));
331 break;
332 }
333 usleep(10000);
334 continue;
335 }
336 if(size == (int)conn->ib_size) {
337 conn->ib_size = 0;
338 break;
339 } else {
340 conn->ib_size -= size;
341 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
342 }
343 tm_last = mstime64();
344 }
345 }
346 client_close(conn->client);
347 }
348 }
349 free(conn->ib_buf);
350 free(conn->ob_buf);
351 collection_remove(&conn->dev->connections, conn);
352 free(conn);
353}
354
355int device_start_connect(int device_id, uint16_t dport, struct mux_client *client)
356{
357 struct mux_device *dev = get_mux_device_for_id(device_id);
358 if(!dev) {
359 usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id);
360 return -RESULT_BADDEV;
361 }
362
363 uint16_t sport = find_sport(dev);
364 if(!sport) {
365 usbmuxd_log(LL_WARNING, "Unable to allocate port for device %d", device_id);
366 return -RESULT_BADDEV;
367 }
368
369 struct mux_connection *conn;
370 conn = malloc(sizeof(struct mux_connection));
371 memset(conn, 0, sizeof(struct mux_connection));
372
373 conn->dev = dev;
374 conn->client = client;
375 conn->state = CONN_CONNECTING;
376 conn->sport = sport;
377 conn->dport = dport;
378 conn->tx_seq = 0;
379 conn->tx_ack = 0;
380 conn->tx_acked = 0;
381 conn->tx_win = 131072;
382 conn->rx_recvd = 0;
383 conn->flags = 0;
384 conn->max_payload = USB_MTU - sizeof(struct mux_header) - sizeof(struct tcphdr);
385
386 conn->ob_buf = malloc(CONN_OUTBUF_SIZE);
387 conn->ob_capacity = CONN_OUTBUF_SIZE;
388 conn->ib_buf = malloc(CONN_INBUF_SIZE);
389 conn->ib_capacity = CONN_INBUF_SIZE;
390 conn->ib_size = 0;
391
392 int res;
393
394 res = send_tcp(conn, TH_SYN, NULL, 0);
395 if(res < 0) {
396 usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport);
397 free(conn->ib_buf);
398 free(conn->ob_buf);
399 free(conn);
400 return -RESULT_CONNREFUSED; //bleh
401 }
402 collection_add(&dev->connections, conn);
403 return 0;
404}
405
406/**
407 * Examine the state of a connection's buffers and
408 * update all connection flags and masks accordingly.
409 * Does not do I/O.
410 *
411 * @param conn The connection to update.
412 */
413static void update_connection(struct mux_connection *conn)
414{
415 uint32_t sent = conn->tx_seq - conn->rx_ack;
416
417 if(conn->rx_win > sent)
418 conn->sendable = conn->rx_win - sent;
419 else
420 conn->sendable = 0;
421
422 if(conn->sendable > conn->ob_capacity)
423 conn->sendable = conn->ob_capacity;
424 if(conn->sendable > conn->max_payload)
425 conn->sendable = conn->max_payload;
426
427 if(conn->sendable > 0)
428 conn->events |= POLLIN;
429 else
430 conn->events &= ~POLLIN;
431
432 if(conn->ib_size)
433 conn->events |= POLLOUT;
434 else
435 conn->events &= ~POLLOUT;
436
437 if(conn->tx_acked != conn->tx_ack)
438 conn->flags |= CONN_ACK_PENDING;
439 else
440 conn->flags &= ~CONN_ACK_PENDING;
441
442 usbmuxd_log(LL_SPEW, "update_connection: sendable %d, events %d, flags %d", conn->sendable, conn->events, conn->flags);
443 client_set_events(conn->client, conn->events);
444}
445
446static int send_tcp_ack(struct mux_connection *conn)
447{
448 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
449 usbmuxd_log(LL_ERROR, "Error sending TCP ACK (%d->%d)", conn->sport, conn->dport);
450 connection_teardown(conn);
451 return -1;
452 }
453
454 update_connection(conn);
455
456 return 0;
457}
458
459/**
460 * Flush input and output buffers for a client connection.
461 *
462 * @param device_id Numeric id for the device.
463 * @param client The client to flush buffers for.
464 * @param events event mask for the client. POLLOUT means that
465 * the client is ready to receive data, POLLIN that it has
466 * data to be read (and send along to the device).
467 */
468void device_client_process(int device_id, struct mux_client *client, short events)
469{
470 mutex_lock(&device_list_mutex);
471 struct mux_connection *conn = get_mux_connection(device_id, client);
472 mutex_unlock(&device_list_mutex);
473 if(!conn) {
474 usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client);
475 return;
476 }
477 usbmuxd_log(LL_SPEW, "device_client_process (%d)", events);
478
479 int res;
480 int size;
481 if((events & POLLOUT) && conn->ib_size > 0) {
482 // Client is ready to receive data, send what we have
483 // in the client's connection buffer (if there is any)
484 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
485 if(size <= 0) {
486 usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size);
487 connection_teardown(conn);
488 return;
489 }
490 conn->tx_ack += size;
491 if(size == (int)conn->ib_size) {
492 conn->ib_size = 0;
493 } else {
494 conn->ib_size -= size;
495 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
496 }
497 }
498 if((events & POLLIN) && conn->sendable > 0) {
499 // There is inbound trafic on the client socket,
500 // convert it to tcp and send to the device
501 // (if the device's input buffer is not full)
502 size = client_read(conn->client, conn->ob_buf, conn->sendable);
503 if(size <= 0) {
504 if (size < 0) {
505 usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size);
506 }
507 connection_teardown(conn);
508 return;
509 }
510 res = send_tcp(conn, TH_ACK, conn->ob_buf, size);
511 if(res < 0) {
512 connection_teardown(conn);
513 return;
514 }
515 conn->tx_seq += size;
516 }
517
518 update_connection(conn);
519}
520
521/**
522 * Copy a payload to a connection's in-buffer and
523 * set the POLLOUT event mask on the connection so
524 * the next main_loop iteration will dispatch the
525 * buffer if the connection socket is writable.
526 *
527 * Connection buffers are flushed in the
528 * device_client_process() function.
529 *
530 * @param conn The connection to add incoming data to.
531 * @param payload Payload to prepare for writing.
532 * The payload will be copied immediately so you are
533 * free to alter or free the payload buffer when this
534 * function returns.
535 * @param payload_length number of bytes to copy from from
536 * the payload.
537 */
538static void connection_device_input(struct mux_connection *conn, unsigned char *payload, uint32_t payload_length)
539{
540 if((conn->ib_size + payload_length) > conn->ib_capacity) {
541 usbmuxd_log(LL_ERROR, "Input buffer overflow on device %d connection %d->%d (space=%d, payload=%d)", conn->dev->id, conn->sport, conn->dport, conn->ib_capacity-conn->ib_size, payload_length);
542 connection_teardown(conn);
543 return;
544 }
545 memcpy(conn->ib_buf + conn->ib_size, payload, payload_length);
546 conn->ib_size += payload_length;
547 conn->rx_recvd += payload_length;
548 update_connection(conn);
549}
550
551void device_abort_connect(int device_id, struct mux_client *client)
552{
553 struct mux_connection *conn = get_mux_connection(device_id, client);
554 if (conn) {
555 conn->client = NULL;
556 connection_teardown(conn);
557 } else {
558 usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
559 }
560}
561
562static void device_version_input(struct mux_device *dev, struct version_header *vh)
563{
564 if(dev->state != MUXDEV_INIT) {
565 usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id);
566 return;
567 }
568 vh->major = ntohl(vh->major);
569 vh->minor = ntohl(vh->minor);
570 if(vh->major != 2 && vh->major != 1) {
571 usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d", dev->id, vh->major, vh->minor);
572 mutex_lock(&device_list_mutex);
573 collection_remove(&device_list, dev);
574 mutex_unlock(&device_list_mutex);
575 free(dev);
576 return;
577 }
578 dev->version = vh->major;
579
580 if (dev->version >= 2) {
581 send_packet(dev, MUX_PROTO_SETUP, NULL, "\x07", 1);
582 }
583
584 usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", dev->version, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev));
585 dev->state = MUXDEV_ACTIVE;
586 collection_init(&dev->connections);
587 struct device_info info;
588 info.id = dev->id;
589 info.location = usb_get_location(dev->usbdev);
590 info.serial = usb_get_serial(dev->usbdev);
591 info.pid = usb_get_pid(dev->usbdev);
592 info.speed = usb_get_speed(dev->usbdev);
593 preflight_worker_device_add(&info);
594}
595
596static void device_control_input(struct mux_device *dev, unsigned char *payload, uint32_t payload_length)
597{
598 if (payload_length > 0) {
599 switch (payload[0]) {
600 case 3:
601 if (payload_length > 1) {
602 usbmuxd_log(LL_ERROR, "Device %d: ERROR: %.*s", dev->id, payload_length-1, payload+1);
603 } else {
604 usbmuxd_log(LL_ERROR, "%s: Device %d: Got device error payload with empty message", __func__, dev->id);
605 }
606 break;
607 case 5:
608 if (payload_length > 1) {
609 usbmuxd_log(LL_WARNING, "Device %d: WARNING: %.*s", dev->id, payload_length-1, payload+1);
610 } else {
611 usbmuxd_log(LL_WARNING, "%s: Device %d: Got payload type %d with empty message", __func__, dev->id, payload[0]);
612 }
613 break;
614 case 7:
615 if (payload_length > 1) {
616 usbmuxd_log(LL_INFO, "Device %d: %.*s", dev->id, payload_length-1, payload+1);
617 } else {
618 usbmuxd_log(LL_WARNING, "%s: Device %d: Got payload type %d with empty message", __func__, dev->id, payload[0]);
619 }
620 break;
621 default:
622 usbmuxd_log(LL_WARNING, "%s: Device %d: Got unhandled payload type %d: %.*s", __func__, dev->id, payload[0], payload_length-1, payload+1);
623 break;
624 }
625 } else {
626 usbmuxd_log(LL_WARNING, "%s: Got a type 1 packet without payload for device %d", __func__, dev->id);
627 }
628}
629
630/**
631 * Handle an incoming TCP packet from the device.
632 *
633 * @param dev The device handle TCP input on.
634 * @param th Pointer to the TCP header struct.
635 * @param payload Payload data.
636 * @param payload_length Number of bytes in payload.
637 */
638static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, uint32_t payload_length)
639{
640 uint16_t sport = ntohs(th->th_dport);
641 uint16_t dport = ntohs(th->th_sport);
642 struct mux_connection *conn = NULL;
643
644 usbmuxd_log(LL_DEBUG, "[IN] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
645 dev->id, dport, sport, ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length);
646
647 if(dev->state != MUXDEV_ACTIVE) {
648 usbmuxd_log(LL_ERROR, "Received TCP packet from device %d but the device isn't active yet, discarding", dev->id);
649 return;
650 }
651
652 // Find the connection on this device that has the right sport and dport
653 FOREACH(struct mux_connection *lconn, &dev->connections) {
654 if(lconn->sport == sport && lconn->dport == dport) {
655 conn = lconn;
656 break;
657 }
658 } ENDFOREACH
659
660 if(!conn) {
661 if(!(th->th_flags & TH_RST)) {
662 usbmuxd_log(LL_INFO, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
663 if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0)
664 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", dev->id, sport, dport);
665 }
666 return;
667 }
668
669 conn->rx_seq = ntohl(th->th_seq);
670 conn->rx_ack = ntohl(th->th_ack);
671 conn->rx_win = ntohs(th->th_win) << 8;
672
673 if(th->th_flags & TH_RST) {
674 char *buf = malloc(payload_length+1);
675 memcpy(buf, payload, payload_length);
676 if(payload_length && (buf[payload_length-1] == '\n'))
677 buf[payload_length-1] = 0;
678 buf[payload_length] = 0;
679 usbmuxd_log(LL_DEBUG, "RST reason: %s", buf);
680 free(buf);
681 }
682
683 if(conn->state == CONN_CONNECTING) {
684 if(th->th_flags != (TH_SYN|TH_ACK)) {
685 if(th->th_flags & TH_RST)
686 conn->state = CONN_REFUSED;
687 usbmuxd_log(LL_INFO, "Connection refused by device %d (%d->%d)", dev->id, sport, dport);
688 connection_teardown(conn); //this also sends the notification to the client
689 } else {
690 conn->tx_seq++;
691 conn->tx_ack++;
692 conn->rx_recvd = conn->rx_seq;
693 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
694 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, sport, dport);
695 connection_teardown(conn);
696 return;
697 }
698 conn->state = CONN_CONNECTED;
699 usbmuxd_log(LL_INFO, "Client connected to device %d (%d->%d)", dev->id, sport, dport);
700 if(client_notify_connect(conn->client, RESULT_OK) < 0) {
701 conn->client = NULL;
702 connection_teardown(conn);
703 }
704 update_connection(conn);
705 }
706 } else if(conn->state == CONN_CONNECTED) {
707 if(th->th_flags != TH_ACK) {
708 usbmuxd_log(LL_INFO, "Connection reset by device %d (%d->%d)", dev->id, sport, dport);
709 if(th->th_flags & TH_RST)
710 conn->state = CONN_DYING;
711 connection_teardown(conn);
712 } else {
713 connection_device_input(conn, payload, payload_length);
714
715 // Device likes it best when we are prompty ACKing data
716 send_tcp_ack(conn);
717 }
718 }
719}
720
721/**
722 * Take input data from the device that has been read into a buffer
723 * and dispatch it to the right protocol backend (eg. TCP).
724 *
725 * @param usbdev
726 * @param buffer
727 * @param length
728 */
729void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_t length)
730{
731 struct mux_device *dev = NULL;
732 mutex_lock(&device_list_mutex);
733 FOREACH(struct mux_device *tdev, &device_list) {
734 if(tdev->usbdev == usbdev) {
735 dev = tdev;
736 break;
737 }
738 } ENDFOREACH
739 mutex_unlock(&device_list_mutex);
740 if(!dev) {
741 usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
742 return;
743 }
744
745 if(!length)
746 return;
747
748 // sanity check (should never happen with current USB implementation)
749 if((length > USB_MRU) || (length > DEV_MRU)) {
750 usbmuxd_log(LL_ERROR, "Too much data received from USB (%d), file a bug", length);
751 return;
752 }
753
754 usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length);
755
756 // handle broken up transfers
757 if(dev->pktlen) {
758 if((length + dev->pktlen) > DEV_MRU) {
759 usbmuxd_log(LL_ERROR, "Incoming split packet is too large (%d so far), dropping!", length + dev->pktlen);
760 dev->pktlen = 0;
761 return;
762 }
763 memcpy(dev->pktbuf + dev->pktlen, buffer, length);
764 struct mux_header *mhdr = (struct mux_header *)dev->pktbuf;
765 if((length < USB_MRU) || (ntohl(mhdr->length) == (length + dev->pktlen))) {
766 buffer = dev->pktbuf;
767 length += dev->pktlen;
768 dev->pktlen = 0;
769 usbmuxd_log(LL_SPEW, "Gathered mux data from buffer (total size: %d)", length);
770 } else {
771 dev->pktlen += length;
772 usbmuxd_log(LL_SPEW, "Appended mux data to buffer (total size: %d)", dev->pktlen);
773 return;
774 }
775 } else {
776 struct mux_header *mhdr = (struct mux_header *)buffer;
777 if((length == USB_MRU) && (length < ntohl(mhdr->length))) {
778 memcpy(dev->pktbuf, buffer, length);
779 dev->pktlen = length;
780 usbmuxd_log(LL_SPEW, "Copied mux data to buffer (size: %d)", dev->pktlen);
781 return;
782 }
783 }
784
785 struct mux_header *mhdr = (struct mux_header *)buffer;
786 int mux_header_size = ((dev->version < 2) ? 8 : sizeof(struct mux_header));
787 if(ntohl(mhdr->length) != length) {
788 usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length);
789 return;
790 }
791
792 struct tcphdr *th;
793 unsigned char *payload;
794 uint32_t payload_length;
795
796 if (dev->version >= 2) {
797 dev->rx_seq = ntohs(mhdr->rx_seq);
798 }
799
800 switch(ntohl(mhdr->protocol)) {
801 case MUX_PROTO_VERSION:
802 if(length < (mux_header_size + sizeof(struct version_header))) {
803 usbmuxd_log(LL_ERROR, "Incoming version packet is too small (%d)", length);
804 return;
805 }
806 device_version_input(dev, (struct version_header *)((char*)mhdr+mux_header_size));
807 break;
808 case MUX_PROTO_CONTROL:
809 payload = (unsigned char *)(mhdr+1);
810 payload_length = length - mux_header_size;
811 device_control_input(dev, payload, payload_length);
812 break;
813 case MUX_PROTO_TCP:
814 if(length < (mux_header_size + sizeof(struct tcphdr))) {
815 usbmuxd_log(LL_ERROR, "Incoming TCP packet is too small (%d)", length);
816 return;
817 }
818 th = (struct tcphdr *)((char*)mhdr+mux_header_size);
819 payload = (unsigned char *)(th+1);
820 payload_length = length - sizeof(struct tcphdr) - mux_header_size;
821 device_tcp_input(dev, th, payload, payload_length);
822 break;
823 default:
824 usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol));
825 break;
826 }
827
828}
829
830int device_add(struct usb_device *usbdev)
831{
832 int res;
833 int id = get_next_device_id();
834 struct mux_device *dev;
835 usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(usbdev), id);
836 dev = malloc(sizeof(struct mux_device));
837 dev->id = id;
838 dev->usbdev = usbdev;
839 dev->state = MUXDEV_INIT;
840 dev->visible = 0;
841 dev->next_sport = 1;
842 dev->pktbuf = malloc(DEV_MRU);
843 dev->pktlen = 0;
844 dev->preflight_cb_data = NULL;
845 dev->version = 0;
846 struct version_header vh;
847 vh.major = htonl(2);
848 vh.minor = htonl(0);
849 vh.padding = 0;
850 if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) {
851 usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d", id);
852 free(dev->pktbuf);
853 free(dev);
854 return res;
855 }
856 mutex_lock(&device_list_mutex);
857 collection_add(&device_list, dev);
858 mutex_unlock(&device_list_mutex);
859 return 0;
860}
861
862void device_remove(struct usb_device *usbdev)
863{
864 mutex_lock(&device_list_mutex);
865 FOREACH(struct mux_device *dev, &device_list) {
866 if(dev->usbdev == usbdev) {
867 usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev));
868 if(dev->state == MUXDEV_ACTIVE) {
869 dev->state = MUXDEV_DEAD;
870 FOREACH(struct mux_connection *conn, &dev->connections) {
871 connection_teardown(conn);
872 } ENDFOREACH
873 client_device_remove(dev->id);
874 collection_free(&dev->connections);
875 }
876 if (dev->preflight_cb_data) {
877 preflight_device_remove_cb(dev->preflight_cb_data);
878 }
879 collection_remove(&device_list, dev);
880 mutex_unlock(&device_list_mutex);
881 free(dev->pktbuf);
882 free(dev);
883 return;
884 }
885 } ENDFOREACH
886 mutex_unlock(&device_list_mutex);
887
888 usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
889}
890
891void device_set_visible(int device_id)
892{
893 mutex_lock(&device_list_mutex);
894 FOREACH(struct mux_device *dev, &device_list) {
895 if(dev->id == device_id) {
896 dev->visible = 1;
897 break;
898 }
899 } ENDFOREACH
900 mutex_unlock(&device_list_mutex);
901}
902
903void device_set_preflight_cb_data(int device_id, void* data)
904{
905 mutex_lock(&device_list_mutex);
906 FOREACH(struct mux_device *dev, &device_list) {
907 if(dev->id == device_id) {
908 dev->preflight_cb_data = data;
909 break;
910 }
911 } ENDFOREACH
912 mutex_unlock(&device_list_mutex);
913}
914
915int device_get_count(int include_hidden)
916{
917 int count = 0;
918 struct collection dev_list = {NULL, 0};
919 mutex_lock(&device_list_mutex);
920 collection_copy(&dev_list, &device_list);
921 mutex_unlock(&device_list_mutex);
922
923 FOREACH(struct mux_device *dev, &dev_list) {
924 if((dev->state == MUXDEV_ACTIVE) && (include_hidden || dev->visible))
925 count++;
926 } ENDFOREACH
927
928 collection_free(&dev_list);
929 return count;
930}
931
932int device_get_list(int include_hidden, struct device_info **devices)
933{
934 int count = 0;
935 struct collection dev_list = {NULL, 0};
936 mutex_lock(&device_list_mutex);
937 collection_copy(&dev_list, &device_list);
938 mutex_unlock(&device_list_mutex);
939
940 *devices = malloc(sizeof(struct device_info) * dev_list.capacity);
941 struct device_info *p = *devices;
942
943 FOREACH(struct mux_device *dev, &dev_list) {
944 if((dev->state == MUXDEV_ACTIVE) && (include_hidden || dev->visible)) {
945 p->id = dev->id;
946 p->serial = usb_get_serial(dev->usbdev);
947 p->location = usb_get_location(dev->usbdev);
948 p->pid = usb_get_pid(dev->usbdev);
949 p->speed = usb_get_speed(dev->usbdev);
950 count++;
951 p++;
952 }
953 } ENDFOREACH
954
955 collection_free(&dev_list);
956
957 return count;
958}
959
960int device_get_timeout(void)
961{
962 uint64_t oldest = (uint64_t)-1LL;
963 mutex_lock(&device_list_mutex);
964 FOREACH(struct mux_device *dev, &device_list) {
965 if(dev->state == MUXDEV_ACTIVE) {
966 FOREACH(struct mux_connection *conn, &dev->connections) {
967 if((conn->state == CONN_CONNECTED) && (conn->flags & CONN_ACK_PENDING) && conn->last_ack_time < oldest)
968 oldest = conn->last_ack_time;
969 } ENDFOREACH
970 }
971 } ENDFOREACH
972 mutex_unlock(&device_list_mutex);
973 uint64_t ct = mstime64();
974 if((int64_t)oldest == -1LL)
975 return 100000; //meh
976 if((ct - oldest) > ACK_TIMEOUT)
977 return 0;
978 return ACK_TIMEOUT - (ct - oldest);
979}
980
981void device_check_timeouts(void)
982{
983 uint64_t ct = mstime64();
984 mutex_lock(&device_list_mutex);
985 FOREACH(struct mux_device *dev, &device_list) {
986 if(dev->state == MUXDEV_ACTIVE) {
987 FOREACH(struct mux_connection *conn, &dev->connections) {
988 if((conn->state == CONN_CONNECTED) &&
989 (conn->flags & CONN_ACK_PENDING) &&
990 (ct - conn->last_ack_time) > ACK_TIMEOUT) {
991 usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct);
992 send_tcp_ack(conn);
993 }
994 } ENDFOREACH
995 }
996 } ENDFOREACH
997 mutex_unlock(&device_list_mutex);
998}
999
1000void device_init(void)
1001{
1002 usbmuxd_log(LL_DEBUG, "device_init");
1003 collection_init(&device_list);
1004 mutex_init(&device_list_mutex);
1005 next_device_id = 1;
1006}
1007
1008void device_kill_connections(void)
1009{
1010 usbmuxd_log(LL_DEBUG, "device_kill_connections");
1011 FOREACH(struct mux_device *dev, &device_list) {
1012 if(dev->state != MUXDEV_INIT) {
1013 FOREACH(struct mux_connection *conn, &dev->connections) {
1014 connection_teardown(conn);
1015 } ENDFOREACH
1016 }
1017 } ENDFOREACH
1018 // give USB a while to send the final connection RSTs and the like
1019 usb_process_timeout(100);
1020}
1021
1022void device_shutdown(void)
1023{
1024 usbmuxd_log(LL_DEBUG, "device_shutdown");
1025 mutex_lock(&device_list_mutex);
1026 FOREACH(struct mux_device *dev, &device_list) {
1027 FOREACH(struct mux_connection *conn, &dev->connections) {
1028 connection_teardown(conn);
1029 } ENDFOREACH
1030 collection_free(&dev->connections);
1031 collection_remove(&device_list, dev);
1032 free(dev);
1033 } ENDFOREACH
1034 mutex_unlock(&device_list_mutex);
1035 mutex_destroy(&device_list_mutex);
1036 collection_free(&device_list);
1037}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..85703e4
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,56 @@
1/*
2 * device.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef DEVICE_H
21#define DEVICE_H
22
23#include "usb.h"
24#include "client.h"
25
26struct device_info {
27 int id;
28 const char *serial;
29 uint32_t location;
30 uint16_t pid;
31 uint64_t speed;
32};
33
34void device_data_input(struct usb_device *dev, unsigned char *buf, uint32_t length);
35
36int device_add(struct usb_device *dev);
37void device_remove(struct usb_device *dev);
38
39int device_start_connect(int device_id, uint16_t port, struct mux_client *client);
40void device_client_process(int device_id, struct mux_client *client, short events);
41void device_abort_connect(int device_id, struct mux_client *client);
42
43void device_set_visible(int device_id);
44void device_set_preflight_cb_data(int device_id, void* data);
45
46int device_get_count(int include_hidden);
47int device_get_list(int include_hidden, struct device_info **devices);
48
49int device_get_timeout(void);
50void device_check_timeouts(void);
51
52void device_init(void);
53void device_kill_connections(void);
54void device_shutdown(void);
55
56#endif
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..cd7c2d5
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,101 @@
1/*
2 * log.c
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdarg.h>
29#include <time.h>
30#include <sys/time.h>
31#include <syslog.h>
32
33#include "log.h"
34#include "utils.h"
35
36unsigned int log_level = LL_WARNING;
37
38int log_syslog = 0;
39
40void log_enable_syslog()
41{
42 if (!log_syslog) {
43 openlog("usbmuxd", LOG_PID, 0);
44 log_syslog = 1;
45 }
46}
47
48void log_disable_syslog()
49{
50 if (log_syslog) {
51 closelog();
52 }
53}
54
55static int level_to_syslog_level(int level)
56{
57 int result = level + LOG_CRIT;
58 if (result > LOG_DEBUG) {
59 result = LOG_DEBUG;
60 }
61 return result;
62}
63
64void usbmuxd_log(enum loglevel level, const char *fmt, ...)
65{
66 va_list ap;
67 char *fs;
68
69 if(level > log_level)
70 return;
71
72 fs = malloc(20 + strlen(fmt));
73
74 if(log_syslog) {
75 sprintf(fs, "[%d] %s\n", level, fmt);
76 } else {
77 struct timeval ts;
78 struct tm tp_;
79 struct tm *tp;
80
81 gettimeofday(&ts, NULL);
82#ifdef HAVE_LOCALTIME_R
83 tp = localtime_r(&ts.tv_sec, &tp_);
84#else
85 tp = localtime(&ts.tv_sec);
86#endif
87
88 strftime(fs, 10, "[%H:%M:%S", tp);
89 sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
90 }
91
92 va_start(ap, fmt);
93 if (log_syslog) {
94 vsyslog(level_to_syslog_level(level), fs, ap);
95 } else {
96 vfprintf(stderr, fs, ap);
97 }
98 va_end(ap);
99
100 free(fs);
101}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..858e7d0
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,42 @@
1/*
2 * log.h
3 *
4 * Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 or version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifndef LOG_H
22#define LOG_H
23
24enum loglevel {
25 LL_FATAL = 0,
26 LL_ERROR,
27 LL_WARNING,
28 LL_NOTICE,
29 LL_INFO,
30 LL_DEBUG,
31 LL_SPEW,
32 LL_FLOOD,
33};
34
35extern unsigned int log_level;
36
37void log_enable_syslog();
38void log_disable_syslog();
39
40void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
41
42#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..8702a4b
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,920 @@
1/*
2 * main.c
3 *
4 * Copyright (C) 2009-2021 Nikias Bassen <nikias@gmx.li>
5 * Copyright (C) 2013-2014 Martin Szulecki <m.szulecki@libimobiledevice.org>
6 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
7 * Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 or version 3.
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
23#define _DEFAULT_SOURCE
24#define _BSD_SOURCE
25#define _GNU_SOURCE
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include <stdio.h>
32#include <errno.h>
33#include <string.h>
34#include <stdlib.h>
35#include <signal.h>
36#include <unistd.h>
37#include <sys/socket.h>
38#include <sys/un.h>
39#include <netinet/in.h>
40#include <netdb.h>
41#include <arpa/inet.h>
42#include <sys/stat.h>
43#include <sys/types.h>
44#include <sys/resource.h>
45#include <fcntl.h>
46#include <getopt.h>
47#include <pwd.h>
48#include <grp.h>
49
50#include "log.h"
51#include "usb.h"
52#include "device.h"
53#include "client.h"
54#include "conf.h"
55
56static const char *socket_path = "/var/run/usbmuxd";
57#define DEFAULT_LOCKFILE "/var/run/usbmuxd.pid"
58static const char *lockfile = DEFAULT_LOCKFILE;
59
60// Global state used in other files
61int should_exit;
62int should_discover;
63int use_logfile = 0;
64int no_preflight = 0;
65
66// Global state for main.c
67static int verbose = 0;
68static int foreground = 0;
69static int drop_privileges = 0;
70static const char *drop_user = NULL;
71static int opt_disable_hotplug = 0;
72static int opt_enable_exit = 0;
73static int opt_exit = 0;
74static int exit_signal = 0;
75static int daemon_pipe;
76static const char *listen_addr = NULL;
77
78static int report_to_parent = 0;
79
80static int create_socket(void)
81{
82 int listenfd;
83 const char* socket_addr = socket_path;
84 const char* tcp_port;
85 char listen_addr_str[256];
86
87 if (listen_addr) {
88 socket_addr = listen_addr;
89 }
90 tcp_port = strrchr(socket_addr, ':');
91 if (tcp_port) {
92 tcp_port++;
93 size_t nlen = tcp_port - socket_addr;
94 char* hostname = malloc(nlen);
95 struct addrinfo hints;
96 struct addrinfo *result, *rp;
97 int yes = 1;
98 int res;
99
100 strncpy(hostname, socket_addr, nlen-1);
101 hostname[nlen-1] = '\0';
102
103 memset(&hints, '\0', sizeof(struct addrinfo));
104 hints.ai_family = AF_UNSPEC;
105 hints.ai_socktype = SOCK_STREAM;
106 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
107 hints.ai_protocol = IPPROTO_TCP;
108
109 res = getaddrinfo(hostname, tcp_port, &hints, &result);
110 free(hostname);
111 if (res != 0) {
112 usbmuxd_log(LL_FATAL, "%s: getaddrinfo() failed: %s\n", __func__, gai_strerror(res));
113 return -1;
114 }
115
116 for (rp = result; rp != NULL; rp = rp->ai_next) {
117 listenfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
118 if (listenfd == -1) {
119 listenfd = -1;
120 continue;
121 }
122
123 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
124 usbmuxd_log(LL_ERROR, "%s: setsockopt(): %s", __func__, strerror(errno));
125 close(listenfd);
126 listenfd = -1;
127 continue;
128 }
129
130#ifdef SO_NOSIGPIPE
131 if (setsockopt(listenfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
132 usbmuxd_log(LL_ERROR, "%s: setsockopt(): %s", __func__, strerror(errno));
133 close(listenfd);
134 listenfd = -1;
135 continue;
136 }
137#endif
138
139#if defined(AF_INET6) && defined(IPV6_V6ONLY)
140 if (rp->ai_family == AF_INET6) {
141 if (setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(int)) == -1) {
142 usbmuxd_log(LL_ERROR, "%s: setsockopt() IPV6_V6ONLY: %s", __func__, strerror(errno));
143 }
144 }
145#endif
146
147 if (bind(listenfd, rp->ai_addr, rp->ai_addrlen) < 0) {
148 usbmuxd_log(LL_FATAL, "%s: bind() failed: %s", __func__, strerror(errno));
149 close(listenfd);
150 listenfd = -1;
151 continue;
152 }
153
154 const void *addrdata = NULL;
155 if (rp->ai_family == AF_INET) {
156 addrdata = &((struct sockaddr_in*)rp->ai_addr)->sin_addr;
157 }
158#ifdef AF_INET6
159 else if (rp->ai_family == AF_INET6) {
160 addrdata = &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr;
161 }
162#endif
163 if (addrdata) {
164 char* endp = NULL;
165 uint16_t listen_port = 0;
166 if (rp->ai_family == AF_INET) {
167 listen_port = ntohs(((struct sockaddr_in*)rp->ai_addr)->sin_port);
168 if (inet_ntop(AF_INET, addrdata, listen_addr_str, sizeof(listen_addr_str)-6)) {
169 endp = &listen_addr_str[0] + strlen(listen_addr_str);
170 }
171 }
172#ifdef AF_INET6
173 else if (rp->ai_family == AF_INET6) {
174 listen_port = ntohs(((struct sockaddr_in6*)rp->ai_addr)->sin6_port);
175 listen_addr_str[0] = '[';
176 if (inet_ntop(AF_INET6, addrdata, listen_addr_str+1, sizeof(listen_addr_str)-8)) {
177 endp = &listen_addr_str[0] + strlen(listen_addr_str);
178 }
179 if (endp) {
180 *endp = ']';
181 endp++;
182 }
183 }
184#endif
185 if (endp) {
186 sprintf(endp, ":%u", listen_port);
187 }
188 }
189 break;
190 }
191 freeaddrinfo(result);
192 if (listenfd == -1) {
193 usbmuxd_log(LL_FATAL, "%s: Failed to create listening socket", __func__);
194 return -1;
195 }
196 } else {
197 struct sockaddr_un bind_addr;
198
199 if (strcmp(socket_addr, socket_path) != 0) {
200 struct stat fst;
201 if (stat(socket_addr, &fst) == 0) {
202 if (!S_ISSOCK(fst.st_mode)) {
203 usbmuxd_log(LL_FATAL, "FATAL: File '%s' already exists and is not a socket file. Refusing to continue.", socket_addr);
204 return -1;
205 }
206 }
207 }
208
209 if (unlink(socket_addr) == -1 && errno != ENOENT) {
210 usbmuxd_log(LL_FATAL, "%s: unlink(%s) failed: %s", __func__, socket_addr, strerror(errno));
211 return -1;
212 }
213
214 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
215 if (listenfd == -1) {
216 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
217 return -1;
218 }
219
220 bzero(&bind_addr, sizeof(bind_addr));
221 bind_addr.sun_family = AF_UNIX;
222 strncpy(bind_addr.sun_path, socket_addr, sizeof(bind_addr.sun_path));
223 bind_addr.sun_path[sizeof(bind_addr.sun_path) - 1] = '\0';
224
225 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
226 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
227 return -1;
228 }
229 chmod(socket_addr, 0666);
230
231 snprintf(listen_addr_str, sizeof(listen_addr_str), "%s", socket_addr);
232 }
233
234 int flags = fcntl(listenfd, F_GETFL, 0);
235 if (flags < 0) {
236 usbmuxd_log(LL_FATAL, "ERROR: Could not get flags for socket");
237 } else {
238 if (fcntl(listenfd, F_SETFL, flags | O_NONBLOCK) < 0) {
239 usbmuxd_log(LL_FATAL, "ERROR: Could not set socket to non-blocking");
240 }
241 }
242
243 // Start listening
244 if (listen(listenfd, 256) != 0) {
245 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
246 return -1;
247 }
248
249 usbmuxd_log(LL_INFO, "Listening on %s", listen_addr_str);
250
251 return listenfd;
252}
253
254static void handle_signal(int sig)
255{
256 if (sig != SIGUSR1 && sig != SIGUSR2) {
257 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
258 should_exit = 1;
259 } else {
260 if(opt_enable_exit) {
261 if (sig == SIGUSR1) {
262 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
263 if (device_get_count(1) > 0) {
264 // we can't quit, there are still devices attached.
265 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
266 } else {
267 // it's safe to quit
268 should_exit = 1;
269 }
270 } else if (sig == SIGUSR2) {
271 usbmuxd_log(LL_INFO, "Caught SIGUSR2, scheduling device discovery");
272 should_discover = 1;
273 }
274 } else {
275 usbmuxd_log(LL_INFO, "Caught SIGUSR1/2 but this instance was not started with \"--enable-exit\", ignoring.");
276 }
277 }
278}
279
280static void set_signal_handlers(void)
281{
282 struct sigaction sa;
283 sigset_t set;
284
285 // Mask all signals we handle. They will be unmasked by ppoll().
286 sigemptyset(&set);
287 sigaddset(&set, SIGINT);
288 sigaddset(&set, SIGQUIT);
289 sigaddset(&set, SIGTERM);
290 sigaddset(&set, SIGUSR1);
291 sigaddset(&set, SIGUSR2);
292 sigprocmask(SIG_SETMASK, &set, NULL);
293
294 memset(&sa, 0, sizeof(struct sigaction));
295 sa.sa_handler = handle_signal;
296 sigaction(SIGINT, &sa, NULL);
297 sigaction(SIGQUIT, &sa, NULL);
298 sigaction(SIGTERM, &sa, NULL);
299 sigaction(SIGUSR1, &sa, NULL);
300 sigaction(SIGUSR2, &sa, NULL);
301}
302
303#ifndef HAVE_PPOLL
304static int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask)
305{
306 int ready;
307 sigset_t origmask;
308 int to = timeout->tv_sec*1000 + timeout->tv_nsec/1000000;
309
310 sigprocmask(SIG_SETMASK, sigmask, &origmask);
311 ready = poll(fds, nfds, to);
312 sigprocmask(SIG_SETMASK, &origmask, NULL);
313
314 return ready;
315}
316#endif
317
318static int main_loop(int listenfd)
319{
320 int to, cnt, i, dto;
321 struct fdlist pollfds;
322 struct timespec tspec;
323
324 sigset_t empty_sigset;
325 sigemptyset(&empty_sigset); // unmask all signals
326
327 fdlist_create(&pollfds);
328 while(!should_exit) {
329 usbmuxd_log(LL_FLOOD, "main_loop iteration");
330 to = usb_get_timeout();
331 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
332 dto = device_get_timeout();
333 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", dto);
334 if(dto < to)
335 to = dto;
336
337 fdlist_reset(&pollfds);
338 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
339 usb_get_fds(&pollfds);
340 client_get_fds(&pollfds);
341 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
342
343 tspec.tv_sec = to / 1000;
344 tspec.tv_nsec = (to % 1000) * 1000000;
345 cnt = ppoll(pollfds.fds, pollfds.count, &tspec, &empty_sigset);
346 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
347 if(cnt == -1) {
348 if(errno == EINTR) {
349 if(should_exit) {
350 usbmuxd_log(LL_INFO, "Event processing interrupted");
351 break;
352 }
353 if(should_discover) {
354 should_discover = 0;
355 usbmuxd_log(LL_INFO, "Device discovery triggered");
356 usb_discover();
357 }
358 }
359 } else if(cnt == 0) {
360 if(usb_process() < 0) {
361 usbmuxd_log(LL_FATAL, "usb_process() failed");
362 fdlist_free(&pollfds);
363 return -1;
364 }
365 device_check_timeouts();
366 } else {
367 int done_usb = 0;
368 for(i=0; i<pollfds.count; i++) {
369 if(pollfds.fds[i].revents) {
370 if(!done_usb && pollfds.owners[i] == FD_USB) {
371 if(usb_process() < 0) {
372 usbmuxd_log(LL_FATAL, "usb_process() failed");
373 fdlist_free(&pollfds);
374 return -1;
375 }
376 done_usb = 1;
377 }
378 if(pollfds.owners[i] == FD_LISTEN) {
379 if(client_accept(listenfd) < 0) {
380 usbmuxd_log(LL_FATAL, "client_accept() failed");
381 fdlist_free(&pollfds);
382 return -1;
383 }
384 }
385 if(pollfds.owners[i] == FD_CLIENT) {
386 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
387 }
388 }
389 }
390 }
391 }
392 fdlist_free(&pollfds);
393 return 0;
394}
395
396/**
397 * make this program run detached from the current console
398 */
399static int daemonize(void)
400{
401 pid_t pid;
402 pid_t sid;
403 int pfd[2];
404 int res;
405
406 // already a daemon
407 if (getppid() == 1)
408 return 0;
409
410 if((res = pipe(pfd)) < 0) {
411 usbmuxd_log(LL_FATAL, "pipe() failed.");
412 return res;
413 }
414
415 pid = fork();
416 if (pid < 0) {
417 usbmuxd_log(LL_FATAL, "fork() failed.");
418 return pid;
419 }
420
421 if (pid > 0) {
422 // exit parent process
423 int status;
424 close(pfd[1]);
425
426 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
427 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
428 exit(1);
429 }
430 if(status != 0)
431 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
432 exit(status);
433 }
434 // At this point we are executing as the child process
435 // but we need to do one more fork
436
437 daemon_pipe = pfd[1];
438 close(pfd[0]);
439 report_to_parent = 1;
440
441 // Create a new SID for the child process
442 sid = setsid();
443 if (sid < 0) {
444 usbmuxd_log(LL_FATAL, "setsid() failed.");
445 return -1;
446 }
447
448 pid = fork();
449 if (pid < 0) {
450 usbmuxd_log(LL_FATAL, "fork() failed (second).");
451 return pid;
452 }
453
454 if (pid > 0) {
455 // exit parent process
456 close(daemon_pipe);
457 exit(0);
458 }
459
460 // Change the current working directory.
461 if ((chdir("/")) < 0) {
462 usbmuxd_log(LL_FATAL, "chdir() failed");
463 return -2;
464 }
465 // Redirect standard files to /dev/null
466 if (!freopen("/dev/null", "r", stdin)) {
467 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
468 return -3;
469 }
470 if (!freopen("/dev/null", "w", stdout)) {
471 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
472 return -3;
473 }
474
475 return 0;
476}
477
478static int notify_parent(int status)
479{
480 int res;
481
482 report_to_parent = 0;
483 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
484 usbmuxd_log(LL_FATAL, "Could not notify parent!");
485 if(res >= 0)
486 return -2;
487 else
488 return res;
489 }
490 close(daemon_pipe);
491 if (!freopen("/dev/null", "w", stderr)) {
492 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
493 return -1;
494 }
495 return 0;
496}
497
498static void usage()
499{
500 printf("Usage: %s [OPTIONS]\n", PACKAGE_NAME);
501 printf("\n");
502 printf("Expose a socket to multiplex connections from and to iOS devices.\n");
503 printf("\n");
504 printf("OPTIONS:\n");
505 printf(" -h, --help\t\tPrint this message.\n");
506 printf(" -v, --verbose\t\tBe verbose (use twice or more to increase).\n");
507 printf(" -f, --foreground\tDo not daemonize (implies one -v).\n");
508 printf(" -U, --user USER\tChange to this user after startup (needs USB privileges).\n");
509 printf(" -n, --disable-hotplug\tDisables automatic discovery of devices on hotplug.\n");
510 printf(" \tStarting another instance will trigger discovery instead.\n");
511 printf(" -z, --enable-exit\tEnable \"--exit\" request from other instances and exit\n");
512 printf(" \tautomatically if no device is attached.\n");
513 printf(" -p, --no-preflight\tDisable lockdownd preflight on new device.\n");
514#ifdef HAVE_UDEV
515 printf(" -u, --udev\t\tRun in udev operation mode (implies -n and -z).\n");
516#endif
517#ifdef HAVE_SYSTEMD
518 printf(" -s, --systemd\t\tRun in systemd operation mode (implies -z and -f).\n");
519#endif
520 printf(" -S, --socket ADDR:PORT | PATH Specify source ADDR and PORT or a UNIX\n");
521 printf(" \t\tsocket PATH to use for the listening socket.\n");
522 printf(" \t\tDefault: %s\n", socket_path);
523 printf(" -P, --pidfile PATH\tSpecify a different location for the pid file, or pass\n");
524 printf(" \t\tNONE to disable. Default: %s\n", DEFAULT_LOCKFILE);
525 printf(" -x, --exit\t\tNotify a running instance to exit if there are no devices\n");
526 printf(" \t\tconnected (sends SIGUSR1 to running instance) and exit.\n");
527 printf(" -X, --force-exit\tNotify a running instance to exit even if there are still\n");
528 printf(" \tdevices connected (always works) and exit.\n");
529 printf(" -l, --logfile=LOGFILE\tLog (append) to LOGFILE instead of stderr or syslog.\n");
530 printf(" -V, --version\t\tPrint version information and exit.\n");
531 printf("\n");
532 printf("Homepage: <" PACKAGE_URL ">\n");
533 printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
534}
535
536static void parse_opts(int argc, char **argv)
537{
538 static struct option longopts[] = {
539 {"help", no_argument, NULL, 'h'},
540 {"foreground", no_argument, NULL, 'f'},
541 {"verbose", no_argument, NULL, 'v'},
542 {"user", required_argument, NULL, 'U'},
543 {"disable-hotplug", no_argument, NULL, 'n'},
544 {"enable-exit", no_argument, NULL, 'z'},
545 {"no-preflight", no_argument, NULL, 'p'},
546#ifdef HAVE_UDEV
547 {"udev", no_argument, NULL, 'u'},
548#endif
549#ifdef HAVE_SYSTEMD
550 {"systemd", no_argument, NULL, 's'},
551#endif
552 {"socket", required_argument, NULL, 'S'},
553 {"pidfile", required_argument, NULL, 'P'},
554 {"exit", no_argument, NULL, 'x'},
555 {"force-exit", no_argument, NULL, 'X'},
556 {"logfile", required_argument, NULL, 'l'},
557 {"version", no_argument, NULL, 'V'},
558 {NULL, 0, NULL, 0}
559 };
560 int c;
561
562#ifdef HAVE_SYSTEMD
563 const char* opts_spec = "hfvVuU:xXsnzl:pS:P:";
564#elif HAVE_UDEV
565 const char* opts_spec = "hfvVuU:xXnzl:pS:P:";
566#else
567 const char* opts_spec = "hfvVU:xXnzl:pS:P:";
568#endif
569
570 while (1) {
571 c = getopt_long(argc, argv, opts_spec, longopts, (int *) 0);
572 if (c == -1) {
573 break;
574 }
575
576 switch (c) {
577 case 'h':
578 usage();
579 exit(0);
580 case 'f':
581 foreground = 1;
582 break;
583 case 'v':
584 ++verbose;
585 break;
586 case 'V':
587 printf("%s\n", PACKAGE_STRING);
588 exit(0);
589 case 'U':
590 drop_privileges = 1;
591 drop_user = optarg;
592 break;
593 case 'p':
594 no_preflight = 1;
595 break;
596#ifdef HAVE_UDEV
597 case 'u':
598 opt_disable_hotplug = 1;
599 opt_enable_exit = 1;
600 break;
601#endif
602#ifdef HAVE_SYSTEMD
603 case 's':
604 opt_enable_exit = 1;
605 foreground = 1;
606 break;
607#endif
608 case 'n':
609 opt_disable_hotplug = 1;
610 break;
611 case 'z':
612 opt_enable_exit = 1;
613 break;
614 case 'S':
615 if (!*optarg || *optarg == '-') {
616 usbmuxd_log(LL_FATAL, "ERROR: --socket requires an argument");
617 usage();
618 exit(2);
619 }
620 listen_addr = optarg;
621 break;
622 case 'P':
623 if (!*optarg || *optarg == '-') {
624 usbmuxd_log(LL_FATAL, "ERROR: --pidfile requires an argument");
625 usage();
626 exit(2);
627 }
628 if (!strcmp(optarg, "NONE")) {
629 lockfile = NULL;
630 } else {
631 lockfile = optarg;
632 }
633 break;
634 case 'x':
635 opt_exit = 1;
636 exit_signal = SIGUSR1;
637 break;
638 case 'X':
639 opt_exit = 1;
640 exit_signal = SIGTERM;
641 break;
642 case 'l':
643 if (!*optarg) {
644 usbmuxd_log(LL_FATAL, "ERROR: --logfile requires a non-empty filename");
645 usage();
646 exit(2);
647 }
648 if (use_logfile) {
649 usbmuxd_log(LL_FATAL, "ERROR: --logfile cannot be used multiple times");
650 exit(2);
651 }
652 if (!freopen(optarg, "a", stderr)) {
653 usbmuxd_log(LL_FATAL, "ERROR: fdreopen: %s", strerror(errno));
654 } else {
655 use_logfile = 1;
656 }
657 break;
658 default:
659 usage();
660 exit(2);
661 }
662 }
663}
664
665int main(int argc, char *argv[])
666{
667 int listenfd;
668 int res = 0;
669 int lfd;
670 struct flock lock;
671 char pids[10];
672
673 parse_opts(argc, argv);
674
675 argc -= optind;
676 argv += optind;
677
678 if (!foreground && !use_logfile) {
679 verbose += LL_WARNING;
680 log_enable_syslog();
681 } else {
682 verbose += LL_NOTICE;
683 }
684
685 /* set log level to specified verbosity */
686 log_level = verbose;
687
688 usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", PACKAGE_VERSION);
689 should_exit = 0;
690 should_discover = 0;
691
692 set_signal_handlers();
693 signal(SIGPIPE, SIG_IGN);
694
695 if (lockfile) {
696 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
697 if(res == -1) {
698 usbmuxd_log(LL_FATAL, "Could not open lockfile");
699 goto terminate;
700 }
701 lock.l_type = F_WRLCK;
702 lock.l_whence = SEEK_SET;
703 lock.l_start = 0;
704 lock.l_len = 0;
705 lock.l_pid = 0;
706 fcntl(lfd, F_GETLK, &lock);
707 close(lfd);
708 }
709 if (lockfile && lock.l_type != F_UNLCK) {
710 if (opt_exit) {
711 if (lock.l_pid && !kill(lock.l_pid, 0)) {
712 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
713 res = 0;
714 if (kill(lock.l_pid, exit_signal) < 0) {
715 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
716 res = -1;
717 }
718 goto terminate;
719 } else {
720 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
721 res = -1;
722 goto terminate;
723 }
724 } else {
725 if (!opt_disable_hotplug) {
726 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
727 res = -1;
728 } else {
729 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid);
730 if (lock.l_pid && !kill(lock.l_pid, 0)) {
731 usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid);
732 res = 0;
733 if (kill(lock.l_pid, SIGUSR2) < 0) {
734 usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid);
735 res = -1;
736 }
737 } else {
738 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
739 res = -1;
740 }
741 }
742 goto terminate;
743 }
744 }
745 if (lockfile) {
746 unlink(lockfile);
747 }
748
749 if (opt_exit) {
750 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. Exiting.");
751 goto terminate;
752 }
753
754 if (!foreground) {
755 if ((res = daemonize()) < 0) {
756 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
757 usbmuxd_log(LL_FATAL, "Could not daemonize!");
758 goto terminate;
759 }
760 }
761
762 if (lockfile) {
763 // now open the lockfile and place the lock
764 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
765 if(res < 0) {
766 usbmuxd_log(LL_FATAL, "Could not open pidfile '%s'", lockfile);
767 goto terminate;
768 }
769 lock.l_type = F_WRLCK;
770 lock.l_whence = SEEK_SET;
771 lock.l_start = 0;
772 lock.l_len = 0;
773 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
774 usbmuxd_log(LL_FATAL, "Locking pidfile '%s' failed!", lockfile);
775 goto terminate;
776 }
777 sprintf(pids, "%d", getpid());
778 if ((size_t)(res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
779 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
780 if(res >= 0)
781 res = -2;
782 goto terminate;
783 }
784 }
785
786 // set number of file descriptors to higher value
787 struct rlimit rlim;
788 getrlimit(RLIMIT_NOFILE, &rlim);
789 rlim.rlim_max = 65536;
790 setrlimit(RLIMIT_NOFILE, (const struct rlimit*)&rlim);
791
792 usbmuxd_log(LL_INFO, "Creating socket");
793 res = listenfd = create_socket();
794 if(listenfd < 0)
795 goto terminate;
796
797#ifdef HAVE_LIBIMOBILEDEVICE
798 const char* userprefdir = config_get_config_dir();
799 struct stat fst;
800 memset(&fst, '\0', sizeof(struct stat));
801 if (stat(userprefdir, &fst) < 0) {
802 if (mkdir(userprefdir, 0775) < 0) {
803 usbmuxd_log(LL_FATAL, "Failed to create required directory '%s': %s", userprefdir, strerror(errno));
804 res = -1;
805 goto terminate;
806 }
807 if (stat(userprefdir, &fst) < 0) {
808 usbmuxd_log(LL_FATAL, "stat() failed after creating directory '%s': %s", userprefdir, strerror(errno));
809 res = -1;
810 goto terminate;
811 }
812 }
813
814 // make sure permission bits are set correctly
815 if (fst.st_mode != 02775) {
816 if (chmod(userprefdir, 02775) < 0) {
817 usbmuxd_log(LL_WARNING, "chmod(%s, 02775) failed: %s", userprefdir, strerror(errno));
818 }
819 }
820#endif
821
822 // drop elevated privileges
823 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
824 struct passwd *pw;
825 if (!drop_user) {
826 usbmuxd_log(LL_FATAL, "No user to drop privileges to?");
827 res = -1;
828 goto terminate;
829 }
830 pw = getpwnam(drop_user);
831 if (!pw) {
832 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
833 res = -1;
834 goto terminate;
835 }
836 if (pw->pw_uid == 0) {
837 usbmuxd_log(LL_INFO, "Not dropping privileges to root");
838 } else {
839#ifdef HAVE_LIBIMOBILEDEVICE
840 /* make sure the non-privileged user has proper access to the config directory */
841 if ((fst.st_uid != pw->pw_uid) || (fst.st_gid != pw->pw_gid)) {
842 if (chown(userprefdir, pw->pw_uid, pw->pw_gid) < 0) {
843 usbmuxd_log(LL_WARNING, "chown(%s, %d, %d) failed: %s", userprefdir, pw->pw_uid, pw->pw_gid, strerror(errno));
844 }
845 }
846#endif
847
848 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
849 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
850 goto terminate;
851 }
852 if ((res = setgid(pw->pw_gid)) < 0) {
853 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
854 goto terminate;
855 }
856 if ((res = setuid(pw->pw_uid)) < 0) {
857 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
858 goto terminate;
859 }
860
861 // security check
862 if (setuid(0) != -1) {
863 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
864 res = -1;
865 goto terminate;
866 }
867 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
868 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
869 res = -1;
870 goto terminate;
871 }
872 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
873 }
874 }
875
876 client_init();
877 device_init();
878 usbmuxd_log(LL_INFO, "Initializing USB");
879 if((res = usb_init()) < 0)
880 goto terminate;
881
882 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
883
884 usbmuxd_log(LL_NOTICE, "Initialization complete");
885
886 if (report_to_parent)
887 if((res = notify_parent(0)) < 0)
888 goto terminate;
889
890 if(opt_disable_hotplug) {
891 usbmuxd_log(LL_NOTICE, "Automatic device discovery on hotplug disabled.");
892 usb_autodiscover(0); // discovery to be triggered by new instance
893 }
894 if (opt_enable_exit) {
895 usbmuxd_log(LL_NOTICE, "Enabled exit on SIGUSR1 if no devices are attached. Start a new instance with \"--exit\" to trigger.");
896 }
897
898 res = main_loop(listenfd);
899 if(res < 0)
900 usbmuxd_log(LL_FATAL, "main_loop failed");
901
902 usbmuxd_log(LL_NOTICE, "usbmuxd shutting down");
903 device_kill_connections();
904 usb_shutdown();
905 device_shutdown();
906 client_shutdown();
907 usbmuxd_log(LL_NOTICE, "Shutdown complete");
908
909terminate:
910 log_disable_syslog();
911
912 if (res < 0)
913 res = -res;
914 else
915 res = 0;
916 if (report_to_parent)
917 notify_parent(res);
918
919 return res;
920}
diff --git a/src/preflight.c b/src/preflight.c
new file mode 100644
index 0000000..9c57e98
--- /dev/null
+++ b/src/preflight.c
@@ -0,0 +1,406 @@
1/*
2 * preflight.c
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28
29#include <sys/time.h>
30
31#ifdef HAVE_LIBIMOBILEDEVICE
32#include <libimobiledevice/libimobiledevice.h>
33#include <libimobiledevice/lockdown.h>
34#include <libimobiledevice/notification_proxy.h>
35#endif
36
37#include <libimobiledevice-glue/thread.h>
38
39#include "preflight.h"
40#include "device.h"
41#include "client.h"
42#include "conf.h"
43#include "log.h"
44#include "usb.h"
45
46extern int no_preflight;
47
48#ifdef HAVE_LIBIMOBILEDEVICE
49#ifndef HAVE_ENUM_IDEVICE_CONNECTION_TYPE
50enum idevice_connection_type {
51 CONNECTION_USBMUXD = 1,
52 CONNECTION_NETWORK
53};
54#endif
55
56struct idevice_private {
57 char *udid;
58 uint32_t mux_id;
59 enum idevice_connection_type conn_type;
60 void *conn_data;
61 int version;
62 int device_class;
63};
64
65struct cb_data {
66 idevice_t dev;
67 np_client_t np;
68 int is_device_connected;
69 int is_finished;
70};
71
72static void lockdownd_set_untrusted_host_buid(lockdownd_client_t lockdown)
73{
74 char* system_buid = NULL;
75 config_get_system_buid(&system_buid);
76 usbmuxd_log(LL_DEBUG, "%s: Setting UntrustedHostBUID to %s", __func__, system_buid);
77 lockdownd_set_value(lockdown, NULL, "UntrustedHostBUID", plist_new_string(system_buid));
78 free(system_buid);
79}
80
81void preflight_device_remove_cb(void *data)
82{
83 if (!data)
84 return;
85 struct cb_data *cbdata = (struct cb_data*)data;
86 cbdata->is_device_connected = 0;
87}
88
89static void np_callback(const char* notification, void* userdata)
90{
91 struct cb_data *cbdata = (struct cb_data*)userdata;
92 idevice_t dev = cbdata->dev;
93 struct idevice_private *_dev = (struct idevice_private*)dev;
94
95 lockdownd_client_t lockdown = NULL;
96 lockdownd_error_t lerr;
97
98 if (strlen(notification) == 0) {
99 cbdata->np = NULL;
100 return;
101 }
102
103 if (strcmp(notification, "com.apple.mobile.lockdown.request_pair") == 0) {
104 usbmuxd_log(LL_INFO, "%s: user trusted this computer on device %s, pairing now", __func__, _dev->udid);
105 lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd");
106 if (lerr != LOCKDOWN_E_SUCCESS) {
107 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
108 cbdata->is_finished = 1;
109 return;
110 }
111
112 lerr = lockdownd_pair(lockdown, NULL);
113 if (lerr != LOCKDOWN_E_SUCCESS) {
114 usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
115 lockdownd_client_free(lockdown);
116 cbdata->is_finished = 1;
117 return;
118 }
119 lockdownd_client_free(lockdown);
120 cbdata->is_finished = 1;
121
122 } else if (strcmp(notification, "com.apple.mobile.lockdown.request_host_buid") == 0) {
123 lerr = lockdownd_client_new(cbdata->dev, &lockdown, "usbmuxd");
124 if (lerr != LOCKDOWN_E_SUCCESS) {
125 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
126 } else {
127 lockdownd_set_untrusted_host_buid(lockdown);
128 lockdownd_client_free(lockdown);
129 }
130 }
131}
132
133static void* preflight_worker_handle_device_add(void* userdata)
134{
135 struct device_info *info = (struct device_info*)userdata;
136 struct idevice_private *_dev = (struct idevice_private*)malloc(sizeof(struct idevice_private));
137 _dev->udid = strdup(info->serial);
138 _dev->mux_id = info->id;
139 _dev->conn_type = CONNECTION_USBMUXD;
140 _dev->conn_data = NULL;
141 _dev->version = 0;
142 _dev->device_class = 0;
143
144 idevice_t dev = (idevice_t)_dev;
145
146 lockdownd_client_t lockdown = NULL;
147 lockdownd_error_t lerr;
148
149 plist_t value = NULL;
150 char* version_str = NULL;
151 char* deviceclass_str = NULL;
152
153 usbmuxd_log(LL_INFO, "%s: Starting preflight on device %s...", __func__, _dev->udid);
154
155retry:
156 lerr = lockdownd_client_new(dev, &lockdown, "usbmuxd");
157 if (lerr != LOCKDOWN_E_SUCCESS) {
158 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not connect to lockdownd on device %s, lockdown error %d", __func__, _dev->udid, lerr);
159 goto leave;
160 }
161
162 char *type = NULL;
163 lerr = lockdownd_query_type(lockdown, &type);
164 if (!type) {
165 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get lockdownd type from device %s, lockdown error %d", __func__, _dev->udid, lerr);
166 goto leave;
167 }
168
169 if (strcmp(type, "com.apple.mobile.lockdown") != 0) {
170 // make restore mode devices visible
171 free(type);
172 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
173 client_device_add(info);
174 goto leave;
175 }
176 free(type);
177
178 int is_device_paired = 0;
179 char *host_id = NULL;
180 if (config_has_device_record(dev->udid)) {
181 config_device_record_get_host_id(dev->udid, &host_id);
182 lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
183 if (host_id)
184 free(host_id);
185 if (lerr == LOCKDOWN_E_SUCCESS) {
186 usbmuxd_log(LL_INFO, "%s: StartSession success for device %s", __func__, _dev->udid);
187 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
188 client_device_add(info);
189 goto leave;
190 }
191
192 usbmuxd_log(LL_INFO, "%s: StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr);
193 } else {
194 lerr = LOCKDOWN_E_INVALID_HOST_ID;
195 }
196 switch (lerr) {
197 case LOCKDOWN_E_INVALID_HOST_ID:
198 usbmuxd_log(LL_INFO, "%s: Device %s is not paired with this host.", __func__, _dev->udid);
199 break;
200 case LOCKDOWN_E_SSL_ERROR:
201 usbmuxd_log(LL_ERROR, "%s: The stored pair record for device %s is invalid. Removing.", __func__, _dev->udid);
202 if (config_remove_device_record(_dev->udid) == 0) {
203 lockdownd_client_free(lockdown);
204 lockdown = NULL;
205 goto retry;
206 } else {
207 usbmuxd_log(LL_ERROR, "%s: Could not remove pair record for device %s", __func__, _dev->udid);
208 }
209 break;
210 default:
211 is_device_paired = 1;
212 break;
213 }
214
215 lerr = lockdownd_get_value(lockdown, NULL, "ProductVersion", &value);
216 if (lerr != LOCKDOWN_E_SUCCESS) {
217 usbmuxd_log(LL_WARNING, "%s: Could not get ProductVersion from device %s, lockdown error %d", __func__, _dev->udid, lerr);
218 /* assume old iOS version */
219 version_str = strdup("1.0");
220 } else {
221 if (value && plist_get_node_type(value) == PLIST_STRING) {
222 plist_get_string_val(value, &version_str);
223 }
224 plist_free(value);
225
226 if (!version_str) {
227 usbmuxd_log(LL_ERROR, "%s: Could not get ProductVersion string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data);
228 goto leave;
229 }
230 }
231
232 lerr = lockdownd_get_value(lockdown, NULL, "DeviceClass", &value);
233 if (lerr != LOCKDOWN_E_SUCCESS) {
234 usbmuxd_log(LL_ERROR, "%s: ERROR: Could not get DeviceClass from device %s, lockdown error %d", __func__, _dev->udid, lerr);
235 goto leave;
236 }
237 if (value && plist_get_node_type(value) == PLIST_STRING) {
238 plist_get_string_val(value, &deviceclass_str);
239 }
240 plist_free(value);
241
242 if (!deviceclass_str) {
243 usbmuxd_log(LL_ERROR, "%s: Could not get DeviceClass string from device %s handle %d", __func__, _dev->udid, (int)(long)_dev->conn_data);
244 goto leave;
245 }
246
247 int version_major = strtol(version_str, NULL, 10);
248 if (((!strcmp(deviceclass_str, "iPhone") || !strcmp(deviceclass_str, "iPad")) && version_major >= 7)
249 || (!strcmp(deviceclass_str, "Watch") && version_major >= 2)
250 || (!strcmp(deviceclass_str, "AppleTV") && version_major >= 9)
251 ) {
252 /* iOS 7.0 / watchOS 2.0 / tvOS 9.0 and later */
253 usbmuxd_log(LL_INFO, "%s: Found %s %s device %s", __func__, deviceclass_str, version_str, _dev->udid);
254
255 lockdownd_set_untrusted_host_buid(lockdown);
256
257 /* if not paired, trigger the trust dialog to make sure it appears */
258 if (!is_device_paired) {
259 if (lockdownd_pair(lockdown, NULL) == LOCKDOWN_E_SUCCESS) {
260 /* if device is still showing the setup screen it will pair even without trust dialog */
261 usbmuxd_log(LL_INFO, "%s: Pair success for device %s", __func__, _dev->udid);
262 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
263 client_device_add(info);
264 goto leave;
265 }
266 }
267
268 lockdownd_service_descriptor_t service = NULL;
269 lerr = lockdownd_start_service(lockdown, "com.apple.mobile.insecure_notification_proxy", &service);
270 if (lerr != LOCKDOWN_E_SUCCESS) {
271 /* even though we failed, simple mode should still work, so only warn of an error */
272 usbmuxd_log(LL_INFO, "%s: ERROR: Could not start insecure_notification_proxy on %s, lockdown error %d", __func__, _dev->udid, lerr);
273 client_device_add(info);
274 goto leave;
275 }
276
277 np_client_t np = NULL;
278 np_client_new(dev, service, &np);
279
280 lockdownd_service_descriptor_free(service);
281 service = NULL;
282
283 lockdownd_client_free(lockdown);
284 lockdown = NULL;
285
286 struct cb_data cbdata;
287 cbdata.dev = dev;
288 cbdata.np = np;
289 cbdata.is_device_connected = 1;
290 cbdata.is_finished = 0;
291
292 np_set_notify_callback(np, np_callback, (void*)&cbdata);
293 device_set_preflight_cb_data(info->id, (void*)&cbdata);
294
295 const char* spec[] = {
296 "com.apple.mobile.lockdown.request_pair",
297 "com.apple.mobile.lockdown.request_host_buid",
298 NULL
299 };
300 np_observe_notifications(np, spec);
301
302 /* TODO send notification to user's desktop */
303
304 usbmuxd_log(LL_INFO, "%s: Waiting for user to trust this computer on device %s", __func__, _dev->udid);
305
306 /* make device visible anyways */
307 client_device_add(info);
308
309 while (cbdata.np && cbdata.is_device_connected && !cbdata.is_finished) {
310 sleep(1);
311 }
312 device_set_preflight_cb_data(info->id, NULL);
313
314 usbmuxd_log(LL_INFO, "%s: Finished waiting for notification from device %s, is_device_connected %d", __func__, _dev->udid, cbdata.is_device_connected);
315
316 if (cbdata.np) {
317 np_client_free(cbdata.np);
318 }
319 } else {
320 /* iOS 6.x and earlier */
321 lerr = lockdownd_pair(lockdown, NULL);
322 if (lerr != LOCKDOWN_E_SUCCESS) {
323 if (lerr == LOCKDOWN_E_PASSWORD_PROTECTED) {
324 usbmuxd_log(LL_INFO, "%s: Device %s is locked with a passcode. Cannot pair.", __func__, _dev->udid);
325 /* TODO send notification to user's desktop */
326 } else {
327 usbmuxd_log(LL_ERROR, "%s: ERROR: Pair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
328 }
329
330 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
331
332 /* make device visible anyways */
333 client_device_add(info);
334
335 goto leave;
336 }
337
338 host_id = NULL;
339 config_device_record_get_host_id(dev->udid, &host_id);
340 lerr = lockdownd_start_session(lockdown, host_id, NULL, NULL);
341 free(host_id);
342 if (lerr != LOCKDOWN_E_SUCCESS) {
343 usbmuxd_log(LL_ERROR, "%s: ERROR StartSession failed on device %s, lockdown error %d", __func__, _dev->udid, lerr);
344 goto leave;
345 }
346
347 lerr = lockdownd_validate_pair(lockdown, NULL);
348 if (lerr != LOCKDOWN_E_SUCCESS) {
349 usbmuxd_log(LL_ERROR, "%s: ERROR: ValidatePair failed for device %s, lockdown error %d", __func__, _dev->udid, lerr);
350 goto leave;
351 }
352
353 usbmuxd_log(LL_INFO, "%s: Finished preflight on device %s", __func__, _dev->udid);
354
355 /* emit device added event and thus make device visible to clients */
356 client_device_add(info);
357 }
358
359leave:
360 free(deviceclass_str);
361 free(version_str);
362 if (lockdown)
363 lockdownd_client_free(lockdown);
364 if (dev)
365 idevice_free(dev);
366
367 free((char*)info->serial);
368 free(info);
369
370 return NULL;
371}
372#else
373void preflight_device_remove_cb(void *data)
374{
375}
376#endif
377
378void preflight_worker_device_add(struct device_info* info)
379{
380 if (info->pid == PID_APPLE_T2_COPROCESSOR || no_preflight == 1) {
381 client_device_add(info);
382 return;
383 }
384
385#ifdef HAVE_LIBIMOBILEDEVICE
386 struct device_info *infocopy = (struct device_info*)malloc(sizeof(struct device_info));
387
388 memcpy(infocopy, info, sizeof(struct device_info));
389 if (info->serial) {
390 infocopy->serial = strdup(info->serial);
391 }
392
393 THREAD_T th;
394 int perr = thread_new(&th, preflight_worker_handle_device_add, infocopy);
395 if (perr != 0) {
396 free((char*)infocopy->serial);
397 free(infocopy);
398 usbmuxd_log(LL_ERROR, "ERROR: failed to start preflight worker thread for device %s: %s (%d). Invoking client_device_add() directly but things might not work as expected.", info->serial, strerror(perr), perr);
399 client_device_add(info);
400 } else {
401 thread_detach(th);
402 }
403#else
404 client_device_add(info);
405#endif
406}
diff --git a/src/preflight.h b/src/preflight.h
new file mode 100644
index 0000000..dd8647e
--- /dev/null
+++ b/src/preflight.h
@@ -0,0 +1,28 @@
1/*
2 * preflight.h
3 *
4 * Copyright (C) 2013 Nikias Bassen <nikias@gmx.li>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 or version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifndef PREFLIGHT_H
21#define PREFLIGHT_H
22
23#include "device.h"
24
25void preflight_device_remove_cb(void *data);
26void preflight_worker_device_add(struct device_info* info);
27
28#endif
diff --git a/src/usb.c b/src/usb.c
new file mode 100644
index 0000000..d3cb17c
--- /dev/null
+++ b/src/usb.c
@@ -0,0 +1,1084 @@
1/*
2 * usb.c
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 * Copyright (C) 2009-2020 Martin Szulecki <martin.szulecki@libimobiledevice.org>
7 * Copyright (C) 2014 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@xamarin.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 or version 3.
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
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <string.h>
31
32#include <libusb.h>
33
34#include <libimobiledevice-glue/collection.h>
35
36#include "usb.h"
37#include "log.h"
38#include "device.h"
39#include "utils.h"
40
41#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
42#define HAVE_LIBUSB_HOTPLUG_API 1
43#endif
44
45// interval for device connection/disconnection polling, in milliseconds
46// we need this because there is currently no asynchronous device discovery mechanism in libusb
47#define DEVICE_POLL_TIME 1000
48
49// Number of parallel bulk transfers we have running for reading data from the device.
50// Older versions of usbmuxd kept only 1, which leads to a mostly dormant USB port.
51// 3 seems to be an all round sensible number - giving better read perf than
52// Apples usbmuxd, at least.
53#define NUM_RX_LOOPS 3
54
55struct usb_device {
56 libusb_device_handle *handle;
57 uint8_t bus, address;
58 char serial[256];
59 int alive;
60 uint8_t interface, ep_in, ep_out;
61 struct collection rx_xfers;
62 struct collection tx_xfers;
63 int wMaxPacketSize;
64 uint64_t speed;
65 struct libusb_device_descriptor devdesc;
66};
67
68struct mode_context {
69 struct libusb_device* dev;
70 uint8_t bus, address;
71 uint8_t bRequest;
72 uint16_t wValue, wIndex, wLength;
73 unsigned int timeout;
74};
75
76static struct collection device_list;
77
78static struct timeval next_dev_poll_time;
79
80static int devlist_failures;
81static int device_polling;
82static int device_hotplug = 1;
83
84static void usb_disconnect(struct usb_device *dev)
85{
86 if(!dev->handle) {
87 return;
88 }
89
90 // kill the rx xfer and tx xfers and try to make sure the callbacks
91 // get called before we free the device
92 FOREACH(struct libusb_transfer *xfer, &dev->rx_xfers) {
93 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer %p", xfer);
94 libusb_cancel_transfer(xfer);
95 } ENDFOREACH
96
97 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
98 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
99 libusb_cancel_transfer(xfer);
100 } ENDFOREACH
101
102 // Busy-wait until all xfers are closed
103 while(collection_count(&dev->rx_xfers) || collection_count(&dev->tx_xfers)) {
104 struct timeval tv;
105 int res;
106
107 tv.tv_sec = 0;
108 tv.tv_usec = 1000;
109 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
110 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %s", libusb_error_name(res));
111 break;
112 }
113 }
114
115 collection_free(&dev->tx_xfers);
116 collection_free(&dev->rx_xfers);
117 libusb_release_interface(dev->handle, dev->interface);
118 libusb_close(dev->handle);
119 dev->handle = NULL;
120 collection_remove(&device_list, dev);
121 free(dev);
122}
123
124static void reap_dead_devices(void) {
125 FOREACH(struct usb_device *usbdev, &device_list) {
126 if(!usbdev->alive) {
127 device_remove(usbdev);
128 usb_disconnect(usbdev);
129 }
130 } ENDFOREACH
131}
132
133// Callback from write operation
134static void tx_callback(struct libusb_transfer *xfer)
135{
136 struct usb_device *dev = xfer->user_data;
137 usbmuxd_log(LL_SPEW, "TX callback dev %d-%d len %d -> %d status %d", dev->bus, dev->address, xfer->length, xfer->actual_length, xfer->status);
138 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
139 switch(xfer->status) {
140 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
141 case LIBUSB_TRANSFER_ERROR:
142 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
143 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
144 break;
145 case LIBUSB_TRANSFER_TIMED_OUT:
146 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
147 break;
148 case LIBUSB_TRANSFER_CANCELLED:
149 usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
150 break;
151 case LIBUSB_TRANSFER_STALL:
152 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
153 break;
154 case LIBUSB_TRANSFER_NO_DEVICE:
155 // other times, this happens, and also even when we abort the transfer after device removal
156 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
157 break;
158 case LIBUSB_TRANSFER_OVERFLOW:
159 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
160 break;
161 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
162 default:
163 // this should never be reached.
164 break;
165 }
166 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
167 // we'll do device_remove there too
168 dev->alive = 0;
169 }
170 if(xfer->buffer)
171 free(xfer->buffer);
172 collection_remove(&dev->tx_xfers, xfer);
173 libusb_free_transfer(xfer);
174}
175
176int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
177{
178 int res;
179 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
180 libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_out, (void*)buf, length, tx_callback, dev, 0);
181 if((res = libusb_submit_transfer(xfer)) < 0) {
182 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %s", buf, length, dev->bus, dev->address, libusb_error_name(res));
183 libusb_free_transfer(xfer);
184 return res;
185 }
186 collection_add(&dev->tx_xfers, xfer);
187 if (length % dev->wMaxPacketSize == 0) {
188 usbmuxd_log(LL_DEBUG, "Send ZLP");
189 // Send Zero Length Packet
190 xfer = libusb_alloc_transfer(0);
191 void *buffer = malloc(1);
192 libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_out, buffer, 0, tx_callback, dev, 0);
193 if((res = libusb_submit_transfer(xfer)) < 0) {
194 usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res));
195 libusb_free_transfer(xfer);
196 return res;
197 }
198 collection_add(&dev->tx_xfers, xfer);
199 }
200 return 0;
201}
202
203// Callback from read operation
204// Under normal operation this issues a new read transfer request immediately,
205// doing a kind of read-callback loop
206static void rx_callback(struct libusb_transfer *xfer)
207{
208 struct usb_device *dev = xfer->user_data;
209 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
210 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
211 device_data_input(dev, xfer->buffer, xfer->actual_length);
212 libusb_submit_transfer(xfer);
213 } else {
214 switch(xfer->status) {
215 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
216 case LIBUSB_TRANSFER_ERROR:
217 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
218 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
219 break;
220 case LIBUSB_TRANSFER_TIMED_OUT:
221 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
222 break;
223 case LIBUSB_TRANSFER_CANCELLED:
224 usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
225 break;
226 case LIBUSB_TRANSFER_STALL:
227 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
228 break;
229 case LIBUSB_TRANSFER_NO_DEVICE:
230 // other times, this happens, and also even when we abort the transfer after device removal
231 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
232 break;
233 case LIBUSB_TRANSFER_OVERFLOW:
234 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
235 break;
236 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
237 default:
238 // this should never be reached.
239 break;
240 }
241
242 free(xfer->buffer);
243 collection_remove(&dev->rx_xfers, xfer);
244 libusb_free_transfer(xfer);
245
246 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
247 // we'll do device_remove there too
248 dev->alive = 0;
249 }
250}
251
252// Start a read-callback loop for this device
253static int start_rx_loop(struct usb_device *dev)
254{
255 int res;
256 void *buf;
257 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
258 buf = malloc(USB_MRU);
259 libusb_fill_bulk_transfer(xfer, dev->handle, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0);
260 if((res = libusb_submit_transfer(xfer)) != 0) {
261 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %s", dev->bus, dev->address, libusb_error_name(res));
262 libusb_free_transfer(xfer);
263 return res;
264 }
265
266 collection_add(&dev->rx_xfers, xfer);
267
268 return 0;
269}
270
271static void get_serial_callback(struct libusb_transfer *transfer)
272{
273 unsigned int di, si;
274 struct usb_device *usbdev = transfer->user_data;
275
276 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
277 usbmuxd_log(LL_ERROR, "Failed to request serial for device %d-%d (%i)", usbdev->bus, usbdev->address, transfer->status);
278 libusb_free_transfer(transfer);
279 return;
280 }
281
282 /* De-unicode, taken from libusb */
283 unsigned char *data = libusb_control_transfer_get_data(transfer);
284 for (di = 0, si = 2; si < data[0] && di < sizeof(usbdev->serial)-1; si += 2) {
285 if ((data[si] & 0x80) || (data[si + 1])) /* non-ASCII */
286 usbdev->serial[di++] = '?';
287 else if (data[si] == '\0')
288 break;
289 else
290 usbdev->serial[di++] = data[si];
291 }
292 usbdev->serial[di] = '\0';
293
294 usbmuxd_log(LL_INFO, "Got serial '%s' for device %d-%d", usbdev->serial, usbdev->bus, usbdev->address);
295
296 libusb_free_transfer(transfer);
297
298 /* new style UDID: add hyphen between first 8 and following 16 digits */
299 if (di == 24) {
300 memmove(&usbdev->serial[9], &usbdev->serial[8], 16);
301 usbdev->serial[8] = '-';
302 usbdev->serial[di+1] = '\0';
303 }
304
305 /* Finish setup now */
306 if(device_add(usbdev) < 0) {
307 usb_disconnect(usbdev);
308 return;
309 }
310
311 // Spin up NUM_RX_LOOPS parallel usb data retrieval loops
312 // Old usbmuxds used only 1 rx loop, but that leaves the
313 // USB port sleeping most of the time
314 int rx_loops = NUM_RX_LOOPS;
315 for (rx_loops = NUM_RX_LOOPS; rx_loops > 0; rx_loops--) {
316 if(start_rx_loop(usbdev) < 0) {
317 usbmuxd_log(LL_WARNING, "Failed to start RX loop number %d", NUM_RX_LOOPS - rx_loops);
318 break;
319 }
320 }
321
322 // Ensure we have at least 1 RX loop going
323 if (rx_loops == NUM_RX_LOOPS) {
324 usbmuxd_log(LL_FATAL, "Failed to start any RX loop for device %d-%d",
325 usbdev->bus, usbdev->address);
326 device_remove(usbdev);
327 usb_disconnect(usbdev);
328 return;
329 } else if (rx_loops > 0) {
330 usbmuxd_log(LL_WARNING, "Failed to start all %d RX loops. Going on with %d loops. "
331 "This may have negative impact on device read speed.",
332 NUM_RX_LOOPS, NUM_RX_LOOPS - rx_loops);
333 } else {
334 usbmuxd_log(LL_DEBUG, "All %d RX loops started successfully", NUM_RX_LOOPS);
335 }
336}
337
338static void get_langid_callback(struct libusb_transfer *transfer)
339{
340 int res;
341 struct usb_device *usbdev = transfer->user_data;
342
343 transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
344
345 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
346 usbmuxd_log(LL_ERROR, "Failed to request lang ID for device %d-%d (%i)", usbdev->bus,
347 usbdev->address, transfer->status);
348 libusb_free_transfer(transfer);
349 return;
350 }
351
352 unsigned char *data = libusb_control_transfer_get_data(transfer);
353 uint16_t langid = (uint16_t)(data[2] | (data[3] << 8));
354 usbmuxd_log(LL_INFO, "Got lang ID %u for device %d-%d", langid, usbdev->bus, usbdev->address);
355
356 /* re-use the same transfer */
357 libusb_fill_control_setup(transfer->buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR,
358 (uint16_t)((LIBUSB_DT_STRING << 8) | usbdev->devdesc.iSerialNumber),
359 langid, 1024 + LIBUSB_CONTROL_SETUP_SIZE);
360 libusb_fill_control_transfer(transfer, usbdev->handle, transfer->buffer, get_serial_callback, usbdev, 1000);
361
362 if((res = libusb_submit_transfer(transfer)) < 0) {
363 usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res));
364 libusb_free_transfer(transfer);
365 }
366}
367
368static int submit_vendor_specific(struct libusb_device_handle *handle, struct mode_context *context, libusb_transfer_cb_fn callback)
369{
370 struct libusb_transfer* ctrl_transfer = libusb_alloc_transfer(0);
371 int ret = 0;
372 unsigned char* buffer = calloc(LIBUSB_CONTROL_SETUP_SIZE + context->wLength, 1);
373 uint8_t bRequestType = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE;
374 libusb_fill_control_setup(buffer, bRequestType, context->bRequest, context->wValue, context->wIndex, context->wLength);
375
376 ctrl_transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
377 libusb_fill_control_transfer(ctrl_transfer, handle, buffer, callback, context, context->timeout);
378
379 ret = libusb_submit_transfer(ctrl_transfer);
380 return ret;
381}
382
383static struct usb_device* find_device(int bus, int address)
384{
385 FOREACH(struct usb_device *usbdev, &device_list) {
386 if(usbdev->bus == bus && usbdev->address == address) {
387 return usbdev;
388 }
389 } ENDFOREACH
390 return NULL;
391}
392
393/// @brief guess the current mode
394/// @param dev
395/// @param usbdev
396/// @param handle
397/// @return 0 - undetermined, 1 - initial, 2 - valeria, 3 - cdc_ncm
398static int guess_mode(struct libusb_device* dev, struct usb_device *usbdev)
399{
400 int res, j;
401 int has_valeria = 0, has_cdc_ncm = 0, has_usbmux = 0;
402 struct libusb_device_descriptor devdesc = usbdev->devdesc;
403 struct libusb_config_descriptor *config;
404 int bus = usbdev->bus;
405 int address = usbdev->address;
406
407 if(devdesc.bNumConfigurations <= 4) {
408 // Assume this is initial mode
409 return 1;
410 }
411
412 if(devdesc.bNumConfigurations != 5) {
413 // No known modes with more then 5 configurations
414 return 0;
415 }
416
417 if((res = libusb_get_config_descriptor_by_value(dev, 5, &config)) != 0) {
418 usbmuxd_log(LL_NOTICE, "Could not get configuration 5 descriptor for device %i-%i: %s", bus, address, libusb_error_name(res));
419 return 0;
420 }
421
422 // Require both usbmux and one of the other interfaces to determine this is a valid configuration
423 for(j = 0 ; j < config->bNumInterfaces ; j++) {
424 const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0];
425 if(intf->bInterfaceClass == INTERFACE_CLASS &&
426 intf->bInterfaceSubClass == 42 &&
427 intf->bInterfaceProtocol == 255) {
428 has_valeria = 1;
429 }
430 // https://github.com/torvalds/linux/blob/72a85e2b0a1e1e6fb4ee51ae902730212b2de25c/include/uapi/linux/usb/cdc.h#L22
431 // 2 for Communication class, 0xd for CDC NCM subclass
432 if(intf->bInterfaceClass == 2 &&
433 intf->bInterfaceSubClass == 0xd) {
434 has_cdc_ncm = 1;
435 }
436 if(intf->bInterfaceClass == INTERFACE_CLASS &&
437 intf->bInterfaceSubClass == INTERFACE_SUBCLASS &&
438 intf->bInterfaceProtocol == INTERFACE_PROTOCOL) {
439 has_usbmux = 1;
440 }
441 }
442
443 libusb_free_config_descriptor(config);
444
445 if(has_valeria && has_usbmux) {
446 usbmuxd_log(LL_NOTICE, "Found Valeria and Apple USB Multiplexor in device %i-%i configuration 5", bus, address);
447 return 2;
448 }
449
450 if(has_cdc_ncm && has_usbmux) {
451 usbmuxd_log(LL_NOTICE, "Found CDC-NCM and Apple USB Multiplexor in device %i-%i configuration 5", bus, address);
452 return 3;
453 }
454
455 return 0;
456}
457
458/// @brief Finds and sets the valid configuration, interface and endpoints on the usb_device
459static int set_valid_configuration(struct libusb_device* dev, struct usb_device *usbdev, struct libusb_device_handle *handle)
460{
461 int j, k, res, found = 0;
462 struct libusb_config_descriptor *config;
463 const struct libusb_interface_descriptor *intf;
464 struct libusb_device_descriptor devdesc = usbdev->devdesc;
465 int bus = usbdev->bus;
466 int address = usbdev->address;
467 int current_config = 0;
468
469 if((res = libusb_get_configuration(handle, &current_config)) != 0) {
470 usbmuxd_log(LL_WARNING, "Could not get current configuration for device %d-%d: %s", bus, address, libusb_error_name(res));
471 return -1;
472 }
473
474 for(j = devdesc.bNumConfigurations ; j > 0 ; j--) {
475 if((res = libusb_get_config_descriptor_by_value(dev, j, &config)) != 0) {
476 usbmuxd_log(LL_NOTICE, "Could not get configuration %i descriptor for device %i-%i: %s", j, bus, address, libusb_error_name(res));
477 continue;
478 }
479 for(k = 0 ; k < config->bNumInterfaces ; k++) {
480 intf = &config->interface[k].altsetting[0];
481 if(intf->bInterfaceClass == INTERFACE_CLASS ||
482 intf->bInterfaceSubClass == INTERFACE_SUBCLASS ||
483 intf->bInterfaceProtocol == INTERFACE_PROTOCOL) {
484 usbmuxd_log(LL_NOTICE, "Found usbmux interface for device %i-%i: %i", bus, address, intf->bInterfaceNumber);
485 if(intf->bNumEndpoints != 2) {
486 usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address);
487 continue;
488 }
489 if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
490 (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
491 usbdev->interface = intf->bInterfaceNumber;
492 usbdev->ep_out = intf->endpoint[0].bEndpointAddress;
493 usbdev->ep_in = intf->endpoint[1].bEndpointAddress;
494 usbmuxd_log(LL_INFO, "Found interface %i with endpoints %02x/%02x for device %i-%i", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address);
495 found = 1;
496 break;
497 } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT &&
498 (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
499 usbdev->interface = intf->bInterfaceNumber;
500 usbdev->ep_out = intf->endpoint[1].bEndpointAddress;
501 usbdev->ep_in = intf->endpoint[0].bEndpointAddress;
502 usbmuxd_log(LL_INFO, "Found interface %i with swapped endpoints %02x/%02x for device %i-%i", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address);
503 found = 1;
504 break;
505 } else {
506 usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %i of device %i-%i", intf->bInterfaceNumber, bus, address);
507 }
508 }
509 }
510 if(!found) {
511 libusb_free_config_descriptor(config);
512 continue;
513 }
514 // If set configuration is required, try to first detach all kernel drivers
515 if (current_config == 0) {
516 usbmuxd_log(LL_DEBUG, "Device %d-%d is unconfigured", bus, address);
517 }
518 if(current_config == 0 || config->bConfigurationValue != current_config) {
519 usbmuxd_log(LL_NOTICE, "Changing configuration of device %i-%i: %i -> %i", bus, address, current_config, config->bConfigurationValue);
520 for(k=0 ; k < config->bNumInterfaces ; k++) {
521 const struct libusb_interface_descriptor *intf1 = &config->interface[k].altsetting[0];
522 if((res = libusb_kernel_driver_active(handle, intf1->bInterfaceNumber)) < 0) {
523 usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %s", intf1->bInterfaceNumber, bus, address, libusb_error_name(res));
524 continue;
525 }
526 if(res == 1) {
527 usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf1->bInterfaceNumber);
528 if((res = libusb_detach_kernel_driver(handle, intf1->bInterfaceNumber)) < 0) {
529 usbmuxd_log(LL_WARNING, "Could not detach kernel driver, configuration change will probably fail! %s", libusb_error_name(res));
530 continue;
531 }
532 }
533 }
534 if((res = libusb_set_configuration(handle, j)) != 0) {
535 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %s", j, bus, address, libusb_error_name(res));
536 libusb_free_config_descriptor(config);
537 continue;
538 }
539 }
540
541 libusb_free_config_descriptor(config);
542 break;
543 }
544
545 if(!found) {
546 usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %i-%i", bus, address);
547 return -1;
548 }
549
550 return 0;
551}
552
553static void device_complete_initialization(struct mode_context *context, struct libusb_device_handle *handle)
554{
555 struct usb_device *usbdev = find_device(context->bus, context->address);
556 if(!usbdev) {
557 usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting initialization", context->bus, context->address);
558 return;
559 }
560 struct libusb_device *dev = context->dev;
561 struct libusb_device_descriptor devdesc = usbdev->devdesc;
562 int bus = context->bus;
563 int address = context->address;
564 int res;
565 struct libusb_transfer *transfer;
566
567 if((res = set_valid_configuration(dev, usbdev, handle)) != 0) {
568 usbdev->alive = 0;
569 return;
570 }
571
572 if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) {
573 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %s", usbdev->interface, bus, address, libusb_error_name(res));
574 usbdev->alive = 0;
575 return;
576 }
577
578 transfer = libusb_alloc_transfer(0);
579 if(!transfer) {
580 usbmuxd_log(LL_WARNING, "Failed to allocate transfer for device %d-%d: %s", bus, address, libusb_error_name(res));
581 usbdev->alive = 0;
582 return;
583 }
584
585 unsigned char *transfer_buffer = malloc(1024 + LIBUSB_CONTROL_SETUP_SIZE + 8);
586 if (!transfer_buffer) {
587 usbmuxd_log(LL_WARNING, "Failed to allocate transfer buffer for device %d-%d: %s", bus, address, libusb_error_name(res));
588 usbdev->alive = 0;
589 return;
590 }
591 memset(transfer_buffer, '\0', 1024 + LIBUSB_CONTROL_SETUP_SIZE + 8);
592
593 usbdev->serial[0] = 0;
594 usbdev->bus = bus;
595 usbdev->address = address;
596 usbdev->devdesc = devdesc;
597 usbdev->speed = 480000000;
598 usbdev->handle = handle;
599 usbdev->alive = 1;
600 usbdev->wMaxPacketSize = libusb_get_max_packet_size(dev, usbdev->ep_out);
601 if (usbdev->wMaxPacketSize <= 0) {
602 usbmuxd_log(LL_ERROR, "Could not determine wMaxPacketSize for device %d-%d, setting to 64", usbdev->bus, usbdev->address);
603 usbdev->wMaxPacketSize = 64;
604 } else {
605 usbmuxd_log(LL_INFO, "Using wMaxPacketSize=%d for device %d-%d", usbdev->wMaxPacketSize, usbdev->bus, usbdev->address);
606 }
607
608 switch (libusb_get_device_speed(dev)) {
609 case LIBUSB_SPEED_LOW:
610 usbdev->speed = 1500000;
611 break;
612 case LIBUSB_SPEED_FULL:
613 usbdev->speed = 12000000;
614 break;
615 case LIBUSB_SPEED_SUPER:
616 usbdev->speed = 5000000000;
617 break;
618 case LIBUSB_SPEED_HIGH:
619 case LIBUSB_SPEED_UNKNOWN:
620 default:
621 usbdev->speed = 480000000;
622 break;
623 }
624
625 usbmuxd_log(LL_INFO, "USB Speed is %g MBit/s for device %d-%d", (double)(usbdev->speed / 1000000.0), usbdev->bus, usbdev->address);
626
627 /**
628 * From libusb:
629 * Asking for the zero'th index is special - it returns a string
630 * descriptor that contains all the language IDs supported by the
631 * device.
632 **/
633 libusb_fill_control_setup(transfer_buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8, 0, 1024 + LIBUSB_CONTROL_SETUP_SIZE);
634 libusb_fill_control_transfer(transfer, handle, transfer_buffer, get_langid_callback, usbdev, 1000);
635
636 if((res = libusb_submit_transfer(transfer)) < 0) {
637 usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d: %s", usbdev->bus, usbdev->address, libusb_error_name(res));
638 libusb_free_transfer(transfer);
639 free(transfer_buffer);
640 usbdev->alive = 0;
641 return;
642 }
643}
644
645static void switch_mode_cb(struct libusb_transfer* transfer)
646{
647 // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode
648 struct mode_context* context = transfer->user_data;
649 struct usb_device *dev = find_device(context->bus, context->address);
650 if(!dev) {
651 usbmuxd_log(LL_WARNING, "Device %d-%d is missing from device list", context->bus, context->address);
652 }
653 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
654 usbmuxd_log(LL_ERROR, "Failed to request mode switch for device %i-%i (%i). Completing initialization in current mode",
655 context->bus, context->address, transfer->status);
656 device_complete_initialization(context, transfer->dev_handle);
657 }
658 else {
659 unsigned char *data = libusb_control_transfer_get_data(transfer);
660 if(data[0] != 0) {
661 usbmuxd_log(LL_INFO, "Received unexpected response for device %i-%i mode switch (%i). Completing initialization in current mode",
662 context->bus, context->address, data[0]);
663 device_complete_initialization(context, transfer->dev_handle);
664 }
665 }
666 free(context);
667 if(transfer->buffer)
668 free(transfer->buffer);
669}
670
671static void get_mode_cb(struct libusb_transfer* transfer)
672{
673 // For old devices not supporting mode swtich, if anything goes wrong - continue in current mode
674 int res;
675 struct mode_context* context = transfer->user_data;
676 struct usb_device *dev = find_device(context->bus, context->address);
677 if(!dev) {
678 usbmuxd_log(LL_ERROR, "Device %d-%d is missing from device list, aborting mode switch", context->bus, context->address);
679 free(context);
680 return;
681 }
682
683 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
684 usbmuxd_log(LL_ERROR, "Failed to request get mode for device %i-%i (%i). Completing initialization in current mode",
685 context->bus, context->address, transfer->status);
686 device_complete_initialization(context, transfer->dev_handle);
687 free(context);
688 return;
689 }
690
691 unsigned char *data = libusb_control_transfer_get_data(transfer);
692
693 char* desired_mode_char = getenv(ENV_DEVICE_MODE);
694 int desired_mode = desired_mode_char ? atoi(desired_mode_char) : 3;
695 int guessed_mode = guess_mode(context->dev, dev);
696
697 // Response is 3:3:3:0 for initial mode, 5:3:3:0 otherwise.
698 usbmuxd_log(LL_INFO, "Received response %i:%i:%i:%i for get_mode request for device %i-%i", data[0], data[1], data[2], data[3], context->bus, context->address);
699 if(desired_mode >= 1 && desired_mode <= 3 &&
700 guessed_mode > 0 && // do not switch mode if guess failed
701 guessed_mode != desired_mode) {
702 usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", context->bus, context->address, desired_mode);
703
704 context->bRequest = APPLE_VEND_SPECIFIC_SET_MODE;
705 context->wValue = 0;
706 context->wIndex = desired_mode;
707 context->wLength = 1;
708
709 if((res = submit_vendor_specific(transfer->dev_handle, context, switch_mode_cb)) != 0) {
710 usbmuxd_log(LL_WARNING, "Could not request to switch mode %i for device %i-%i (%i)", context->wIndex, context->bus, context->address, res);
711 dev->alive = 0;
712 free(context);
713 }
714 }
715 else {
716 usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode from %i to %i", context->bus, context->address, guessed_mode, desired_mode);
717 device_complete_initialization(context, transfer->dev_handle);
718 free(context);
719 }
720 if(transfer->buffer)
721 free(transfer->buffer);
722}
723
724static int usb_device_add(libusb_device* dev)
725{
726 int res;
727 // the following are non-blocking operations on the device list
728 uint8_t bus = libusb_get_bus_number(dev);
729 uint8_t address = libusb_get_device_address(dev);
730 struct libusb_device_descriptor devdesc;
731 struct usb_device *usbdev = find_device(bus, address);
732 if(usbdev) {
733 usbdev->alive = 1;
734 return 0; //device already found
735 }
736
737 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
738 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %s", bus, address, libusb_error_name(res));
739 return -1;
740 }
741 if(devdesc.idVendor != VID_APPLE)
742 return -1;
743 if((devdesc.idProduct != PID_APPLE_T2_COPROCESSOR) &&
744 ((devdesc.idProduct < PID_APPLE_SILICON_RESTORE_LOW) ||
745 (devdesc.idProduct > PID_APPLE_SILICON_RESTORE_MAX)) &&
746 ((devdesc.idProduct < PID_RANGE_LOW) ||
747 (devdesc.idProduct > PID_RANGE_MAX)))
748 return -1;
749 libusb_device_handle *handle;
750 usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
751 // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any
752 // blocking call
753 if((res = libusb_open(dev, &handle)) != 0) {
754 usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %s", bus, address, libusb_error_name(res));
755 return -1;
756 }
757
758 // Add the created handle to the device list, so we can close it in case of failure/disconnection
759 usbdev = malloc(sizeof(struct usb_device));
760 memset(usbdev, 0, sizeof(*usbdev));
761
762 usbdev->serial[0] = 0;
763 usbdev->bus = bus;
764 usbdev->address = address;
765 usbdev->devdesc = devdesc;
766 usbdev->speed = 0;
767 usbdev->handle = handle;
768 usbdev->alive = 1;
769
770 collection_init(&usbdev->tx_xfers);
771 collection_init(&usbdev->rx_xfers);
772
773 collection_add(&device_list, usbdev);
774
775 // On top of configurations, Apple have multiple "modes" for devices, namely:
776 // 1: An "initial" mode with 4 configurations
777 // 2: "Valeria" mode, where configuration 5 is included with interface for H.265 video capture (activated when recording screen with QuickTime in macOS)
778 // 3: "CDC NCM" mode, where configuration 5 is included with interface for Ethernet/USB (activated using internet-sharing feature in macOS)
779 // Request current mode asynchroniously, so it can be changed in callback if needed
780 usbmuxd_log(LL_INFO, "Requesting current mode from device %i-%i", bus, address);
781 struct mode_context* context = malloc(sizeof(struct mode_context));
782 context->dev = dev;
783 context->bus = bus;
784 context->address = address;
785 context->bRequest = APPLE_VEND_SPECIFIC_GET_MODE;
786 context->wValue = 0;
787 context->wIndex = 0;
788 context->wLength = 4;
789 context->timeout = 1000;
790
791 if(submit_vendor_specific(handle, context, get_mode_cb) != 0) {
792 usbmuxd_log(LL_WARNING, "Could not request current mode from device %d-%d", bus, address);
793 // Schedule device for close and cleanup
794 usbdev->alive = 0;
795 return -1;
796 }
797 return 0;
798}
799
800int usb_discover(void)
801{
802 int cnt, i;
803 int valid_count = 0;
804 libusb_device **devs;
805
806 cnt = libusb_get_device_list(NULL, &devs);
807 if(cnt < 0) {
808 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
809 devlist_failures++;
810 // sometimes libusb fails getting the device list if you've just removed something
811 if(devlist_failures > 5) {
812 usbmuxd_log(LL_FATAL, "Too many errors getting device list");
813 return cnt;
814 } else {
815 get_tick_count(&next_dev_poll_time);
816 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
817 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
818 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
819 return 0;
820 }
821 }
822 devlist_failures = 0;
823
824 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
825
826 // Mark all devices as dead, and do a mark-sweep like
827 // collection of dead devices
828 FOREACH(struct usb_device *usbdev, &device_list) {
829 usbdev->alive = 0;
830 } ENDFOREACH
831
832 // Enumerate all USB devices and mark the ones we already know
833 // about as live, again
834 for(i=0; i<cnt; i++) {
835 libusb_device *dev = devs[i];
836 if (usb_device_add(dev) < 0) {
837 continue;
838 }
839 valid_count++;
840 }
841
842 // Clean out any device we didn't mark back as live
843 reap_dead_devices();
844
845 libusb_free_device_list(devs, 1);
846
847 get_tick_count(&next_dev_poll_time);
848 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
849 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
850 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
851
852 return valid_count;
853}
854
855const char *usb_get_serial(struct usb_device *dev)
856{
857 if(!dev->handle) {
858 return NULL;
859 }
860 return dev->serial;
861}
862
863uint32_t usb_get_location(struct usb_device *dev)
864{
865 if(!dev->handle) {
866 return 0;
867 }
868 return (dev->bus << 16) | dev->address;
869}
870
871uint16_t usb_get_pid(struct usb_device *dev)
872{
873 if(!dev->handle) {
874 return 0;
875 }
876 return dev->devdesc.idProduct;
877}
878
879uint64_t usb_get_speed(struct usb_device *dev)
880{
881 if (!dev->handle) {
882 return 0;
883 }
884 return dev->speed;
885}
886
887void usb_get_fds(struct fdlist *list)
888{
889 const struct libusb_pollfd **usbfds;
890 const struct libusb_pollfd **p;
891 usbfds = libusb_get_pollfds(NULL);
892 if(!usbfds) {
893 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
894 return;
895 }
896 p = usbfds;
897 while(*p) {
898 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
899 p++;
900 }
901 free(usbfds);
902}
903
904void usb_autodiscover(int enable)
905{
906 usbmuxd_log(LL_DEBUG, "usb polling enable: %d", enable);
907 device_polling = enable;
908 device_hotplug = enable;
909}
910
911static int dev_poll_remain_ms(void)
912{
913 int msecs;
914 struct timeval tv;
915 if(!device_polling)
916 return 100000; // devices will never be polled if this is > 0
917 get_tick_count(&tv);
918 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
919 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
920 if(msecs < 0)
921 return 0;
922 return msecs;
923}
924
925int usb_get_timeout(void)
926{
927 struct timeval tv;
928 int msec;
929 int res;
930 int pollrem;
931 pollrem = dev_poll_remain_ms();
932 res = libusb_get_next_timeout(NULL, &tv);
933 if(res == 0)
934 return pollrem;
935 if(res < 0) {
936 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %s", libusb_error_name(res));
937 return pollrem;
938 }
939 msec = tv.tv_sec * 1000;
940 msec += tv.tv_usec / 1000;
941 if(msec > pollrem)
942 return pollrem;
943 return msec;
944}
945
946int usb_process(void)
947{
948 int res;
949 struct timeval tv;
950 tv.tv_sec = tv.tv_usec = 0;
951 res = libusb_handle_events_timeout(NULL, &tv);
952 if(res < 0) {
953 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res));
954 return res;
955 }
956
957 // reap devices marked dead due to an RX error
958 reap_dead_devices();
959
960 if(dev_poll_remain_ms() <= 0) {
961 res = usb_discover();
962 if(res < 0) {
963 usbmuxd_log(LL_ERROR, "usb_discover failed: %s", libusb_error_name(res));
964 return res;
965 }
966 }
967 return 0;
968}
969
970int usb_process_timeout(int msec)
971{
972 int res;
973 struct timeval tleft, tcur, tfin;
974 get_tick_count(&tcur);
975 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
976 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
977 tfin.tv_sec += tfin.tv_usec / 1000000;
978 tfin.tv_usec %= 1000000;
979 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
980 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
981 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
982 if(tleft.tv_usec < 0) {
983 tleft.tv_usec += 1000000;
984 tleft.tv_sec -= 1;
985 }
986 res = libusb_handle_events_timeout(NULL, &tleft);
987 if(res < 0) {
988 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %s", libusb_error_name(res));
989 return res;
990 }
991 // reap devices marked dead due to an RX error
992 reap_dead_devices();
993 get_tick_count(&tcur);
994 }
995 return 0;
996}
997
998#ifdef HAVE_LIBUSB_HOTPLUG_API
999static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
1000
1001static int usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
1002{
1003 if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
1004 if (device_hotplug) {
1005 usb_device_add(device);
1006 }
1007 } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
1008 uint8_t bus = libusb_get_bus_number(device);
1009 uint8_t address = libusb_get_device_address(device);
1010 FOREACH(struct usb_device *usbdev, &device_list) {
1011 if(usbdev->bus == bus && usbdev->address == address) {
1012 usbdev->alive = 0;
1013 device_remove(usbdev);
1014 break;
1015 }
1016 } ENDFOREACH
1017 } else {
1018 usbmuxd_log(LL_ERROR, "Unhandled event %d", event);
1019 }
1020 return 0;
1021}
1022#endif
1023
1024int usb_init(void)
1025{
1026 int res;
1027 const struct libusb_version* libusb_version_info = libusb_get_version();
1028 usbmuxd_log(LL_NOTICE, "Using libusb %d.%d.%d", libusb_version_info->major, libusb_version_info->minor, libusb_version_info->micro);
1029
1030 devlist_failures = 0;
1031 device_polling = 1;
1032 res = libusb_init(NULL);
1033
1034 if (res != 0) {
1035 usbmuxd_log(LL_FATAL, "libusb_init failed: %s", libusb_error_name(res));
1036 return -1;
1037 }
1038
1039#if LIBUSB_API_VERSION >= 0x01000106
1040 libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, (log_level >= LL_DEBUG ? LIBUSB_LOG_LEVEL_DEBUG: (log_level >= LL_WARNING ? LIBUSB_LOG_LEVEL_WARNING: LIBUSB_LOG_LEVEL_NONE)));
1041#else
1042 libusb_set_debug(NULL, (log_level >= LL_DEBUG ? LIBUSB_LOG_LEVEL_DEBUG: (log_level >= LL_WARNING ? LIBUSB_LOG_LEVEL_WARNING: LIBUSB_LOG_LEVEL_NONE)));
1043#endif
1044
1045 collection_init(&device_list);
1046
1047#ifdef HAVE_LIBUSB_HOTPLUG_API
1048 if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
1049 usbmuxd_log(LL_INFO, "Registering for libusb hotplug events");
1050 res = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, VID_APPLE, LIBUSB_HOTPLUG_MATCH_ANY, 0, usb_hotplug_cb, NULL, &usb_hotplug_cb_handle);
1051 if (res == LIBUSB_SUCCESS) {
1052 device_polling = 0;
1053 } else {
1054 usbmuxd_log(LL_ERROR, "ERROR: Could not register for libusb hotplug events. %s", libusb_error_name(res));
1055 }
1056 } else {
1057 usbmuxd_log(LL_ERROR, "libusb does not support hotplug events");
1058 }
1059#endif
1060 if (device_polling) {
1061 res = usb_discover();
1062 if (res >= 0) {
1063 }
1064 } else {
1065 res = collection_count(&device_list);
1066 }
1067 return res;
1068}
1069
1070void usb_shutdown(void)
1071{
1072 usbmuxd_log(LL_DEBUG, "usb_shutdown");
1073
1074#ifdef HAVE_LIBUSB_HOTPLUG_API
1075 libusb_hotplug_deregister_callback(NULL, usb_hotplug_cb_handle);
1076#endif
1077
1078 FOREACH(struct usb_device *usbdev, &device_list) {
1079 device_remove(usbdev);
1080 usb_disconnect(usbdev);
1081 } ENDFOREACH
1082 collection_free(&device_list);
1083 libusb_exit(NULL);
1084}
diff --git a/src/usb.h b/src/usb.h
new file mode 100644
index 0000000..4e44cce
--- /dev/null
+++ b/src/usb.h
@@ -0,0 +1,73 @@
1/*
2 * usb.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 * Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
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 or version 3.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef USB_H
23#define USB_H
24
25#include <stdint.h>
26#include "utils.h"
27
28#define INTERFACE_CLASS 255
29#define INTERFACE_SUBCLASS 254
30#define INTERFACE_PROTOCOL 2
31
32// libusb fragments packets larger than this (usbfs limitation)
33// on input, this creates race conditions and other issues
34#define USB_MRU 16384
35
36// max transmission packet size
37// libusb fragments these too, but doesn't send ZLPs so we're safe
38// but we need to send a ZLP ourselves at the end (see usb-linux.c)
39// we're using 3 * 16384 to optimize for the fragmentation
40// this results in three URBs per full transfer, 32 USB packets each
41// if there are ZLP issues this should make them show up easily too
42#define USB_MTU (3 * 16384)
43
44#define USB_PACKET_SIZE 512
45
46#define VID_APPLE 0x5ac
47#define PID_RANGE_LOW 0x1290
48#define PID_RANGE_MAX 0x12af
49#define PID_APPLE_T2_COPROCESSOR 0x8600
50#define PID_APPLE_SILICON_RESTORE_LOW 0x1901
51#define PID_APPLE_SILICON_RESTORE_MAX 0x1905
52
53#define ENV_DEVICE_MODE "USBMUXD_DEFAULT_DEVICE_MODE"
54#define APPLE_VEND_SPECIFIC_GET_MODE 0x45
55#define APPLE_VEND_SPECIFIC_SET_MODE 0x52
56
57struct usb_device;
58
59int usb_init(void);
60void usb_shutdown(void);
61const char *usb_get_serial(struct usb_device *dev);
62uint32_t usb_get_location(struct usb_device *dev);
63uint16_t usb_get_pid(struct usb_device *dev);
64uint64_t usb_get_speed(struct usb_device *dev);
65void usb_get_fds(struct fdlist *list);
66int usb_get_timeout(void);
67int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
68int usb_discover(void);
69void usb_autodiscover(int enable);
70int usb_process(void);
71int usb_process_timeout(int msec);
72
73#endif
diff --git a/src/usbmuxd-proto.h b/src/usbmuxd-proto.h
new file mode 100644
index 0000000..93df00e
--- /dev/null
+++ b/src/usbmuxd-proto.h
@@ -0,0 +1,95 @@
1/*
2 * usbmuxd-proto.h
3 *
4 * Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
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 or version 3.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22/* Protocol definition for usbmuxd proxy protocol */
23#ifndef USBMUXD_PROTO_H
24#define USBMUXD_PROTO_H
25
26#include <stdint.h>
27#define USBMUXD_PROTOCOL_VERSION 0
28
29#if defined(WIN32) || defined(__CYGWIN__)
30#define USBMUXD_SOCKET_PORT 27015
31#else
32#define USBMUXD_SOCKET_FILE "/var/run/usbmuxd"
33#endif
34
35#ifdef __cplusplus
36extern "C" {
37#endif
38
39enum usbmuxd_result {
40 RESULT_OK = 0,
41 RESULT_BADCOMMAND = 1,
42 RESULT_BADDEV = 2,
43 RESULT_CONNREFUSED = 3,
44 // ???
45 // ???
46 RESULT_BADVERSION = 6,
47};
48
49enum usbmuxd_msgtype {
50 MESSAGE_RESULT = 1,
51 MESSAGE_CONNECT = 2,
52 MESSAGE_LISTEN = 3,
53 MESSAGE_DEVICE_ADD = 4,
54 MESSAGE_DEVICE_REMOVE = 5,
55 MESSAGE_DEVICE_PAIRED = 6,
56 //???
57 MESSAGE_PLIST = 8,
58};
59
60struct usbmuxd_header {
61 uint32_t length; // length of message, including header
62 uint32_t version; // protocol version
63 uint32_t message; // message type
64 uint32_t tag; // responses to this query will echo back this tag
65} __attribute__((__packed__));
66
67struct usbmuxd_result_msg {
68 struct usbmuxd_header header;
69 uint32_t result;
70} __attribute__((__packed__));
71
72struct usbmuxd_connect_request {
73 struct usbmuxd_header header;
74 uint32_t device_id;
75 uint16_t port; // TCP port number
76 uint16_t reserved; // set to zero
77} __attribute__((__packed__));
78
79struct usbmuxd_listen_request {
80 struct usbmuxd_header header;
81} __attribute__((__packed__));
82
83struct usbmuxd_device_record {
84 uint32_t device_id;
85 uint16_t product_id;
86 char serial_number[256];
87 uint16_t padding;
88 uint32_t location;
89} __attribute__((__packed__));
90
91#ifdef __cplusplus
92}
93#endif
94
95#endif /* USBMUXD_PROTO_H */
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..2cc5675
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,131 @@
1/*
2 * utils.c
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 * Copyright (c) 2013 Federico Mena Quintero
7 *
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation, either version 2.1 of the
11 * License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include <stdarg.h>
31#include <time.h>
32#include <sys/time.h>
33#include <errno.h>
34#ifdef __APPLE__
35#include <mach/mach_time.h>
36#endif
37
38#include "utils.h"
39
40#include "log.h"
41#define util_error(...) usbmuxd_log(LL_ERROR, __VA_ARGS__)
42
43void fdlist_create(struct fdlist *list)
44{
45 list->count = 0;
46 list->capacity = 4;
47 list->owners = malloc(sizeof(*list->owners) * list->capacity);
48 list->fds = malloc(sizeof(*list->fds) * list->capacity);
49}
50void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
51{
52 if(list->count == list->capacity) {
53 list->capacity *= 2;
54 list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity);
55 list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity);
56 }
57 list->owners[list->count] = owner;
58 list->fds[list->count].fd = fd;
59 list->fds[list->count].events = events;
60 list->fds[list->count].revents = 0;
61 list->count++;
62}
63
64void fdlist_free(struct fdlist *list)
65{
66 list->count = 0;
67 list->capacity = 0;
68 free(list->owners);
69 list->owners = NULL;
70 free(list->fds);
71 list->fds = NULL;
72}
73
74void fdlist_reset(struct fdlist *list)
75{
76 list->count = 0;
77}
78
79#ifndef HAVE_CLOCK_GETTIME
80typedef int clockid_t;
81#define CLOCK_MONOTONIC 1
82
83static int clock_gettime(clockid_t clk_id, struct timespec *ts)
84{
85 // See http://developer.apple.com/library/mac/qa/qa1398
86
87 uint64_t mach_time, nano_sec;
88
89 static mach_timebase_info_data_t base_info;
90
91 mach_time = mach_absolute_time();
92
93 if (base_info.denom == 0) {
94 (void) mach_timebase_info(&base_info);
95 }
96
97 if (base_info.numer == 1 && base_info.denom == 1)
98 nano_sec = mach_time;
99 else
100 nano_sec = mach_time * base_info.numer / base_info.denom;
101
102 ts->tv_sec = nano_sec / 1000000000;
103 ts->tv_nsec = nano_sec % 1000000000;
104
105 return 0;
106}
107#endif
108
109void get_tick_count(struct timeval * tv)
110{
111 struct timespec ts;
112 if(0 == clock_gettime(CLOCK_MONOTONIC, &ts)) {
113 tv->tv_sec = ts.tv_sec;
114 tv->tv_usec = ts.tv_nsec / 1000;
115 } else {
116 gettimeofday(tv, NULL);
117 }
118}
119
120/**
121 * Get number of milliseconds since the epoch.
122 */
123uint64_t mstime64(void)
124{
125 struct timeval tv;
126 get_tick_count(&tv);
127
128 // Careful, avoid overflow on 32 bit systems
129 // time_t could be 4 bytes
130 return ((long long)tv.tv_sec) * 1000LL + ((long long)tv.tv_usec) / 1000LL;
131}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..ce3b2e0
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,49 @@
1/*
2 * utils.h
3 *
4 * Copyright (C) 2009 Hector Martin <hector@marcansoft.com>
5 * Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 2.1 of the
10 * License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef UTILS_H
23#define UTILS_H
24
25#include <poll.h>
26#include <plist/plist.h>
27
28enum fdowner {
29 FD_LISTEN,
30 FD_CLIENT,
31 FD_USB
32};
33
34struct fdlist {
35 int count;
36 int capacity;
37 enum fdowner *owners;
38 struct pollfd *fds;
39};
40
41void fdlist_create(struct fdlist *list);
42void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
43void fdlist_free(struct fdlist *list);
44void fdlist_reset(struct fdlist *list);
45
46uint64_t mstime64(void);
47void get_tick_count(struct timeval * tv);
48
49#endif