summaryrefslogtreecommitdiffstats
path: root/daemon
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2009-08-25 03:26:22 +0200
committerGravatar Nikias Bassen2009-08-25 03:26:22 +0200
commit4711a2b493f76561e9803bf7e8be77186f3e7798 (patch)
tree339684fe1b996e01047c3eb220a8e22e4491c5e2 /daemon
parentde30ca5d5c98a8cbee3d8748601519e2263b3e1d (diff)
downloadusbmuxd-4711a2b493f76561e9803bf7e8be77186f3e7798.tar.gz
usbmuxd-4711a2b493f76561e9803bf7e8be77186f3e7798.tar.bz2
Renamed directory 'usbmuxd' to more suitable 'daemon'.
Diffstat (limited to 'daemon')
-rw-r--r--daemon/CMakeLists.txt32
-rw-r--r--daemon/client.c430
-rw-r--r--daemon/client.h46
-rw-r--r--daemon/device.c751
-rw-r--r--daemon/device.h52
-rw-r--r--daemon/log.c94
-rw-r--r--daemon/log.h43
-rw-r--r--daemon/main.c548
-rw-r--r--daemon/usb-linux.c542
-rw-r--r--daemon/usb.h66
10 files changed, 2604 insertions, 0 deletions
diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt
new file mode 100644
index 0000000..45565d1
--- /dev/null
+++ b/daemon/CMakeLists.txt
@@ -0,0 +1,32 @@
1find_package(USB REQUIRED)
2include_directories(${USB_INCLUDE_DIRS})
3set(LIBS ${LIBS} ${USB_LIBRARIES})
4
5include_directories (${CMAKE_SOURCE_DIR}/common)
6include_directories (${CMAKE_SOURCE_DIR}/daemon)
7include_directories (${CMAKE_SOURCE_DIR}/libusbmuxd)
8
9include(CheckConstantExists)
10set(CMAKE_REQUIRED_INCLUDES ${USB_INCLUDE_DIRS})
11check_constant_exists(LIBUSB_TRANSFER_ZERO_PACKET libusb.h HAVE_LIBUSB_ZLP)
12
13if(NOT HAVE_LIBUSB_ZLP)
14 message("
15================================================================================
16==================================== WARNING ===================================
17================================================================================
18Your libusb is missing proper Zero Length Packet support! If you are using a
19recent libusb Git, things may or may not work. If you are using libusb 1.0.2 or
20earlier, things will definitely not work properly.
21
22Please apply the patch in the contrib/ directory to your libusb 1.0 tree.
23================================================================================
24")
25 add_definitions(-DEXPLICIT_ZLP_TRANSACTION)
26endif(NOT HAVE_LIBUSB_ZLP)
27
28add_definitions(-Wall -O2 -g -DUSBMUXD_DAEMON)
29add_executable(usbmuxd main.c usb-linux.c log.c ../common/utils.c device.c client.c)
30target_link_libraries(usbmuxd ${LIBS})
31
32install(TARGETS usbmuxd RUNTIME DESTINATION sbin)
diff --git a/daemon/client.c b/daemon/client.c
new file mode 100644
index 0000000..0e47e84
--- /dev/null
+++ b/daemon/client.c
@@ -0,0 +1,430 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <errno.h>
27#include <unistd.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <arpa/inet.h>
32
33#include "log.h"
34#include "usb.h"
35#include "client.h"
36#include "device.h"
37
38#define CMD_BUF_SIZE 256
39#define REPLY_BUF_SIZE 1024
40
41enum client_state {
42 CLIENT_COMMAND, // waiting for command
43 CLIENT_LISTEN, // listening for devices
44 CLIENT_CONNECTING1, // issued connection request
45 CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
46 CLIENT_CONNECTED, // connected
47 CLIENT_DEAD
48};
49
50struct mux_client {
51 int fd;
52 unsigned char *ob_buf;
53 int ob_size;
54 int ob_capacity;
55 unsigned char *ib_buf;
56 int ib_size;
57 int ib_capacity;
58 short events, devents;
59 uint32_t connect_tag;
60 int connect_device;
61 enum client_state state;
62};
63
64static struct collection client_list;
65
66int client_read(struct mux_client *client, void *buffer, int len)
67{
68 usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
69 if(client->state != CLIENT_CONNECTED) {
70 usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
71 return -1;
72 }
73 return recv(client->fd, buffer, len, 0);
74}
75
76int client_write(struct mux_client *client, void *buffer, int len)
77{
78 usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
79 if(client->state != CLIENT_CONNECTED) {
80 usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
81 return -1;
82 }
83 return send(client->fd, buffer, len, 0);
84}
85
86int client_set_events(struct mux_client *client, short events)
87{
88 if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
89 usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
90 return -1;
91 }
92 client->devents = events;
93 if(client->state == CLIENT_CONNECTED)
94 client->events = events;
95 return 0;
96}
97
98int client_accept(int listenfd)
99{
100 struct sockaddr_un addr;
101 int cfd;
102 socklen_t len = sizeof(struct sockaddr_un);
103 cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
104 if (cfd < 0) {
105 usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
106 return cfd;
107 }
108
109 struct mux_client *client;
110 client = malloc(sizeof(struct mux_client));
111 memset(client, 0, sizeof(struct mux_client));
112
113 client->fd = cfd;
114 client->ob_buf = malloc(REPLY_BUF_SIZE);
115 client->ob_size = 0;
116 client->ob_capacity = REPLY_BUF_SIZE;
117 client->ib_buf = malloc(CMD_BUF_SIZE);
118 client->ib_size = 0;
119 client->ib_capacity = CMD_BUF_SIZE;
120 client->state = CLIENT_COMMAND;
121 client->events = POLLIN;
122
123 collection_add(&client_list, client);
124
125 usbmuxd_log(LL_INFO, "New client on fd %d", client->fd);
126 return client->fd;
127}
128
129void client_close(struct mux_client *client)
130{
131 usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd);
132 if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
133 usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
134 client->state = CLIENT_DEAD;
135 device_abort_connect(client->connect_device, client);
136 }
137 close(client->fd);
138 if(client->ob_buf)
139 free(client->ob_buf);
140 if(client->ib_buf)
141 free(client->ib_buf);
142 collection_remove(&client_list, client);
143 free(client);
144}
145
146void client_get_fds(struct fdlist *list)
147{
148 FOREACH(struct mux_client *client, &client_list) {
149 fdlist_add(list, FD_CLIENT, client->fd, client->events);
150 } ENDFOREACH
151}
152
153static int send_pkt(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length)
154{
155 struct usbmuxd_header hdr;
156 hdr.version = USBMUXD_PROTOCOL_VERSION;
157 hdr.length = sizeof(hdr) + payload_length;
158 hdr.message = msg;
159 hdr.tag = tag;
160 usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
161 if((client->ob_capacity - client->ob_size) < hdr.length) {
162 usbmuxd_log(LL_ERROR, "Client %d output buffer full (%d bytes) while sending message %d (%d bytes)", client->fd, client->ob_capacity, hdr.message, hdr.length);
163 client_close(client);
164 return -1;
165 }
166 memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
167 if(payload && payload_length)
168 memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
169 client->ob_size += hdr.length;
170 client->events |= POLLOUT;
171 return hdr.length;
172}
173
174static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
175{
176 return send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
177}
178
179int client_notify_connect(struct mux_client *client, enum usbmuxd_result result)
180{
181 usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
182 if(client->state == CLIENT_DEAD)
183 return -1;
184 if(client->state != CLIENT_CONNECTING1) {
185 usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
186 return -1;
187 }
188 if(send_result(client, client->connect_tag, result) < 0)
189 return -1;
190 if(result == RESULT_OK) {
191 client->state = CLIENT_CONNECTING2;
192 client->events = POLLOUT; // wait for the result packet to go through
193 // no longer need this
194 free(client->ib_buf);
195 client->ib_buf = NULL;
196 } else {
197 client->state = CLIENT_COMMAND;
198 }
199 return 0;
200}
201
202static int notify_device(struct mux_client *client, struct device_info *dev)
203{
204 struct usbmuxd_device_record dmsg;
205 memset(&dmsg, 0, sizeof(dmsg));
206 dmsg.device_id = dev->id;
207 strncpy(dmsg.serial_number, dev->serial, 256);
208 dmsg.serial_number[255] = 0;
209 dmsg.location = dev->location;
210 dmsg.product_id = dev->pid;
211 return send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
212}
213
214static int start_listen(struct mux_client *client)
215{
216 struct device_info *devs;
217 struct device_info *dev;
218 int count, i;
219
220 client->state = CLIENT_LISTEN;
221 count = device_get_count();
222 if(!count)
223 return 0;
224 devs = malloc(sizeof(struct device_info) * count);
225 count = device_get_list(devs);
226
227 // going to need a larger buffer for many devices
228 int needed_buffer = count * (sizeof(struct usbmuxd_device_record) + sizeof(struct usbmuxd_header)) + REPLY_BUF_SIZE;
229 if(client->ob_capacity < needed_buffer) {
230 usbmuxd_log(LL_DEBUG, "Enlarging client %d reply buffer %d -> %d to make space for device notifications", client->fd, client->ob_capacity, needed_buffer);
231 client->ob_buf = realloc(client->ob_buf, needed_buffer);
232 client->ob_capacity = needed_buffer;
233 }
234 dev = devs;
235 for(i=0; i<count; i++) {
236 if(notify_device(client, dev++) < 0) {
237 free(devs);
238 return -1;
239 }
240 }
241 free(devs);
242 return count;
243}
244
245static int client_command(struct mux_client *client, struct usbmuxd_header *hdr, const char *payload)
246{
247 int res;
248 usbmuxd_log(LL_DEBUG, "Client command in fd %d len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag);
249
250 if(client->state != CLIENT_COMMAND) {
251 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd);
252 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
253 return -1;
254 client_close(client);
255 return -1;
256 }
257
258 struct usbmuxd_connect_request *ch;
259 switch(hdr->message) {
260 case MESSAGE_LISTEN:
261 if(send_result(client, hdr->tag, 0) < 0)
262 return -1;
263 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
264 return start_listen(client);
265 case MESSAGE_CONNECT:
266 ch = (void*)payload;
267 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
268 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
269 if(res < 0) {
270 if(send_result(client, hdr->tag, -res) < 0)
271 return -1;
272 } else {
273 client->connect_tag = hdr->tag;
274 client->connect_device = ch->device_id;
275 client->state = CLIENT_CONNECTING1;
276 }
277 return 0;
278 default:
279 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
280 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
281 return -1;
282 return 0;
283 }
284 return -1;
285}
286
287static void process_send(struct mux_client *client)
288{
289 int res;
290 if(!client->ob_size) {
291 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
292 client->events &= ~POLLOUT;
293 return;
294 }
295 res = send(client->fd, client->ob_buf, client->ob_size, 0);
296 if(res <= 0) {
297 usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno));
298 client_close(client);
299 return;
300 }
301 if(res == client->ob_size) {
302 client->ob_size = 0;
303 client->events &= ~POLLOUT;
304 if(client->state == CLIENT_CONNECTING2) {
305 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
306 client->state = CLIENT_CONNECTED;
307 client->events = client->devents;
308 // no longer need this
309 free(client->ob_buf);
310 client->ob_buf = NULL;
311 }
312 } else {
313 client->ob_size -= res;
314 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
315 }
316}
317static void process_recv(struct mux_client *client)
318{
319 int res;
320 int did_read = 0;
321 if(client->ib_size < sizeof(struct usbmuxd_header)) {
322 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0);
323 if(res <= 0) {
324 if(res < 0)
325 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
326 else
327 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
328 client_close(client);
329 return;
330 }
331 client->ib_size += res;
332 if(client->ib_size < sizeof(struct usbmuxd_header))
333 return;
334 did_read = 1;
335 }
336 struct usbmuxd_header *hdr = (void*)client->ib_buf;
337 if(hdr->version != USBMUXD_PROTOCOL_VERSION) {
338 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected %d, got %d", client->fd, USBMUXD_PROTOCOL_VERSION, hdr->version);
339 client_close(client);
340 }
341 if(hdr->length > client->ib_capacity) {
342 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
343 client_close(client);
344 }
345 if(hdr->length < sizeof(struct usbmuxd_header)) {
346 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
347 client_close(client);
348 }
349 if(client->ib_size < hdr->length) {
350 if(did_read)
351 return; //maybe we would block, so defer to next loop
352 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
353 if(res < 0) {
354 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
355 client_close(client);
356 return;
357 } else if(res == 0) {
358 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
359 client_close(client);
360 return;
361 }
362 client->ib_size += res;
363 if(client->ib_size < hdr->length)
364 return;
365 }
366 client_command(client, hdr, (char *)(hdr+1));
367 client->ib_size = 0;
368}
369
370void client_process(int fd, short events)
371{
372 struct mux_client *client = NULL;
373 FOREACH(struct mux_client *lc, &client_list) {
374 if(lc->fd == fd) {
375 client = lc;
376 break;
377 }
378 } ENDFOREACH
379
380 if(!client) {
381 usbmuxd_log(LL_ERROR, "client_process: fd %d not found in client list", fd);
382 return;
383 }
384
385 if(client->state == CLIENT_CONNECTED) {
386 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
387 device_client_process(client->connect_device, client, events);
388 } else {
389 if(events & POLLIN) {
390 process_recv(client);
391 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
392 process_send(client);
393 }
394 }
395
396}
397
398void client_device_add(struct device_info *dev)
399{
400 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
401 FOREACH(struct mux_client *client, &client_list) {
402 if(client->state == CLIENT_LISTEN)
403 notify_device(client, dev);
404 } ENDFOREACH
405}
406void client_device_remove(int device_id)
407{
408 uint32_t id = device_id;
409 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
410 FOREACH(struct mux_client *client, &client_list) {
411 if(client->state == CLIENT_LISTEN)
412 send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &id, sizeof(uint32_t));
413 } ENDFOREACH
414}
415
416
417void client_init(void)
418{
419 usbmuxd_log(LL_DEBUG, "client_init");
420 collection_init(&client_list);
421}
422
423void client_shutdown(void)
424{
425 usbmuxd_log(LL_DEBUG, "client_shutdown");
426 FOREACH(struct mux_client *client, &client_list) {
427 client_close(client);
428 } ENDFOREACH
429 collection_free(&client_list);
430}
diff --git a/daemon/client.h b/daemon/client.h
new file mode 100644
index 0000000..4fc1ab4
--- /dev/null
+++ b/daemon/client.h
@@ -0,0 +1,46 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
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, int len);
31int client_write(struct mux_client *client, void *buffer, int 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);
38
39int client_accept(int fd);
40void client_get_fds(struct fdlist *list);
41void client_process(int fd, short events);
42
43void client_init(void);
44void client_shutdown(void);
45
46#endif
diff --git a/daemon/device.c b/daemon/device.c
new file mode 100644
index 0000000..3a5883c
--- /dev/null
+++ b/daemon/device.c
@@ -0,0 +1,751 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#define _BSD_SOURCE
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <sys/time.h>
28#include <netinet/in.h>
29#include <netinet/tcp.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdint.h>
33#include <inttypes.h>
34#include "device.h"
35#include "client.h"
36#include "usb.h"
37#include "log.h"
38
39int next_device_id;
40
41#define DEV_PKTBUF_SIZE 65536
42
43#define CONN_INBUF_SIZE 262144
44#define CONN_OUTBUF_SIZE 65536
45
46#define ACK_TIMEOUT 30
47
48enum mux_protocol {
49 MUX_PROTO_VERSION = 0,
50 MUX_PROTO_TCP = IPPROTO_TCP,
51};
52
53enum mux_dev_state {
54 MUXDEV_INIT, // sent version packet
55 MUXDEV_ACTIVE, // received version packet, active
56 MUXDEV_DEAD // dead
57};
58
59enum mux_conn_state {
60 CONN_CONNECTING, // SYN
61 CONN_CONNECTED, // SYN/SYNACK/ACK -> active
62 CONN_REFUSED, // RST received during SYN
63 CONN_DYING, // RST received
64 CONN_DEAD // being freed; used to prevent infinite recursion between client<->device freeing
65};
66
67struct mux_header
68{
69 uint32_t protocol;
70 uint32_t length;
71};
72
73struct version_header
74{
75 uint32_t major;
76 uint32_t minor;
77 uint32_t padding;
78};
79
80struct mux_device;
81
82#define CONN_ACK_PENDING 1
83
84struct mux_connection
85{
86 struct mux_device *dev;
87 struct mux_client *client;
88 enum mux_conn_state state;
89 uint16_t sport, dport;
90 uint32_t tx_seq, tx_ack, tx_acked, tx_win;
91 uint32_t rx_seq, rx_recvd, rx_ack, rx_win;
92 int max_payload;
93 int sendable;
94 int flags;
95 unsigned char *ib_buf;
96 int ib_size;
97 int ib_capacity;
98 unsigned char *ob_buf;
99 int ob_capacity;
100 short events;
101 uint64_t last_ack_time;
102};
103
104struct mux_device
105{
106 struct usb_device *usbdev;
107 int id;
108 enum mux_dev_state state;
109 struct collection connections;
110 uint16_t next_sport;
111 unsigned char *pktbuf;
112 int pktlen;
113};
114
115static struct collection device_list;
116
117uint64_t mstime64(void)
118{
119 struct timeval tv;
120 gettimeofday(&tv, NULL);
121 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
122}
123
124static int get_next_device_id(void)
125{
126 while(1) {
127 int ok = 1;
128 FOREACH(struct mux_device *dev, &device_list) {
129 if(dev->id == next_device_id) {
130 next_device_id++;
131 ok = 0;
132 break;
133 }
134 } ENDFOREACH
135 if(ok)
136 return next_device_id++;
137 }
138}
139
140static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, const void *data, int length)
141{
142 unsigned char *buffer;
143 int hdrlen;
144 int res;
145
146 switch(proto) {
147 case MUX_PROTO_VERSION:
148 hdrlen = sizeof(struct version_header);
149 break;
150 case MUX_PROTO_TCP:
151 hdrlen = sizeof(struct tcphdr);
152 break;
153 default:
154 usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length);
155 return -1;
156 }
157 usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length);
158
159 int total = sizeof(struct mux_header) + hdrlen + length;
160
161 if(total > USB_MTU) {
162 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);
163 return -1;
164 }
165
166 buffer = malloc(total);
167 struct mux_header *mhdr = (struct mux_header *)buffer;
168 mhdr->protocol = htonl(proto);
169 mhdr->length = htonl(total);;
170 memcpy(buffer + sizeof(struct mux_header), header, hdrlen);
171 if(data && length)
172 memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length);
173
174 if((res = usb_send(dev->usbdev, buffer, total)) < 0) {
175 usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res);
176 free(buffer);
177 return res;
178 }
179 return total;
180}
181
182static uint16_t find_sport(struct mux_device *dev)
183{
184 if(collection_count(&dev->connections) >= 65535)
185 return 0; //insanity
186
187 while(1) {
188 int ok = 1;
189 FOREACH(struct mux_connection *conn, &dev->connections) {
190 if(dev->next_sport == conn->sport) {
191 dev->next_sport++;
192 ok = 0;
193 break;
194 }
195 } ENDFOREACH
196 if(ok)
197 return dev->next_sport++;
198 }
199}
200
201static int send_anon_rst(struct mux_device *dev, uint16_t sport, uint16_t dport, uint32_t ack)
202{
203 struct tcphdr th;
204 memset(&th, 0, sizeof(th));
205 th.th_sport = htons(sport);
206 th.th_dport = htons(dport);
207 th.th_ack = htonl(ack);
208 th.th_flags = TH_RST;
209 th.th_off = sizeof(th) / 4;
210
211 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d flags=0x%x", dev->id, sport, dport, th.th_flags);
212
213 int res = send_packet(dev, MUX_PROTO_TCP, &th, NULL, 0);
214 return res;
215}
216
217static int send_tcp(struct mux_connection *conn, uint8_t flags, const unsigned char *data, int length)
218{
219 struct tcphdr th;
220 memset(&th, 0, sizeof(th));
221 th.th_sport = htons(conn->sport);
222 th.th_dport = htons(conn->dport);
223 th.th_seq = htonl(conn->tx_seq);
224 th.th_ack = htonl(conn->tx_ack);
225 th.th_flags = flags;
226 th.th_off = sizeof(th) / 4;
227 th.th_win = htons(conn->tx_win >> 8);
228
229 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
230 conn->dev->id, conn->sport, conn->dport, conn->tx_seq, conn->tx_ack, flags, conn->tx_win, conn->tx_win >> 8, length);
231
232 int res = send_packet(conn->dev, MUX_PROTO_TCP, &th, data, length);
233 if(res >= 0) {
234 conn->tx_acked = conn->tx_ack;
235 conn->last_ack_time = mstime64();
236 conn->flags &= ~CONN_ACK_PENDING;
237 }
238 return res;
239}
240
241static void connection_teardown(struct mux_connection *conn)
242{
243 int res;
244 if(conn->state == CONN_DEAD)
245 return;
246 usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport);
247 if(conn->dev->state != MUXDEV_DEAD && conn->state != CONN_DYING && conn->state != CONN_REFUSED) {
248 res = send_tcp(conn, TH_RST, NULL, 0);
249 if(res < 0)
250 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, conn->sport, conn->dport);
251 }
252 if(conn->client) {
253 if(conn->state == CONN_REFUSED || conn->state == CONN_CONNECTING) {
254 client_notify_connect(conn->client, RESULT_CONNREFUSED);
255 } else {
256 conn->state = CONN_DEAD;
257 client_close(conn->client);
258 }
259 }
260 if(conn->ib_buf)
261 free(conn->ib_buf);
262 if(conn->ob_buf)
263 free(conn->ob_buf);
264 collection_remove(&conn->dev->connections, conn);
265 free(conn);
266}
267
268int device_start_connect(int device_id, uint16_t dport, struct mux_client *client)
269{
270 struct mux_device *dev = NULL;
271 FOREACH(struct mux_device *cdev, &device_list) {
272 if(cdev->id == device_id) {
273 dev = cdev;
274 break;
275 }
276 } ENDFOREACH
277 if(!dev) {
278 usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id);
279 return -RESULT_BADDEV;
280 }
281
282 uint16_t sport = find_sport(dev);
283 if(!sport) {
284 usbmuxd_log(LL_WARNING, "Unable to allocate port for device %d", device_id);
285 return -RESULT_BADDEV;
286 }
287
288 struct mux_connection *conn;
289 conn = malloc(sizeof(struct mux_connection));
290 memset(conn, 0, sizeof(struct mux_connection));
291
292 conn->dev = dev;
293 conn->client = client;
294 conn->state = CONN_CONNECTING;
295 conn->sport = sport;
296 conn->dport = dport;
297 conn->tx_seq = 0;
298 conn->tx_ack = 0;
299 conn->tx_acked = 0;
300 conn->tx_win = 131072;
301 conn->rx_recvd = 0;
302 conn->flags = 0;
303 conn->max_payload = USB_MTU - sizeof(struct mux_header) - sizeof(struct tcphdr);
304
305 conn->ob_buf = malloc(CONN_OUTBUF_SIZE);
306 conn->ob_capacity = CONN_OUTBUF_SIZE;
307 conn->ib_buf = malloc(CONN_INBUF_SIZE);
308 conn->ib_capacity = CONN_INBUF_SIZE;
309 conn->ib_size = 0;
310
311 int res;
312
313 res = send_tcp(conn, TH_SYN, NULL, 0);
314 if(res < 0) {
315 usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport);
316 free(conn);
317 return -RESULT_CONNREFUSED; //bleh
318 }
319 collection_add(&dev->connections, conn);
320 return 0;
321}
322
323static void update_connection(struct mux_connection *conn)
324{
325 conn->sendable = conn->rx_win - (conn->tx_seq - conn->rx_ack);
326
327 if(conn->sendable > conn->ob_capacity)
328 conn->sendable = conn->ob_capacity;
329 if(conn->sendable > conn->max_payload)
330 conn->sendable = conn->max_payload;
331
332 if(conn->sendable > 0)
333 conn->events |= POLLIN;
334 else
335 conn->events &= ~POLLIN;
336
337 if(conn->ib_size)
338 conn->events |= POLLOUT;
339 else
340 conn->events &= ~POLLOUT;
341
342 if(conn->tx_acked != conn->tx_ack)
343 conn->flags |= CONN_ACK_PENDING;
344 else
345 conn->flags &= ~CONN_ACK_PENDING;
346
347 usbmuxd_log(LL_SPEW, "update_connection: sendable %d, events %d, flags %d", conn->sendable, conn->events, conn->flags);
348 client_set_events(conn->client, conn->events);
349}
350
351void device_client_process(int device_id, struct mux_client *client, short events)
352{
353 struct mux_connection *conn = NULL;
354 FOREACH(struct mux_device *dev, &device_list) {
355 if(dev->id == device_id) {
356 FOREACH(struct mux_connection *lconn, &dev->connections) {
357 if(lconn->client == client) {
358 conn = lconn;
359 break;
360 }
361 } ENDFOREACH
362 break;
363 }
364 } ENDFOREACH
365
366 if(!conn) {
367 usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client);
368 return;
369 }
370 usbmuxd_log(LL_SPEW, "device_client_process (%d)", events);
371
372 int res;
373 int size;
374 if(events & POLLOUT) {
375 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
376 if(size <= 0) {
377 usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size);
378 connection_teardown(conn);
379 return;
380 }
381 conn->tx_ack += size;
382 if(size == conn->ib_size) {
383 conn->ib_size = 0;
384 } else {
385 conn->ib_size -= size;
386 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
387 }
388 }
389 if(events & POLLIN) {
390 size = client_read(conn->client, conn->ob_buf, conn->sendable);
391 if(size <= 0) {
392 usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size);
393 connection_teardown(conn);
394 return;
395 }
396 res = send_tcp(conn, TH_ACK, conn->ob_buf, size);
397 if(res < 0) {
398 connection_teardown(conn);
399 return;
400 }
401 conn->tx_seq += size;
402 }
403
404 update_connection(conn);
405}
406
407static void connection_device_input(struct mux_connection *conn, unsigned char *payload, int payload_length)
408{
409 if((conn->ib_size + payload_length) > conn->ib_capacity) {
410 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);
411 connection_teardown(conn);
412 return;
413 }
414 memcpy(conn->ib_buf + conn->ib_size, payload, payload_length);
415 conn->ib_size += payload_length;
416 conn->rx_recvd += payload_length;
417 update_connection(conn);
418}
419
420void device_abort_connect(int device_id, struct mux_client *client)
421{
422 FOREACH(struct mux_device *dev, &device_list) {
423 if(dev->id == device_id) {
424 FOREACH(struct mux_connection *conn, &dev->connections) {
425 if(conn->client == client) {
426 connection_teardown(conn);
427 return;
428 }
429 } ENDFOREACH
430 usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
431 return;
432 }
433 } ENDFOREACH
434 usbmuxd_log(LL_WARNING, "Attempted to abort connection for nonexistent device %d", device_id);
435}
436
437static void device_version_input(struct mux_device *dev, struct version_header *vh)
438{
439 if(dev->state != MUXDEV_INIT) {
440 usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id);
441 return;
442 }
443 vh->major = ntohl(vh->major);
444 vh->minor = ntohl(vh->minor);
445 if(vh->major != 1 || vh->minor != 0) {
446 usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor);
447 collection_remove(&device_list, dev);
448 free(dev);
449 return;
450 }
451 usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", vh->major, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev));
452 dev->state = MUXDEV_ACTIVE;
453 collection_init(&dev->connections);
454 struct device_info info;
455 info.id = dev->id;
456 info.location = usb_get_location(dev->usbdev);
457 info.serial = usb_get_serial(dev->usbdev);
458 info.pid = usb_get_pid(dev->usbdev);
459 client_device_add(&info);
460}
461
462static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, int payload_length)
463{
464 usbmuxd_log(LL_DEBUG, "[IN] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
465 dev->id, ntohs(th->th_sport), ntohs(th->th_dport), ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length);
466
467 uint16_t sport = ntohs(th->th_dport);
468 uint16_t dport = ntohs(th->th_sport);
469 struct mux_connection *conn = NULL;
470 FOREACH(struct mux_connection *lconn, &dev->connections) {
471 if(lconn->sport == sport && lconn->dport == dport) {
472 conn = lconn;
473 break;
474 }
475 } ENDFOREACH
476
477 if(!conn) {
478 usbmuxd_log(LL_WARNING, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
479 if(!(th->th_flags & TH_RST)) {
480 if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0)
481 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport);
482 }
483 return;
484 }
485
486 conn->rx_seq = ntohl(th->th_seq);
487 conn->rx_ack = ntohl(th->th_ack);
488 conn->rx_win = ntohs(th->th_win) << 8;
489
490 if(th->th_flags & TH_RST) {
491 char *buf = malloc(payload_length+1);
492 memcpy(buf, payload, payload_length);
493 if(payload_length && (buf[payload_length-1] == '\n'))
494 buf[payload_length-1] = 0;
495 buf[payload_length] = 0;
496 usbmuxd_log(LL_DEBUG, "RST reason: %s", buf);
497 free(buf);
498 }
499
500 if(conn->state == CONN_CONNECTING) {
501 if(th->th_flags != (TH_SYN|TH_ACK)) {
502 if(th->th_flags & TH_RST)
503 conn->state = CONN_REFUSED;
504 usbmuxd_log(LL_INFO, "Connection refused by device %d (%d->%d)", dev->id, sport, dport);
505 connection_teardown(conn); //this also sends the notification to the client
506 } else {
507 conn->tx_seq++;
508 conn->tx_ack++;
509 conn->rx_recvd = conn->rx_seq;
510 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
511 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, sport, dport);
512 connection_teardown(conn);
513 return;
514 }
515 conn->state = CONN_CONNECTED;
516 if(client_notify_connect(conn->client, RESULT_OK) < 0) {
517 conn->client = NULL;
518 connection_teardown(conn);
519 }
520 update_connection(conn);
521 }
522 } else if(conn->state == CONN_CONNECTED) {
523 if(th->th_flags != TH_ACK) {
524 usbmuxd_log(LL_INFO, "Connection reset by device %d (%d->%d)", dev->id, sport, dport);
525 if(th->th_flags & TH_RST)
526 conn->state = CONN_DYING;
527 connection_teardown(conn);
528 } else {
529 connection_device_input(conn, payload, payload_length);
530 }
531 }
532}
533
534void device_data_input(struct usb_device *usbdev, unsigned char *buffer, int length)
535{
536 struct mux_device *dev = NULL;
537 FOREACH(struct mux_device *tdev, &device_list) {
538 if(tdev->usbdev == usbdev) {
539 dev = tdev;
540 break;
541 }
542 } ENDFOREACH
543 if(!dev) {
544 usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
545 return;
546 }
547
548 if(!length)
549 return;
550
551 usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length);
552
553 // handle broken up transfers
554 if(dev->pktlen) {
555 memcpy(dev->pktbuf + dev->pktlen, buffer, length);
556 struct mux_header *mhdr = (struct mux_header *)dev->pktbuf;
557 if((length < USB_MRU) || (ntohl(mhdr->length) == length)) {
558 buffer = dev->pktbuf;
559 length += dev->pktlen;
560 dev->pktlen = 0;
561 usbmuxd_log(LL_SPEW, "Gathered mux data from buffer (total size: %d)", length);
562 } else {
563 dev->pktlen += length;
564 usbmuxd_log(LL_SPEW, "Appended mux data to buffer (total size: %d)", dev->pktlen);
565 return;
566 }
567 } else {
568 struct mux_header *mhdr = (struct mux_header *)buffer;
569 if((length == USB_MRU) && (length < ntohl(mhdr->length))) {
570 memcpy(dev->pktbuf, buffer, length);
571 dev->pktlen = length;
572 usbmuxd_log(LL_SPEW, "Copied mux data to buffer (size: %d)", dev->pktlen);
573 return;
574 }
575 }
576
577 struct mux_header *mhdr = (struct mux_header *)buffer;
578
579 if(ntohl(mhdr->length) != length) {
580 usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length);
581 return;
582 }
583
584 struct tcphdr *th;
585 unsigned char *payload;
586 int payload_length;
587
588 switch(ntohl(mhdr->protocol)) {
589 case MUX_PROTO_VERSION:
590 device_version_input(dev, (struct version_header *)(mhdr+1));
591 break;
592 case MUX_PROTO_TCP:
593 th = (struct tcphdr *)(mhdr+1);
594 payload = (unsigned char *)(th+1);
595 payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header);
596 device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length);
597 break;
598 default:
599 usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol));
600 break;
601 }
602
603}
604
605int device_add(struct usb_device *usbdev)
606{
607 int res;
608 int id = get_next_device_id();
609 struct mux_device *dev;
610 usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(usbdev), id);
611 dev = malloc(sizeof(struct mux_device));
612 dev->id = id;
613 dev->usbdev = usbdev;
614 dev->state = MUXDEV_INIT;
615 dev->next_sport = 1;
616 dev->pktbuf = malloc(DEV_PKTBUF_SIZE);
617 dev->pktlen = 0;
618 struct version_header vh;
619 vh.major = htonl(1);
620 vh.minor = htonl(0);
621 vh.padding = 0;
622 if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) {
623 usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d\n", id);
624 free(dev);
625 return res;
626 }
627 collection_add(&device_list, dev);
628 return 0;
629}
630
631void device_remove(struct usb_device *usbdev)
632{
633 FOREACH(struct mux_device *dev, &device_list) {
634 if(dev->usbdev == usbdev) {
635 usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev));
636 if(dev->state == MUXDEV_ACTIVE) {
637 dev->state = MUXDEV_DEAD;
638 FOREACH(struct mux_connection *conn, &dev->connections) {
639 connection_teardown(conn);
640 } ENDFOREACH
641 client_device_remove(dev->id);
642 collection_free(&dev->connections);
643 }
644 collection_remove(&device_list, dev);
645 free(dev->pktbuf);
646 free(dev);
647 return;
648 }
649 } ENDFOREACH
650 usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
651}
652
653int device_get_count(void)
654{
655 int count = 0;
656 FOREACH(struct mux_device *dev, &device_list) {
657 if(dev->state == MUXDEV_ACTIVE)
658 count++;
659 } ENDFOREACH
660 return count;
661}
662
663int device_get_list(struct device_info *p)
664{
665 int count = 0;
666 FOREACH(struct mux_device *dev, &device_list) {
667 if(dev->state == MUXDEV_ACTIVE) {
668 p->id = dev->id;
669 p->serial = usb_get_serial(dev->usbdev);
670 p->location = usb_get_location(dev->usbdev);
671 p->pid = usb_get_pid(dev->usbdev);
672 count++;
673 p++;
674 }
675 } ENDFOREACH
676 return count;
677}
678
679int device_get_timeout(void)
680{
681 uint64_t oldest = (uint64_t)-1;
682 FOREACH(struct mux_device *dev, &device_list) {
683 if(dev->state == MUXDEV_ACTIVE) {
684 FOREACH(struct mux_connection *conn, &dev->connections) {
685 if((conn->state == CONN_CONNECTED) && (conn->flags & CONN_ACK_PENDING) && conn->last_ack_time < oldest)
686 oldest = conn->last_ack_time;
687 } ENDFOREACH
688 }
689 } ENDFOREACH
690 uint64_t ct = mstime64();
691 if(oldest == -1)
692 return 100000; //meh
693 if((ct - oldest) > ACK_TIMEOUT)
694 return 0;
695 return ACK_TIMEOUT - (ct - oldest);
696}
697
698void device_check_timeouts(void)
699{
700 uint64_t ct = mstime64();
701 FOREACH(struct mux_device *dev, &device_list) {
702 if(dev->state == MUXDEV_ACTIVE) {
703 FOREACH(struct mux_connection *conn, &dev->connections) {
704 if((conn->state == CONN_CONNECTED) &&
705 (conn->flags & CONN_ACK_PENDING) &&
706 (ct - conn->last_ack_time) > ACK_TIMEOUT) {
707 usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct);
708 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
709 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, conn->sport, conn->dport);
710 connection_teardown(conn);
711 }
712 }
713 } ENDFOREACH
714 }
715 } ENDFOREACH
716}
717
718void device_init(void)
719{
720 usbmuxd_log(LL_DEBUG, "device_init");
721 collection_init(&device_list);
722 next_device_id = 1;
723}
724
725void device_kill_connections(void)
726{
727 usbmuxd_log(LL_DEBUG, "device_kill_connections");
728 FOREACH(struct mux_device *dev, &device_list) {
729 if(dev->state != MUXDEV_INIT) {
730 FOREACH(struct mux_connection *conn, &dev->connections) {
731 connection_teardown(conn);
732 } ENDFOREACH
733 }
734 } ENDFOREACH
735 // give USB a while to send the final connection RSTs and the like
736 usb_process_timeout(100);
737}
738
739void device_shutdown(void)
740{
741 usbmuxd_log(LL_DEBUG, "device_shutdown");
742 FOREACH(struct mux_device *dev, &device_list) {
743 FOREACH(struct mux_connection *conn, &dev->connections) {
744 connection_teardown(conn);
745 } ENDFOREACH
746 collection_free(&dev->connections);
747 collection_remove(&device_list, dev);
748 free(dev);
749 } ENDFOREACH
750 collection_free(&device_list);
751}
diff --git a/daemon/device.h b/daemon/device.h
new file mode 100644
index 0000000..ce6c50b
--- /dev/null
+++ b/daemon/device.h
@@ -0,0 +1,52 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __DEVICE_H__
22#define __DEVICE_H__
23
24#include "usb.h"
25#include "client.h"
26
27struct device_info {
28 int id;
29 const char *serial;
30 uint32_t location;
31 uint16_t pid;
32};
33
34void device_data_input(struct usb_device *dev, unsigned char *buf, int 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
43int device_get_count(void);
44int device_get_list(struct device_info *p);
45
46int device_get_timeout(void);
47void device_check_timeouts(void);
48
49void device_init(void);
50void device_kill_connections(void);
51void device_shutdown(void);
52#endif
diff --git a/daemon/log.c b/daemon/log.c
new file mode 100644
index 0000000..2ccb3cc
--- /dev/null
+++ b/daemon/log.c
@@ -0,0 +1,94 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
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
35int log_level = LL_WARNING;
36
37int log_syslog = 0;
38
39void log_enable_syslog()
40{
41 if (!log_syslog) {
42 openlog("usbmuxd", LOG_PID, 0);
43 log_syslog = 1;
44 }
45}
46
47void log_disable_syslog()
48{
49 if (log_syslog) {
50 closelog();
51 }
52}
53
54static int level_to_syslog_level(int level)
55{
56 int result = level + LOG_CRIT;
57 if (result > LOG_DEBUG) {
58 result = LOG_DEBUG;
59 }
60 return result;
61}
62
63void usbmuxd_log(enum loglevel level, const char *fmt, ...)
64{
65 va_list ap;
66 char *fs;
67 struct timeval ts;
68 struct tm *tp;
69
70 gettimeofday(&ts, NULL);
71 tp = localtime(&ts.tv_sec);
72
73 if(level > log_level)
74 return;
75
76 fs = malloc(20 + strlen(fmt));
77
78 if(log_syslog) {
79 sprintf(fs, "[%d] %s\n", level, fmt);
80 } else {
81 strftime(fs, 10, "[%H:%M:%S", tp);
82 sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
83 }
84
85 va_start(ap, fmt);
86 if (log_syslog) {
87 vsyslog(level_to_syslog_level(level), fs, ap);
88 } else {
89 vfprintf(stderr, fs, ap);
90 }
91 va_end(ap);
92
93 free(fs);
94}
diff --git a/daemon/log.h b/daemon/log.h
new file mode 100644
index 0000000..4a2ac2e
--- /dev/null
+++ b/daemon/log.h
@@ -0,0 +1,43 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
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 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
43#endif
diff --git a/daemon/main.c b/daemon/main.c
new file mode 100644
index 0000000..dde99c2
--- /dev/null
+++ b/daemon/main.c
@@ -0,0 +1,548 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#define _BSD_SOURCE
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <errno.h>
29#include <string.h>
30#include <stdlib.h>
31#include <signal.h>
32#include <unistd.h>
33#include <sys/socket.h>
34#include <sys/un.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <fcntl.h>
38#include <getopt.h>
39#include <pwd.h>
40#include <grp.h>
41
42#include "log.h"
43#include "usb.h"
44#include "device.h"
45#include "client.h"
46
47static const char *socket_path = "/var/run/usbmuxd";
48static const char *lockfile = "/var/run/usbmuxd.pid";
49
50int should_exit;
51
52static int verbose = 0;
53static int foreground = 0;
54static int drop_privileges = 0;
55static const char *drop_user = "usbmux";
56static int opt_udev = 0;
57static int opt_exit = 0;
58static int exit_signal = 0;
59static int daemon_pipe;
60
61static int report_to_parent = 0;
62
63int create_socket(void) {
64 struct sockaddr_un bind_addr;
65 int listenfd;
66
67 if(unlink(socket_path) == -1 && errno != ENOENT) {
68 usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
69 return -1;
70 }
71
72 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
73 if (listenfd == -1) {
74 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
75 return -1;
76 }
77
78 bzero(&bind_addr, sizeof(bind_addr));
79 bind_addr.sun_family = AF_UNIX;
80 strcpy(bind_addr.sun_path, socket_path);
81 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
82 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
83 return -1;
84 }
85
86 // Start listening
87 if (listen(listenfd, 5) != 0) {
88 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
89 return -1;
90 }
91
92 chmod(socket_path, 0666);
93
94 return listenfd;
95}
96
97void handle_signal(int sig)
98{
99 if (sig != SIGUSR1) {
100 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
101 should_exit = 1;
102 } else {
103 if(opt_udev) {
104 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
105 if (device_get_count() > 0) {
106 // we can't quit, there are still devices attached.
107 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
108 } else {
109 // it's safe to quit
110 should_exit = 1;
111 }
112 } else {
113 usbmuxd_log(LL_INFO, "Caught SIGUSR1 but we weren't started in --udev mode, ignoring");
114 }
115 }
116}
117
118void set_signal_handlers(void)
119{
120 struct sigaction sa;
121 memset(&sa, 0, sizeof(struct sigaction));
122 sa.sa_handler = handle_signal;
123 sigaction(SIGINT, &sa, NULL);
124 sigaction(SIGQUIT, &sa, NULL);
125 sigaction(SIGTERM, &sa, NULL);
126 sigaction(SIGUSR1, &sa, NULL);
127}
128
129int main_loop(int listenfd)
130{
131 int to, cnt, i, dto;
132 struct fdlist pollfds;
133
134 while(!should_exit) {
135 usbmuxd_log(LL_FLOOD, "main_loop iteration");
136 to = usb_get_timeout();
137 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
138 dto = device_get_timeout();
139 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to);
140 if(dto < to)
141 to = dto;
142
143 fdlist_create(&pollfds);
144 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
145 usb_get_fds(&pollfds);
146 client_get_fds(&pollfds);
147 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
148
149 cnt = poll(pollfds.fds, pollfds.count, to);
150 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
151
152 if(cnt == -1) {
153 if(errno == EINTR && should_exit) {
154 usbmuxd_log(LL_INFO, "event processing interrupted");
155 fdlist_free(&pollfds);
156 return 0;
157 }
158 } else if(cnt == 0) {
159 if(usb_process() < 0) {
160 usbmuxd_log(LL_FATAL, "usb_process() failed");
161 fdlist_free(&pollfds);
162 return -1;
163 }
164 device_check_timeouts();
165 } else {
166 int done_usb = 0;
167 for(i=0; i<pollfds.count; i++) {
168 if(pollfds.fds[i].revents) {
169 if(!done_usb && pollfds.owners[i] == FD_USB) {
170 if(usb_process() < 0) {
171 usbmuxd_log(LL_FATAL, "usb_process() failed");
172 fdlist_free(&pollfds);
173 return -1;
174 }
175 done_usb = 1;
176 }
177 if(pollfds.owners[i] == FD_LISTEN) {
178 if(client_accept(listenfd) < 0) {
179 usbmuxd_log(LL_FATAL, "client_accept() failed");
180 fdlist_free(&pollfds);
181 return -1;
182 }
183 }
184 if(pollfds.owners[i] == FD_CLIENT) {
185 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
186 }
187 }
188 }
189 }
190 fdlist_free(&pollfds);
191 }
192 return 0;
193}
194
195/**
196 * make this program run detached from the current console
197 */
198static int daemonize(void)
199{
200 pid_t pid;
201 pid_t sid;
202 int pfd[2];
203 int res;
204
205 // already a daemon
206 if (getppid() == 1)
207 return 0;
208
209 if((res = pipe(pfd)) < 0) {
210 usbmuxd_log(LL_FATAL, "pipe() failed.");
211 return res;
212 }
213
214 pid = fork();
215 if (pid < 0) {
216 usbmuxd_log(LL_FATAL, "fork() failed.");
217 return pid;
218 }
219
220 if (pid > 0) {
221 // exit parent process
222 int status;
223 close(pfd[1]);
224
225 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
226 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
227 exit(1);
228 }
229 if(status != 0)
230 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
231 exit(status);
232 }
233 // At this point we are executing as the child process
234 // but we need to do one more fork
235
236 daemon_pipe = pfd[1];
237 close(pfd[0]);
238 report_to_parent = 1;
239
240 // Change the file mode mask
241 umask(0);
242
243 // Create a new SID for the child process
244 sid = setsid();
245 if (sid < 0) {
246 usbmuxd_log(LL_FATAL, "setsid() failed.");
247 return -1;
248 }
249
250 pid = fork();
251 if (pid < 0) {
252 usbmuxd_log(LL_FATAL, "fork() failed (second).");
253 return pid;
254 }
255
256 if (pid > 0) {
257 // exit parent process
258 close(daemon_pipe);
259 exit(0);
260 }
261
262 // Change the current working directory.
263 if ((chdir("/")) < 0) {
264 usbmuxd_log(LL_FATAL, "chdir() failed");
265 return -2;
266 }
267 // Redirect standard files to /dev/null
268 if (!freopen("/dev/null", "r", stdin)) {
269 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
270 return -3;
271 }
272 if (!freopen("/dev/null", "w", stdout)) {
273 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
274 return -3;
275 }
276
277 return 0;
278}
279
280static int notify_parent(int status)
281{
282 int res;
283
284 report_to_parent = 0;
285 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
286 usbmuxd_log(LL_FATAL, "Could not notify parent!");
287 if(res >= 0)
288 return -2;
289 else
290 return res;
291 }
292 close(daemon_pipe);
293 if (!freopen("/dev/null", "w", stderr)) {
294 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
295 return -1;
296 }
297 return 0;
298}
299
300static void usage()
301{
302 printf("usage: usbmuxd [options]\n");
303 printf("\t-h|--help Print this message.\n");
304 printf("\t-v|--verbose Be verbose (use twice or more to increase).\n");
305 printf("\t-f|--foreground Do not daemonize (implies one -v).\n");
306 printf("\t-U|--user[=USER] Change to this user after startup (needs usb privileges).\n");
307 printf("\t If USER is not specified, defaults to usbmux.\n");
308 printf("\t-u|--udev Run in udev operation mode.\n");
309 printf("\t-x|--exit Tell a running instance to exit if there are no devices\n");
310 printf("\t connected (must be in udev mode).\n");
311 printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n");
312 printf("\t devices connected (always works).\n");
313 printf("\n");
314}
315
316static void parse_opts(int argc, char **argv)
317{
318 static struct option longopts[] = {
319 {"help", 0, NULL, 'h'},
320 {"foreground", 0, NULL, 'f'},
321 {"verbose", 0, NULL, 'v'},
322 {"user", 2, NULL, 'U'},
323 {"udev", 0, NULL, 'u'},
324 {"exit", 0, NULL, 'x'},
325 {"force-exit", 0, NULL, 'X'},
326 {NULL, 0, NULL, 0}
327 };
328 int c;
329
330 while (1) {
331 c = getopt_long(argc, argv, "hfvuU::xX", longopts, (int *) 0);
332 if (c == -1) {
333 break;
334 }
335
336 switch (c) {
337 case 'h':
338 usage();
339 exit(0);
340 case 'f':
341 foreground = 1;
342 break;
343 case 'v':
344 ++verbose;
345 break;
346 case 'U':
347 drop_privileges = 1;
348 if(optarg)
349 drop_user = optarg;
350 break;
351 case 'u':
352 opt_udev = 1;
353 break;
354 case 'x':
355 opt_exit = 1;
356 exit_signal = SIGUSR1;
357 break;
358 case 'X':
359 opt_exit = 1;
360 exit_signal = SIGTERM;
361 break;
362 default:
363 usage();
364 exit(2);
365 }
366 }
367}
368
369int main(int argc, char *argv[])
370{
371 int listenfd;
372 int res = 0;
373 int lfd;
374 struct flock lock;
375 char pids[10];
376
377 parse_opts(argc, argv);
378
379 argc -= optind;
380 argv += optind;
381
382 if (!foreground) {
383 verbose += LL_WARNING;
384 log_enable_syslog();
385 } else {
386 verbose += LL_NOTICE;
387 }
388
389 /* set log level to specified verbosity */
390 log_level = verbose;
391
392 usbmuxd_log(LL_NOTICE, "usbmux v0.1 starting up");
393 should_exit = 0;
394
395 set_signal_handlers();
396
397 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
398 if(res == -1) {
399 usbmuxd_log(LL_FATAL, "Could not open lockfile");
400 goto terminate;
401 }
402 lock.l_type = F_WRLCK;
403 lock.l_whence = SEEK_SET;
404 lock.l_start = 0;
405 lock.l_len = 0;
406 fcntl(lfd, F_GETLK, &lock);
407 close(lfd);
408 if (lock.l_type != F_UNLCK) {
409 if (opt_exit) {
410 if (lock.l_pid && !kill(lock.l_pid, 0)) {
411 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
412 res = 0;
413 if (kill(lock.l_pid, exit_signal) < 0) {
414 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
415 res = -1;
416 }
417 goto terminate;
418 } else {
419 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
420 res = -1;
421 goto terminate;
422 }
423 } else {
424 if (!opt_udev) {
425 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
426 res = -1;
427 } else {
428 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). exiting.", lock.l_pid);
429 res = 0;
430 }
431 goto terminate;
432 }
433 }
434 unlink(lockfile);
435
436 if (opt_exit) {
437 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
438 goto terminate;
439 }
440
441 if (!foreground) {
442 if ((res = daemonize()) < 0) {
443 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
444 usbmuxd_log(LL_FATAL, "Could not daemonize!");
445 goto terminate;
446 }
447 }
448
449 // now open the lockfile and place the lock
450 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
451 if(res < 0) {
452 usbmuxd_log(LL_FATAL, "Could not open lockfile");
453 goto terminate;
454 }
455 lock.l_type = F_WRLCK;
456 lock.l_whence = SEEK_SET;
457 lock.l_start = 0;
458 lock.l_len = 0;
459 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
460 usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
461 goto terminate;
462 }
463 sprintf(pids, "%d", getpid());
464 if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
465 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
466 if(res >= 0)
467 res = -2;
468 goto terminate;
469 }
470
471 usbmuxd_log(LL_INFO, "Creating socket");
472 res = listenfd = create_socket();
473 if(listenfd < 0)
474 goto terminate;
475
476 // drop elevated privileges
477 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
478 struct passwd *pw = getpwnam(drop_user);
479 if (!pw) {
480 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
481 res = -1;
482 goto terminate;
483 }
484
485 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
486 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
487 goto terminate;
488 }
489 if ((res = setgid(pw->pw_gid)) < 0) {
490 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
491 goto terminate;
492 }
493 if ((res = setuid(pw->pw_uid)) < 0) {
494 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
495 goto terminate;
496 }
497
498 // security check
499 if (setuid(0) != -1) {
500 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
501 res = -1;
502 goto terminate;
503 }
504 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
505 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
506 res = -1;
507 goto terminate;
508 }
509 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
510 }
511
512 client_init();
513 device_init();
514 usbmuxd_log(LL_INFO, "Initializing USB");
515 if((res = usb_init()) < 0)
516 goto terminate;
517
518 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
519
520 usbmuxd_log(LL_NOTICE, "Initialization complete");
521
522 if (report_to_parent)
523 if((res = notify_parent(0)) < 0)
524 goto terminate;
525
526 res = main_loop(listenfd);
527 if(res < 0)
528 usbmuxd_log(LL_FATAL, "main_loop failed");
529
530 usbmuxd_log(LL_NOTICE, "usbmux shutting down");
531 device_kill_connections();
532 usb_shutdown();
533 device_shutdown();
534 client_shutdown();
535 usbmuxd_log(LL_NOTICE, "Shutdown complete");
536
537terminate:
538 log_disable_syslog();
539
540 if (res < 0)
541 res = -res;
542 else
543 res = 0;
544 if (report_to_parent)
545 notify_parent(res);
546
547 return res;
548}
diff --git a/daemon/usb-linux.c b/daemon/usb-linux.c
new file mode 100644
index 0000000..c09cdc9
--- /dev/null
+++ b/daemon/usb-linux.c
@@ -0,0 +1,542 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdint.h>
28#include <string.h>
29
30#include <libusb.h>
31
32#include "usb.h"
33#include "log.h"
34#include "device.h"
35
36// interval for device connection/disconnection polling, in milliseconds
37// we need this because there is currently no asynchronous device discovery mechanism in libusb
38#define DEVICE_POLL_TIME 1000
39
40struct usb_device {
41 libusb_device_handle *dev;
42 uint8_t bus, address;
43 uint16_t vid, pid;
44 char serial[256];
45 int alive;
46 struct libusb_transfer *rx_xfer;
47 struct collection tx_xfers;
48 int wMaxPacketSize;
49};
50
51static struct collection device_list;
52
53static struct timeval next_dev_poll_time;
54
55static int devlist_failures;
56
57static void usb_disconnect(struct usb_device *dev)
58{
59 if(!dev->dev) {
60 return;
61 }
62
63 // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device
64 if(dev->rx_xfer) {
65 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer");
66 libusb_cancel_transfer(dev->rx_xfer);
67 }
68 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
69 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
70 libusb_cancel_transfer(xfer);
71 } ENDFOREACH
72
73 while(dev->rx_xfer || collection_count(&dev->tx_xfers)) {
74 struct timeval tv;
75 int res;
76
77 tv.tv_sec = 0;
78 tv.tv_usec = 1000;
79 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
80 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res);
81 break;
82 }
83 }
84 collection_free(&dev->tx_xfers);
85 libusb_release_interface(dev->dev, USB_INTERFACE);
86 libusb_close(dev->dev);
87 dev->dev = NULL;
88 collection_remove(&device_list, dev);
89 free(dev);
90}
91
92static void tx_callback(struct libusb_transfer *xfer)
93{
94 struct usb_device *dev = xfer->user_data;
95 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);
96 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
97 switch(xfer->status) {
98 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
99 case LIBUSB_TRANSFER_ERROR:
100 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
101 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
102 break;
103 case LIBUSB_TRANSFER_TIMED_OUT:
104 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
105 break;
106 case LIBUSB_TRANSFER_CANCELLED:
107 usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
108 break;
109 case LIBUSB_TRANSFER_STALL:
110 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
111 break;
112 case LIBUSB_TRANSFER_NO_DEVICE:
113 // other times, this happens, and also even when we abort the transfer after device removal
114 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
115 break;
116 case LIBUSB_TRANSFER_OVERFLOW:
117 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
118 break;
119 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
120 }
121 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
122 // we'll do device_remove there too
123 dev->alive = 0;
124 }
125 if(xfer->buffer)
126 free(xfer->buffer);
127 collection_remove(&dev->tx_xfers, xfer);
128 libusb_free_transfer(xfer);
129}
130
131int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
132{
133 int res;
134 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
135 libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, (void*)buf, length, tx_callback, dev, 0);
136 xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
137#ifndef EXPLICIT_ZLP_TRANSACTION
138 if (length % dev->wMaxPacketSize == 0) {
139 xfer->flags |= LIBUSB_TRANSFER_ZERO_PACKET;
140 }
141#endif
142 if((res = libusb_submit_transfer(xfer)) < 0) {
143 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res);
144 libusb_free_transfer(xfer);
145 return res;
146 }
147 collection_add(&dev->tx_xfers, xfer);
148#ifdef EXPLICIT_ZLP_TRANSACTION
149 if (length % dev->wMaxPacketSize == 0) {
150 usbmuxd_log(LL_DEBUG, "Send ZLP");
151 // Send Zero Length Packet
152 xfer = libusb_alloc_transfer(0);
153 void *buffer = malloc(1);
154 libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, buffer, 0, tx_callback, dev, 0);
155 xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
156 if((res = libusb_submit_transfer(xfer)) < 0) {
157 usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res);
158 libusb_free_transfer(xfer);
159 return res;
160 }
161 collection_add(&dev->tx_xfers, xfer);
162 }
163#endif
164 return 0;
165}
166
167static void rx_callback(struct libusb_transfer *xfer)
168{
169 struct usb_device *dev = xfer->user_data;
170 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
171 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
172 device_data_input(dev, xfer->buffer, xfer->actual_length);
173 libusb_submit_transfer(xfer);
174 } else {
175 switch(xfer->status) {
176 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
177 case LIBUSB_TRANSFER_ERROR:
178 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
179 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
180 break;
181 case LIBUSB_TRANSFER_TIMED_OUT:
182 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
183 break;
184 case LIBUSB_TRANSFER_CANCELLED:
185 usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
186 break;
187 case LIBUSB_TRANSFER_STALL:
188 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
189 break;
190 case LIBUSB_TRANSFER_NO_DEVICE:
191 // other times, this happens, and also even when we abort the transfer after device removal
192 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
193 break;
194 case LIBUSB_TRANSFER_OVERFLOW:
195 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
196 break;
197 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
198 }
199 free(xfer->buffer);
200 dev->rx_xfer = NULL;
201 libusb_free_transfer(xfer);
202 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
203 // we'll do device_remove there too
204 dev->alive = 0;
205 }
206}
207
208static int start_rx(struct usb_device *dev)
209{
210 int res;
211 void *buf;
212 dev->rx_xfer = libusb_alloc_transfer(0);
213 buf = malloc(USB_MRU);
214 libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, BULK_IN, buf, USB_MRU, rx_callback, dev, 0);
215 if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) {
216 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
217 libusb_free_transfer(dev->rx_xfer);
218 dev->rx_xfer = NULL;
219 return res;
220 }
221 return 0;
222}
223
224static int usb_discover(void)
225{
226 int cnt, i, res;
227 int valid_count = 0;
228 libusb_device **devs;
229
230 cnt = libusb_get_device_list(NULL, &devs);
231 if(cnt < 0) {
232 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
233 devlist_failures++;
234 // sometimes libusb fails getting the device list if you've just removed something
235 if(devlist_failures > 5) {
236 usbmuxd_log(LL_FATAL, "Too many errors getting device list\n");
237 return cnt;
238 } else {
239 gettimeofday(&next_dev_poll_time, NULL);
240 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
241 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
242 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
243 return 0;
244 }
245 }
246 devlist_failures = 0;
247
248 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
249
250 FOREACH(struct usb_device *usbdev, &device_list) {
251 usbdev->alive = 0;
252 } ENDFOREACH
253
254 for(i=0; i<cnt; i++) {
255 // the following are non-blocking operations on the device list
256 libusb_device *dev = devs[i];
257 uint8_t bus = libusb_get_bus_number(dev);
258 uint8_t address = libusb_get_device_address(dev);
259 struct libusb_device_descriptor devdesc;
260 int found = 0;
261 FOREACH(struct usb_device *usbdev, &device_list) {
262 if(usbdev->bus == bus && usbdev->address == address) {
263 valid_count++;
264 usbdev->alive = 1;
265 found = 1;
266 break;
267 }
268 } ENDFOREACH
269 if(found)
270 continue; //device already found
271 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
272 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res);
273 continue;
274 }
275 if(devdesc.idVendor != VID_APPLE)
276 continue;
277 if( (devdesc.idProduct != PID_IPHONE2G) &&
278 (devdesc.idProduct != PID_ITOUCH1G) &&
279 (devdesc.idProduct != PID_IPHONE3G) &&
280 (devdesc.idProduct != PID_ITOUCH2G) &&
281 (devdesc.idProduct != PID_IPHONE3GS))
282 continue;
283 libusb_device_handle *handle;
284 usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
285 // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable
286 if((res = libusb_open(dev, &handle)) != 0) {
287 usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res);
288 continue;
289 }
290 int current_config = 0;
291 if((res = libusb_get_configuration(handle, &current_config)) != 0) {
292 usbmuxd_log(LL_WARNING, "Could not get configuration for device %d-%d: %d", bus, address, res);
293 libusb_close(handle);
294 continue;
295 }
296 if (current_config != devdesc.bNumConfigurations) {
297 if((res = libusb_set_configuration(handle, devdesc.bNumConfigurations)) != 0) {
298 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", devdesc.bNumConfigurations, bus, address, res);
299 libusb_close(handle);
300 continue;
301 }
302 }
303 if((res = libusb_claim_interface(handle, USB_INTERFACE)) != 0) {
304 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", USB_INTERFACE, bus, address, res);
305 libusb_close(handle);
306 continue;
307 }
308 struct usb_device *usbdev;
309 usbdev = malloc(sizeof(struct usb_device));
310
311 if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) {
312 usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res);
313 libusb_release_interface(handle, USB_INTERFACE);
314 libusb_close(handle);
315 free(usbdev);
316 continue;
317 }
318 usbdev->serial[res] = 0;
319 usbdev->bus = bus;
320 usbdev->address = address;
321 usbdev->vid = devdesc.idVendor;
322 usbdev->pid = devdesc.idProduct;
323 usbdev->dev = handle;
324 usbdev->alive = 1;
325 usbdev->wMaxPacketSize = 0;
326 struct libusb_config_descriptor *cfg;
327 if (libusb_get_active_config_descriptor(dev, &cfg) == 0
328 && cfg && cfg->bNumInterfaces >= (USB_INTERFACE+1)) {
329 const struct libusb_interface *ifp = &cfg->interface[USB_INTERFACE];
330 if (ifp && ifp->num_altsetting >= 1) {
331 const struct libusb_interface_descriptor *as = &ifp->altsetting[0];
332 int i;
333 for (i = 0; i < as->bNumEndpoints; i++) {
334 const struct libusb_endpoint_descriptor *ep = &as->endpoint[i];
335 if (ep->bEndpointAddress == BULK_OUT) {
336 usbdev->wMaxPacketSize = ep->wMaxPacketSize;
337 }
338 }
339 }
340 }
341 if (usbdev->wMaxPacketSize == 0) {
342 usbmuxd_log(LL_ERROR, "Could not determine wMaxPacketSize for device %d-%d, setting to 64", usbdev->bus, usbdev->address);
343 usbdev->wMaxPacketSize = 64;
344 } else {
345 usbmuxd_log(LL_INFO, "Using wMaxPacketSize=%d for device %d-%d", usbdev->wMaxPacketSize, usbdev->bus, usbdev->address);
346 }
347
348 collection_init(&usbdev->tx_xfers);
349
350 collection_add(&device_list, usbdev);
351
352 if(device_add(usbdev) < 0) {
353 usb_disconnect(usbdev);
354 continue;
355 }
356 if(start_rx(usbdev) < 0) {
357 device_remove(usbdev);
358 usb_disconnect(usbdev);
359 continue;
360 }
361 valid_count++;
362 }
363 FOREACH(struct usb_device *usbdev, &device_list) {
364 if(!usbdev->alive) {
365 device_remove(usbdev);
366 usb_disconnect(usbdev);
367 }
368 } ENDFOREACH
369
370 libusb_free_device_list(devs, 1);
371
372 gettimeofday(&next_dev_poll_time, NULL);
373 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
374 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
375 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
376
377 return valid_count;
378}
379
380const char *usb_get_serial(struct usb_device *dev)
381{
382 if(!dev->dev) {
383 return NULL;
384 }
385 return dev->serial;
386}
387
388uint32_t usb_get_location(struct usb_device *dev)
389{
390 if(!dev->dev) {
391 return 0;
392 }
393 return (dev->bus << 16) | dev->address;
394}
395
396uint16_t usb_get_pid(struct usb_device *dev)
397{
398 if(!dev->dev) {
399 return 0;
400 }
401 return dev->pid;
402}
403
404void usb_get_fds(struct fdlist *list)
405{
406 const struct libusb_pollfd **usbfds;
407 const struct libusb_pollfd **p;
408 usbfds = libusb_get_pollfds(NULL);
409 if(!usbfds) {
410 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
411 return;
412 }
413 p = usbfds;
414 while(*p) {
415 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
416 p++;
417 }
418 free(usbfds);
419}
420
421static int dev_poll_remain_ms(void)
422{
423 int msecs;
424 struct timeval tv;
425 gettimeofday(&tv, NULL);
426 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
427 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
428 if(msecs < 0)
429 return 0;
430 return msecs;
431}
432
433int usb_get_timeout(void)
434{
435 struct timeval tv;
436 int msec;
437 int res;
438 int pollrem;
439 pollrem = dev_poll_remain_ms();
440 res = libusb_get_next_timeout(NULL, &tv);
441 if(res == 0)
442 return pollrem;
443 if(res < 0) {
444 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res);
445 return pollrem;
446 }
447 msec = tv.tv_sec * 1000;
448 msec += tv.tv_usec / 1000;
449 if(msec > pollrem)
450 return pollrem;
451 return msec;
452}
453
454int usb_process(void)
455{
456 int res;
457 struct timeval tv;
458 tv.tv_sec = tv.tv_usec = 0;
459 res = libusb_handle_events_timeout(NULL, &tv);
460 if(res < 0) {
461 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
462 return res;
463 }
464 // reap devices marked dead due to an RX error
465 FOREACH(struct usb_device *usbdev, &device_list) {
466 if(!usbdev->alive) {
467 device_remove(usbdev);
468 usb_disconnect(usbdev);
469 }
470 } ENDFOREACH
471
472 if(dev_poll_remain_ms() <= 0) {
473 res = usb_discover();
474 if(res < 0) {
475 usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res);
476 return res;
477 }
478 }
479 return 0;
480}
481
482int usb_process_timeout(int msec)
483{
484 int res;
485 struct timeval tleft, tcur, tfin;
486 gettimeofday(&tcur, NULL);
487 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
488 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
489 tfin.tv_sec += tfin.tv_usec / 1000000;
490 tfin.tv_usec %= 1000000;
491 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
492 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
493 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
494 if(tleft.tv_usec < 0) {
495 tleft.tv_usec += 1000000;
496 tleft.tv_sec -= 1;
497 }
498 res = libusb_handle_events_timeout(NULL, &tleft);
499 if(res < 0) {
500 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
501 return res;
502 }
503 // reap devices marked dead due to an RX error
504 FOREACH(struct usb_device *usbdev, &device_list) {
505 if(!usbdev->alive) {
506 device_remove(usbdev);
507 usb_disconnect(usbdev);
508 }
509 } ENDFOREACH
510 gettimeofday(&tcur, NULL);
511 }
512 return 0;
513}
514
515int usb_init(void)
516{
517 int res;
518 usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
519
520 devlist_failures = 0;
521 res = libusb_init(NULL);
522 //libusb_set_debug(NULL, 3);
523 if(res != 0) {
524 usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
525 return -1;
526 }
527
528 collection_init(&device_list);
529
530 return usb_discover();
531}
532
533void usb_shutdown(void)
534{
535 usbmuxd_log(LL_DEBUG, "usb_shutdown");
536 FOREACH(struct usb_device *usbdev, &device_list) {
537 device_remove(usbdev);
538 usb_disconnect(usbdev);
539 } ENDFOREACH
540 collection_free(&device_list);
541 libusb_exit(NULL);
542}
diff --git a/daemon/usb.h b/daemon/usb.h
new file mode 100644
index 0000000..621ccb2
--- /dev/null
+++ b/daemon/usb.h
@@ -0,0 +1,66 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __USB_H__
22#define __USB_H__
23
24#include <stdint.h>
25#include "utils.h"
26
27#define BULK_IN 0x85
28#define BULK_OUT 0x04
29
30// libusb fragments packets larger than this (usbfs limitation)
31// on input, this creates race conditions and other issues
32#define USB_MRU 16384
33
34// max transmission packet size
35// libusb fragments these too, but doesn't send ZLPs so we're safe
36// but we need to send a ZLP ourselves at the end (see usb-linux.c)
37// we're using 3 * 16384 to optimize for the fragmentation
38// this results in three URBs per full transfer, 32 USB packets each
39// if there are ZLP issues this should make them show up easily too
40#define USB_MTU (3 * 16384)
41
42#define USB_PACKET_SIZE 512
43
44#define VID_APPLE 0x5ac
45#define PID_IPHONE2G 0x1290
46#define PID_ITOUCH1G 0x1291
47#define PID_IPHONE3G 0x1292
48#define PID_ITOUCH2G 0x1293
49#define PID_IPHONE3GS 0x1294
50
51#define USB_INTERFACE 1
52
53struct usb_device;
54
55int usb_init(void);
56void usb_shutdown(void);
57const char *usb_get_serial(struct usb_device *dev);
58uint32_t usb_get_location(struct usb_device *dev);
59uint16_t usb_get_pid(struct usb_device *dev);
60void usb_get_fds(struct fdlist *list);
61int usb_get_timeout(void);
62int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
63int usb_process(void);
64int usb_process_timeout(int msec);
65
66#endif