summaryrefslogtreecommitdiffstats
path: root/daemon/client.c
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/client.c
parentde30ca5d5c98a8cbee3d8748601519e2263b3e1d (diff)
downloadusbmuxd-4711a2b493f76561e9803bf7e8be77186f3e7798.tar.gz
usbmuxd-4711a2b493f76561e9803bf7e8be77186f3e7798.tar.bz2
Renamed directory 'usbmuxd' to more suitable 'daemon'.
Diffstat (limited to 'daemon/client.c')
-rw-r--r--daemon/client.c430
1 files changed, 430 insertions, 0 deletions
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}