From 523c44d0018c2e8480b37ac917aef7469b4b3a65 Mon Sep 17 00:00:00 2001 From: Paul Sladen Date: Wed, 25 Feb 2009 20:01:34 +0100 Subject: A Readme, Makefile updates and cosmetic changes. --- .gitignore | 6 + Makefile | 35 +-- Readme.txt | 34 +++ iproxy.c | 54 ++-- main.c | 853 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sock_stuff.c | 8 +- sock_stuff.h | 6 +- testclient.c | 32 ++- usbmuxd.c | 855 ----------------------------------------------------------- usbmuxd.h | 46 ++-- 10 files changed, 987 insertions(+), 942 deletions(-) create mode 100644 .gitignore create mode 100644 Readme.txt create mode 100644 main.c delete mode 100644 usbmuxd.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26cda67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*~ +*.o +.*.swp +iproxy +testclient +usbmuxd diff --git a/Makefile b/Makefile index 04a36b3..2036ddc 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,31 @@ -TARGET=usbmuxd -CFLAGS=-Wall -LDFLAGS=-lpthread -lusb -lrt +TARGETS=usbmuxd iproxy testclient +CFLAGS=-Wall -g +LIBS=-lpthread -lusb -lrt +LDFLAGS= -objects = sock_stuff.o usbmuxd.o iphone.o +all: $(TARGETS) -all: $(TARGET) +main.o: main.c usbmuxd.h sock_stuff.h iphone.h +iphone.o: iproxy.c iphone.h usbmuxd.h sock_stuff.h +sock_stuff.o: sock_stuff.c sock_stuff.h +testclient.o: testclient.c sock_stuff.h -%.o: %.c %.h +%.o: %.c $(CC) -o $@ $(CFLAGS) -c $< -$(TARGET): $(objects) - $(CC) -o $@ $(LDFLAGS) $^ +usbmuxd: main.o sock_stuff.o iphone.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +testclient: testclient.o sock_stuff.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) + +iproxy: iproxy.o sock_stuff.o + $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) clean: - rm -f *.o $(TARGET) + rm -f *.o $(TARGETS) realclean: clean rm -f *~ -testclient: testclient.c sock_stuff.o - $(CC) $(LDFLAGS) -o testclient $(CFLAGS) $< sock_stuff.o - -iproxy: iproxy.c sock_stuff.o - $(CC) -lpthread -o iproxy $(CFLAGS) $< sock_stuff.o - +.PHONY: all clean realclean diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000..deece52 --- /dev/null +++ b/Readme.txt @@ -0,0 +1,34 @@ +Build +===== + + make + +Use +=== + + sudo ./usbmuxd & + ./iproxy 2222 22 & + ssh -p 2222 root@localhost + +Muwahahaha. Hopefully you get the normal SSH login prompt. + + Unfortunately, as of 2009-02-24 this only survives for a single + connection. You will have to restart the 'iproxy' part. + +SSH +=== + +If your iphone is rooted, but isn't running SSH, you will need to +mount it with 'ifuse --afc2' (to access the root directory of the +device). + +And then edit: + + /Library/LaunchDaemons/com.openssh.sshd.plist + +to _remove_ the lines: + + Diabled + + +Reboot the device and then sshd should be running. diff --git a/iproxy.c b/iproxy.c index a58365a..775b819 100644 --- a/iproxy.c +++ b/iproxy.c @@ -34,8 +34,6 @@ #include "usbmuxd.h" #include "sock_stuff.h" -#define SOCKET_FILE "/var/run/usbmuxd" - static uint16_t listen_port = 0; static uint16_t device_port = 0; @@ -50,7 +48,7 @@ struct client_data { int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t *result) { - struct usbmux_result res; + struct usbmuxd_result res; int recv_len; int i; uint32_t rrr[5]; @@ -71,7 +69,7 @@ int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t *result) if ((recv_len == sizeof(res)) && (res.header.length == recv_len) && (res.header.reserved == 0) - && (res.header.type == usbmux_result) + && (res.header.type == USBMUXD_RESULT) ) { *result = res.result; if (res.header.tag == tag) { @@ -185,8 +183,8 @@ void *acceptor_thread(void *arg) int connected; uint32_t pktlen; unsigned char *buf; - struct usbmux_header hello; - struct usbmux_dev_info device_info; + struct usbmuxd_hello hello; + struct usbmuxd_device_info device_info; pthread_t ctos; if (!arg) { @@ -196,26 +194,26 @@ void *acceptor_thread(void *arg) cdata = (struct client_data*)arg; - cdata->sfd = connect_unix_socket(SOCKET_FILE); + cdata->sfd = connect_unix_socket(USBMUXD_SOCKET_FILE); if (cdata->sfd < 0) { printf("error opening socket, terminating.\n"); return NULL; } // send hello - hello.length = sizeof(struct usbmux_header); - hello.reserved = 0; - hello.type = usbmux_hello; - hello.tag = 2; + hello.header.length = sizeof(struct usbmuxd_hello); + hello.header.reserved = 0; + hello.header.type = USBMUXD_HELLO; + hello.header.tag = 2; hello_done = 0; connected = 0; fprintf(stdout, "sending Hello packet\n"); - if (send(cdata->sfd, &hello, hello.length, 0) == hello.length) { + if (send(cdata->sfd, &hello, hello.header.length, 0) == hello.header.length) { uint32_t res = -1; // get response - if (usbmuxd_get_result(cdata->sfd, hello.tag, &res) && (res==0)) { + if (usbmuxd_get_result(cdata->sfd, hello.header.tag, &res) && (res==0)) { fprintf(stdout, "Got Hello Response!\n"); hello_done = 1; } else { @@ -241,7 +239,7 @@ void *acceptor_thread(void *arg) } fprintf(stdout, "Received device data\n"); //log_debug_buffer(stdout, (char*)buf, pktlen); - memcpy(&device_info, buf + sizeof(struct usbmux_header), sizeof(device_info)); + memcpy(&device_info, buf + sizeof(struct usbmuxd_header), sizeof(device_info)); free(buf); } else { // we _should_ have all of them now. @@ -252,17 +250,17 @@ void *acceptor_thread(void *arg) } if (device_info.device_id > 0) { - struct usbmux_connect_request c_req; + struct usbmuxd_connect_request c_req; fprintf(stdout, "Requesting connecion to device %d port %d\n", device_info.device_id, device_port); // try to connect to last device found c_req.header.length = sizeof(c_req); c_req.header.reserved = 0; - c_req.header.type = usbmux_connect; + c_req.header.type = USBMUXD_CONNECT; c_req.header.tag = 3; c_req.device_id = device_info.device_id; - c_req.port = htons(device_port); + c_req.tcp_dport = htons(device_port); c_req.reserved = 0; if (send_buf(cdata->sfd, &c_req, sizeof(c_req)) < 0) { @@ -346,26 +344,26 @@ int main(int argc, char **argv) } /* - sfd = connect_unix_socket(SOCKET_FILE); + sfd = connect_unix_socket(USBMUXD_SOCKET_FILE); if (sfd < 0) { printf("error opening socket, terminating.\n"); return -1; } // send hello - hello.length = sizeof(struct usbmux_header); - hello.reserved = 0; - hello.type = usbmux_hello; - hello.tag = 2; + hello.header.length = sizeof(hello); + hello.header.reserved = 0; + hello.header.type = USBMUXD_HELLO; + hello.header.tag = 2; hello_done = 0; connected = 0; fprintf(stdout, "sending Hello packet\n"); - if (send(sfd, &hello, hello.length, 0) == hello.length) { + if (send(sfd, &hello, hello.header.length, 0) == hello.header.length) { uint32_t res = -1; // get response - if (usbmuxd_get_result(sfd, hello.tag, &res) && (res==0)) { + if (usbmuxd_get_result(sfd, hello.header.tag, &res) && (res==0)) { fprintf(stdout, "Got Hello Response!\n"); hello_done = 1; } else { @@ -390,7 +388,7 @@ int main(int argc, char **argv) } fprintf(stdout, "Received device data\n"); //log_debug_buffer(stdout, (char*)buf, pktlen); - memcpy(&device_info, buf + sizeof(struct usbmux_header), sizeof(device_info)); + memcpy(&device_info, buf + sizeof(struct usbmuxd_header), sizeof(device_info)); free(buf); } else { // we _should_ have all of them now. @@ -401,17 +399,17 @@ int main(int argc, char **argv) } if (device_info.device_id > 0) { - struct usbmux_connect_request c_req; + struct usbmuxd_connect_request c_req; fprintf(stdout, "Requesting connecion to device %d port %d\n", device_info.device_id, device_port); // try to connect to last device found c_req.header.length = sizeof(c_req); c_req.header.reserved = 0; - c_req.header.type = usbmux_connect; + c_req.header.type = USBMUXD_CONNECT; c_req.header.tag = 3; c_req.device_id = device_info.device_id; - c_req.port = htons(device_port); + c_req.tcp_dport = htons(device_port); c_req.reserved = 0; if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) { diff --git a/main.c b/main.c new file mode 100644 index 0000000..bf062b7 --- /dev/null +++ b/main.c @@ -0,0 +1,853 @@ +/* + * usbmuxd -- daemon for communication with iPhone/iPod via USB + * + * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. + * Based upon iTunnel source code, Copyright (c) 2008 Jing Su. + * http://www.cs.toronto.edu/~jingsu/itunnel/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbmuxd.h" +#include "sock_stuff.h" + +#include "iphone.h" + +#define DEFAULT_TIMEOUT 4000 +#define DEFAULT_CHILDREN_CAPACITY 10 + +static int quit_flag = 0; +static int fsock = -1; + +struct device_use_info { + uint32_t device_id; + iphone_device_t phone; + int use_count; +}; + +struct client_data { + volatile int dead; + int socket; + int tag; + pthread_t thread; + pthread_t handler; + pthread_t reader; + int reader_quit; + int reader_dead; + int handler_dead; + iphone_umux_client_t muxclient; + struct device_use_info *duinfo; +}; + +static struct device_use_info **device_use_list = NULL; +static int device_use_count = 0; +static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * mutex for mutual exclusion of calling the iphone_mux_send function + * TODO: I don't know if we really need this? + */ +static pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * mutex to keep the reader threads from reading partial packages + */ +static pthread_mutex_t reader_mutex = PTHREAD_MUTEX_INITIALIZER; + +#ifdef DEBUG +/** + * for debugging purposes. + */ +static void print_buffer(const char *data, const int length) +{ + int i; + int j; + unsigned char c; + + for(i=0; i= length) { + printf(" "); + continue; + } + printf("%02hhx ", *(data+i+j)); + } + printf(" | "); + for(j=0;j<16;j++) { + if (i+j >= length) + break; + c = *(data+i+j); + if ((c < 32) || (c > 127)) { + printf("."); + continue; + } + printf("%c", c); + } + printf("\n"); + } + printf("\n"); +} +#endif + +/** + * Read incoming usbmuxd packet. If the packet is larger than + * the size specified by len, the data will be truncated. + * + * @param fd the file descriptor to read from. + * @param data pointer to a buffer to store the read data to. + * @param len the length of the data to be read. The buffer + * pointed to by data should be at least len bytes in size. + * + * @return + */ +static int usbmuxd_get_request(int fd, void *data, size_t len) +{ + uint32_t pktlen; + int recv_len; + + if (peek_buf(fd, &pktlen, sizeof(pktlen)) < sizeof(pktlen)) { + return -errno; + } + + if (len < pktlen) { + // target buffer is to small to hold this packet! fix it! + fprintf(stderr, "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.\n", __func__, pktlen, len); + pktlen = len; + } + + recv_len = recv_buf(fd, data, pktlen); + if ((recv_len > 0) && (recv_len < pktlen)) { + fprintf(stderr, "%s: Uh-oh, we got less than the packet's size, %d instead of %d...\n", __func__, recv_len, pktlen); + } + + return recv_len; +} + +/** + * Send a usbmuxd result packet with given tag and result_code. + * + * @param fd the file descriptor to write to. + * @param tag the tag value that identifies where this message belongs to. + * @param result_code the error value (0 = Success, most likely errno values otherwise) + * + * @return the return value returned by send_buf (normally the number of bytes sent) + */ +static int usbmuxd_send_result(int fd, uint32_t tag, uint32_t result_code) +{ + struct usbmuxd_result res; + + res.header.length = sizeof(res); + res.header.reserved = 0; + res.header.type = USBMUXD_RESULT; + res.header.tag = tag; + res.result = result_code; + + fprintf(stderr, "%s: tag=%d result=%d\n", __func__, res.header.tag, res.result); + + return send_buf(fd, &res, sizeof(res)); +} + +/** + * this thread reads from the usb connection and writes the + * data to the connected client. + * + * @param arg pointer to a client_data structure. + * + * @return NULL in any case + */ +static void *usbmuxd_client_reader_thread(void *arg) +{ + struct client_data *cdata; + + char rbuffer[512]; + uint32_t rbuffersize = 512; + uint32_t rlen; + iphone_error_t err; + char *cursor; + ssize_t len; + int result; + + if (!arg) { + fprintf(stderr, "%s: invalid client_data supplied!\n", __func__); + cdata->reader_dead = 1; + return NULL; + } + + cdata = (struct client_data*)arg; + + cdata->reader_dead = 0; + + fprintf(stdout, "%s[%d:%d]: started\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); + + while (!quit_flag && !cdata->reader_quit) { + result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT); + if (result <= 0) { + if (result < 0) { + fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno)); + } + continue; + } + + rlen = 0; + //pthread_mutex_lock(&usbmux_mutex); + err = iphone_mux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize, &rlen, DEFAULT_TIMEOUT); + //pthread_mutex_unlock(&usbmux_mutex); + if (err != 0) { + fprintf(stderr, "%s[%d:%d]: encountered USB read error: %d\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, err); + break; + } + + cursor = rbuffer; + while (rlen > 0) { + len = send_buf(cdata->socket, cursor, rlen); + // calculate remainder + rlen -= len; + // advance cursor + cursor += len; + } + fsync(cdata->socket); + } + + fprintf(stdout, "%s[%d:%d]: terminated\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); + + cdata->reader_dead = 1; + + return NULL; +} + +/** + * This function handles the connecting procedure to a previously + * set up usbmux client. + * Sends a usbmuxd result packet denoting success or failure. + * A successful result is mandatory for later communication. + * + * @param cdata pointer to a previously initialized client_data structure + * + * @return + */ +static int usbmuxd_handleConnectResult(struct client_data *cdata) +{ + int result; + char buffer[512]; + char err_type[64]; + int err_code; + ssize_t maxlen = 512; + uint32_t rlen; + iphone_error_t err; + + if (!cdata) { + fprintf(stderr, "%s: Invalid client_data provided!\n", __func__); + return -EINVAL; + } + + result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT); + if (result <= 0) { + if (result < 0) { + fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno)); + return result; + } + } else { + result = 0; + err = iphone_mux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen, DEFAULT_TIMEOUT); + if (err != 0) { + fprintf(stderr, "%s: encountered USB read error: %d\n", __func__, err); + usbmuxd_send_result(cdata->socket, cdata->tag, -err); + return err; + } else { + if (rlen > 0) { + if ((buffer[0] == 1) && (rlen > 20) && !memcmp(buffer+1, "handleConnectResult:", 20)) { + // hm... we got an error message! + buffer[rlen] = 0; + fprintf(stderr, "%s: %s\n", __func__, buffer+22); + + if (sscanf(buffer+22, "%s - %d\n", err_type, &err_code) == 2) { + usbmuxd_send_result(cdata->socket, cdata->tag, err_code); + return -err_code; + } else { + usbmuxd_send_result(cdata->socket, cdata->tag, ENODATA); + return -ENODATA; + } + } else { + // send success result + usbmuxd_send_result(cdata->socket, cdata->tag, 0); + // and the server greeting message + send_buf(cdata->socket, buffer, rlen); + } + } else { + // no server greeting? this seems to be ok. send success. + usbmuxd_send_result(cdata->socket, cdata->tag, 0); + } + } + //fsync(cdata->socket); + } + return result; +} + +/** + * This thread handles the communication between the connected iPhone/iPod + * and the client that created the connection. + */ +static void *usbmuxd_client_handler_thread(void *arg) +{ + struct client_data *cdata; + int result; + char *cursor; + char buffer[1024]; + ssize_t len; + ssize_t maxlen = sizeof(buffer); + uint32_t wlen; + iphone_error_t err; + + if (!arg) { + fprintf(stderr, "%s: invalid client_data provided!\n", __func__); + return NULL; + } + + cdata = (struct client_data*)arg; + + fprintf(stdout, "%s[%d:%d]: started\n", __func__, cdata->duinfo->device_id,cdata->duinfo->use_count); + + if (usbmuxd_handleConnectResult(cdata)) { + goto leave; + } + + // starting mux reader thread + cdata->reader_quit = 0; + cdata->reader_dead = 0; + if (pthread_create(&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) { + fprintf(stderr, "%s: could not start client_reader thread\n", __func__); + cdata->reader = 0; + } + + while (!quit_flag && !cdata->reader_dead) { + result = check_fd(cdata->socket, FD_READ, DEFAULT_TIMEOUT); + if (result <= 0) { + if (result < 0) { + fprintf(stderr, "%s: Error: checkfd: %s\n", __func__, strerror(errno)); + } + continue; + } + + // check_fd told us there's data available, so read from client + // and push to USB device. + len = recv(cdata->socket, buffer, maxlen, 0); + if (len == 0) { + break; + } + if (len < 0) { + fprintf(stderr, "%s[%d:%d]: Error: recv: %s\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, strerror(errno)); + break; + } + + cursor = buffer; + + pthread_mutex_lock(&writer_mutex); + do { + wlen = 0; + err = iphone_mux_send(cdata->muxclient, cursor, len, &wlen); + if (err == IPHONE_E_TIMEOUT) { + // some kind of timeout... just be patient and retry. + } else if (err != IPHONE_E_SUCCESS) { + fprintf(stderr, "%s[%d:%d]: USB write error: %d\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, err); + len = -1; + break; + } + + // calculate remainder. + len -= wlen; + // advance cursor appropiately. + cursor += wlen; + } while ((len > 0) && !quit_flag); + pthread_mutex_unlock(&writer_mutex); + if (len < 0) { + break; + } + } + +leave: + // cleanup + fprintf(stdout, "%s[%d:%d]: terminating\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); + if (cdata->reader != 0) { + cdata->reader_quit = 1; + pthread_join(cdata->reader, NULL); + } + + cdata->handler_dead = 1; + + fprintf(stdout, "%s[%d:%d]: terminated\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); + return NULL; +} + +/** + * This thread is started when a new connection is accepted. + * It performs the handshake, then waits for the connect packet and + * on success it starts the usbmuxd_client_handler thread. + */ +static void *usbmuxd_client_init_thread(void *arg) +{ + struct client_data *cdata; + struct usbmuxd_hello hello; + struct usbmuxd_device_info_request dev_info_req; + struct usbmuxd_connect_request c_req; + + struct usb_bus *bus; + struct usb_device *dev; + + int recv_len; + int found = 0; + int res; + int i; + int sent_result; + iphone_error_t err; + + iphone_device_t phone; + struct device_use_info *cur_dev = NULL; + + if (!arg) { + fprintf(stderr, "%s: invalid client_data provided!\n", __func__); + return NULL; + } + + cdata = (struct client_data*)arg; + cdata->dead = 0; + + fprintf(stdout, "%s: started (fd=%d)\n", __func__, cdata->socket); + + if ((recv_len = usbmuxd_get_request(cdata->socket, &hello, sizeof(hello))) <= 0) { + fprintf(stderr, "%s: No Hello packet received, error %s\n", __func__, strerror(errno)); + goto leave; + } + + if ((recv_len == sizeof(hello)) && (hello.header.length == sizeof(hello)) + && (hello.header.reserved == 0) && (hello.header.type == USBMUXD_HELLO)) { + // send success response + usbmuxd_send_result(cdata->socket, hello.header.tag, 0); + } else { + // send error response and exit + fprintf(stderr, "%s: Invalid Hello packet received.\n", __func__); + // TODO is this required?! + usbmuxd_send_result(cdata->socket, hello.header.tag, EINVAL); + goto leave; + } + + // gather data about all iPhones/iPods attached + usb_init(); + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == 0x05ac + && dev->descriptor.idProduct >= 0x1290 + && dev->descriptor.idProduct <= 0x1293) + { + fprintf(stdout, "%s: Found device on bus %d, id %d\n", __func__, bus->location, dev->devnum); + found++; + + // construct packet + memset(&dev_info_req, 0, sizeof(dev_info_req)); + dev_info_req.header.length = sizeof(dev_info_req); + dev_info_req.header.type = USBMUXD_DEVICE_INFO; + dev_info_req.device_info.device_id = dev->devnum; + dev_info_req.device_info.product_id = dev->descriptor.idProduct; + if (dev->descriptor.iSerialNumber) { + usb_dev_handle *udev; + //pthread_mutex_lock(&usbmux_mutex); + udev = usb_open(dev); + if (udev) { + usb_get_string_simple(udev, dev->descriptor.iSerialNumber, dev_info_req.device_info.serial_number, sizeof(dev_info_req.device_info.serial_number)+1); + usb_close(udev); + } + //pthread_mutex_unlock(&usbmux_mutex); + } + +#ifdef DEBUG + print_buffer((char*)&dev_info_req, sizeof(dev_info_req)); +#endif + + // send it + if (send_buf(cdata->socket, &dev_info_req, sizeof(dev_info_req)) <= 0) { + fprintf(stderr, "%s: Error: Could not send device info: %s\n", __func__, strerror(errno)); + found--; + } + } + } + } + + // now wait for connect request + if (found <= 0) { + fprintf(stderr, "%s: No attached iPhone/iPod devices found.\n", __func__); + goto leave; + } + + memset(&c_req, 0, sizeof(c_req)); + if ((recv_len = usbmuxd_get_request(cdata->socket, &c_req, sizeof(c_req))) <= 0) { + fprintf(stderr, "%s: Did not receive any connect request.\n", __func__); + goto leave; + } + + if (c_req.header.type != USBMUXD_CONNECT) { + fprintf(stderr, "%s: Unexpected packet of type %d received.\n", __func__, c_req.header.type); + goto leave; + } + + fprintf(stdout, "%s: Setting up connection to usb device #%d on port %d\n", __func__, c_req.device_id, ntohs(c_req.tcp_dport)); + + // find the device, and open usb connection + phone = NULL; + cur_dev = NULL; + // first check if we already have an open connection + if (device_use_list) { + pthread_mutex_lock(&usbmux_mutex); + for (i = 0; i < device_use_count; i++) { + if (device_use_list[i]) { + if (device_use_list[i]->device_id == c_req.device_id) { + device_use_list[i]->use_count++; + cur_dev = device_use_list[i]; + phone = cur_dev->phone; + break; + } + } + } + pthread_mutex_unlock(&usbmux_mutex); + } + if (!phone) { + // if not found, make a new connection + if (iphone_get_specific_device(0, c_req.device_id, &phone) != IPHONE_E_SUCCESS) { + fprintf(stderr, "%s: device_id %d could not be opened\n", __func__, c_req.device_id); + usbmuxd_send_result(cdata->socket, c_req.header.tag, ENODEV); + goto leave; + } + // add to device list + cur_dev = (struct device_use_info*)malloc(sizeof(struct device_use_info)); + memset(cur_dev, 0, sizeof(struct device_use_info)); + cur_dev->use_count = 1; + cur_dev->device_id = c_req.device_id; + cur_dev->phone = phone; + + fprintf(stdout, "%s: device_use_count = %d\n", __func__, device_use_count); + + pthread_mutex_lock(&usbmux_mutex); + device_use_list = (struct device_use_info**)realloc(device_use_list, sizeof(struct device_use_info*) * (device_use_count+1)); + if (device_use_list) { + device_use_list[device_use_count] = cur_dev; + device_use_count++; + } + pthread_mutex_unlock(&usbmux_mutex); + } else { + fprintf(stdout, "%s: reusing usb connection device_id %d\n", __func__, c_req.device_id); + } + + // setup connection to iPhone/iPod +// pthread_mutex_lock(&usbmux_mutex); + res = iphone_mux_new_client(cur_dev->phone, 0, ntohs(c_req.tcp_dport), &(cdata->muxclient)); +// pthread_mutex_unlock(&usbmux_mutex); + + if (res != 0) { + usbmuxd_send_result(cdata->socket, c_req.header.tag, res); + fprintf(stderr, "%s: mux_new_client returned %d, aborting.\n", __func__, res); + goto leave; + } + + // start connection handler thread + cdata->handler_dead = 0; + cdata->tag = c_req.header.tag; + cdata->duinfo = cur_dev; + if (pthread_create(&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0) { + fprintf(stderr, "%s: could not create usbmuxd_client_handler_thread!\n", __func__); + cdata->handler = 0; + goto leave; + } + + sent_result = 0; + + // start reading data from the connected device + while (!quit_flag && !cdata->handler_dead) { + pthread_mutex_lock(&reader_mutex); + iphone_mux_pullbulk(cur_dev->phone); + err = iphone_mux_get_error(cdata->muxclient); + pthread_mutex_unlock(&reader_mutex); + if (err != IPHONE_E_SUCCESS) { + break; + } + } + + if (!sent_result) { + //fprintf(stderr, "Sending error message %d tag %d\n", err, c_req.header.tag); + err = iphone_mux_get_error(cdata->muxclient); + //usbmuxd_send_result(cdata->socket, c_req.header.tag, err); + } + + fprintf(stdout, "%s: terminating\n", __func__); + + // wait for handler thread to finish its work + if (cdata->handler != 0) { + pthread_join(cdata->handler, NULL); + } + + // time to clean up + if (cdata && cdata->muxclient) { // should be non-NULL + iphone_mux_free_client(cdata->muxclient); + } + +leave: + // this has to be freed only if it's not in use anymore as it closes + // the USB connection + if (cur_dev) { + if (cur_dev->use_count > 1) { + cur_dev->use_count--; + } else { + iphone_free_device(cur_dev->phone); + cur_dev->use_count = 0; + free(cur_dev); + cur_dev = NULL; + pthread_mutex_lock(&usbmux_mutex); + if (device_use_count > 1) { + struct device_use_info **newlist; + int j; + + newlist = (struct device_use_info**)malloc(sizeof(struct device_use_info*) * device_use_count-1); + for (i = 0; i < device_use_count; i++) { + if (device_use_list[i] != NULL) { + newlist[j++] = device_use_list[i]; + } + } + free(device_use_list); + device_use_list = newlist; + } else { + free(device_use_list); + device_use_list = NULL; + } + pthread_mutex_unlock(&usbmux_mutex); + } + } + + cdata->dead = 1; + close(cdata->socket); + + fprintf(stdout, "%s: terminated\n", __func__); + + return NULL; +} + +/** + * make this program run detached from the current console + */ +static int daemonize() +{ + // TODO still to be implemented, also logging is missing! + return 0; +} + +/** + * signal handler function for cleaning up properly + */ +static void clean_exit(int sig) +{ + if (sig == SIGINT) { + fprintf(stdout, "CTRL+C pressed\n"); + } + quit_flag = 1; +} + +/** + * thread function that performs accept() and starts the required child + * threads to perform the rest of the communication stuff. + */ +static void *usbmuxd_accept_thread(void *arg) +{ + struct sockaddr_un c_addr; + socklen_t len = sizeof(struct sockaddr_un); + struct client_data *cdata; + struct client_data **children = NULL; + int children_capacity = DEFAULT_CHILDREN_CAPACITY; + int i = 0; + int result = 0; + int cnt; + + // Reserve space for 10 clients which should be enough. If not, the + // buffer gets enlarged later. + children = (struct client_data**)malloc(sizeof(struct client_data*) * children_capacity); + if (!children) { + fprintf(stderr, "%s: Out of memory when allocating memory for child threads. Terminating.\n", __func__); + exit(EXIT_FAILURE); + } + memset(children, 0, sizeof(struct client_data*) * children_capacity); + + fprintf(stdout, "%s: waiting for connection\n", __func__); + while (!quit_flag) { + // Check the file descriptor before accepting a connection. + // If no connection attempt is made, just repeat... + result = check_fd(fsock, FD_READ, 1000); + if (result <= 0) { + if (result == 0) { + // cleanup + for (i = 0; i < children_capacity; i++) { + if (children[i]) { + if (children[i]->dead != 0) { + pthread_join(children[i]->thread, NULL); + fprintf(stdout, "%s: reclaimed client thread (fd=%d)\n", __func__, children[i]->socket); + free(children[i]); + children[i] = NULL; + cnt++; + } else { + cnt = 0; + } + } else { + cnt++; + } + } + + if ((children_capacity > DEFAULT_CHILDREN_CAPACITY) + && ((children_capacity - cnt) <= DEFAULT_CHILDREN_CAPACITY)) { + children_capacity = DEFAULT_CHILDREN_CAPACITY; + children = realloc(children, sizeof(struct client_data*) * children_capacity); + } + continue; + } else { + fprintf(stderr, "select error: %s\n", strerror(errno)); + continue; + } + } + + cdata = (struct client_data*)malloc(sizeof(struct client_data)); + memset(cdata, 0, sizeof(struct client_data)); + if (!cdata) { + quit_flag = 1; + fprintf(stderr, "%s: Error: Out of memory! Terminating.\n", __func__); + break; + } + + cdata->socket = accept(fsock, (struct sockaddr*)&c_addr, &len); + if (cdata->socket < 0) { + free(cdata); + if (errno == EINTR) { + continue; + } else { + fprintf(stderr, "%s: Error in accept: %s\n", __func__, strerror(errno)); + continue; + } + } + + fprintf(stdout, "%s: new client connected (fd=%d)\n", __func__, cdata->socket); + + // create client thread: + if (pthread_create(&cdata->thread, NULL, usbmuxd_client_init_thread, cdata) == 0) { + for (i = 0; i < children_capacity; i++) { + if (children[i] == NULL) break; + } + if (i == children_capacity) { + // enlarge buffer + children_capacity++; + children = realloc(children, sizeof(struct client_data*) * children_capacity); + if (!children) { + fprintf(stderr, "%s: Out of memory when enlarging child thread buffer\n", __func__); + } + } + children[i] = cdata; + } else { + fprintf(stderr, "%s: Failed to create client_init_thread.\n", __func__); + close(cdata->socket); + free(cdata); + cdata = NULL; + } + } + + fprintf(stdout, "%s: terminating\n", __func__); + + // preparing for shutdown: wait for child threads to terminate (if any) + fprintf(stdout, "%s: waiting for child threads to terminate...\n", __func__); + for (i = 0; i < children_capacity; i++) { + if (children[i] != NULL) { + pthread_join(children[i]->thread, NULL); + free(children[i]); + } + } + + // delete the children set. + free(children); + children = NULL; + + fprintf(stdout, "%s: terminated.\n", __func__); + + return NULL; +} + +/** + * main function. + */ +int main(int argc, char **argv) +{ + int foreground = 1; + pthread_t acceptor; + + fprintf(stdout, "usbmuxd: starting\n"); + + // TODO: Parameter checking. + + fsock = create_unix_socket(USBMUXD_SOCKET_FILE); + if (fsock < 0) { + fprintf(stderr, "Could not create socket, exiting\n"); + return -1; + } + + chmod(USBMUXD_SOCKET_FILE, 0666); + + if (!foreground) { + if (daemonize() < 0) { + exit(EXIT_FAILURE); + } + } + + // signal(SIGHUP, reload_conf); // none yet + signal(SIGINT, clean_exit); + signal(SIGQUIT, clean_exit); + signal(SIGTERM, clean_exit); + signal(SIGPIPE, SIG_IGN); + + if (pthread_create(&acceptor, NULL, usbmuxd_accept_thread, NULL) != 0) { + fprintf(stderr, "Failed to create server thread.\n"); + close(fsock); + return -1; + } + + // Relax here. Just wait for the accept thread to terminate. + pthread_join(acceptor, NULL); + + fprintf(stdout, "usbmuxd: terminating\n"); + if (fsock >= 0) { + close(fsock); + } + + unlink(USBMUXD_SOCKET_FILE); + + return 0; +} + diff --git a/sock_stuff.c b/sock_stuff.c index 1a23bc1..3d11a27 100644 --- a/sock_stuff.c +++ b/sock_stuff.c @@ -208,13 +208,13 @@ int check_fd(int fd, fd_mode fdm, unsigned int timeout) do { eagain = 0; switch(fdm) { - case fdread: + case FD_READ: sret = select(fd+1,&fds,NULL,NULL,&to); break; - case fdwrite: + case FD_WRITE: sret = select(fd+1,NULL,&fds,NULL,&to); break; - case fdexcept: + case FD_EXCEPT: sret = select(fd+1,NULL,NULL,&fds,&to); break; } @@ -255,7 +255,7 @@ int recv_buf_timeout(int fd, void *data, size_t length, int flags, unsigned int int result; // check if data is available - res = check_fd(fd, fdread, timeout); + res = check_fd(fd, FD_READ, timeout); if (res <= 0) { return res; } diff --git a/sock_stuff.h b/sock_stuff.h index 01082d1..9965f4e 100644 --- a/sock_stuff.h +++ b/sock_stuff.h @@ -5,9 +5,9 @@ enum fd_mode { - fdread, - fdwrite, - fdexcept + FD_READ, + FD_WRITE, + FD_EXCEPT }; typedef enum fd_mode fd_mode; diff --git a/testclient.c b/testclient.c index fafbf23..679b6d0 100644 --- a/testclient.c +++ b/testclient.c @@ -10,11 +10,9 @@ #include "usbmuxd.h" #include "sock_stuff.h" -#define SOCKET_FILE "/var/run/usbmuxd" - int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t *result) { - struct usbmux_result res; + struct usbmuxd_result res; int recv_len; if (!result) { @@ -28,7 +26,7 @@ int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t *result) if ((recv_len == sizeof(res)) && (res.header.length == recv_len) && (res.header.reserved == 0) - && (res.header.type == usbmux_result) + && (res.header.type == USBMUXD_RESULT) ) { *result = res.result; if (res.header.tag == tag) { @@ -50,29 +48,29 @@ int main(int argc, char **argv) int connected; uint32_t pktlen; unsigned char *buf; - struct usbmux_header hello; - struct usbmux_dev_info device_info; + struct usbmuxd_hello hello; + struct usbmuxd_device_info device_info; - sfd = connect_unix_socket(SOCKET_FILE); + sfd = connect_unix_socket(USBMUXD_SOCKET_FILE); if (sfd < 0) { printf("error opening socket, terminating.\n"); return -1; } // send hello - hello.length = sizeof(struct usbmux_header); - hello.reserved = 0; - hello.type = usbmux_hello; - hello.tag = 2; + hello.header.length = sizeof(struct usbmuxd_hello); + hello.header.reserved = 0; + hello.header.type = USBMUXD_HELLO; + hello.header.tag = 2; hello_done = 0; connected = 0; fprintf(stdout, "sending Hello packet\n"); - if (send(sfd, &hello, hello.length, 0) == hello.length) { + if (send(sfd, &hello, hello.header.length, 0) == hello.header.length) { uint32_t res = -1; // get response - if (usbmuxd_get_result(sfd, hello.tag, &res) && (res==0)) { + if (usbmuxd_get_result(sfd, hello.header.tag, &res) && (res==0)) { fprintf(stdout, "Got Hello Response!\n"); hello_done = 1; } else { @@ -97,7 +95,7 @@ int main(int argc, char **argv) } fprintf(stdout, "got device data:\n"); //log_debug_buffer(stdout, (char*)buf, pktlen); - memcpy(&device_info, buf + sizeof(struct usbmux_header), sizeof(device_info)); + memcpy(&device_info, buf + sizeof(struct usbmuxd_header), sizeof(device_info)); free(buf); } else { // we _should_ have all of them now. @@ -108,15 +106,15 @@ int main(int argc, char **argv) } if (device_info.device_id > 0) { - struct usbmux_connect_request c_req; + struct usbmuxd_connect_request c_req; // try to connect to last device found c_req.header.length = sizeof(c_req); c_req.header.reserved = 0; - c_req.header.type = usbmux_connect; + c_req.header.type = USBMUXD_CONNECT; c_req.header.tag = 3; c_req.device_id = device_info.device_id; - c_req.port = htons(22); + c_req.tcp_dport = htons(22); c_req.reserved = 0; if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) { diff --git a/usbmuxd.c b/usbmuxd.c deleted file mode 100644 index 0f4339c..0000000 --- a/usbmuxd.c +++ /dev/null @@ -1,855 +0,0 @@ -/* - * usbmuxd -- daemon for communication with iPhone/iPod via USB - * - * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. - * Based upon iTunnel source code, Copyright (c) 2008 Jing Su. - * http://www.cs.toronto.edu/~jingsu/itunnel/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "usbmuxd.h" -#include "sock_stuff.h" - -#include "iphone.h" - -#define SOCKET_FILE "/var/run/usbmuxd" - -#define DEFAULT_TIMEOUT 4000 -#define DEFAULT_CHILDREN_CAPACITY 10 - -static int quit_flag = 0; -static int fsock = -1; - -struct device_use_info { - uint32_t device_id; - iphone_device_t phone; - int use_count; -}; - -struct client_data { - volatile int dead; - int socket; - int tag; - pthread_t thread; - pthread_t handler; - pthread_t reader; - int reader_quit; - int reader_dead; - int handler_dead; - iphone_umux_client_t muxclient; - struct device_use_info *duinfo; -}; - -static struct device_use_info **device_use_list = NULL; -static int device_use_count = 0; -static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER; - -/** - * mutex for mutual exclusion of calling the iphone_mux_send function - * TODO: I don't know if we really need this? - */ -static pthread_mutex_t writer_mutex = PTHREAD_MUTEX_INITIALIZER; - -/** - * mutex to keep the reader threads from reading partial packages - */ -static pthread_mutex_t reader_mutex = PTHREAD_MUTEX_INITIALIZER; - -#ifdef DEBUG -/** - * for debugging purposes. - */ -static void print_buffer(const char *data, const int length) -{ - int i; - int j; - unsigned char c; - - for(i=0; i= length) { - printf(" "); - continue; - } - printf("%02hhx ", *(data+i+j)); - } - printf(" | "); - for(j=0;j<16;j++) { - if (i+j >= length) - break; - c = *(data+i+j); - if ((c < 32) || (c > 127)) { - printf("."); - continue; - } - printf("%c", c); - } - printf("\n"); - } - printf("\n"); -} -#endif - -/** - * Read incoming usbmuxd packet. If the packet is larger than - * the size specified by len, the data will be truncated. - * - * @param fd the file descriptor to read from. - * @param data pointer to a buffer to store the read data to. - * @param len the length of the data to be read. The buffer - * pointed to by data should be at least len bytes in size. - * - * @return - */ -static int usbmuxd_get_request(int fd, void *data, size_t len) -{ - uint32_t pktlen; - int recv_len; - - if (peek_buf(fd, &pktlen, sizeof(pktlen)) < sizeof(pktlen)) { - return -errno; - } - - if (len < pktlen) { - // target buffer is to small to hold this packet! fix it! - fprintf(stderr, "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.\n", __func__, pktlen, len); - pktlen = len; - } - - recv_len = recv_buf(fd, data, pktlen); - if ((recv_len > 0) && (recv_len < pktlen)) { - fprintf(stderr, "%s: Uh-oh, we got less than the packet's size, %d instead of %d...\n", __func__, recv_len, pktlen); - } - - return recv_len; -} - -/** - * Send a usbmuxd result packet with given tag and result_code. - * - * @param fd the file descriptor to write to. - * @param tag the tag value that identifies where this message belongs to. - * @param result_code the error value (0 = Success, most likely errno values otherwise) - * - * @return the return value returned by send_buf (normally the number of bytes sent) - */ -static int usbmuxd_send_result(int fd, uint32_t tag, uint32_t result_code) -{ - struct usbmux_result res; - - res.header.length = sizeof(res); - res.header.reserved = 0; - res.header.type = usbmux_result; - res.header.tag = tag; - res.result = result_code; - - fprintf(stderr, "%s: tag=%d result=%d\n", __func__, res.header.tag, res.result); - - return send_buf(fd, &res, sizeof(res)); -} - -/** - * this thread reads from the usb connection and writes the - * data to the connected client. - * - * @param arg pointer to a client_data structure. - * - * @return NULL in any case - */ -static void *usbmuxd_client_reader_thread(void *arg) -{ - struct client_data *cdata; - - char rbuffer[512]; - uint32_t rbuffersize = 512; - uint32_t rlen; - iphone_error_t err; - char *cursor; - ssize_t len; - int result; - - if (!arg) { - fprintf(stderr, "%s: invalid client_data supplied!\n", __func__); - cdata->reader_dead = 1; - return NULL; - } - - cdata = (struct client_data*)arg; - - cdata->reader_dead = 0; - - fprintf(stdout, "%s[%d:%d]: started\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); - - while (!quit_flag && !cdata->reader_quit) { - result = check_fd(cdata->socket, fdwrite, DEFAULT_TIMEOUT); - if (result <= 0) { - if (result < 0) { - fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno)); - } - continue; - } - - rlen = 0; - //pthread_mutex_lock(&usbmux_mutex); - err = iphone_mux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize, &rlen, DEFAULT_TIMEOUT); - //pthread_mutex_unlock(&usbmux_mutex); - if (err != 0) { - fprintf(stderr, "%s[%d:%d]: encountered USB read error: %d\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, err); - break; - } - - cursor = rbuffer; - while (rlen > 0) { - len = send_buf(cdata->socket, cursor, rlen); - // calculate remainder - rlen -= len; - // advance cursor - cursor += len; - } - fsync(cdata->socket); - } - - fprintf(stdout, "%s[%d:%d]: terminated\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); - - cdata->reader_dead = 1; - - return NULL; -} - -/** - * This function handles the connecting procedure to a previously - * set up usbmux client. - * Sends a usbmuxd result packet denoting success or failure. - * A successful result is mandatory for later communication. - * - * @param cdata pointer to a previously initialized client_data structure - * - * @return - */ -static int usbmuxd_handleConnectResult(struct client_data *cdata) -{ - int result; - char buffer[512]; - char err_type[64]; - int err_code; - ssize_t maxlen = 512; - uint32_t rlen; - iphone_error_t err; - - if (!cdata) { - fprintf(stderr, "%s: Invalid client_data provided!\n", __func__); - return -EINVAL; - } - - result = check_fd(cdata->socket, fdwrite, DEFAULT_TIMEOUT); - if (result <= 0) { - if (result < 0) { - fprintf(stderr, "%s: select error: %s\n", __func__, strerror(errno)); - return result; - } - } else { - result = 0; - err = iphone_mux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen, DEFAULT_TIMEOUT); - if (err != 0) { - fprintf(stderr, "%s: encountered USB read error: %d\n", __func__, err); - usbmuxd_send_result(cdata->socket, cdata->tag, -err); - return err; - } else { - if (rlen > 0) { - if ((buffer[0] == 1) && (rlen > 20) && !memcmp(buffer+1, "handleConnectResult:", 20)) { - // hm... we got an error message! - buffer[rlen] = 0; - fprintf(stderr, "%s: %s\n", __func__, buffer+22); - - if (sscanf(buffer+22, "%s - %d\n", err_type, &err_code) == 2) { - usbmuxd_send_result(cdata->socket, cdata->tag, err_code); - return -err_code; - } else { - usbmuxd_send_result(cdata->socket, cdata->tag, ENODATA); - return -ENODATA; - } - } else { - // send success result - usbmuxd_send_result(cdata->socket, cdata->tag, 0); - // and the server greeting message - send_buf(cdata->socket, buffer, rlen); - } - } else { - // no server greeting? this seems to be ok. send success. - usbmuxd_send_result(cdata->socket, cdata->tag, 0); - } - } - //fsync(cdata->socket); - } - return result; -} - -/** - * This thread handles the communication between the connected iPhone/iPod - * and the client that created the connection. - */ -static void *usbmuxd_client_handler_thread(void *arg) -{ - struct client_data *cdata; - int result; - char *cursor; - char buffer[1024]; - ssize_t len; - ssize_t maxlen = sizeof(buffer); - uint32_t wlen; - iphone_error_t err; - - if (!arg) { - fprintf(stderr, "%s: invalid client_data provided!\n", __func__); - return NULL; - } - - cdata = (struct client_data*)arg; - - fprintf(stdout, "%s[%d:%d]: started\n", __func__, cdata->duinfo->device_id,cdata->duinfo->use_count); - - if (usbmuxd_handleConnectResult(cdata)) { - goto leave; - } - - // starting mux reader thread - cdata->reader_quit = 0; - cdata->reader_dead = 0; - if (pthread_create(&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) { - fprintf(stderr, "%s: could not start client_reader thread\n", __func__); - cdata->reader = 0; - } - - while (!quit_flag && !cdata->reader_dead) { - result = check_fd(cdata->socket, fdread, DEFAULT_TIMEOUT); - if (result <= 0) { - if (result < 0) { - fprintf(stderr, "%s: Error: checkfd: %s\n", __func__, strerror(errno)); - } - continue; - } - - // check_fd told us there's data available, so read from client - // and push to USB device. - len = recv(cdata->socket, buffer, maxlen, 0); - if (len == 0) { - break; - } - if (len < 0) { - fprintf(stderr, "%s[%d:%d]: Error: recv: %s\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, strerror(errno)); - break; - } - - cursor = buffer; - - pthread_mutex_lock(&writer_mutex); - do { - wlen = 0; - err = iphone_mux_send(cdata->muxclient, cursor, len, &wlen); - if (err == IPHONE_E_TIMEOUT) { - // some kind of timeout... just be patient and retry. - } else if (err != IPHONE_E_SUCCESS) { - fprintf(stderr, "%s[%d:%d]: USB write error: %d\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count, err); - len = -1; - break; - } - - // calculate remainder. - len -= wlen; - // advance cursor appropiately. - cursor += wlen; - } while ((len > 0) && !quit_flag); - pthread_mutex_unlock(&writer_mutex); - if (len < 0) { - break; - } - } - -leave: - // cleanup - fprintf(stdout, "%s[%d:%d]: terminating\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); - if (cdata->reader != 0) { - cdata->reader_quit = 1; - pthread_join(cdata->reader, NULL); - } - - cdata->handler_dead = 1; - - fprintf(stdout, "%s[%d:%d]: terminated\n", __func__, cdata->duinfo->device_id, cdata->duinfo->use_count); - return NULL; -} - -/** - * This thread is started when a new connection is accepted. - * It performs the handshake, then waits for the connect packet and - * on success it starts the usbmuxd_client_handler thread. - */ -static void *usbmuxd_client_init_thread(void *arg) -{ - struct client_data *cdata; - struct usbmux_header hello; - struct usbmux_dev_info_request dev_info_req; - struct usbmux_connect_request c_req; - - struct usb_bus *bus; - struct usb_device *dev; - - int recv_len; - int found = 0; - int res; - int i; - int sent_result; - iphone_error_t err; - - iphone_device_t phone; - struct device_use_info *cur_dev = NULL; - - if (!arg) { - fprintf(stderr, "%s: invalid client_data provided!\n", __func__); - return NULL; - } - - cdata = (struct client_data*)arg; - cdata->dead = 0; - - fprintf(stdout, "%s: started (fd=%d)\n", __func__, cdata->socket); - - if ((recv_len = usbmuxd_get_request(cdata->socket, &hello, sizeof(hello))) <= 0) { - fprintf(stderr, "%s: No Hello packet received, error %s\n", __func__, strerror(errno)); - goto leave; - } - - if ((recv_len == 16) && (hello.length == 16) - && (hello.reserved == 0) && (hello.type == usbmux_hello)) { - // send success response - usbmuxd_send_result(cdata->socket, hello.tag, 0); - } else { - // send error response and exit - fprintf(stderr, "%s: Invalid Hello packet received.\n", __func__); - // TODO is this required?! - usbmuxd_send_result(cdata->socket, hello.tag, EINVAL); - goto leave; - } - - // gather data about all iPhones/iPods attached - usb_init(); - usb_find_busses(); - usb_find_devices(); - - for (bus = usb_get_busses(); bus; bus = bus->next) { - for (dev = bus->devices; dev; dev = dev->next) { - if (dev->descriptor.idVendor == 0x05ac - && dev->descriptor.idProduct >= 0x1290 - && dev->descriptor.idProduct <= 0x1293) - { - fprintf(stdout, "%s: Found device on bus %d, id %d\n", __func__, bus->location, dev->devnum); - found++; - - // construct packet - memset(&dev_info_req, 0, sizeof(dev_info_req)); - dev_info_req.header.length = sizeof(dev_info_req); - dev_info_req.header.type = usbmux_device_info; - dev_info_req.dev_info.device_id = dev->devnum; - dev_info_req.dev_info.product_id = dev->descriptor.idProduct; - if (dev->descriptor.iSerialNumber) { - usb_dev_handle *udev; - //pthread_mutex_lock(&usbmux_mutex); - udev = usb_open(dev); - if (udev) { - usb_get_string_simple(udev, dev->descriptor.iSerialNumber, dev_info_req.dev_info.serial_number, sizeof(dev_info_req.dev_info.serial_number)+1); - usb_close(udev); - } - //pthread_mutex_unlock(&usbmux_mutex); - } - -#ifdef DEBUG - print_buffer((char*)&dev_info_req, sizeof(dev_info_req)); -#endif - - // send it - if (send_buf(cdata->socket, &dev_info_req, sizeof(dev_info_req)) <= 0) { - fprintf(stderr, "%s: Error: Could not send device info: %s\n", __func__, strerror(errno)); - found--; - } - } - } - } - - // now wait for connect request - if (found <= 0) { - fprintf(stderr, "%s: No attached iPhone/iPod devices found.\n", __func__); - goto leave; - } - - memset(&c_req, 0, sizeof(c_req)); - if ((recv_len = usbmuxd_get_request(cdata->socket, &c_req, sizeof(c_req))) <= 0) { - fprintf(stderr, "%s: Did not receive any connect request.\n", __func__); - goto leave; - } - - if (c_req.header.type != usbmux_connect) { - fprintf(stderr, "%s: Unexpected packet of type %d received.\n", __func__, c_req.header.type); - goto leave; - } - - fprintf(stdout, "%s: Setting up connection to usb device #%d on port %d\n", __func__, c_req.device_id, ntohs(c_req.port)); - - // find the device, and open usb connection - phone = NULL; - cur_dev = NULL; - // first check if we already have an open connection - if (device_use_list) { - pthread_mutex_lock(&usbmux_mutex); - for (i = 0; i < device_use_count; i++) { - if (device_use_list[i]) { - if (device_use_list[i]->device_id == c_req.device_id) { - device_use_list[i]->use_count++; - cur_dev = device_use_list[i]; - phone = cur_dev->phone; - break; - } - } - } - pthread_mutex_unlock(&usbmux_mutex); - } - if (!phone) { - // if not found, make a new connection - if (iphone_get_specific_device(0, c_req.device_id, &phone) != IPHONE_E_SUCCESS) { - fprintf(stderr, "%s: device_id %d could not be opened\n", __func__, c_req.device_id); - usbmuxd_send_result(cdata->socket, c_req.header.tag, ENODEV); - goto leave; - } - // add to device list - cur_dev = (struct device_use_info*)malloc(sizeof(struct device_use_info)); - memset(cur_dev, 0, sizeof(struct device_use_info)); - cur_dev->use_count = 1; - cur_dev->device_id = c_req.device_id; - cur_dev->phone = phone; - - fprintf(stdout, "%s: device_use_count = %d\n", __func__, device_use_count); - - pthread_mutex_lock(&usbmux_mutex); - device_use_list = (struct device_use_info**)realloc(device_use_list, sizeof(struct device_use_info*) * (device_use_count+1)); - if (device_use_list) { - device_use_list[device_use_count] = cur_dev; - device_use_count++; - } - pthread_mutex_unlock(&usbmux_mutex); - } else { - fprintf(stdout, "%s: reusing usb connection device_id %d\n", __func__, c_req.device_id); - } - - // setup connection to iPhone/iPod -// pthread_mutex_lock(&usbmux_mutex); - res = iphone_mux_new_client(cur_dev->phone, 0, ntohs(c_req.port), &(cdata->muxclient)); -// pthread_mutex_unlock(&usbmux_mutex); - - if (res != 0) { - usbmuxd_send_result(cdata->socket, c_req.header.tag, res); - fprintf(stderr, "%s: mux_new_client returned %d, aborting.\n", __func__, res); - goto leave; - } - - // start connection handler thread - cdata->handler_dead = 0; - cdata->tag = c_req.header.tag; - cdata->duinfo = cur_dev; - if (pthread_create(&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0) { - fprintf(stderr, "%s: could not create usbmuxd_client_handler_thread!\n", __func__); - cdata->handler = 0; - goto leave; - } - - sent_result = 0; - - // start reading data from the connected device - while (!quit_flag && !cdata->handler_dead) { - pthread_mutex_lock(&reader_mutex); - iphone_mux_pullbulk(cur_dev->phone); - err = iphone_mux_get_error(cdata->muxclient); - pthread_mutex_unlock(&reader_mutex); - if (err != IPHONE_E_SUCCESS) { - break; - } - } - - if (!sent_result) { - //fprintf(stderr, "Sending error message %d tag %d\n", err, c_req.header.tag); - err = iphone_mux_get_error(cdata->muxclient); - //usbmuxd_send_result(cdata->socket, c_req.header.tag, err); - } - - fprintf(stdout, "%s: terminating\n", __func__); - - // wait for handler thread to finish its work - if (cdata->handler != 0) { - pthread_join(cdata->handler, NULL); - } - - // time to clean up - if (cdata && cdata->muxclient) { // should be non-NULL - iphone_mux_free_client(cdata->muxclient); - } - -leave: - // this has to be freed only if it's not in use anymore as it closes - // the USB connection - if (cur_dev) { - if (cur_dev->use_count > 1) { - cur_dev->use_count--; - } else { - iphone_free_device(cur_dev->phone); - cur_dev->use_count = 0; - free(cur_dev); - cur_dev = NULL; - pthread_mutex_lock(&usbmux_mutex); - if (device_use_count > 1) { - struct device_use_info **newlist; - int j; - - newlist = (struct device_use_info**)malloc(sizeof(struct device_use_info*) * device_use_count-1); - for (i = 0; i < device_use_count; i++) { - if (device_use_list[i] != NULL) { - newlist[j++] = device_use_list[i]; - } - } - free(device_use_list); - device_use_list = newlist; - } else { - free(device_use_list); - device_use_list = NULL; - } - pthread_mutex_unlock(&usbmux_mutex); - } - } - - cdata->dead = 1; - close(cdata->socket); - - fprintf(stdout, "%s: terminated\n", __func__); - - return NULL; -} - -/** - * make this program run detached from the current console - */ -static int daemonize() -{ - // TODO still to be implemented, also logging is missing! - return 0; -} - -/** - * signal handler function for cleaning up properly - */ -static void clean_exit(int sig) -{ - if (sig == SIGINT) { - fprintf(stdout, "CTRL+C pressed\n"); - } - quit_flag = 1; -} - -/** - * thread function that performs accept() and starts the required child - * threads to perform the rest of the communication stuff. - */ -static void *usbmuxd_accept_thread(void *arg) -{ - struct sockaddr_un c_addr; - socklen_t len = sizeof(struct sockaddr_un); - struct client_data *cdata; - struct client_data **children = NULL; - int children_capacity = DEFAULT_CHILDREN_CAPACITY; - int i = 0; - int result = 0; - int cnt; - - // Reserve space for 10 clients which should be enough. If not, the - // buffer gets enlarged later. - children = (struct client_data**)malloc(sizeof(struct client_data*) * children_capacity); - if (!children) { - fprintf(stderr, "%s: Out of memory when allocating memory for child threads. Terminating.\n", __func__); - exit(EXIT_FAILURE); - } - memset(children, 0, sizeof(struct client_data*) * children_capacity); - - fprintf(stdout, "%s: waiting for connection\n", __func__); - while (!quit_flag) { - // Check the file descriptor before accepting a connection. - // If no connection attempt is made, just repeat... - result = check_fd(fsock, fdread, 1000); - if (result <= 0) { - if (result == 0) { - // cleanup - for (i = 0; i < children_capacity; i++) { - if (children[i]) { - if (children[i]->dead != 0) { - pthread_join(children[i]->thread, NULL); - fprintf(stdout, "%s: reclaimed client thread (fd=%d)\n", __func__, children[i]->socket); - free(children[i]); - children[i] = NULL; - cnt++; - } else { - cnt = 0; - } - } else { - cnt++; - } - } - - if ((children_capacity > DEFAULT_CHILDREN_CAPACITY) - && ((children_capacity - cnt) <= DEFAULT_CHILDREN_CAPACITY)) { - children_capacity = DEFAULT_CHILDREN_CAPACITY; - children = realloc(children, sizeof(struct client_data*) * children_capacity); - } - continue; - } else { - fprintf(stderr, "select error: %s\n", strerror(errno)); - continue; - } - } - - cdata = (struct client_data*)malloc(sizeof(struct client_data)); - memset(cdata, 0, sizeof(struct client_data)); - if (!cdata) { - quit_flag = 1; - fprintf(stderr, "%s: Error: Out of memory! Terminating.\n", __func__); - break; - } - - cdata->socket = accept(fsock, (struct sockaddr*)&c_addr, &len); - if (cdata->socket < 0) { - free(cdata); - if (errno == EINTR) { - continue; - } else { - fprintf(stderr, "%s: Error in accept: %s\n", __func__, strerror(errno)); - continue; - } - } - - fprintf(stdout, "%s: new client connected (fd=%d)\n", __func__, cdata->socket); - - // create client thread: - if (pthread_create(&cdata->thread, NULL, usbmuxd_client_init_thread, cdata) == 0) { - for (i = 0; i < children_capacity; i++) { - if (children[i] == NULL) break; - } - if (i == children_capacity) { - // enlarge buffer - children_capacity++; - children = realloc(children, sizeof(struct client_data*) * children_capacity); - if (!children) { - fprintf(stderr, "%s: Out of memory when enlarging child thread buffer\n", __func__); - } - } - children[i] = cdata; - } else { - fprintf(stderr, "%s: Failed to create client_init_thread.\n", __func__); - close(cdata->socket); - free(cdata); - cdata = NULL; - } - } - - fprintf(stdout, "%s: terminating\n", __func__); - - // preparing for shutdown: wait for child threads to terminate (if any) - fprintf(stdout, "%s: waiting for child threads to terminate...\n", __func__); - for (i = 0; i < children_capacity; i++) { - if (children[i] != NULL) { - pthread_join(children[i]->thread, NULL); - free(children[i]); - } - } - - // delete the children set. - free(children); - children = NULL; - - fprintf(stdout, "%s: terminated.\n", __func__); - - return NULL; -} - -/** - * main function. - */ -int main(int argc, char **argv) -{ - int foreground = 1; - pthread_t acceptor; - - fprintf(stdout, "usbmuxd: starting\n"); - - // TODO: Parameter checking. - - fsock = create_unix_socket(SOCKET_FILE); - if (fsock < 0) { - fprintf(stderr, "Could not create socket, exiting\n"); - return -1; - } - - chmod(SOCKET_FILE, 0666); - - if (!foreground) { - if (daemonize() < 0) { - exit(EXIT_FAILURE); - } - } - - // signal(SIGHUP, reload_conf); // none yet - signal(SIGINT, clean_exit); - signal(SIGQUIT, clean_exit); - signal(SIGTERM, clean_exit); - signal(SIGPIPE, SIG_IGN); - - if (pthread_create(&acceptor, NULL, usbmuxd_accept_thread, NULL) != 0) { - fprintf(stderr, "Failed to create server thread.\n"); - close(fsock); - return -1; - } - - // Relax here. Just wait for the accept thread to terminate. - pthread_join(acceptor, NULL); - - fprintf(stdout, "usbmuxd: terminating\n"); - if (fsock >= 0) { - close(fsock); - } - - unlink(SOCKET_FILE); - - return 0; -} - diff --git a/usbmuxd.h b/usbmuxd.h index fcbee52..d749baf 100644 --- a/usbmuxd.h +++ b/usbmuxd.h @@ -3,42 +3,48 @@ #include -struct usbmux_header { +#define USBMUXD_SOCKET_FILE "/var/run/usbmuxd" + +struct usbmuxd_header { uint32_t length; // length of message, including header uint32_t reserved; // always zero uint32_t type; // message type uint32_t tag; // responses to this query will echo back this tag -}; +} __attribute__((__packed__)); -struct usbmux_result { - struct usbmux_header header; +struct usbmuxd_result { + struct usbmuxd_header header; uint32_t result; -}; +} __attribute__((__packed__)); -struct usbmux_connect_request { - struct usbmux_header header; +struct usbmuxd_connect_request { + struct usbmuxd_header header; uint32_t device_id; - uint16_t port; // TCP port number + uint16_t tcp_dport; // TCP port number uint16_t reserved; // set to zero -}; +} __attribute__((__packed__)); -struct usbmux_dev_info { +struct usbmuxd_device_info { uint32_t device_id; uint16_t product_id; char serial_number[40]; -}; +} __attribute__((__packed__)); -struct usbmux_dev_info_request { - struct usbmux_header header; - struct usbmux_dev_info dev_info; - unsigned char padding[222]; -}; +struct usbmuxd_device_info_request { + struct usbmuxd_header header; + struct usbmuxd_device_info device_info; + char padding[222]; +} __attribute__((__packed__)); + +struct usbmuxd_hello { + struct usbmuxd_header header; +} __attribute__((__packed__)); enum { - usbmux_result = 1, - usbmux_connect = 2, - usbmux_hello = 3, - usbmux_device_info = 4, + USBMUXD_RESULT = 1, + USBMUXD_CONNECT = 2, + USBMUXD_HELLO = 3, + USBMUXD_DEVICE_INFO = 4, }; #endif -- cgit v1.1-32-gdbae