From 3ce3417d4dd28635328eba18de0c78d8e36b1e9e Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Sat, 2 May 2009 13:39:55 +0200 Subject: [PATCH] Use autotools build system and add license --- 85-usbmuxd.rules | 37 -- COPYING | 340 +++++++++++++ Makefile | 55 -- Makefile.am | 6 + autogen.sh | 6 + configure.ac | 28 + iproxy.c | 241 --------- libusbmuxd.c | 202 -------- main.c | 1351 ------------------------------------------------- sock_stuff.c | 298 ----------- sock_stuff.h | 28 - src/Makefile.am | 31 ++ src/libusbmuxd.c | 202 ++++++++ src/main.c | 1351 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sock_stuff.c | 298 +++++++++++ src/sock_stuff.h | 28 + src/usbmux.c | 1259 +++++++++++++++++++++++++++++++++++++++++++++ src/usbmux.h | 51 ++ src/usbmuxd-proto.h | 52 ++ src/usbmuxd.h | 45 ++ tools/Makefile.am | 5 + tools/iproxy.c | 241 +++++++++ udev/85-usbmuxd.rules | 37 ++ udev/Makefile.am | 4 + usbmux.c | 1259 --------------------------------------------- usbmux.h | 51 -- usbmuxd-proto.h | 52 -- usbmuxd.h | 45 -- 28 files changed, 3984 insertions(+), 3619 deletions(-) delete mode 100644 85-usbmuxd.rules create mode 100644 COPYING delete mode 100644 Makefile create mode 100644 Makefile.am create mode 100755 autogen.sh create mode 100644 configure.ac delete mode 100644 iproxy.c delete mode 100644 libusbmuxd.c delete mode 100644 main.c delete mode 100644 sock_stuff.c delete mode 100644 sock_stuff.h create mode 100644 src/Makefile.am create mode 100644 src/libusbmuxd.c create mode 100644 src/main.c create mode 100644 src/sock_stuff.c create mode 100644 src/sock_stuff.h create mode 100644 src/usbmux.c create mode 100644 src/usbmux.h create mode 100644 src/usbmuxd-proto.h create mode 100644 src/usbmuxd.h create mode 100644 tools/Makefile.am create mode 100644 tools/iproxy.c create mode 100644 udev/85-usbmuxd.rules create mode 100644 udev/Makefile.am delete mode 100644 usbmux.c delete mode 100644 usbmux.h delete mode 100644 usbmuxd-proto.h delete mode 100644 usbmuxd.h diff --git a/85-usbmuxd.rules b/85-usbmuxd.rules deleted file mode 100644 index 69ddef8..0000000 --- a/85-usbmuxd.rules +++ /dev/null @@ -1,37 +0,0 @@ -# usbmuxd (iPhone "Apple Mobile Device" MUXer listening on /var/run/usbmuxd) -#if -#SUBSYSTEMS=="usb_interface", SYMLINK+="usbmux/interface" - -#SUBSYSTEMS!="usb", GOTO="usbmuxd_rules_end" # stops the whole script working -ATTR{idVendor}!="05ac", GOTO="usbmuxd_rules_end" - -# If it's plug insertion, flip it into dual "PTP + Apple Mobile Device" configuration -# This allows another application to switch it later without it getting switched back (hopefully) -# TODO: check iPod Touch/3G -SUBSYSTEM=="usb", ACTION=="add", ATTR{product}=="iPhone", ATTR{bConfigurationValue}!="3", ATTR{bConfigurationValue}="3", GOTO="usbmuxd_rules_end" - -# SYMLINK the usbmux endpoints, if we get them -# TODO: Multiple devices -# TODO: work out how to make nice, incrementing usbmux/{0,1,2,3}-in for -LABEL="usbmuxd_rules_usbmux" - -# ff/fe/02 == usbmux -# -#ACTION=="add", - -# Try to symlink the interface, containing the endpoints. -# ...But it doesn't work -#KERNELS=="7-3:3.1", SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="01", ATTRS{bAlternateSetting}==" 0", ATTRS{bNumEndpoints}=="02", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="fe", ATTRS{bInterfaceProtocol}=="02", SYMLINK+="usbmux/prettyplease", RUN+="/bin/ls -l /dev/usbmux/prettyplease" - -#ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="fe", ATTRS{bInterfaceProtocol}=="02", SYMLINK+="usbmux/interface" - -# Cute names, really they should have nice numerically increasing names. -ACTION=="add", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep04", SYMLINK+="usbmux/in" -ACTION=="add", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep85", SYMLINK+="usbmux/out" - -# Start and stop 'usbmuxd' as required. -ACTION=="add", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep85", RUN+="/sbin/start-stop-daemon --start --oknodo --exec /usr/local/sbin/usbmuxd" -ACTION=="remove", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep85", RUN+="/sbin/start-stop-daemon --stop --signal 2 --exec /usr/local/sbin/usbmuxd" - -# skip -LABEL="usbmuxd_rules_end" diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..08ddefd --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/Makefile b/Makefile deleted file mode 100644 index 535e028..0000000 --- a/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -TARGETS=usbmuxd libusbmuxd.so iproxy -CFLAGS=-I. -g -DDEBUG -fPIC -Wall -Wextra -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -LIBS=-lpthread -lusb -lrt -LDFLAGS=-L. -INSTALL_PREFIX=/usr/local - -all: $(TARGETS) - -main.o: main.c usbmuxd-proto.h sock_stuff.h usbmux.h -usbmux.o: usbmux.c usbmux.h usbmuxd.h sock_stuff.h -sock_stuff.o: sock_stuff.c sock_stuff.h -libusbmuxd.o: libusbmuxd.c usbmuxd.h usbmuxd-proto.h -iproxy.o: iproxy.c sock_stuff.h -libusbmuxd.so: libusbmuxd.o sock_stuff.o - -%.so: %.o - $(CC) -o $@ -shared -Wl,-soname,$@.1 $^ - -%.o: %.c - $(CC) -o $@ $(CFLAGS) -c $< - -usbmuxd: main.o sock_stuff.o usbmux.o - $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) - -iproxy: iproxy.o - $(CC) -o $@ $(LDFLAGS) $^ $(LIBS) -lusbmuxd - -clean: - rm -f *.o *.so $(TARGETS) - -realclean: clean - rm -f *~ - -install: all - install -m 755 usbmuxd $(INSTALL_PREFIX)/sbin/ - # udev crack - #install -D -m 644 85-usbmuxd.rules $(INSTALL_PREFIX)/lib/udev/rules.d/85-usbmuxd.rules - install -m 644 85-usbmuxd.rules /etc/udev/rules.d/85-usbmuxd.rules - # protocol - install -m 644 usbmuxd-proto.h $(INSTALL_PREFIX)/include/ - # iproxy - install -m 644 libusbmuxd.so $(INSTALL_PREFIX)/lib/ - install -m 644 usbmuxd.h $(INSTALL_PREFIX)/include/ - install -m 755 iproxy $(INSTALL_PREFIX)/bin/ - -uninstall: - -rm $(INSTALL_PREFIX)/sbin/usbmuxd - #-rm $(INSTALL_PREFIX)/lib/udev/rules.d/85-usbmuxd.rules - -rm /etc/udev/rules.d/85-usbmuxd.rules - -rm $(INSTALL_PREFIX)/include/usbmuxd-proto.h - -rm $(INSTALL_PREFIX)/lib/libusbmuxd.so - -rm $(INSTALL_PREFIX)/include/usbmuxd.h - -rm $(INSTALL_PREFIX)/bin/iproxy - -.PHONY: all clean realclean diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..43dc592 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = src udev tools + +indent: + indent -kr -ut -ts4 -l120 src/*.c src/*.h tools/*.c + +EXTRA_DIST = README.devel diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..90f6046 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,6 @@ +#!/bin/sh +aclocal -I m4 || exit 1 +libtoolize || exit 1 +autoheader || exit 1 +automake --add-missing || exit 1 +autoconf || exit 1 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..7c54416 --- /dev/null +++ b/configure.ac @@ -0,0 +1,28 @@ +# Inital configuration + +AC_PREREQ(2.61) + +AC_INIT([usbmuxd], [0.1.0], [nikias@gmx.li]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2]) + +AC_CONFIG_HEADERS([config.h]) + +# Check for programs + +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_LIBTOOL +AC_PROG_INSTALL + +# Check for libraries + +PKG_CHECK_MODULES(libusb, libusb >= 0.1.12) + +# Output files + +AC_OUTPUT([ +Makefile +src/Makefile +tools/Makefile +udev/Makefile +]) diff --git a/iproxy.c b/iproxy.c deleted file mode 100644 index 3cb2894..0000000 --- a/iproxy.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * iproxy -- proxy that enables tcp service access to iPhone/iPod - * via USB cable - * TODO: improve code... - * - * 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 "sock_stuff.h" -#include "usbmuxd.h" - -static uint16_t listen_port = 0; -static uint16_t device_port = 0; - -pthread_mutex_t smutex = PTHREAD_MUTEX_INITIALIZER; - -struct client_data { - int fd; - int sfd; - volatile int stop_ctos; - volatile int stop_stoc; -}; - -void *run_stoc_loop(void *arg) -{ - struct client_data *cdata = (struct client_data*)arg; - int recv_len; - int sent; - char buffer[131072]; - - printf("%s: fd = %d\n", __func__, cdata->fd); - - while (!cdata->stop_stoc && cdata->fd>0 && cdata->sfd>0) { - recv_len = recv_buf_timeout(cdata->sfd, buffer, sizeof(buffer), 0, 5000); - if (recv_len <= 0) { - if (recv_len == 0) { - // try again - continue; - } else { - fprintf(stderr, "recv failed: %s\n", strerror(errno)); - break; - } - } else { -// printf("received %d bytes from server\n", recv_len); - // send to socket - sent = send_buf(cdata->fd, buffer, recv_len); - if (sent < recv_len) { - if (sent <= 0) { - fprintf(stderr, "send failed: %s\n", strerror(errno)); - break; - } else { - fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); - } - } else { - // sending succeeded, receive from device -// printf("pushed %d bytes to client\n", sent); - } - } - } - close(cdata->fd); - cdata->fd = -1; - cdata->stop_ctos = 1; - - return NULL; -} - -void *run_ctos_loop(void *arg) -{ - struct client_data *cdata = (struct client_data*)arg; - int recv_len; - int sent; - char buffer[131072]; - pthread_t stoc = 0; - - printf("%s: fd = %d\n", __func__, cdata->fd); - - cdata->stop_stoc = 0; - pthread_create(&stoc, NULL, run_stoc_loop, cdata); - - while (!cdata->stop_ctos && cdata->fd>0 && cdata->sfd>0) { - recv_len = recv_buf_timeout(cdata->fd, buffer, sizeof(buffer), 0, 5000); - if (recv_len <= 0) { - if (recv_len == 0) { - // try again - continue; - } else { - fprintf(stderr, "recv failed: %s\n", strerror(errno)); - break; - } - } else { -// printf("pulled %d bytes from client\n", recv_len); - // send to local socket - sent = send_buf(cdata->sfd, buffer, recv_len); - if (sent < recv_len) { - if (sent <= 0) { - fprintf(stderr, "send failed: %s\n", strerror(errno)); - break; - } else { - fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); - } - } else { - // sending succeeded, receive from device -// printf("sent %d bytes to server\n", sent); - } - } - } - close(cdata->fd); - cdata->fd = -1; - cdata->stop_stoc = 1; - - pthread_join(stoc, NULL); - - return NULL; -} - -void *acceptor_thread(void *arg) -{ - struct client_data *cdata; - usbmuxd_scan_result *dev_list = NULL; - pthread_t ctos; - int count; - - if (!arg) { - fprintf(stderr, "invalid client_data provided!\n"); - return NULL; - } - - cdata = (struct client_data*)arg; - - if ((count = usbmuxd_scan(&dev_list)) < 0) { - printf("Connecting to usbmuxd failed, terminating.\n"); - free(dev_list); - return NULL; - } - - fprintf(stdout, "Number of available devices == %d\n", count); - - if (dev_list == NULL || dev_list[0].handle == 0) { - printf("No connected device found, terminating.\n"); - free(dev_list); - return NULL; - } - - fprintf(stdout, "Requesting connecion to device handle == %d (serial: %s), port %d\n", dev_list[0].handle, dev_list[0].serial_number, device_port); - - cdata->sfd = usbmuxd_connect(dev_list[0].handle, device_port); - free(dev_list); - if (cdata->sfd < 0) { - fprintf(stderr, "Error connecting to device!\n"); - } else { - cdata->stop_ctos = 0; - pthread_create(&ctos, NULL, run_ctos_loop, cdata); - pthread_join(ctos, NULL); - } - - if (cdata->fd > 0) { - close(cdata->fd); - } - if (cdata->sfd > 0) { - close(cdata->sfd); - } - - return NULL; -} - -int main(int argc, char **argv) -{ - int mysock = -1; - - if (argc != 3) { - printf("usage: %s LOCAL_TCP_PORT DEVICE_TCP_PORT\n", argv[0]); - return 0; - } - - listen_port = atoi(argv[1]); - device_port = atoi(argv[2]); - - if (!listen_port) { - fprintf(stderr, "Invalid listen_port specified!\n"); - return -EINVAL; - } - - if (!device_port) { - fprintf(stderr, "Invalid device_port specified!\n"); - return -EINVAL; - } - - // first create the listening socket endpoint waiting for connections. - mysock = create_socket(listen_port); - if (mysock < 0) { - fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); - return -errno; - } else { - pthread_t acceptor; - struct sockaddr_in c_addr; - socklen_t len = sizeof(struct sockaddr_in); - struct client_data cdata; - int c_sock; - while (1) { - printf("waiting for connection\n"); - c_sock = accept(mysock, (struct sockaddr*)&c_addr, &len); - if (c_sock) { - printf("accepted connection, fd = %d\n", c_sock); - cdata.fd = c_sock; - pthread_create(&acceptor, NULL, acceptor_thread, &cdata); - pthread_join(acceptor, NULL); - } else { - break; - } - } - close(c_sock); - close(mysock); - } - - return 0; -} diff --git a/libusbmuxd.c b/libusbmuxd.c deleted file mode 100644 index c8acbf8..0000000 --- a/libusbmuxd.c +++ /dev/null @@ -1,202 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -// usbmuxd public interface -#include -// usbmuxd protocol -#include -// socket utility functions -#include "sock_stuff.h" - -static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result) -{ - struct usbmuxd_result res; - int recv_len; - - if (!result) { - return -EINVAL; - } - - if ((recv_len = recv_buf(sfd, &res, sizeof(res))) <= 0) { - perror("recv"); - return -errno; - } else { - if ((recv_len == sizeof(res)) - && (res.header.length == (uint32_t) recv_len) - && (res.header.reserved == 0) - && (res.header.type == USBMUXD_RESULT) - ) { - *result = res.result; - if (res.header.tag == tag) { - return 1; - } else { - return 0; - } - } - } - - return -1; -} - -int usbmuxd_scan(usbmuxd_scan_result ** available_devices) -{ - struct usbmuxd_scan_request s_req; - int sfd; - int scan_success = 0; - uint32_t res; - uint32_t pktlen; - int recv_len; - usbmuxd_scan_result *newlist = NULL; - struct usbmuxd_device_info_record dev_info_pkt; - int dev_cnt = 0; - - sfd = connect_unix_socket(USBMUXD_SOCKET_FILE); - if (sfd < 0) { - fprintf(stderr, "%s: error opening socket!\n", __func__); - return sfd; - } - - s_req.header.length = sizeof(struct usbmuxd_scan_request); - s_req.header.reserved = 0; - s_req.header.type = USBMUXD_SCAN; - s_req.header.tag = 2; - - // send scan request packet - if (send_buf(sfd, &s_req, s_req.header.length) == - (int) s_req.header.length) { - res = -1; - // get response - if (usbmuxd_get_result(sfd, s_req.header.tag, &res) && (res == 0)) { - scan_success = 1; - } else { - fprintf(stderr, - "%s: Did not get response to scan request (with result=0)...\n", - __func__); - close(sfd); - return res; - } - } - - if (!scan_success) { - fprintf(stderr, "%s: Could not send scan request!\n", __func__); - return -1; - } - - *available_devices = NULL; - // receive device list - while (1) { - if (recv_buf_timeout(sfd, &pktlen, 4, MSG_PEEK, 1000) == 4) { - if (pktlen != sizeof(dev_info_pkt)) { - // invalid packet size received! - fprintf(stderr, - "%s: Invalid packet size (%d) received when expecting a device info record.\n", - __func__, pktlen); - break; - } - - recv_len = recv_buf(sfd, &dev_info_pkt, pktlen); - if (recv_len <= 0) { - fprintf(stderr, - "%s: Error when receiving device info record\n", - __func__); - break; - } else if ((uint32_t) recv_len < pktlen) { - fprintf(stderr, - "%s: received less data than specified in header!\n", - __func__); - } else { - //fprintf(stderr, "%s: got device record with id %d, UUID=%s\n", __func__, dev_info_pkt.device_info.device_id, dev_info_pkt.device_info.serial_number); - newlist = - (usbmuxd_scan_result *) realloc(*available_devices, - sizeof - (usbmuxd_scan_result) * - (dev_cnt + 1)); - if (newlist) { - newlist[dev_cnt].handle = - (int) dev_info_pkt.device.device_id; - newlist[dev_cnt].product_id = - dev_info_pkt.device.product_id; - memset(newlist[dev_cnt].serial_number, '\0', - sizeof(newlist[dev_cnt].serial_number)); - memcpy(newlist[dev_cnt].serial_number, - dev_info_pkt.device.serial_number, - sizeof(dev_info_pkt.device.serial_number)); - *available_devices = newlist; - dev_cnt++; - } else { - fprintf(stderr, - "%s: ERROR: out of memory when trying to realloc!\n", - __func__); - break; - } - } - } else { - // we _should_ have all of them now. - // or perhaps an error occured. - break; - } - } - - // terminating zero record - newlist = - (usbmuxd_scan_result *) realloc(*available_devices, - sizeof(usbmuxd_scan_result) * - (dev_cnt + 1)); - memset(newlist + dev_cnt, 0, sizeof(usbmuxd_scan_result)); - *available_devices = newlist; - - return dev_cnt; -} - -int usbmuxd_connect(const int handle, const unsigned short tcp_port) -{ - int sfd; - struct usbmuxd_connect_request c_req; - int connected = 0; - uint32_t res = -1; - - sfd = connect_unix_socket(USBMUXD_SOCKET_FILE); - if (sfd < 0) { - fprintf(stderr, "%s: Error: Connection to usbmuxd failed: %s\n", - __func__, strerror(errno)); - return sfd; - } - - c_req.header.length = sizeof(c_req); - c_req.header.reserved = 0; - c_req.header.type = USBMUXD_CONNECT; - c_req.header.tag = 3; - c_req.device_id = (uint32_t) handle; - c_req.tcp_dport = htons(tcp_port); - c_req.reserved = 0; - - if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) { - perror("send"); - } else { - // read ACK - //fprintf(stderr, "%s: Reading connect result...\n", __func__); - if (usbmuxd_get_result(sfd, c_req.header.tag, &res)) { - if (res == 0) { - //fprintf(stderr, "%s: Connect success!\n", __func__); - connected = 1; - } else { - fprintf(stderr, "%s: Connect failed, Error code=%d\n", - __func__, res); - } - } - } - - if (connected) { - return sfd; - } - - close(sfd); - - return -1; -} diff --git a/main.c b/main.c deleted file mode 100644 index e7292cc..0000000 --- a/main.c +++ /dev/null @@ -1,1351 +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 -#include -#include -#include -#include - -#include "usbmuxd-proto.h" -#include "sock_stuff.h" - -#include "usbmux.h" - -#define DEFAULT_TIMEOUT 4000 -#define DEFAULT_CHILDREN_CAPACITY 10 -#define DEBUG_LEVEL 0 - -#define LOCKFILE "/var/run/usbmuxd.lock" - -#define THREAD (unsigned int)pthread_self() - -static int quit_flag = 0; -static int fsock = -1; -static int verbose = DEBUG_LEVEL; -static int foreground = 0; -static int exit_on_no_devices = 0; - -struct device_info { - uint32_t device_id; - usbmux_device_t phone; - int use_count; - pthread_t bulk_reader; - pthread_mutex_t mutex; - /* mutex for mutual exclusion of calling the usbmux_send function - * TODO: I don't know if we need really need this? */ - pthread_mutex_t writer_mutex; -}; - -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; - int connected; - usbmux_client_t muxclient; - struct device_info *dev; -}; - -static struct device_info **devices = NULL; -static int device_count = 0; -static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t usb_mutex = PTHREAD_MUTEX_INITIALIZER; - -/** - * logs a message to syslog when running as daemon or to stdout/stderr when - * running in foreground. - * @param prio The logging priority. - * @param format The message to be printed. - */ -static void logmsg(int prio, const char *format, ...) -{ - va_list args; - va_start(args, format); - - if (!foreground) { - // daemon. log using syslog. - vsyslog(prio, format, args); - } else { - // running in foreground. log to stdout/stderr. - char msgbuf[256]; - FILE *lfp = stdout; - switch (prio) { - case LOG_EMERG: - case LOG_ALERT: - case LOG_CRIT: - case LOG_ERR: - case LOG_WARNING: - lfp = stderr; - break; - default: - lfp = stdout; - } - strcpy(msgbuf, "usbmuxd: "); - vsnprintf(msgbuf + 9, 244, format, args); - strcat(msgbuf, "\n"); - fputs(msgbuf, lfp); - } - - va_end(args); -} - -#ifdef DEBUG -/** - * for debugging purposes. - */ -static void print_buffer(FILE * fp, const char *data, const int length) -{ - int i; - int j; - unsigned char c; - - for (i = 0; i < length; i += 16) { - if (verbose >= 4) - fprintf(fp, "%04x: ", i); - for (j = 0; j < 16; j++) { - if (i + j >= length) { - if (verbose >= 4) - fprintf(fp, " "); - continue; - } - if (verbose >= 4) - fprintf(fp, "%02hhx ", *(data + i + j)); - } - if (verbose >= 4) - fprintf(fp, " | "); - for (j = 0; j < 16; j++) { - if (i + j >= length) - break; - c = *(data + i + j); - if ((c < 32) || (c > 127)) { - if (verbose >= 4) - fprintf(fp, "."); - continue; - } - if (verbose >= 4) - fprintf(fp, "%c", c); - } - if (verbose >= 4) - fprintf(fp, "\n"); - } - if (verbose >= 4) - fprintf(fp, "\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)) < (int) sizeof(pktlen)) { - return -errno; - } - - if (len == 0) { - // allocate buffer space - *data = malloc(pktlen); - } else if (len < pktlen) { - // target buffer is to small to hold this packet! fix it! - if (verbose >= 2) - logmsg(LOG_WARNING, - "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.", - __func__, pktlen, len); - pktlen = len; - } - - recv_len = recv_buf(fd, *data, pktlen); - if ((recv_len > 0) && ((uint32_t) recv_len < pktlen)) { - if (verbose >= 2) - logmsg(LOG_WARNING, - "%s: Uh-oh, we got less than the packet's size, %d instead of %d...", - __func__, recv_len, pktlen); - } -#ifdef DEBUG - if (*data && (recv_len > 0) && verbose >= 4) { - fprintf(stderr, "%s: received:\n", __func__); - print_buffer(stderr, *data, recv_len); - } -#endif - - 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; - int ret; - - res.header.length = sizeof(res); - res.header.reserved = 0; - res.header.type = USBMUXD_RESULT; - res.header.tag = tag; - res.result = result_code; - - if (verbose >= 4) - logmsg(LOG_NOTICE, "%s: tag=%d result=%d", __func__, - res.header.tag, res.result); - - ret = send_buf(fd, &res, sizeof(res)); - fsync(fd); // let's get it sent - return ret; -} - -/** - * 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; - int err; - char *cursor; - ssize_t len; - int result; - - if (!arg) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: invalid client_data supplied!", __func__); - cdata->reader_dead = 1; - return NULL; - } - - cdata = (struct client_data *) arg; - - cdata->reader_dead = 0; - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%d:%d]: started", __func__, - cdata->dev->device_id, cdata->dev->use_count); - - while (!quit_flag && !cdata->reader_quit) { - result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT); - if (result <= 0) { - if (result < 0) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: select error: %s", __func__, - strerror(errno)); - } - continue; - } - - rlen = 0; - err = - usbmux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize, - &rlen, DEFAULT_TIMEOUT); - if (err != 0) { - if (verbose >= 2) - logmsg(LOG_ERR, - "%s[%d:%d]: encountered USB read error: %d", - __func__, cdata->dev->device_id, - cdata->dev->use_count, err); - break; - } - - cursor = rbuffer; - while (rlen > 0) { - len = send_buf(cdata->socket, cursor, rlen); - if (len <= 0) { - logmsg(LOG_ERR, "%s: Error: send returned %d", __func__, - len); - err = 1; - break; - } - // calculate remainder - rlen -= len; - // advance cursor - cursor += len; - } - if (err != 0) { - logmsg(LOG_ERR, "%s: Error when writing to client...", - __func__); - break; - } - fsync(cdata->socket); - } - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%d:%d]: terminated", __func__, - cdata->dev->device_id, cdata->dev->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; - int err; - - if (!cdata) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: Invalid client_data provided!", __func__); - return -EINVAL; - } - - result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT); - if (result <= 0) { - if (result < 0) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: select error: %s", __func__, - strerror(errno)); - return result; - } - } else { - result = 0; - err = - usbmux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen, - 100); - if (err < 0) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: encountered USB read error: %d", - __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; - if (verbose >= 1) - logmsg(LOG_ERR, "%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[65536]; - ssize_t len; - ssize_t maxlen = sizeof(buffer); - uint32_t wlen; - int err; - - if (!arg) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: invalid client_data provided!", __func__); - return NULL; - } - - cdata = (struct client_data *) arg; - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%d:%d]: started", __func__, - cdata->dev->device_id, cdata->dev->use_count); - - if (usbmuxd_handleConnectResult(cdata)) { - if (verbose >= 3) - logmsg(LOG_ERR, "handleConnectResult: Error"); - goto leave; - } else { - if (verbose >= 3) - logmsg(LOG_NOTICE, "handleConnectResult: Success"); - } - - // starting mux reader thread - cdata->reader_quit = 0; - cdata->reader_dead = 0; - if (pthread_create - (&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: could not start client_reader thread", - __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) { - if (verbose >= 3) - logmsg(LOG_ERR, "%s: Error: checkfd: %s", __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) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s[%d:%d]: Error: recv: %s", __func__, - cdata->dev->device_id, cdata->dev->use_count, - strerror(errno)); - break; - } - - cursor = buffer; - - pthread_mutex_lock(&cdata->dev->writer_mutex); - do { - wlen = 0; - err = usbmux_send(cdata->muxclient, cursor, len, &wlen); - if (err == -ETIMEDOUT) { - // some kind of timeout... just be patient and retry. - } else if (err < 0) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s[%d:%d]: USB write error: %d", - __func__, cdata->dev->device_id, - cdata->dev->use_count, err); - len = -1; - break; - } - // calculate remainder. - len -= wlen; - // advance cursor appropiately. - cursor += wlen; - } - while ((len > 0) && !quit_flag); - pthread_mutex_unlock(&cdata->dev->writer_mutex); - if (len < 0) { - break; - } - } - - leave: - // cleanup - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%d:%d]: terminating", __func__, - cdata->dev->device_id, cdata->dev->use_count); - if (cdata->reader != 0) { - cdata->reader_quit = 1; - pthread_join(cdata->reader, NULL); - } - - cdata->handler_dead = 1; - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%d:%d]: terminated", __func__, - cdata->dev->device_id, cdata->dev->use_count); - return NULL; -} - -/** - * Thread performing usb_bulk_read from the connected device. - * One thread per device. Lives as long as the device is in use. - */ -static void *usbmuxd_bulk_reader_thread(void *arg) -{ - struct device_info *cur_dev; - int err; - - if (!arg) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s: Invalid client_data provided", __func__); - return NULL; - } - - cur_dev = (struct device_info *) arg; - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s: started", __func__); - - while (!quit_flag && cur_dev) { - - pthread_mutex_lock(&cur_dev->mutex); - if (cur_dev->use_count <= 0) { - pthread_mutex_unlock(&cur_dev->mutex); - break; - } - pthread_mutex_unlock(&cur_dev->mutex); - - if ((err = usbmux_pullbulk(cur_dev->phone)) < 0) { - if (verbose >= 1) - logmsg(LOG_ERR, "%s: error %d when reading from device", - __func__, err); - break; - } - } - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s: terminated", __func__); - - 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_scan_request *s_req = NULL; - struct usbmuxd_device_info_record dev_info_rec; - struct usbmuxd_connect_request *c_req = NULL; - - struct usb_bus *bus; - struct usb_device *dev; - - int recv_len; - int found = 0; - int res; - int i; - - usbmux_device_t phone = NULL; - struct device_info *cur_dev = NULL; - - if (!arg) { - if (verbose >= 1) - logmsg(LOG_ERR, "%s[%x]: invalid client_data provided!", - __func__, THREAD); - return NULL; - } - - cdata = (struct client_data *) arg; - cdata->dead = 0; - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%x]: started (fd=%d)", __func__, THREAD, - cdata->socket); - - if ((recv_len = - usbmuxd_get_request(cdata->socket, (void **) &s_req, 0)) <= 0) { - if (verbose >= 2) - logmsg(LOG_ERR, "%s[%x]: No scan packet received, error %s", - __func__, THREAD, strerror(errno)); - goto leave; - } - - if ((recv_len == sizeof(struct usbmuxd_scan_request)) - && (s_req->header.length == sizeof(struct usbmuxd_scan_request)) - && (s_req->header.reserved == 0) - && (s_req->header.type == USBMUXD_SCAN)) { - // send success response - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%x]: Got scan packet!", __func__, - THREAD); - usbmuxd_send_result(cdata->socket, s_req->header.tag, 0); - } else if ((recv_len == sizeof(struct usbmuxd_connect_request)) - && (s_req->header.type == USBMUXD_CONNECT)) { - c_req = (struct usbmuxd_connect_request *) s_req; - s_req = NULL; - goto connect; - } else { - // send error response and exit - if (verbose >= 2) - logmsg(LOG_ERR, "%s[%x]: Invalid scan packet received.", - __func__, THREAD); - // TODO is this required?! - usbmuxd_send_result(cdata->socket, s_req->header.tag, EINVAL); - goto leave; - } - - pthread_mutex_lock(&usb_mutex); - // gather data about all iPhones/iPods attached - - if (verbose >= 5) - logmsg(LOG_DEBUG, "%s[%x]: usb init", __func__, THREAD); - usb_init(); - if (verbose >= 5) - logmsg(LOG_DEBUG, "%s[%x]: usb find busses", __func__, THREAD); - usb_find_busses(); - if (verbose >= 5) - logmsg(LOG_DEBUG, "%s[%x]: usb find devices", __func__, THREAD); - usb_find_devices(); - - if (verbose >= 2) - logmsg(LOG_NOTICE, "%s[%x]: Looking for attached devices...", - __func__, THREAD); - - 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) { - if (verbose >= 1) - logmsg(LOG_NOTICE, - "%s[%x]: Found device on bus %d, id %d", - __func__, THREAD, bus->location, dev->devnum); - found++; - - // construct packet - memset(&dev_info_rec, 0, sizeof(dev_info_rec)); - dev_info_rec.header.length = sizeof(dev_info_rec); - dev_info_rec.header.type = USBMUXD_DEVICE_INFO; - dev_info_rec.device.device_id = dev->devnum; - dev_info_rec.device.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_rec.device. - serial_number, - sizeof(dev_info_rec.device. - serial_number) + 1); - usb_close(udev); - } - //pthread_mutex_unlock(&usbmux_mutex); - } -#ifdef DEBUG - if (verbose >= 4) - print_buffer(stderr, (char *) &dev_info_rec, - sizeof(dev_info_rec)); -#endif - - // send it - if (send_buf - (cdata->socket, &dev_info_rec, - sizeof(dev_info_rec)) <= 0) { - if (verbose >= 3) - logmsg(LOG_ERR, - "%s[%x]: Error: Could not send device info: %s", - __func__, THREAD, strerror(errno)); - found--; - } - } - } - } - pthread_mutex_unlock(&usb_mutex); - - if (found <= 0) { - if (verbose >= 1) - logmsg(LOG_NOTICE, - "%s[%x]: No attached iPhone/iPod devices found.", - __func__, THREAD); - goto leave; - } - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%x]: Waiting for connect request", __func__, - THREAD); - - // now wait for connect request - //memset(&c_req, 0, sizeof(c_req)); - if ((recv_len = - usbmuxd_get_request(cdata->socket, (void **) &c_req, 0)) <= 0) { - if (verbose >= 3) - logmsg(LOG_NOTICE, - "%s[%x]: Did not receive any connect request.", - __func__, THREAD); - goto leave; - } - - connect: - - if (c_req->header.type != USBMUXD_CONNECT) { - if (verbose >= 2) - logmsg(LOG_ERR, - "%s[%x]: Unexpected packet of type %d received.", - __func__, THREAD, c_req->header.type); - goto leave; - } - - if (verbose >= 3) - logmsg(LOG_NOTICE, - "%s[%x]: Setting up connection to usb device #%d on port %d", - __func__, THREAD, c_req->device_id, - ntohs(c_req->tcp_dport)); - - // find the device, and open usb connection - pthread_mutex_lock(&usbmux_mutex); - phone = NULL; - cur_dev = NULL; - // first check if we already have an open connection - if (devices) { - for (i = 0; i < device_count; i++) { - if (devices[i]) { - if (devices[i]->device_id == c_req->device_id) { - devices[i]->use_count++; - cur_dev = devices[i]; - phone = cur_dev->phone; - break; - } - } - } - } - if (!phone) { - // if not found, make a new connection - if (verbose >= 2) - logmsg(LOG_NOTICE, - "%s[%x]: creating new usb connection, device_id=%d", - __func__, THREAD, c_req->device_id); - - pthread_mutex_lock(&usb_mutex); - if (usbmux_get_specific_device(0, c_req->device_id, &phone) < 0) { - pthread_mutex_unlock(&usb_mutex); - pthread_mutex_unlock(&usbmux_mutex); - if (verbose >= 1) - logmsg(LOG_ERR, "%s[%x]: device_id %d could not be opened", - __func__, THREAD, c_req->device_id); - usbmuxd_send_result(cdata->socket, c_req->header.tag, ENODEV); - goto leave; - } - pthread_mutex_unlock(&usb_mutex); - - // create device object - if (verbose >= 3) - logmsg(LOG_DEBUG, "%s[%x]: add to device list", __func__, - THREAD); - cur_dev = - (struct device_info *) malloc(sizeof(struct device_info)); - memset(cur_dev, 0, sizeof(struct device_info)); - cur_dev->use_count = 1; - cur_dev->device_id = c_req->device_id; - cur_dev->phone = phone; - cur_dev->bulk_reader = 0; - pthread_mutex_init(&cur_dev->mutex, NULL); - pthread_mutex_init(&cur_dev->writer_mutex, NULL); - - if (verbose >= 3) - logmsg(LOG_DEBUG, "%s[%x]: device_count = %d", __func__, - THREAD, device_count); - - // add to list of devices - devices = - (struct device_info **) realloc(devices, - sizeof(struct device_info *) * - (device_count + 1)); - if (devices) { - devices[device_count] = cur_dev; - device_count++; - } - } else { - if (verbose >= 2) - logmsg(LOG_NOTICE, - "%s[%x]: reusing usb connection, device_id=%d", - __func__, THREAD, c_req->device_id); - } - pthread_mutex_unlock(&usbmux_mutex); - - // setup connection to iPhone/iPod -// pthread_mutex_lock(&usbmux_mutex); - res = - usbmux_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); - if (verbose >= 1) - logmsg(LOG_ERR, - "%s[%x]: mux_new_client returned %d, aborting.", - __func__, THREAD, res); - goto leave; - } - // start bulk reader thread (once per device) - pthread_mutex_lock(&cur_dev->mutex); - if (cur_dev->bulk_reader == 0) { - pthread_create(&cur_dev->bulk_reader, NULL, - usbmuxd_bulk_reader_thread, cur_dev); - } - pthread_mutex_unlock(&cur_dev->mutex); - - // start connection handler thread - cdata->handler_dead = 0; - cdata->tag = c_req->header.tag; - cdata->dev = cur_dev; - if (pthread_create - (&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0) - { - if (verbose >= 1) - logmsg(LOG_ERR, - "%s[%x]: could not create usbmuxd_client_handler_thread!", - __func__, THREAD); - cdata->handler = 0; - goto leave; - } - // wait for handler thread to finish its work - if (cdata->handler != 0) { - pthread_join(cdata->handler, NULL); - } - - if (verbose >= 2) - logmsg(LOG_NOTICE, "%s[%x]: closing connection", __func__, THREAD); - - // time to clean up - if (cdata && cdata->muxclient) { // should be non-NULL - usbmux_free_client(cdata->muxclient); - } - - leave: - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%x]: terminating", __func__, THREAD); - - if (s_req) { - free(s_req); - } - if (c_req) { - free(c_req); - } - // this has to be freed only if it's not in use anymore as it closes - // the USB connection - pthread_mutex_lock(&usbmux_mutex); - if (cur_dev) { - pthread_mutex_lock(&cur_dev->mutex); - if (cur_dev->use_count > 1) { - if (verbose >= 2) - logmsg(LOG_NOTICE, - "%s[%x]: decreasing device use count (from %d to %d)", - __func__, THREAD, cur_dev->use_count, - cur_dev->use_count - 1); - cur_dev->use_count--; - pthread_mutex_unlock(&cur_dev->mutex); - } else { - if (verbose >= 2) - logmsg(LOG_NOTICE, - "%s[%x]: last client disconnected, cleaning up", - __func__, THREAD); - cur_dev->use_count = 0; - pthread_mutex_unlock(&cur_dev->mutex); - if (cur_dev->bulk_reader != 0) { - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%x]: joining bulk_reader...", - __func__, THREAD); - pthread_join(cur_dev->bulk_reader, NULL); - } - pthread_mutex_lock(&usb_mutex); - usbmux_free_device(cur_dev->phone); - pthread_mutex_unlock(&usb_mutex); - pthread_mutex_destroy(&cur_dev->writer_mutex); - pthread_mutex_destroy(&cur_dev->mutex); - free(cur_dev); - cur_dev = NULL; - if (device_count > 1) { - struct device_info **newlist; - int j; - - newlist = - (struct device_info **) - malloc(sizeof(struct device_info *) - * device_count - 1); - for (i = 0; i < device_count; i++) { - if (devices[i] != NULL) { - newlist[j++] = devices[i]; - } - } - free(devices); - devices = newlist; - device_count--; - } else { - free(devices); - devices = NULL; - device_count = 0; - } - } - } - pthread_mutex_unlock(&usbmux_mutex); - - cdata->dead = 1; - close(cdata->socket); - - if (verbose >= 3) - logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD); - - return NULL; -} - -/** - * make this program run detached from the current console - */ -static int daemonize() -{ - pid_t pid; - pid_t sid; - - // already a daemon - if (getppid() == 1) - return 0; - - pid = fork(); - if (pid < 0) { - exit(EXIT_FAILURE); - } - - if (pid > 0) { - // exit parent process - exit(EXIT_SUCCESS); - } - // At this point we are executing as the child process - - // Change the file mode mask - umask(0); - - // Create a new SID for the child process - sid = setsid(); - if (sid < 0) { - return -1; - } - // Change the current working directory. - if ((chdir("/")) < 0) { - return -2; - } - // Redirect standard files to /dev/null - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); - - return 0; -} - -/** - * signal handler function for cleaning up properly - */ -static void clean_exit(int sig) -{ - if (sig == SIGINT) { - if (verbose >= 1) - fprintf(stderr, "CTRL+C pressed\n"); - } - quit_flag = 1; -} - -static void usage() -{ - printf("usage: usbmuxd [options]\n"); - printf("\t-h|--help print this message.\n"); - printf("\t-v|--verbose be verbose\n"); - printf("\t-f|--foreground do not daemonize\n"); - printf("\n"); -} - -static void parse_opts(int argc, char **argv) -{ - static struct option longopts[] = { - {"help", 0, NULL, 'h'}, - {"foreground", 0, NULL, 'f'}, - {"verbose", 0, NULL, 'v'}, - {"exit-on-no-devices", 0, NULL, 'e'}, - {NULL, 0, NULL, 0} - }; - int c; - - while (1) { - c = getopt_long(argc, argv, "hfve", longopts, (int *) 0); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - usage(); - exit(0); - case 'f': - foreground = 1; - break; - case 'v': - sock_stuff_set_verbose(++verbose); - break; - case 'e': - exit_on_no_devices = 1; - break; - default: - usage(); - exit(2); - } - } -} - -/** - * checks for attached devices - * - * @return number of devices found - */ -static int devices_attached() -{ - struct usb_bus *bus; - struct usb_device *dev; - int res = 0; - - 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) { - res++; - } - } - } - - return res; -} - -/** - * main function. Initializes all stuff and then loops waiting in accept. - */ -int main(int argc, char **argv) -{ - struct sockaddr_un c_addr; - socklen_t len = sizeof(struct sockaddr_un); - struct client_data *cdata = NULL; - struct client_data **children = NULL; - int children_capacity = DEFAULT_CHILDREN_CAPACITY; - int i; - int result = 0; - int cnt = 0; - FILE *lfd = NULL; - struct flock lock; - - parse_opts(argc, argv); - - argc -= optind; - argv += optind; - - if (!foreground) { - openlog("usbmuxd", LOG_PID, 0); - } - - if (verbose >= 2) - logmsg(LOG_NOTICE, "starting"); - - // signal(SIGHUP, reload_conf); // none yet - signal(SIGINT, clean_exit); - signal(SIGQUIT, clean_exit); - signal(SIGTERM, clean_exit); - signal(SIGPIPE, SIG_IGN); - - // check for other running instance - lfd = fopen(LOCKFILE, "r"); - if (lfd) { - lock.l_type = 0; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - fcntl(fileno(lfd), F_GETLK, &lock); - fclose(lfd); - if (lock.l_type != F_UNLCK) { - logmsg(LOG_NOTICE, - "another instance is already running. exiting."); - return -1; - } - } - - if (exit_on_no_devices) { - if (devices_attached() <= 0) { - logmsg(LOG_NOTICE, "no devices attached. exiting."); - return 0; - } - } - - fsock = create_unix_socket(USBMUXD_SOCKET_FILE); - if (fsock < 0) { - logmsg(LOG_ERR, "Could not create socket, exiting"); - if (!foreground) { - closelog(); - } - return -1; - } - - chmod(USBMUXD_SOCKET_FILE, 0666); - - if (verbose >= 3) - usbmux_set_debug(1); - - if (!foreground) { - if (daemonize() < 0) { - fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n"); - syslog(LOG_ERR, "FATAL: Could not daemonize!"); - closelog(); - exit(EXIT_FAILURE); - } - } - // now open the lockfile and place the lock - lfd = fopen(LOCKFILE, "w"); - if (lfd) { - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - if (fcntl(fileno(lfd), F_SETLK, &lock) == -1) { - logmsg(LOG_ERR, "ERROR: lockfile locking failed!"); - } - } - // drop elevated privileges - if (getuid() == 0 || geteuid() == 0) { - struct passwd *pw = getpwnam("nobody"); - if (pw) { - setuid(pw->pw_uid); - } else { - logmsg(LOG_ERR, - "ERROR: Dropping privileges failed, check if user 'nobody' exists! Will now terminate."); - exit(EXIT_FAILURE); - } - - // security check - if (setuid(0) != -1) { - logmsg(LOG_ERR, "ERROR: Failed to drop privileges properly!"); - exit(EXIT_FAILURE); - } - if (verbose >= 2) - logmsg(LOG_NOTICE, "Successfully dropped privileges"); - } - // 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) { - logmsg(LOG_ERR, - "Out of memory when allocating memory for child threads. Terminating."); - if (!foreground) { - closelog(); - } - exit(EXIT_FAILURE); - } - memset(children, 0, sizeof(struct client_data *) * children_capacity); - - if (verbose >= 2) - logmsg(LOG_NOTICE, "waiting for connection"); - 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); - if (verbose >= 3) - logmsg(LOG_NOTICE, - "reclaimed client thread (fd=%d)", - 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 { - if (verbose >= 3) - logmsg(LOG_ERR, "usbmuxd: select error: %s", - strerror(errno)); - continue; - } - } - - cdata = (struct client_data *) malloc(sizeof(struct client_data)); - memset(cdata, 0, sizeof(struct client_data)); - if (!cdata) { - quit_flag = 1; - logmsg(LOG_ERR, "Error: Out of memory! Terminating."); - break; - } - - cdata->socket = accept(fsock, (struct sockaddr *) &c_addr, &len); - if (cdata->socket < 0) { - free(cdata); - if (errno == EINTR) { - continue; - } else { - if (verbose >= 3) - logmsg(LOG_ERR, "Error in accept: %s", - strerror(errno)); - continue; - } - } - - if (verbose >= 1) - logmsg(LOG_NOTICE, "new client connected (fd=%d)", - 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) { - logmsg(LOG_ERR, - "Out of memory when enlarging child thread buffer"); - } - } - children[i] = cdata; - } else { - logmsg(LOG_ERR, "Failed to create client_init_thread."); - close(cdata->socket); - free(cdata); - cdata = NULL; - } - } - - if (verbose >= 3) - logmsg(LOG_NOTICE, "terminating"); - - // preparing for shutdown: wait for child threads to terminate (if any) - if (verbose >= 2) - logmsg(LOG_NOTICE, "waiting for child threads to terminate..."); - 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; - - - if (fsock >= 0) { - close(fsock); - } - - unlink(USBMUXD_SOCKET_FILE); - - // unlock lock file and close it. - if (lfd) { - lock.l_type = F_UNLCK; - fcntl(fileno(lfd), F_SETLK, &lock); - fclose(lfd); - } - - if (verbose >= 1) - logmsg(LOG_NOTICE, "usbmuxd: terminated"); - if (!foreground) { - closelog(); - } - - return 0; -} diff --git a/sock_stuff.c b/sock_stuff.c deleted file mode 100644 index b51d6ba..0000000 --- a/sock_stuff.c +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sock_stuff.h" - -#define RECV_TIMEOUT 20000 - -static int verbose = 0; - -void sock_stuff_set_verbose(int level) -{ - verbose = level; -} - -int create_unix_socket(const char *filename) -{ - struct sockaddr_un name; - int sock; - size_t size; - - // remove if still present - unlink(filename); - - /* Create the socket. */ - sock = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sock < 0) { - perror("socket"); - return -1; - } - - /* Bind a name to the socket. */ - name.sun_family = AF_LOCAL; - strncpy(name.sun_path, filename, sizeof(name.sun_path)); - name.sun_path[sizeof(name.sun_path) - 1] = '\0'; - - /* The size of the address is - the offset of the start of the filename, - plus its length, - plus one for the terminating null byte. - Alternatively you can just do: - size = SUN_LEN (&name); - */ - size = (offsetof(struct sockaddr_un, sun_path) - + strlen(name.sun_path) + 1); - - if (bind(sock, (struct sockaddr *) &name, size) < 0) { - perror("bind"); - close(sock); - return -1; - } - - if (listen(sock, 10) < 0) { - perror("listen"); - close(sock); - return -1; - } - - return sock; -} - -int connect_unix_socket(const char *filename) -{ - struct sockaddr_un name; - int sfd = -1; - size_t size; - struct stat fst; - - // check if socket file exists... - if (stat(filename, &fst) != 0) { - if (verbose >= 2) - fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename, - strerror(errno)); - return -1; - } - // ... and if it is a unix domain socket - if (!S_ISSOCK(fst.st_mode)) { - if (verbose >= 2) - fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__, - filename); - return -1; - } - // make a new socket - if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - if (verbose >= 2) - fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno)); - return -1; - } - // and connect to 'filename' - name.sun_family = AF_LOCAL; - strncpy(name.sun_path, filename, sizeof(name.sun_path)); - name.sun_path[sizeof(name.sun_path) - 1] = 0; - - size = (offsetof(struct sockaddr_un, sun_path) - + strlen(name.sun_path) + 1); - - if (connect(sfd, (struct sockaddr *) &name, size) < 0) { - close(sfd); - if (verbose >= 2) - fprintf(stderr, "%s: connect: %s\n", __func__, - strerror(errno)); - return -1; - } - - return sfd; -} - -int create_socket(uint16_t port) -{ - int sfd = -1; - int yes = 1; - struct sockaddr_in saddr; - - if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { - perror("socket()"); - return -1; - } - - if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { - perror("setsockopt()"); - close(sfd); - return -1; - } - - memset((void *) &saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = htonl(INADDR_ANY); - saddr.sin_port = htons(port); - - if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) { - perror("bind()"); - close(sfd); - return -1; - } - - if (listen(sfd, 1) == -1) { - perror("listen()"); - close(sfd); - return -1; - } - - return sfd; -} - -int connect_socket(const char *addr, uint16_t port) -{ - int sfd = -1; - int yes = 1; - struct hostent *hp; - struct sockaddr_in saddr; - - if (!addr) { - errno = EINVAL; - return -1; - } - - if ((hp = gethostbyname(addr)) == NULL) { - if (verbose >= 2) - fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr); - return -1; - } - - if (!hp->h_addr) { - if (verbose >= 2) - fprintf(stderr, "%s: gethostbyname returned NULL address!\n", - __func__); - return -1; - } - - if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { - perror("socket()"); - return -1; - } - - if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { - perror("setsockopt()"); - close(sfd); - return -1; - } - - memset((void *) &saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr; - saddr.sin_port = htons(port); - - if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { - perror("connect"); - close(sfd); - return -2; - } - - return sfd; -} - -int check_fd(int fd, fd_mode fdm, unsigned int timeout) -{ - fd_set fds; - int sret; - int eagain; - struct timeval to; - - if (fd <= 0) { - if (verbose >= 2) - fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd); - return -1; - } - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - to.tv_sec = (time_t) (timeout / 1000); - to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); - - sret = -1; - - do { - eagain = 0; - switch (fdm) { - case FD_READ: - sret = select(fd + 1, &fds, NULL, NULL, &to); - break; - case FD_WRITE: - sret = select(fd + 1, NULL, &fds, NULL, &to); - break; - case FD_EXCEPT: - sret = select(fd + 1, NULL, NULL, &fds, &to); - break; - default: - return -1; - } - - if (sret < 0) { - switch (errno) { - case EINTR: - // interrupt signal in select - if (verbose >= 2) - fprintf(stderr, "%s: EINTR\n", __func__); - eagain = 1; - break; - case EAGAIN: - if (verbose >= 2) - fprintf(stderr, "%s: EAGAIN\n", __func__); - break; - default: - if (verbose >= 2) - fprintf(stderr, "%s: select failed: %s\n", __func__, - strerror(errno)); - return -1; - } - } - } while (eagain); - - return sret; -} - -int recv_buf(int fd, void *data, size_t length) -{ - return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT); -} - -int peek_buf(int fd, void *data, size_t length) -{ - return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); -} - -int recv_buf_timeout(int fd, void *data, size_t length, int flags, - unsigned int timeout) -{ - int res; - int result; - - // check if data is available - res = check_fd(fd, FD_READ, timeout); - if (res <= 0) { - return res; - } - // if we get here, there _is_ data available - result = recv(fd, data, length, flags); - if (res > 0 && result == 0) { - // but this is an error condition - if (verbose >= 3) - fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd); - return -1; - } - return result; -} - -int send_buf(int fd, void *data, size_t length) -{ - return send(fd, data, length, 0); -} diff --git a/sock_stuff.h b/sock_stuff.h deleted file mode 100644 index 190f7e1..0000000 --- a/sock_stuff.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __SOCK_STUFF_H -#define __SOCK_STUFF_H - -#include - -enum fd_mode { - FD_READ, - FD_WRITE, - FD_EXCEPT -}; -typedef enum fd_mode fd_mode; - -int create_unix_socket(const char *filename); -int connect_unix_socket(const char *filename); -int create_socket(uint16_t port); -int connect_socket(const char *addr, uint16_t port); -int check_fd(int fd, fd_mode fdm, unsigned int timeout); - -int recv_buf(int fd, void *data, size_t size); -int peek_buf(int fd, void *data, size_t size); -int recv_buf_timeout(int fd, void *data, size_t size, int flags, - unsigned int timeout); - -int send_buf(int fd, void *data, size_t size); - -void sock_stuff_set_verbose(int level); - -#endif /* __SOCK_STUFF_H */ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..547870e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,31 @@ +AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS) +AM_LDFLAGS = $(libusb_LIBS) -lpthread -lrt + +# Libraries + +noinst_LTLIBRARIES = libusbmux.la libsock_stuff.la +libsock_stuff_la_SOURCES = sock_stuff.c \ + sock_stuff.h + +libusbmux_la_SOURCES = usbmux.c \ + usbmux.h +libusbmux_la_CFLAGS = $(AM_CFLAGS) +libusbmux_la_LDFLAGS = $(AM_LDFLAGS) + +lib_LTLIBRARIES = libusbmuxd.la +libusbmuxd_la_SOURCES = libusbmuxd.c \ + usbmuxd.h \ + usbmuxd-proto.h +libusbmuxd_la_LIBADD = libsock_stuff.la + +include_HEADERS = usbmuxd.h \ + usbmuxd-proto.h + +# Programs + +sbin_PROGRAMS = usbmuxd + +usbmuxd_SOURCES = main.c +usbmuxd_LDADD = libusbmux.la \ + libsock_stuff.la + diff --git a/src/libusbmuxd.c b/src/libusbmuxd.c new file mode 100644 index 0000000..c8acbf8 --- /dev/null +++ b/src/libusbmuxd.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// usbmuxd public interface +#include +// usbmuxd protocol +#include +// socket utility functions +#include "sock_stuff.h" + +static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result) +{ + struct usbmuxd_result res; + int recv_len; + + if (!result) { + return -EINVAL; + } + + if ((recv_len = recv_buf(sfd, &res, sizeof(res))) <= 0) { + perror("recv"); + return -errno; + } else { + if ((recv_len == sizeof(res)) + && (res.header.length == (uint32_t) recv_len) + && (res.header.reserved == 0) + && (res.header.type == USBMUXD_RESULT) + ) { + *result = res.result; + if (res.header.tag == tag) { + return 1; + } else { + return 0; + } + } + } + + return -1; +} + +int usbmuxd_scan(usbmuxd_scan_result ** available_devices) +{ + struct usbmuxd_scan_request s_req; + int sfd; + int scan_success = 0; + uint32_t res; + uint32_t pktlen; + int recv_len; + usbmuxd_scan_result *newlist = NULL; + struct usbmuxd_device_info_record dev_info_pkt; + int dev_cnt = 0; + + sfd = connect_unix_socket(USBMUXD_SOCKET_FILE); + if (sfd < 0) { + fprintf(stderr, "%s: error opening socket!\n", __func__); + return sfd; + } + + s_req.header.length = sizeof(struct usbmuxd_scan_request); + s_req.header.reserved = 0; + s_req.header.type = USBMUXD_SCAN; + s_req.header.tag = 2; + + // send scan request packet + if (send_buf(sfd, &s_req, s_req.header.length) == + (int) s_req.header.length) { + res = -1; + // get response + if (usbmuxd_get_result(sfd, s_req.header.tag, &res) && (res == 0)) { + scan_success = 1; + } else { + fprintf(stderr, + "%s: Did not get response to scan request (with result=0)...\n", + __func__); + close(sfd); + return res; + } + } + + if (!scan_success) { + fprintf(stderr, "%s: Could not send scan request!\n", __func__); + return -1; + } + + *available_devices = NULL; + // receive device list + while (1) { + if (recv_buf_timeout(sfd, &pktlen, 4, MSG_PEEK, 1000) == 4) { + if (pktlen != sizeof(dev_info_pkt)) { + // invalid packet size received! + fprintf(stderr, + "%s: Invalid packet size (%d) received when expecting a device info record.\n", + __func__, pktlen); + break; + } + + recv_len = recv_buf(sfd, &dev_info_pkt, pktlen); + if (recv_len <= 0) { + fprintf(stderr, + "%s: Error when receiving device info record\n", + __func__); + break; + } else if ((uint32_t) recv_len < pktlen) { + fprintf(stderr, + "%s: received less data than specified in header!\n", + __func__); + } else { + //fprintf(stderr, "%s: got device record with id %d, UUID=%s\n", __func__, dev_info_pkt.device_info.device_id, dev_info_pkt.device_info.serial_number); + newlist = + (usbmuxd_scan_result *) realloc(*available_devices, + sizeof + (usbmuxd_scan_result) * + (dev_cnt + 1)); + if (newlist) { + newlist[dev_cnt].handle = + (int) dev_info_pkt.device.device_id; + newlist[dev_cnt].product_id = + dev_info_pkt.device.product_id; + memset(newlist[dev_cnt].serial_number, '\0', + sizeof(newlist[dev_cnt].serial_number)); + memcpy(newlist[dev_cnt].serial_number, + dev_info_pkt.device.serial_number, + sizeof(dev_info_pkt.device.serial_number)); + *available_devices = newlist; + dev_cnt++; + } else { + fprintf(stderr, + "%s: ERROR: out of memory when trying to realloc!\n", + __func__); + break; + } + } + } else { + // we _should_ have all of them now. + // or perhaps an error occured. + break; + } + } + + // terminating zero record + newlist = + (usbmuxd_scan_result *) realloc(*available_devices, + sizeof(usbmuxd_scan_result) * + (dev_cnt + 1)); + memset(newlist + dev_cnt, 0, sizeof(usbmuxd_scan_result)); + *available_devices = newlist; + + return dev_cnt; +} + +int usbmuxd_connect(const int handle, const unsigned short tcp_port) +{ + int sfd; + struct usbmuxd_connect_request c_req; + int connected = 0; + uint32_t res = -1; + + sfd = connect_unix_socket(USBMUXD_SOCKET_FILE); + if (sfd < 0) { + fprintf(stderr, "%s: Error: Connection to usbmuxd failed: %s\n", + __func__, strerror(errno)); + return sfd; + } + + c_req.header.length = sizeof(c_req); + c_req.header.reserved = 0; + c_req.header.type = USBMUXD_CONNECT; + c_req.header.tag = 3; + c_req.device_id = (uint32_t) handle; + c_req.tcp_dport = htons(tcp_port); + c_req.reserved = 0; + + if (send_buf(sfd, &c_req, sizeof(c_req)) < 0) { + perror("send"); + } else { + // read ACK + //fprintf(stderr, "%s: Reading connect result...\n", __func__); + if (usbmuxd_get_result(sfd, c_req.header.tag, &res)) { + if (res == 0) { + //fprintf(stderr, "%s: Connect success!\n", __func__); + connected = 1; + } else { + fprintf(stderr, "%s: Connect failed, Error code=%d\n", + __func__, res); + } + } + } + + if (connected) { + return sfd; + } + + close(sfd); + + return -1; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..e7292cc --- /dev/null +++ b/src/main.c @@ -0,0 +1,1351 @@ +/* + * 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 +#include +#include +#include +#include + +#include "usbmuxd-proto.h" +#include "sock_stuff.h" + +#include "usbmux.h" + +#define DEFAULT_TIMEOUT 4000 +#define DEFAULT_CHILDREN_CAPACITY 10 +#define DEBUG_LEVEL 0 + +#define LOCKFILE "/var/run/usbmuxd.lock" + +#define THREAD (unsigned int)pthread_self() + +static int quit_flag = 0; +static int fsock = -1; +static int verbose = DEBUG_LEVEL; +static int foreground = 0; +static int exit_on_no_devices = 0; + +struct device_info { + uint32_t device_id; + usbmux_device_t phone; + int use_count; + pthread_t bulk_reader; + pthread_mutex_t mutex; + /* mutex for mutual exclusion of calling the usbmux_send function + * TODO: I don't know if we need really need this? */ + pthread_mutex_t writer_mutex; +}; + +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; + int connected; + usbmux_client_t muxclient; + struct device_info *dev; +}; + +static struct device_info **devices = NULL; +static int device_count = 0; +static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t usb_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * logs a message to syslog when running as daemon or to stdout/stderr when + * running in foreground. + * @param prio The logging priority. + * @param format The message to be printed. + */ +static void logmsg(int prio, const char *format, ...) +{ + va_list args; + va_start(args, format); + + if (!foreground) { + // daemon. log using syslog. + vsyslog(prio, format, args); + } else { + // running in foreground. log to stdout/stderr. + char msgbuf[256]; + FILE *lfp = stdout; + switch (prio) { + case LOG_EMERG: + case LOG_ALERT: + case LOG_CRIT: + case LOG_ERR: + case LOG_WARNING: + lfp = stderr; + break; + default: + lfp = stdout; + } + strcpy(msgbuf, "usbmuxd: "); + vsnprintf(msgbuf + 9, 244, format, args); + strcat(msgbuf, "\n"); + fputs(msgbuf, lfp); + } + + va_end(args); +} + +#ifdef DEBUG +/** + * for debugging purposes. + */ +static void print_buffer(FILE * fp, const char *data, const int length) +{ + int i; + int j; + unsigned char c; + + for (i = 0; i < length; i += 16) { + if (verbose >= 4) + fprintf(fp, "%04x: ", i); + for (j = 0; j < 16; j++) { + if (i + j >= length) { + if (verbose >= 4) + fprintf(fp, " "); + continue; + } + if (verbose >= 4) + fprintf(fp, "%02hhx ", *(data + i + j)); + } + if (verbose >= 4) + fprintf(fp, " | "); + for (j = 0; j < 16; j++) { + if (i + j >= length) + break; + c = *(data + i + j); + if ((c < 32) || (c > 127)) { + if (verbose >= 4) + fprintf(fp, "."); + continue; + } + if (verbose >= 4) + fprintf(fp, "%c", c); + } + if (verbose >= 4) + fprintf(fp, "\n"); + } + if (verbose >= 4) + fprintf(fp, "\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)) < (int) sizeof(pktlen)) { + return -errno; + } + + if (len == 0) { + // allocate buffer space + *data = malloc(pktlen); + } else if (len < pktlen) { + // target buffer is to small to hold this packet! fix it! + if (verbose >= 2) + logmsg(LOG_WARNING, + "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.", + __func__, pktlen, len); + pktlen = len; + } + + recv_len = recv_buf(fd, *data, pktlen); + if ((recv_len > 0) && ((uint32_t) recv_len < pktlen)) { + if (verbose >= 2) + logmsg(LOG_WARNING, + "%s: Uh-oh, we got less than the packet's size, %d instead of %d...", + __func__, recv_len, pktlen); + } +#ifdef DEBUG + if (*data && (recv_len > 0) && verbose >= 4) { + fprintf(stderr, "%s: received:\n", __func__); + print_buffer(stderr, *data, recv_len); + } +#endif + + 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; + int ret; + + res.header.length = sizeof(res); + res.header.reserved = 0; + res.header.type = USBMUXD_RESULT; + res.header.tag = tag; + res.result = result_code; + + if (verbose >= 4) + logmsg(LOG_NOTICE, "%s: tag=%d result=%d", __func__, + res.header.tag, res.result); + + ret = send_buf(fd, &res, sizeof(res)); + fsync(fd); // let's get it sent + return ret; +} + +/** + * 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; + int err; + char *cursor; + ssize_t len; + int result; + + if (!arg) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: invalid client_data supplied!", __func__); + cdata->reader_dead = 1; + return NULL; + } + + cdata = (struct client_data *) arg; + + cdata->reader_dead = 0; + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%d:%d]: started", __func__, + cdata->dev->device_id, cdata->dev->use_count); + + while (!quit_flag && !cdata->reader_quit) { + result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT); + if (result <= 0) { + if (result < 0) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: select error: %s", __func__, + strerror(errno)); + } + continue; + } + + rlen = 0; + err = + usbmux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize, + &rlen, DEFAULT_TIMEOUT); + if (err != 0) { + if (verbose >= 2) + logmsg(LOG_ERR, + "%s[%d:%d]: encountered USB read error: %d", + __func__, cdata->dev->device_id, + cdata->dev->use_count, err); + break; + } + + cursor = rbuffer; + while (rlen > 0) { + len = send_buf(cdata->socket, cursor, rlen); + if (len <= 0) { + logmsg(LOG_ERR, "%s: Error: send returned %d", __func__, + len); + err = 1; + break; + } + // calculate remainder + rlen -= len; + // advance cursor + cursor += len; + } + if (err != 0) { + logmsg(LOG_ERR, "%s: Error when writing to client...", + __func__); + break; + } + fsync(cdata->socket); + } + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%d:%d]: terminated", __func__, + cdata->dev->device_id, cdata->dev->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; + int err; + + if (!cdata) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: Invalid client_data provided!", __func__); + return -EINVAL; + } + + result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT); + if (result <= 0) { + if (result < 0) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: select error: %s", __func__, + strerror(errno)); + return result; + } + } else { + result = 0; + err = + usbmux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen, + 100); + if (err < 0) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: encountered USB read error: %d", + __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; + if (verbose >= 1) + logmsg(LOG_ERR, "%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[65536]; + ssize_t len; + ssize_t maxlen = sizeof(buffer); + uint32_t wlen; + int err; + + if (!arg) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: invalid client_data provided!", __func__); + return NULL; + } + + cdata = (struct client_data *) arg; + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%d:%d]: started", __func__, + cdata->dev->device_id, cdata->dev->use_count); + + if (usbmuxd_handleConnectResult(cdata)) { + if (verbose >= 3) + logmsg(LOG_ERR, "handleConnectResult: Error"); + goto leave; + } else { + if (verbose >= 3) + logmsg(LOG_NOTICE, "handleConnectResult: Success"); + } + + // starting mux reader thread + cdata->reader_quit = 0; + cdata->reader_dead = 0; + if (pthread_create + (&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: could not start client_reader thread", + __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) { + if (verbose >= 3) + logmsg(LOG_ERR, "%s: Error: checkfd: %s", __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) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s[%d:%d]: Error: recv: %s", __func__, + cdata->dev->device_id, cdata->dev->use_count, + strerror(errno)); + break; + } + + cursor = buffer; + + pthread_mutex_lock(&cdata->dev->writer_mutex); + do { + wlen = 0; + err = usbmux_send(cdata->muxclient, cursor, len, &wlen); + if (err == -ETIMEDOUT) { + // some kind of timeout... just be patient and retry. + } else if (err < 0) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s[%d:%d]: USB write error: %d", + __func__, cdata->dev->device_id, + cdata->dev->use_count, err); + len = -1; + break; + } + // calculate remainder. + len -= wlen; + // advance cursor appropiately. + cursor += wlen; + } + while ((len > 0) && !quit_flag); + pthread_mutex_unlock(&cdata->dev->writer_mutex); + if (len < 0) { + break; + } + } + + leave: + // cleanup + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%d:%d]: terminating", __func__, + cdata->dev->device_id, cdata->dev->use_count); + if (cdata->reader != 0) { + cdata->reader_quit = 1; + pthread_join(cdata->reader, NULL); + } + + cdata->handler_dead = 1; + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%d:%d]: terminated", __func__, + cdata->dev->device_id, cdata->dev->use_count); + return NULL; +} + +/** + * Thread performing usb_bulk_read from the connected device. + * One thread per device. Lives as long as the device is in use. + */ +static void *usbmuxd_bulk_reader_thread(void *arg) +{ + struct device_info *cur_dev; + int err; + + if (!arg) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s: Invalid client_data provided", __func__); + return NULL; + } + + cur_dev = (struct device_info *) arg; + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s: started", __func__); + + while (!quit_flag && cur_dev) { + + pthread_mutex_lock(&cur_dev->mutex); + if (cur_dev->use_count <= 0) { + pthread_mutex_unlock(&cur_dev->mutex); + break; + } + pthread_mutex_unlock(&cur_dev->mutex); + + if ((err = usbmux_pullbulk(cur_dev->phone)) < 0) { + if (verbose >= 1) + logmsg(LOG_ERR, "%s: error %d when reading from device", + __func__, err); + break; + } + } + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s: terminated", __func__); + + 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_scan_request *s_req = NULL; + struct usbmuxd_device_info_record dev_info_rec; + struct usbmuxd_connect_request *c_req = NULL; + + struct usb_bus *bus; + struct usb_device *dev; + + int recv_len; + int found = 0; + int res; + int i; + + usbmux_device_t phone = NULL; + struct device_info *cur_dev = NULL; + + if (!arg) { + if (verbose >= 1) + logmsg(LOG_ERR, "%s[%x]: invalid client_data provided!", + __func__, THREAD); + return NULL; + } + + cdata = (struct client_data *) arg; + cdata->dead = 0; + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%x]: started (fd=%d)", __func__, THREAD, + cdata->socket); + + if ((recv_len = + usbmuxd_get_request(cdata->socket, (void **) &s_req, 0)) <= 0) { + if (verbose >= 2) + logmsg(LOG_ERR, "%s[%x]: No scan packet received, error %s", + __func__, THREAD, strerror(errno)); + goto leave; + } + + if ((recv_len == sizeof(struct usbmuxd_scan_request)) + && (s_req->header.length == sizeof(struct usbmuxd_scan_request)) + && (s_req->header.reserved == 0) + && (s_req->header.type == USBMUXD_SCAN)) { + // send success response + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%x]: Got scan packet!", __func__, + THREAD); + usbmuxd_send_result(cdata->socket, s_req->header.tag, 0); + } else if ((recv_len == sizeof(struct usbmuxd_connect_request)) + && (s_req->header.type == USBMUXD_CONNECT)) { + c_req = (struct usbmuxd_connect_request *) s_req; + s_req = NULL; + goto connect; + } else { + // send error response and exit + if (verbose >= 2) + logmsg(LOG_ERR, "%s[%x]: Invalid scan packet received.", + __func__, THREAD); + // TODO is this required?! + usbmuxd_send_result(cdata->socket, s_req->header.tag, EINVAL); + goto leave; + } + + pthread_mutex_lock(&usb_mutex); + // gather data about all iPhones/iPods attached + + if (verbose >= 5) + logmsg(LOG_DEBUG, "%s[%x]: usb init", __func__, THREAD); + usb_init(); + if (verbose >= 5) + logmsg(LOG_DEBUG, "%s[%x]: usb find busses", __func__, THREAD); + usb_find_busses(); + if (verbose >= 5) + logmsg(LOG_DEBUG, "%s[%x]: usb find devices", __func__, THREAD); + usb_find_devices(); + + if (verbose >= 2) + logmsg(LOG_NOTICE, "%s[%x]: Looking for attached devices...", + __func__, THREAD); + + 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) { + if (verbose >= 1) + logmsg(LOG_NOTICE, + "%s[%x]: Found device on bus %d, id %d", + __func__, THREAD, bus->location, dev->devnum); + found++; + + // construct packet + memset(&dev_info_rec, 0, sizeof(dev_info_rec)); + dev_info_rec.header.length = sizeof(dev_info_rec); + dev_info_rec.header.type = USBMUXD_DEVICE_INFO; + dev_info_rec.device.device_id = dev->devnum; + dev_info_rec.device.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_rec.device. + serial_number, + sizeof(dev_info_rec.device. + serial_number) + 1); + usb_close(udev); + } + //pthread_mutex_unlock(&usbmux_mutex); + } +#ifdef DEBUG + if (verbose >= 4) + print_buffer(stderr, (char *) &dev_info_rec, + sizeof(dev_info_rec)); +#endif + + // send it + if (send_buf + (cdata->socket, &dev_info_rec, + sizeof(dev_info_rec)) <= 0) { + if (verbose >= 3) + logmsg(LOG_ERR, + "%s[%x]: Error: Could not send device info: %s", + __func__, THREAD, strerror(errno)); + found--; + } + } + } + } + pthread_mutex_unlock(&usb_mutex); + + if (found <= 0) { + if (verbose >= 1) + logmsg(LOG_NOTICE, + "%s[%x]: No attached iPhone/iPod devices found.", + __func__, THREAD); + goto leave; + } + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%x]: Waiting for connect request", __func__, + THREAD); + + // now wait for connect request + //memset(&c_req, 0, sizeof(c_req)); + if ((recv_len = + usbmuxd_get_request(cdata->socket, (void **) &c_req, 0)) <= 0) { + if (verbose >= 3) + logmsg(LOG_NOTICE, + "%s[%x]: Did not receive any connect request.", + __func__, THREAD); + goto leave; + } + + connect: + + if (c_req->header.type != USBMUXD_CONNECT) { + if (verbose >= 2) + logmsg(LOG_ERR, + "%s[%x]: Unexpected packet of type %d received.", + __func__, THREAD, c_req->header.type); + goto leave; + } + + if (verbose >= 3) + logmsg(LOG_NOTICE, + "%s[%x]: Setting up connection to usb device #%d on port %d", + __func__, THREAD, c_req->device_id, + ntohs(c_req->tcp_dport)); + + // find the device, and open usb connection + pthread_mutex_lock(&usbmux_mutex); + phone = NULL; + cur_dev = NULL; + // first check if we already have an open connection + if (devices) { + for (i = 0; i < device_count; i++) { + if (devices[i]) { + if (devices[i]->device_id == c_req->device_id) { + devices[i]->use_count++; + cur_dev = devices[i]; + phone = cur_dev->phone; + break; + } + } + } + } + if (!phone) { + // if not found, make a new connection + if (verbose >= 2) + logmsg(LOG_NOTICE, + "%s[%x]: creating new usb connection, device_id=%d", + __func__, THREAD, c_req->device_id); + + pthread_mutex_lock(&usb_mutex); + if (usbmux_get_specific_device(0, c_req->device_id, &phone) < 0) { + pthread_mutex_unlock(&usb_mutex); + pthread_mutex_unlock(&usbmux_mutex); + if (verbose >= 1) + logmsg(LOG_ERR, "%s[%x]: device_id %d could not be opened", + __func__, THREAD, c_req->device_id); + usbmuxd_send_result(cdata->socket, c_req->header.tag, ENODEV); + goto leave; + } + pthread_mutex_unlock(&usb_mutex); + + // create device object + if (verbose >= 3) + logmsg(LOG_DEBUG, "%s[%x]: add to device list", __func__, + THREAD); + cur_dev = + (struct device_info *) malloc(sizeof(struct device_info)); + memset(cur_dev, 0, sizeof(struct device_info)); + cur_dev->use_count = 1; + cur_dev->device_id = c_req->device_id; + cur_dev->phone = phone; + cur_dev->bulk_reader = 0; + pthread_mutex_init(&cur_dev->mutex, NULL); + pthread_mutex_init(&cur_dev->writer_mutex, NULL); + + if (verbose >= 3) + logmsg(LOG_DEBUG, "%s[%x]: device_count = %d", __func__, + THREAD, device_count); + + // add to list of devices + devices = + (struct device_info **) realloc(devices, + sizeof(struct device_info *) * + (device_count + 1)); + if (devices) { + devices[device_count] = cur_dev; + device_count++; + } + } else { + if (verbose >= 2) + logmsg(LOG_NOTICE, + "%s[%x]: reusing usb connection, device_id=%d", + __func__, THREAD, c_req->device_id); + } + pthread_mutex_unlock(&usbmux_mutex); + + // setup connection to iPhone/iPod +// pthread_mutex_lock(&usbmux_mutex); + res = + usbmux_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); + if (verbose >= 1) + logmsg(LOG_ERR, + "%s[%x]: mux_new_client returned %d, aborting.", + __func__, THREAD, res); + goto leave; + } + // start bulk reader thread (once per device) + pthread_mutex_lock(&cur_dev->mutex); + if (cur_dev->bulk_reader == 0) { + pthread_create(&cur_dev->bulk_reader, NULL, + usbmuxd_bulk_reader_thread, cur_dev); + } + pthread_mutex_unlock(&cur_dev->mutex); + + // start connection handler thread + cdata->handler_dead = 0; + cdata->tag = c_req->header.tag; + cdata->dev = cur_dev; + if (pthread_create + (&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0) + { + if (verbose >= 1) + logmsg(LOG_ERR, + "%s[%x]: could not create usbmuxd_client_handler_thread!", + __func__, THREAD); + cdata->handler = 0; + goto leave; + } + // wait for handler thread to finish its work + if (cdata->handler != 0) { + pthread_join(cdata->handler, NULL); + } + + if (verbose >= 2) + logmsg(LOG_NOTICE, "%s[%x]: closing connection", __func__, THREAD); + + // time to clean up + if (cdata && cdata->muxclient) { // should be non-NULL + usbmux_free_client(cdata->muxclient); + } + + leave: + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%x]: terminating", __func__, THREAD); + + if (s_req) { + free(s_req); + } + if (c_req) { + free(c_req); + } + // this has to be freed only if it's not in use anymore as it closes + // the USB connection + pthread_mutex_lock(&usbmux_mutex); + if (cur_dev) { + pthread_mutex_lock(&cur_dev->mutex); + if (cur_dev->use_count > 1) { + if (verbose >= 2) + logmsg(LOG_NOTICE, + "%s[%x]: decreasing device use count (from %d to %d)", + __func__, THREAD, cur_dev->use_count, + cur_dev->use_count - 1); + cur_dev->use_count--; + pthread_mutex_unlock(&cur_dev->mutex); + } else { + if (verbose >= 2) + logmsg(LOG_NOTICE, + "%s[%x]: last client disconnected, cleaning up", + __func__, THREAD); + cur_dev->use_count = 0; + pthread_mutex_unlock(&cur_dev->mutex); + if (cur_dev->bulk_reader != 0) { + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%x]: joining bulk_reader...", + __func__, THREAD); + pthread_join(cur_dev->bulk_reader, NULL); + } + pthread_mutex_lock(&usb_mutex); + usbmux_free_device(cur_dev->phone); + pthread_mutex_unlock(&usb_mutex); + pthread_mutex_destroy(&cur_dev->writer_mutex); + pthread_mutex_destroy(&cur_dev->mutex); + free(cur_dev); + cur_dev = NULL; + if (device_count > 1) { + struct device_info **newlist; + int j; + + newlist = + (struct device_info **) + malloc(sizeof(struct device_info *) + * device_count - 1); + for (i = 0; i < device_count; i++) { + if (devices[i] != NULL) { + newlist[j++] = devices[i]; + } + } + free(devices); + devices = newlist; + device_count--; + } else { + free(devices); + devices = NULL; + device_count = 0; + } + } + } + pthread_mutex_unlock(&usbmux_mutex); + + cdata->dead = 1; + close(cdata->socket); + + if (verbose >= 3) + logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD); + + return NULL; +} + +/** + * make this program run detached from the current console + */ +static int daemonize() +{ + pid_t pid; + pid_t sid; + + // already a daemon + if (getppid() == 1) + return 0; + + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + if (pid > 0) { + // exit parent process + exit(EXIT_SUCCESS); + } + // At this point we are executing as the child process + + // Change the file mode mask + umask(0); + + // Create a new SID for the child process + sid = setsid(); + if (sid < 0) { + return -1; + } + // Change the current working directory. + if ((chdir("/")) < 0) { + return -2; + } + // Redirect standard files to /dev/null + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + + return 0; +} + +/** + * signal handler function for cleaning up properly + */ +static void clean_exit(int sig) +{ + if (sig == SIGINT) { + if (verbose >= 1) + fprintf(stderr, "CTRL+C pressed\n"); + } + quit_flag = 1; +} + +static void usage() +{ + printf("usage: usbmuxd [options]\n"); + printf("\t-h|--help print this message.\n"); + printf("\t-v|--verbose be verbose\n"); + printf("\t-f|--foreground do not daemonize\n"); + printf("\n"); +} + +static void parse_opts(int argc, char **argv) +{ + static struct option longopts[] = { + {"help", 0, NULL, 'h'}, + {"foreground", 0, NULL, 'f'}, + {"verbose", 0, NULL, 'v'}, + {"exit-on-no-devices", 0, NULL, 'e'}, + {NULL, 0, NULL, 0} + }; + int c; + + while (1) { + c = getopt_long(argc, argv, "hfve", longopts, (int *) 0); + if (c == -1) { + break; + } + + switch (c) { + case 'h': + usage(); + exit(0); + case 'f': + foreground = 1; + break; + case 'v': + sock_stuff_set_verbose(++verbose); + break; + case 'e': + exit_on_no_devices = 1; + break; + default: + usage(); + exit(2); + } + } +} + +/** + * checks for attached devices + * + * @return number of devices found + */ +static int devices_attached() +{ + struct usb_bus *bus; + struct usb_device *dev; + int res = 0; + + 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) { + res++; + } + } + } + + return res; +} + +/** + * main function. Initializes all stuff and then loops waiting in accept. + */ +int main(int argc, char **argv) +{ + struct sockaddr_un c_addr; + socklen_t len = sizeof(struct sockaddr_un); + struct client_data *cdata = NULL; + struct client_data **children = NULL; + int children_capacity = DEFAULT_CHILDREN_CAPACITY; + int i; + int result = 0; + int cnt = 0; + FILE *lfd = NULL; + struct flock lock; + + parse_opts(argc, argv); + + argc -= optind; + argv += optind; + + if (!foreground) { + openlog("usbmuxd", LOG_PID, 0); + } + + if (verbose >= 2) + logmsg(LOG_NOTICE, "starting"); + + // signal(SIGHUP, reload_conf); // none yet + signal(SIGINT, clean_exit); + signal(SIGQUIT, clean_exit); + signal(SIGTERM, clean_exit); + signal(SIGPIPE, SIG_IGN); + + // check for other running instance + lfd = fopen(LOCKFILE, "r"); + if (lfd) { + lock.l_type = 0; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + fcntl(fileno(lfd), F_GETLK, &lock); + fclose(lfd); + if (lock.l_type != F_UNLCK) { + logmsg(LOG_NOTICE, + "another instance is already running. exiting."); + return -1; + } + } + + if (exit_on_no_devices) { + if (devices_attached() <= 0) { + logmsg(LOG_NOTICE, "no devices attached. exiting."); + return 0; + } + } + + fsock = create_unix_socket(USBMUXD_SOCKET_FILE); + if (fsock < 0) { + logmsg(LOG_ERR, "Could not create socket, exiting"); + if (!foreground) { + closelog(); + } + return -1; + } + + chmod(USBMUXD_SOCKET_FILE, 0666); + + if (verbose >= 3) + usbmux_set_debug(1); + + if (!foreground) { + if (daemonize() < 0) { + fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n"); + syslog(LOG_ERR, "FATAL: Could not daemonize!"); + closelog(); + exit(EXIT_FAILURE); + } + } + // now open the lockfile and place the lock + lfd = fopen(LOCKFILE, "w"); + if (lfd) { + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(fileno(lfd), F_SETLK, &lock) == -1) { + logmsg(LOG_ERR, "ERROR: lockfile locking failed!"); + } + } + // drop elevated privileges + if (getuid() == 0 || geteuid() == 0) { + struct passwd *pw = getpwnam("nobody"); + if (pw) { + setuid(pw->pw_uid); + } else { + logmsg(LOG_ERR, + "ERROR: Dropping privileges failed, check if user 'nobody' exists! Will now terminate."); + exit(EXIT_FAILURE); + } + + // security check + if (setuid(0) != -1) { + logmsg(LOG_ERR, "ERROR: Failed to drop privileges properly!"); + exit(EXIT_FAILURE); + } + if (verbose >= 2) + logmsg(LOG_NOTICE, "Successfully dropped privileges"); + } + // 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) { + logmsg(LOG_ERR, + "Out of memory when allocating memory for child threads. Terminating."); + if (!foreground) { + closelog(); + } + exit(EXIT_FAILURE); + } + memset(children, 0, sizeof(struct client_data *) * children_capacity); + + if (verbose >= 2) + logmsg(LOG_NOTICE, "waiting for connection"); + 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); + if (verbose >= 3) + logmsg(LOG_NOTICE, + "reclaimed client thread (fd=%d)", + 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 { + if (verbose >= 3) + logmsg(LOG_ERR, "usbmuxd: select error: %s", + strerror(errno)); + continue; + } + } + + cdata = (struct client_data *) malloc(sizeof(struct client_data)); + memset(cdata, 0, sizeof(struct client_data)); + if (!cdata) { + quit_flag = 1; + logmsg(LOG_ERR, "Error: Out of memory! Terminating."); + break; + } + + cdata->socket = accept(fsock, (struct sockaddr *) &c_addr, &len); + if (cdata->socket < 0) { + free(cdata); + if (errno == EINTR) { + continue; + } else { + if (verbose >= 3) + logmsg(LOG_ERR, "Error in accept: %s", + strerror(errno)); + continue; + } + } + + if (verbose >= 1) + logmsg(LOG_NOTICE, "new client connected (fd=%d)", + 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) { + logmsg(LOG_ERR, + "Out of memory when enlarging child thread buffer"); + } + } + children[i] = cdata; + } else { + logmsg(LOG_ERR, "Failed to create client_init_thread."); + close(cdata->socket); + free(cdata); + cdata = NULL; + } + } + + if (verbose >= 3) + logmsg(LOG_NOTICE, "terminating"); + + // preparing for shutdown: wait for child threads to terminate (if any) + if (verbose >= 2) + logmsg(LOG_NOTICE, "waiting for child threads to terminate..."); + 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; + + + if (fsock >= 0) { + close(fsock); + } + + unlink(USBMUXD_SOCKET_FILE); + + // unlock lock file and close it. + if (lfd) { + lock.l_type = F_UNLCK; + fcntl(fileno(lfd), F_SETLK, &lock); + fclose(lfd); + } + + if (verbose >= 1) + logmsg(LOG_NOTICE, "usbmuxd: terminated"); + if (!foreground) { + closelog(); + } + + return 0; +} diff --git a/src/sock_stuff.c b/src/sock_stuff.c new file mode 100644 index 0000000..b51d6ba --- /dev/null +++ b/src/sock_stuff.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sock_stuff.h" + +#define RECV_TIMEOUT 20000 + +static int verbose = 0; + +void sock_stuff_set_verbose(int level) +{ + verbose = level; +} + +int create_unix_socket(const char *filename) +{ + struct sockaddr_un name; + int sock; + size_t size; + + // remove if still present + unlink(filename); + + /* Create the socket. */ + sock = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket"); + return -1; + } + + /* Bind a name to the socket. */ + name.sun_family = AF_LOCAL; + strncpy(name.sun_path, filename, sizeof(name.sun_path)); + name.sun_path[sizeof(name.sun_path) - 1] = '\0'; + + /* The size of the address is + the offset of the start of the filename, + plus its length, + plus one for the terminating null byte. + Alternatively you can just do: + size = SUN_LEN (&name); + */ + size = (offsetof(struct sockaddr_un, sun_path) + + strlen(name.sun_path) + 1); + + if (bind(sock, (struct sockaddr *) &name, size) < 0) { + perror("bind"); + close(sock); + return -1; + } + + if (listen(sock, 10) < 0) { + perror("listen"); + close(sock); + return -1; + } + + return sock; +} + +int connect_unix_socket(const char *filename) +{ + struct sockaddr_un name; + int sfd = -1; + size_t size; + struct stat fst; + + // check if socket file exists... + if (stat(filename, &fst) != 0) { + if (verbose >= 2) + fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename, + strerror(errno)); + return -1; + } + // ... and if it is a unix domain socket + if (!S_ISSOCK(fst.st_mode)) { + if (verbose >= 2) + fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__, + filename); + return -1; + } + // make a new socket + if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + if (verbose >= 2) + fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno)); + return -1; + } + // and connect to 'filename' + name.sun_family = AF_LOCAL; + strncpy(name.sun_path, filename, sizeof(name.sun_path)); + name.sun_path[sizeof(name.sun_path) - 1] = 0; + + size = (offsetof(struct sockaddr_un, sun_path) + + strlen(name.sun_path) + 1); + + if (connect(sfd, (struct sockaddr *) &name, size) < 0) { + close(sfd); + if (verbose >= 2) + fprintf(stderr, "%s: connect: %s\n", __func__, + strerror(errno)); + return -1; + } + + return sfd; +} + +int create_socket(uint16_t port) +{ + int sfd = -1; + int yes = 1; + struct sockaddr_in saddr; + + if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { + perror("socket()"); + return -1; + } + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt()"); + close(sfd); + return -1; + } + + memset((void *) &saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + saddr.sin_port = htons(port); + + if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) { + perror("bind()"); + close(sfd); + return -1; + } + + if (listen(sfd, 1) == -1) { + perror("listen()"); + close(sfd); + return -1; + } + + return sfd; +} + +int connect_socket(const char *addr, uint16_t port) +{ + int sfd = -1; + int yes = 1; + struct hostent *hp; + struct sockaddr_in saddr; + + if (!addr) { + errno = EINVAL; + return -1; + } + + if ((hp = gethostbyname(addr)) == NULL) { + if (verbose >= 2) + fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr); + return -1; + } + + if (!hp->h_addr) { + if (verbose >= 2) + fprintf(stderr, "%s: gethostbyname returned NULL address!\n", + __func__); + return -1; + } + + if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { + perror("socket()"); + return -1; + } + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt()"); + close(sfd); + return -1; + } + + memset((void *) &saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr; + saddr.sin_port = htons(port); + + if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { + perror("connect"); + close(sfd); + return -2; + } + + return sfd; +} + +int check_fd(int fd, fd_mode fdm, unsigned int timeout) +{ + fd_set fds; + int sret; + int eagain; + struct timeval to; + + if (fd <= 0) { + if (verbose >= 2) + fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd); + return -1; + } + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + to.tv_sec = (time_t) (timeout / 1000); + to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); + + sret = -1; + + do { + eagain = 0; + switch (fdm) { + case FD_READ: + sret = select(fd + 1, &fds, NULL, NULL, &to); + break; + case FD_WRITE: + sret = select(fd + 1, NULL, &fds, NULL, &to); + break; + case FD_EXCEPT: + sret = select(fd + 1, NULL, NULL, &fds, &to); + break; + default: + return -1; + } + + if (sret < 0) { + switch (errno) { + case EINTR: + // interrupt signal in select + if (verbose >= 2) + fprintf(stderr, "%s: EINTR\n", __func__); + eagain = 1; + break; + case EAGAIN: + if (verbose >= 2) + fprintf(stderr, "%s: EAGAIN\n", __func__); + break; + default: + if (verbose >= 2) + fprintf(stderr, "%s: select failed: %s\n", __func__, + strerror(errno)); + return -1; + } + } + } while (eagain); + + return sret; +} + +int recv_buf(int fd, void *data, size_t length) +{ + return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT); +} + +int peek_buf(int fd, void *data, size_t length) +{ + return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); +} + +int recv_buf_timeout(int fd, void *data, size_t length, int flags, + unsigned int timeout) +{ + int res; + int result; + + // check if data is available + res = check_fd(fd, FD_READ, timeout); + if (res <= 0) { + return res; + } + // if we get here, there _is_ data available + result = recv(fd, data, length, flags); + if (res > 0 && result == 0) { + // but this is an error condition + if (verbose >= 3) + fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd); + return -1; + } + return result; +} + +int send_buf(int fd, void *data, size_t length) +{ + return send(fd, data, length, 0); +} diff --git a/src/sock_stuff.h b/src/sock_stuff.h new file mode 100644 index 0000000..190f7e1 --- /dev/null +++ b/src/sock_stuff.h @@ -0,0 +1,28 @@ +#ifndef __SOCK_STUFF_H +#define __SOCK_STUFF_H + +#include + +enum fd_mode { + FD_READ, + FD_WRITE, + FD_EXCEPT +}; +typedef enum fd_mode fd_mode; + +int create_unix_socket(const char *filename); +int connect_unix_socket(const char *filename); +int create_socket(uint16_t port); +int connect_socket(const char *addr, uint16_t port); +int check_fd(int fd, fd_mode fdm, unsigned int timeout); + +int recv_buf(int fd, void *data, size_t size); +int peek_buf(int fd, void *data, size_t size); +int recv_buf_timeout(int fd, void *data, size_t size, int flags, + unsigned int timeout); + +int send_buf(int fd, void *data, size_t size); + +void sock_stuff_set_verbose(int level); + +#endif /* __SOCK_STUFF_H */ diff --git a/src/usbmux.c b/src/usbmux.c new file mode 100644 index 0000000..e86e3bc --- /dev/null +++ b/src/usbmux.c @@ -0,0 +1,1259 @@ +/* + * Copyright (c) 2008 Jing Su. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "usbmux.h" + +#define BULKIN 0x85 +#define BULKOUT 0x04 +#define HEADERLEN 28 + +static const uint8_t TCP_FIN = 1; +static const uint8_t TCP_SYN = 1 << 1; +static const uint8_t TCP_RST = 1 << 2; +static const uint8_t TCP_PSH = 1 << 3; +static const uint8_t TCP_ACK = 1 << 4; +static const uint8_t TCP_URG = 1 << 5; + +// I have trouble figuring out how to properly manage the windowing to +// the device. It keeps sending back 512 and seems to drop off a cliff +// when the device gets overwhelmed. In addition, the device likes to +// panic and send out RESETS before the window hits zero. Also, waiting +// for responses seems to not be a winning strategy. +// +// Since I'm not sure how in the hell to interpret the window sizes that +// the device is sending back to us, I've figured out some magic number +// constants which seem to work okay. +static const uint32_t WINDOW_MAX = 5 * 1024; +static const uint32_t WINDOW_INCREMENT = 512; + +typedef struct { + char *buffer; + int leftover; + int capacity; +} receivebuf_t; + +struct usbmux_device_int { + struct usb_dev_handle *usbdev; + struct usb_device *__device; + receivebuf_t usbReceive; +}; + +typedef struct { + uint32_t type, length, major, minor, allnull; +} usbmux_version_header; + +typedef struct { + uint32_t type, length; + uint16_t sport, dport; + uint32_t scnt, ocnt; + uint8_t offset, tcp_flags; + uint16_t window, nullnull, length16; +} usbmux_tcp_header; + +struct usbmux_client_int { + usbmux_tcp_header *header; + usbmux_device_t device; + + char *recv_buffer; + int r_len; + pthread_cond_t wait; + + // this contains a conditional variable which usb-writers can wait + // on while waiting for window updates from the device. + pthread_cond_t wr_wait; + // I'm going to do something really cheesy here. We are going to + // just record the most recent scnt that we are expecting to hear + // back on. We will actually halt progress by limiting the number + // of outstanding un-acked bulk sends that we have beamed out. + uint32_t wr_pending_scnt; + long wr_window; + + pthread_mutex_t mutex; + + // this variable is not protected by the mutex. This will always + // be E_SUCCESS, unless an error of some kind breaks this stream. + // this will then be set to the error that caused the broken stream. + // no further operations other than free_client will be allowed. + int error; + + int cleanup; +}; + + +static pthread_mutex_t usbmuxmutex = PTHREAD_MUTEX_INITIALIZER; +static usbmux_client_t *connlist = NULL; +static int clients = 0; + + +/** + */ +int toto_debug = 0; + +void usbmux_set_debug(int e) +{ + toto_debug = e; +} + +void log_debug_msg(const char *format, ...) +{ +#ifndef STRIP_DEBUG_CODE + va_list args; + /* run the real fprintf */ + va_start(args, format); + + if (toto_debug) + vfprintf(stderr, format, args); + + va_end(args); +#endif +} + +#ifdef DEBUG +/** + * for debugging purposes. + */ +static void print_buffer(const char *data, const int length) +{ + if (toto_debug <= 0) { + return; + } + int i; + int j; + unsigned char c; + + for (i = 0; i < length; i += 16) { + printf("%04x: ", i); + for (j = 0; j < 16; j++) { + if (i + j >= 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 + +void hton_header(usbmux_tcp_header * hdr) +{ + if (hdr) { + hdr->length = htonl(hdr->length); + hdr->scnt = htonl(hdr->scnt); + hdr->ocnt = htonl(hdr->ocnt); + hdr->length16 = htons(hdr->length16); + } +} + +void ntoh_header(usbmux_tcp_header * hdr) +{ + if (hdr) { + hdr->length = ntohl(hdr->length); + hdr->scnt = ntohl(hdr->scnt); + hdr->ocnt = ntohl(hdr->ocnt); + hdr->length16 = ntohs(hdr->length16); + } +} + +/** Creates a USBMux header containing version information + * + * @return A USBMux header + */ +usbmux_version_header *version_header() +{ + usbmux_version_header *version = + (usbmux_version_header *) malloc(sizeof(usbmux_version_header)); + version->type = 0; + version->length = htonl(20); + version->major = htonl(1); + version->minor = 0; + version->allnull = 0; + return version; +} + +/** + * This function sets the configuration of the given device to 3 + * and claims the interface 1. If usb_set_configuration fails, it detaches + * the kernel driver that blocks the device, and retries configuration. + * + * @param device which device to configure + */ +static int usbmux_config_usb_device(usbmux_device_t device) +{ + int ret; + int bytes; + char buf[512]; + +#if 0 + log_debug_msg("checking configuration...\n"); + if (device->__device->config->bConfigurationValue != 3) { + log_debug_msg + ("WARNING: usb device configuration is not 3 as expected!\n"); + } + + log_debug_msg("setting configuration...\n"); + ret = usb_set_configuration(device->device, 3); + if (ret != 0) { + log_debug_msg("Hm, usb_set_configuration returned %d: %s\n", ret, + strerror(-ret)); +#if LIBUSB_HAS_GET_DRIVER_NP + log_debug_msg("trying to fix:\n"); + log_debug_msg("-> detaching kernel driver... "); + ret = + usb_detach_kernel_driver_np(device->device, + device->__device->config-> + interface->altsetting-> + bInterfaceNumber); + if (ret != 0) { + log_debug_msg("usb_detach_kernel_driver_np returned %d: %s\n", + ret, strerror(-ret)); + } else { + log_debug_msg("done.\n"); + log_debug_msg("setting configuration again... "); + ret = usb_set_configuration(device->device, 3); + if (ret != 0) { + log_debug_msg + ("Error: usb_set_configuration returned %d: %s\n", ret, + strerror(-ret)); + log_debug_msg("--> trying to continue anyway...\n"); + } else { + log_debug_msg("done.\n"); + } + } +#else + log_debug_msg("--> trying to continue anyway...\n"); +#endif + } else { + log_debug_msg("done.\n"); + } +#endif + + log_debug_msg("claiming interface... "); + ret = usb_claim_interface(device->usbdev, 1); + if (ret != 0) { + log_debug_msg("Error: usb_claim_interface returned %d: %s\n", ret, + strerror(-ret)); + return -ENODEV; + } else { + log_debug_msg("done.\n"); + } + + do { + bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800); + } while (bytes > 0); + + return 0; +} + +/** + * Given a USB bus and device number, returns a device handle to the device on + * that bus. To aid compatibility with future devices, this function does not + * check the vendor and device IDs! To do that, you should use + * usbmux_get_device() or a system-specific API (e.g. HAL). + * + * @param bus_n The USB bus number. + * @param dev_n The USB device number. + * @param device A pointer to a usbmux_device_t, which must be set to NULL upon + * calling usbmux_get_specific_device, which will be filled with a device + * descriptor on return. + * @return 0 if ok, otherwise a negative errno value. + */ +int usbmux_get_specific_device(int bus_n, int dev_n, + usbmux_device_t * device) +{ + struct usb_bus *bus; + struct usb_device *dev; + usbmux_version_header *version; + int bytes = 0; + + //check we can actually write in device + if (!device || (device && *device)) + return -EINVAL; + + usbmux_device_t newdevice = + (usbmux_device_t) malloc(sizeof(struct usbmux_device_int)); + + // Initialize the struct + newdevice->usbdev = NULL; + newdevice->__device = NULL; + + // don't forget these: + newdevice->usbReceive.buffer = NULL; + newdevice->usbReceive.leftover = 0; + newdevice->usbReceive.capacity = 0; + + // Initialize libusb + usb_init(); + usb_find_busses(); + usb_find_devices(); + + // Set the device configuration + for (bus = usb_get_busses(); bus; bus = bus->next) + //if (bus->location == bus_n) + for (dev = bus->devices; dev != NULL; dev = dev->next) + if (dev->devnum == dev_n) { + newdevice->__device = dev; + newdevice->usbdev = usb_open(newdevice->__device); + if (usbmux_config_usb_device(newdevice) == 0) { + goto found; + } + } + + usbmux_free_device(newdevice); + + log_debug_msg("usbmux_get_specific_device: device not found\n"); + return -ENODEV; + + found: + // Send the version command to the device + version = version_header(); + bytes = + usb_bulk_write(newdevice->usbdev, BULKOUT, (char *) version, + sizeof(*version), 800); + if (bytes < 20) { + log_debug_msg("%s: libusb did NOT send enough!\n", __func__); + if (bytes < 0) { + log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n", + __func__, bytes, usb_strerror(), + strerror(-bytes)); + } + } + // Read the device's response + bytes = + usb_bulk_read(newdevice->usbdev, BULKIN, (char *) version, + sizeof(*version), 800); + + // Check for bad response + if (bytes < 20) { + free(version); + usbmux_free_device(newdevice); + log_debug_msg("%s: Invalid version message -- header too short.\n", + __func__); + if (bytes < 0) { + log_debug_msg("%s: libusb error message %d: %s (%s)\n", + __func__, bytes, usb_strerror(), + strerror(-bytes)); + return bytes; + } + return -EBADMSG; + } + // Check for correct version + if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) { + // We're all ready to roll. + log_debug_msg("%s: success\n", __func__); + free(version); + *device = newdevice; + return 0; + } else { + // Bad header + usbmux_free_device(newdevice); + free(version); + log_debug_msg("%s: Received a bad header/invalid version number.", + __func__); + return -EBADMSG; + } + + // If it got to this point it's gotta be bad + log_debug_msg("%s: Unknown error.\n", __func__); + usbmux_free_device(newdevice); + free(version); + return -EBADMSG; // if it got to this point it's gotta be bad +} + +/** Cleans up an usbmux_device_t structure, then frees the structure itself. + * This is a library-level function; deals directly with the device to tear + * down relations, but otherwise is mostly internal. + * + * @param device A pointer to an usbmux_device_t structure. + */ +int usbmux_free_device(usbmux_device_t device) +{ + char buf[512]; + int bytes; + + if (!device) + return -EINVAL; + int ret = 0; + + if (device->usbdev) { + do { + bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800); + } while (bytes > 0); + } + + if (bytes < 0) { + ret = bytes; + } + + if (device->usbReceive.buffer) { + free(device->usbReceive.buffer); + } + if (device->usbdev) { + usb_release_interface(device->usbdev, 1); + usb_close(device->usbdev); + ret = 0; + } + free(device); + + return ret; +} + + + +/** Sends data to the device + * This is a low-level (i.e. directly to device) function. + * + * @param device The device to send data to + * @param data The data to send + * @param datalen The length of the data + * @return The number of bytes sent, or -ERRNO on error + */ +int send_to_device(usbmux_device_t device, char *data, int datalen) +{ + if (!device) + return -EINVAL; + + int timeout = 1000; + int retrycount = 0; + int bytes = 0; + +#ifdef DEBUG +#ifdef DEBUG_MORE + printf("===============================\n%s: trying to send\n", + __func__); + print_buffer(data, datalen); + printf("===============================\n"); +#endif +#endif + do { + if (retrycount > 3) { + log_debug_msg + ("EPIC FAIL! aborting on retry count overload.\n"); + return -ECOMM; + } + + bytes = + usb_bulk_write(device->usbdev, BULKOUT, data, datalen, + timeout); + if (bytes == -ETIMEDOUT) { + // timed out waiting for write. + log_debug_msg("usb_bulk_write timeout error.\n"); + return bytes; + } else if (bytes < 0) { + log_debug_msg + ("usb_bulk_write failed with error. err:%d (%s)(%s)\n", + bytes, usb_strerror(), strerror(-bytes)); + return bytes; + } else if (bytes == 0) { + log_debug_msg("usb_bulk_write sent nothing. retrying.\n"); + timeout = timeout * 4; + retrycount++; + continue; + } else if (bytes < datalen) { + log_debug_msg + ("usb_bulk_write failed to send full dataload. %d of %d\n", + bytes, datalen); + timeout = timeout * 4; + retrycount++; + data += bytes; + datalen -= bytes; + continue; + } + } while (0); // fall out + +#ifdef DEBUG + if (bytes > 0) { + if (toto_debug > 0) { + printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + printf("%s: sent to device\n", __func__); + print_buffer(data, bytes); + printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); + } + } +#endif + return bytes; +} + +/** Receives data from the device + * This function is a low-level (i.e. direct from device) function. + * + * @param device The device to receive data from + * @param data Where to put data read + * @param datalen How much data to read in + * @param timeout How many milliseconds to wait for data + * + * @return How many bytes were read in, or -1 on error. + */ +int recv_from_device_timeout(usbmux_device_t device, char *data, + int datalen, int timeoutmillis) +{ + if (!device) + return -EINVAL; + //log_debug_msg("%s: attempting to receive %i bytes\n", __func__, datalen); + + int bytes = + usb_bulk_read(device->usbdev, BULKIN, data, datalen, + timeoutmillis); + // There are some things which are errors, others which are no problem. + // It's not documented in libUSB, but it seems that the error values + // returned are just negated ERRNO values. + if (bytes < 0) { + if (bytes == -ETIMEDOUT) { + // ignore this. it just means timeout reached before we + // picked up any data. no problem. + return 0; + } else { + fprintf(stderr, "%s: libusb gave me the error %d: %s (%s)\n", + __func__, bytes, usb_strerror(), strerror(-bytes)); + log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n", + __func__, bytes, usb_strerror(), + strerror(-bytes)); + } + return bytes; + } +#ifdef DEBUG + if (bytes > 0) { + if (toto_debug > 0) { + printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + printf("%s: received from device:\n", __func__); + print_buffer(data, bytes); + printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } + } +#endif + + return bytes; +} + +/** Creates a USBMux packet for the given set of ports. + * + * @param s_port The source port for the connection. + * @param d_port The destination port for the connection. + * + * @return A USBMux packet + */ +usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port) +{ + usbmux_tcp_header *conn = + (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); + conn->type = htonl(6); + conn->length = HEADERLEN; + conn->sport = htons(s_port); + conn->dport = htons(d_port); + conn->scnt = 0; + conn->ocnt = 0; + conn->offset = 0x50; + conn->window = htons(0x0200); + conn->nullnull = 0x0000; + conn->length16 = HEADERLEN; + return conn; +} + + +/** Removes a connection from the list of connections made. + * The list of connections is necessary for buffering. + * + * @param connection The connection to delete from the tracking list. + */ +static void delete_connection(usbmux_client_t connection) +{ + usbmux_client_t *newlist = NULL; + + pthread_mutex_lock(&usbmuxmutex); + + // update the global list of connections + if (clients > 1) { + newlist = + (usbmux_client_t *) malloc(sizeof(usbmux_client_t) * + (clients - 1)); + int i = 0, j = 0; + for (i = 0; i < clients; i++) { + if (connlist[i] == connection) + continue; + else { + newlist[j] = connlist[i]; + j++; + } + } + } + if (connlist) { + free(connlist); + } + connlist = newlist; + clients--; + + // free up this connection + pthread_mutex_lock(&connection->mutex); + if (connection->recv_buffer) { + free(connection->recv_buffer); + connection->recv_buffer = NULL; + } + if (connection->header) { + free(connection->header); + connection->header = NULL; + } + connection->r_len = 0; + pthread_mutex_unlock(&connection->mutex); + pthread_mutex_destroy(&connection->mutex); + free(connection); + + pthread_mutex_unlock(&usbmuxmutex); +} + +/** Adds a connection to the list of connections made. + * The connection list is necessary for buffering. + * + * @param connection The connection to add to the global list of connections. + */ + +static void add_connection(usbmux_client_t connection) +{ + pthread_mutex_lock(&usbmuxmutex); + usbmux_client_t *newlist = + (usbmux_client_t *) realloc(connlist, + sizeof(usbmux_client_t) * (clients + + 1)); + newlist[clients] = connection; + connlist = newlist; + clients++; + pthread_mutex_unlock(&usbmuxmutex); +} + +/** + * Get a source port number that is not used by one of our connections + * This is needed for us to make sure we are not sending on another + * connection. + */ +static uint16_t get_free_port() +{ + int i; + uint16_t newport = 30000; + int cnt = 0; + + pthread_mutex_lock(&usbmuxmutex); + while (1) { + cnt = 0; + for (i = 0; i < clients; i++) { + if (ntohs(connlist[i]->header->sport) == newport) { + cnt++; + } + } + if (cnt == 0) { + // newport is not used in our list of connections! + break; + } else { + newport++; + if (newport < 30000) { + // if all ports from 30000 to 65535 are in use, + // the value wraps (16-bit overflow) + // return 0, no port is available. + // This should not happen, but just in case ;) + newport = 0; + break; + } + } + } + pthread_mutex_unlock(&usbmuxmutex); + + return newport; +} + +/** Initializes a connection to 'device' with source port s_port and destination port d_port + * + * @param device The device to initialize a connection on. + * @param src_port The source port + * @param dst_port The destination port -- 0xf27e for lockdownd. + * @param client A mux TCP header for the connection which is used for tracking and data transfer. + * @return 0 on success, a negative errno value otherwise. + */ +int usbmux_new_client(usbmux_device_t device, uint16_t src_port, + uint16_t dst_port, usbmux_client_t * client) +{ + if (!device || !dst_port) + return -EINVAL; + + src_port = get_free_port(); + + if (!src_port) { + // this is a special case, if we get 0, this is not good, so + return -EISCONN; // TODO: error code suitable? + } + // Initialize connection stuff + usbmux_client_t new_connection = + (usbmux_client_t) malloc(sizeof(struct usbmux_client_int)); + new_connection->header = new_mux_packet(src_port, dst_port); + + // send TCP syn + if (new_connection && new_connection->header) { + int err = 0; + new_connection->header->tcp_flags = TCP_SYN; + new_connection->header->length = new_connection->header->length; + new_connection->header->length16 = + new_connection->header->length16; + new_connection->header->scnt = 0; + new_connection->header->ocnt = 0; + new_connection->device = device; + new_connection->recv_buffer = NULL; + new_connection->r_len = 0; + pthread_cond_init(&new_connection->wait, NULL); + pthread_mutex_init(&new_connection->mutex, NULL); + pthread_cond_init(&new_connection->wr_wait, NULL); + new_connection->wr_pending_scnt = 0; + new_connection->wr_window = 0; + add_connection(new_connection); + new_connection->error = 0; + new_connection->cleanup = 0; + hton_header(new_connection->header); + log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__, + ntohs(new_connection->header->sport), + ntohs(new_connection->header->dport)); + err = + send_to_device(device, (char *) new_connection->header, + sizeof(usbmux_tcp_header)); + if (err >= 0) { + *client = new_connection; + return 0; + } else { + delete_connection(new_connection); + return err; + } + } + // if we get to this point it's probably bad + return -ENOMEM; +} + +/** Cleans up the given USBMux connection. + * @note Once a connection is closed it may not be used again. + * + * @param connection The connection to close. + * + * @return 0 on success or a negative errno value on error. + */ +int usbmux_free_client(usbmux_client_t client) +{ + if (!client || !client->device) + return -EINVAL; + + int err = 0; + int result = 0; + pthread_mutex_lock(&client->mutex); + client->header->tcp_flags = TCP_FIN; + client->header->length = 0x1C; + client->header->window = 0; + client->header->length16 = 0x1C; + hton_header(client->header); + + err = + send_to_device(client->device, (char *) client->header, + sizeof(usbmux_tcp_header)); + if (err < 0) { + log_debug_msg("%s: error sending TCP_FIN\n", __func__); + result = err; + } + + client->cleanup = 1; + + // make sure we don't have any last-minute laggards waiting on this. + // I put it after the mutex unlock because we have cases where the + // conditional wait is dependent on re-grabbing that mutex. + pthread_cond_broadcast(&client->wait); + pthread_cond_destroy(&client->wait); + pthread_cond_broadcast(&client->wr_wait); + pthread_cond_destroy(&client->wr_wait); + + pthread_mutex_unlock(&client->mutex); + + return result; +} + +/** Sends the given data over the selected connection. + * + * @param client The client we're sending data on. + * @param data A pointer to the data to send. + * @param datalen How much data we're sending. + * @param sent_bytes The number of bytes sent, minus the header (28) + * + * @return 0 on success or a negative errno value on error. + */ +int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen, + uint32_t * sent_bytes) +{ + if (!client->device || !client || !sent_bytes) + return -EINVAL; + + if (client->error < 0) { + return client->error; + } + + *sent_bytes = 0; + pthread_mutex_lock(&client->mutex); + + int sendresult = 0; + uint32_t blocksize = 0; + if (client->wr_window <= 0) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + //ts.tv_sec += 1; + ts.tv_nsec += 750 * 1000; + if (pthread_cond_timedwait(&client->wait, &client->mutex, &ts) == + ETIMEDOUT) { + // timed out. optimistically grow the window and try to make progress + client->wr_window += WINDOW_INCREMENT; + } + } + + blocksize = sizeof(usbmux_tcp_header) + datalen; + + // client->scnt and client->ocnt should already be in host notation... + // we don't need to change them juuuust yet. + char *buffer = (char *) malloc(blocksize + 2); // allow 2 bytes of safety padding + // Set the length + client->header->length = blocksize; + client->header->length16 = blocksize; + + // Put header into big-endian notation + hton_header(client->header); + // Concatenation of stuff in the buffer. + memcpy(buffer, client->header, sizeof(usbmux_tcp_header)); + memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen); + + log_debug_msg("%s: send_to_device(%d --> %d)\n", __func__, + ntohs(client->header->sport), + ntohs(client->header->dport)); + sendresult = send_to_device(client->device, buffer, blocksize); + // Now that we've sent it off, we can clean up after our sloppy selves. + if (buffer) + free(buffer); + + // revert header fields that have been swapped before trying to send + ntoh_header(client->header); + + // update counts ONLY if the send succeeded. + if ((uint32_t) sendresult == blocksize) { + // Re-calculate scnt + client->header->scnt += datalen; + client->wr_window -= blocksize; + } + + pthread_mutex_unlock(&client->mutex); + + if (sendresult == -ETIMEDOUT || sendresult == 0) { + // no problem for now... + *sent_bytes = 0; + return -ETIMEDOUT; + } else if (sendresult < 0) { + return sendresult; + } else if ((uint32_t) sendresult == blocksize) { + // actual number of data bytes sent. + *sent_bytes = sendresult - HEADERLEN; + return 0; + } else { + fprintf(stderr, + "usbsend managed to dump a packet that is not full size. %d of %d\n", + sendresult, blocksize); + return -EBADMSG; + } +} + +/** append the packet's DATA to the receive buffer for the client. + * + * this has a few other corner-case functions: + * 1. this will properly handle the handshake syn+ack. + * 2. for all receives, this will appropriately update the ocnt. + * + * @return number of bytes consumed (header + data) + */ +uint32_t append_receive_buffer(usbmux_client_t client, char *packet) +{ + if (client == NULL || packet == NULL) + return 0; + + usbmux_tcp_header *header = (usbmux_tcp_header *) packet; + char *data = &packet[HEADERLEN]; + uint32_t packetlen = ntohl(header->length); + uint32_t datalen = packetlen - HEADERLEN; + + int dobroadcast = 0; + + pthread_mutex_lock(&client->mutex); + + // we need to handle a few corner case tasks and book-keeping which + // falls on our responsibility because we are the ones reading in + // feedback. + if (client->header->scnt == 0 && client->header->ocnt == 0) { + log_debug_msg("client is still waiting for handshake.\n"); + if (header->tcp_flags == (TCP_SYN | TCP_ACK)) { + log_debug_msg("yes, got syn+ack ; replying with ack.\n"); + client->header->tcp_flags = TCP_ACK; + client->header->length = sizeof(usbmux_tcp_header); + client->header->length16 = sizeof(usbmux_tcp_header); + client->header->scnt += 1; + client->header->ocnt = header->ocnt; + hton_header(client->header); + // push it to USB + // TODO: need to check for error in the send here.... :( + log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__, + ntohs(client->header->sport), + ntohs(client->header->dport)); + if (send_to_device + (client->device, (char *) client->header, + sizeof(usbmux_tcp_header)) <= 0) { + log_debug_msg("%s: error when pushing to usb...\n", + __func__); + } + // need to revert some of the fields back to host notation. + ntoh_header(client->header); + } else { + client->error = -ECONNABORTED; + // woah... this connection failed us. + // TODO: somehow signal that this stream is a no-go. + log_debug_msg("WOAH! client failed to get proper syn+ack.\n"); + } + } + // update TCP counters and windows. + // + // save the window that we're getting from the USB device. + // apparently the window is bigger than just the 512 that's typically + // advertised. iTunes apparently shifts this value by 8 to get a much + // larger number. + if (header->tcp_flags & TCP_RST) { + client->error = -ECONNRESET; + + if (datalen > 0) { + char e_msg[128]; + e_msg[0] = 0; + if (datalen > 1) { + memcpy(e_msg, data + 1, datalen - 1); + e_msg[datalen - 1] = 0; + } + // fetch the message + switch (data[0]) { + case 0: + // this is not an error, it's just a status message. + log_debug_msg("received status message: %s\n", e_msg); + datalen = 0; + break; + case 1: + log_debug_msg("received error message: %s\n", e_msg); + datalen = 0; + break; + default: + log_debug_msg + ("received unknown message (type 0x%02x): %s\n", + data[0], e_msg); + //datalen = 0; // <-- we let this commented out for testing + break; + } + } else { + log_debug_msg + ("peer sent connection reset. setting error: %d\n", + client->error); + } + } + // the packet's ocnt tells us how much of our data the device has received. + if (header->tcp_flags & TCP_ACK) { + // this is a hacky magic number condition. it seems that once + // the window reported by the device starts to drop below this + // number, we quickly fall into connection reset problems. + // Once we see the reported window size start falling off, + // ut off and wait for solid acks to come back. + if (ntohs(header->window) < 256) + client->wr_window = 0; + + // check what just got acked. + if (ntohl(header->ocnt) < client->header->scnt) { + // we got some kind of ack, but it hasn't caught up + // with the pending that have been sent. + pthread_cond_broadcast(&client->wr_wait); + } else if (ntohl(header->ocnt) > + /*client->wr_pending_scnt */ client->header->scnt) { + fprintf(stderr, + "WTF?! acks overtook pending outstanding. %u,%u\n", + ntohl(header->ocnt), client->wr_pending_scnt); + } else { + // reset the window + client->wr_window = WINDOW_MAX; + pthread_cond_broadcast(&client->wr_wait); + } + } + // the packet's scnt will be our new ocnt. + client->header->ocnt = ntohl(header->scnt); + + // ensure there is enough space, either by first malloc or realloc + if (datalen > 0) { + log_debug_msg("%s: putting %d bytes into client's recv_buffer\n", + __func__, datalen); + if (client->r_len == 0) + dobroadcast = 1; + + if (client->recv_buffer == NULL) { + client->recv_buffer = malloc(datalen); + client->r_len = 0; + } else { + client->recv_buffer = + realloc(client->recv_buffer, client->r_len + datalen); + } + + memcpy(&client->recv_buffer[client->r_len], data, datalen); + client->r_len += datalen; + } + + pthread_mutex_unlock(&client->mutex); + + // I put this outside the mutex unlock just so that when the threads + // wake, we don't have to do another round of unlock+try to grab. + if (dobroadcast) + pthread_cond_broadcast(&client->wait); + + return packetlen; +} + +/** + * @note THERE IS NO MUTEX LOCK IN THIS FUNCTION! + * because we're only called from one location, pullbulk, where the lock + * is already held. + */ +usbmux_client_t find_client(usbmux_tcp_header * recv_header) +{ + // remember, as we're looking for the client, the receive header is + // coming from the USB into our client. This means that when we check + // the src/dst ports, we need to reverse them. + usbmux_client_t retval = NULL; + + // just for debugging check, I'm going to convert the numbers to host-endian. + uint16_t hsport = ntohs(recv_header->sport); + uint16_t hdport = ntohs(recv_header->dport); + + pthread_mutex_lock(&usbmuxmutex); + int i; + for (i = 0; i < clients; i++) { + uint16_t csport = ntohs(connlist[i]->header->sport); + uint16_t cdport = ntohs(connlist[i]->header->dport); + + if (hsport == cdport && hdport == csport) { + retval = connlist[i]; + break; + } + } + pthread_mutex_unlock(&usbmuxmutex); + + return retval; +} + +/** pull in a big USB bulk packet and distribute it to queues appropriately. + */ +int usbmux_pullbulk(usbmux_device_t device) +{ + if (!device) + return -EINVAL; + + int res = 0; + static const int DEFAULT_CAPACITY = 128 * 1024; + if (device->usbReceive.buffer == NULL) { + device->usbReceive.capacity = DEFAULT_CAPACITY; + device->usbReceive.buffer = malloc(device->usbReceive.capacity); + device->usbReceive.leftover = 0; + } + // start the cursor off just ahead of the leftover. + char *cursor = &device->usbReceive.buffer[device->usbReceive.leftover]; + // pull in content, note that the amount we can pull is capacity minus leftover + int readlen = + recv_from_device_timeout(device, cursor, + device->usbReceive.capacity - + device->usbReceive.leftover, 3000); + if (readlen < 0) { + res = readlen; + //fprintf(stderr, "recv_from_device_timeout gave us an error.\n"); + readlen = 0; + } + if (readlen > 0) { + //fprintf(stdout, "recv_from_device_timeout pulled an extra %d bytes\n", readlen); + } + // the amount of content we have to work with is the remainder plus + // what we managed to read + device->usbReceive.leftover += readlen; + + // reset the cursor to the front of that buffer and work through + // trying to decode packets out of them. + cursor = device->usbReceive.buffer; + while (1) { + // check if there's even sufficient data to decode a header + if (device->usbReceive.leftover < HEADERLEN) + break; + usbmux_tcp_header *header = (usbmux_tcp_header *) cursor; + + log_debug_msg("%s: recv_from_device_timeout (%d --> %d)\n", + __func__, ntohs(header->sport), + ntohs(header->dport)); + + // now that we have a header, check if there is sufficient data + // to construct a full packet, including its data + uint32_t packetlen = ntohl(header->length); + if ((uint32_t) device->usbReceive.leftover < packetlen) { + fprintf(stderr, + "%s: not enough data to construct a full packet\n", + __func__); + break; + } + // ok... find the client this packet will get stuffed to. + usbmux_client_t client = find_client(header); + if (client == NULL) { + log_debug_msg + ("WARNING: client for packet cannot be found. dropping packet.\n"); + } else { + // stuff the data + log_debug_msg + ("%s: found client, calling append_receive_buffer\n", + __func__); + append_receive_buffer(client, cursor); + + // perhaps this is too general, == -ECONNRESET + // might be a better check here + if (client->error < 0) { + pthread_mutex_lock(&client->mutex); + if (client->cleanup) { + pthread_mutex_unlock(&client->mutex); + log_debug_msg("freeing up connection (%d->%d)\n", + ntohs(client->header->sport), + ntohs(client->header->dport)); + delete_connection(client); + } else { + pthread_mutex_unlock(&client->mutex); + } + } + } + + // move the cursor and account for the consumption + cursor += packetlen; + device->usbReceive.leftover -= packetlen; + } + + // now, we need to manage any leftovers. + // I'm going to manage the leftovers by alloc'ing a new block and + // copyingthe leftovers to it. This is just to prevent problems with + // memory moves where there may be overlap. Besides, the leftovers + // should be small enough that this copy is minimal in overhead. + // + // if there are no leftovers, we just leave the datastructure as is, + // and re-use the block next time. + if (device->usbReceive.leftover > 0 + && cursor != device->usbReceive.buffer) { + log_debug_msg("%s: we got a leftover, so handle it\n", __func__); + char *newbuff = malloc(DEFAULT_CAPACITY); + memcpy(newbuff, cursor, device->usbReceive.leftover); + free(device->usbReceive.buffer); + device->usbReceive.buffer = newbuff; + device->usbReceive.capacity = DEFAULT_CAPACITY; + } + + return res; +} + +/** + * return the error code stored in usbmux_client_t structure, + * e.g. non-zero when an usb read error occurs. + * + * @param client the usbmux client + * + * @return 0 or a negative errno value. + */ +int usbmux_get_error(usbmux_client_t client) +{ + if (!client) { + return 0; + } + return client->error; +} + +/** This function reads from the client's recv_buffer. + * + * @param client The client to receive data from. + * @param data Where to put the data we receive. + * @param datalen How much data to read. + * @param timeout How many milliseconds to wait for data + * + * @return 0 on success or a negative errno value on failure. + */ +int usbmux_recv_timeout(usbmux_client_t client, char *data, + uint32_t datalen, uint32_t * recv_bytes, + int timeout) +{ + + if (!client || !data || datalen == 0 || !recv_bytes) + return -EINVAL; + + if (client->error < 0) + return client->error; + + pthread_mutex_lock(&client->mutex); + + if (timeout > 0 && (client->recv_buffer == NULL || client->r_len == 0)) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += timeout / 1000; + ts.tv_nsec += (timeout - ((int) (timeout / 1000)) * 1000) * 1000; + pthread_cond_timedwait(&client->wait, &client->mutex, &ts); + } + + *recv_bytes = 0; + if (client->recv_buffer != NULL && client->r_len > 0) { + uint32_t foolen = datalen; + if ((int) foolen > client->r_len) + foolen = client->r_len; + memcpy(data, client->recv_buffer, foolen); + *recv_bytes = foolen; + + // preserve any left-over unread amounts. + int remainder = client->r_len - foolen; + if (remainder > 0) { + char *newbuf = malloc(remainder); + memcpy(newbuf, client->recv_buffer + foolen, remainder); + client->r_len = remainder; + free(client->recv_buffer); + client->recv_buffer = newbuf; + } else { + free(client->recv_buffer); + client->recv_buffer = NULL; + client->r_len = 0; + } + } + + pthread_mutex_unlock(&client->mutex); + + return 0; +} diff --git a/src/usbmux.h b/src/usbmux.h new file mode 100644 index 0000000..2bcdb15 --- /dev/null +++ b/src/usbmux.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008 Jing Su. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __USBMUX_H__ +#define __USBMUX_H__ + +#include +#include +//#include + + +void usbmux_set_debug(int e); + +struct usbmux_device_int; +typedef struct usbmux_device_int *usbmux_device_t; + +struct usbmux_client_int; +typedef struct usbmux_client_int *usbmux_client_t; + +int usbmux_get_device ( usbmux_device_t *device ); +int usbmux_get_specific_device(int bus_n, int dev_n, usbmux_device_t * device); +int usbmux_free_device ( usbmux_device_t device ); + + +int usbmux_new_client ( usbmux_device_t device, uint16_t src_port, uint16_t dst_port, usbmux_client_t *client ); +int usbmux_free_client ( usbmux_client_t client ); + +int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes); + +int usbmux_recv_timeout(usbmux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout); + +int usbmux_pullbulk(usbmux_device_t device); + +int usbmux_get_error(usbmux_client_t client); + +#endif diff --git a/src/usbmuxd-proto.h b/src/usbmuxd-proto.h new file mode 100644 index 0000000..7f8c2d6 --- /dev/null +++ b/src/usbmuxd-proto.h @@ -0,0 +1,52 @@ +/* Protocol defintion for usbmuxd proxy protocol */ + +#ifndef __USBMUXD_PROTO_H +#define __USBMUXD_PROTO_H + +#include + +#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 usbmuxd_result { + struct usbmuxd_header header; + uint32_t result; +} __attribute__((__packed__)); + +struct usbmuxd_connect_request { + struct usbmuxd_header header; + uint32_t device_id; + uint16_t tcp_dport; // TCP port number + uint16_t reserved; // set to zero +} __attribute__((__packed__)); + +struct usbmuxd_device { + uint32_t device_id; + uint16_t product_id; + char serial_number[40]; +} __attribute__((__packed__)); + +struct usbmuxd_device_info_record { + struct usbmuxd_header header; + struct usbmuxd_device device; + char padding[222]; +} __attribute__((__packed__)); + +struct usbmuxd_scan_request { + struct usbmuxd_header header; +} __attribute__((__packed__)); + +enum { + USBMUXD_RESULT = 1, + USBMUXD_CONNECT = 2, + USBMUXD_SCAN = 3, + USBMUXD_DEVICE_INFO = 4, +}; + +#endif /* __USBMUXD_PROTO_H */ diff --git a/src/usbmuxd.h b/src/usbmuxd.h new file mode 100644 index 0000000..15e97ee --- /dev/null +++ b/src/usbmuxd.h @@ -0,0 +1,45 @@ +#ifndef __USBMUXD_H +#define __USBMUXD_H + +/** + * Array entry returned by 'usbmuxd_scan()' scanning. + * + * If more than one device is available, 'product_id' and + * 'serial_number' and be analysed to help make a selection. + * The relevant 'handle' should be passed to 'usbmuxd_connect()', to + * start a proxy connection. The value 'handle' should be considered + * opaque and no presumption made about the meaning of its value. + */ +typedef struct { + int handle; + int product_id; + char serial_number[41]; +} usbmuxd_scan_result; + +/** + * Contacts usbmuxd and performs a scan for connected devices. + * + * @param available_devices pointer to array of usbmuxd_scan_result. + * Array of available devices. The required 'handle' + * should be passed to 'usbmuxd_connect()'. The returned array + * is zero-terminated for convenience; the final (unused) + * entry containing handle == 0. The returned array pointer + * should be freed by passing to 'free()' after use. + * + * @return number of available devices, zero on no devices, or negative on error + */ +int usbmuxd_scan(usbmuxd_scan_result **available_devices); + +/** + * Request proxy connect to + * + * @param handle returned by 'usbmuxd_scan()' + * + * @param tcp_port TCP port number on device, in range 0-65535. + * common values are 62078 for lockdown, and 22 for SSH. + * + * @return file descriptor socket of the connection, or -1 on error + */ +int usbmuxd_connect(const int handle, const unsigned short tcp_port); + +#endif /* __USBMUXD_H */ diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..5d20bbf --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,5 @@ +bin_PROGRAMS = iproxy + +iproxy_SOURCES = iproxy.c +iproxy_CFLAGS = -I$(top_srcdir)/src +iproxy_LDADD = ../src/libusbmuxd.la diff --git a/tools/iproxy.c b/tools/iproxy.c new file mode 100644 index 0000000..3cb2894 --- /dev/null +++ b/tools/iproxy.c @@ -0,0 +1,241 @@ +/* + * iproxy -- proxy that enables tcp service access to iPhone/iPod + * via USB cable + * TODO: improve code... + * + * 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 "sock_stuff.h" +#include "usbmuxd.h" + +static uint16_t listen_port = 0; +static uint16_t device_port = 0; + +pthread_mutex_t smutex = PTHREAD_MUTEX_INITIALIZER; + +struct client_data { + int fd; + int sfd; + volatile int stop_ctos; + volatile int stop_stoc; +}; + +void *run_stoc_loop(void *arg) +{ + struct client_data *cdata = (struct client_data*)arg; + int recv_len; + int sent; + char buffer[131072]; + + printf("%s: fd = %d\n", __func__, cdata->fd); + + while (!cdata->stop_stoc && cdata->fd>0 && cdata->sfd>0) { + recv_len = recv_buf_timeout(cdata->sfd, buffer, sizeof(buffer), 0, 5000); + if (recv_len <= 0) { + if (recv_len == 0) { + // try again + continue; + } else { + fprintf(stderr, "recv failed: %s\n", strerror(errno)); + break; + } + } else { +// printf("received %d bytes from server\n", recv_len); + // send to socket + sent = send_buf(cdata->fd, buffer, recv_len); + if (sent < recv_len) { + if (sent <= 0) { + fprintf(stderr, "send failed: %s\n", strerror(errno)); + break; + } else { + fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); + } + } else { + // sending succeeded, receive from device +// printf("pushed %d bytes to client\n", sent); + } + } + } + close(cdata->fd); + cdata->fd = -1; + cdata->stop_ctos = 1; + + return NULL; +} + +void *run_ctos_loop(void *arg) +{ + struct client_data *cdata = (struct client_data*)arg; + int recv_len; + int sent; + char buffer[131072]; + pthread_t stoc = 0; + + printf("%s: fd = %d\n", __func__, cdata->fd); + + cdata->stop_stoc = 0; + pthread_create(&stoc, NULL, run_stoc_loop, cdata); + + while (!cdata->stop_ctos && cdata->fd>0 && cdata->sfd>0) { + recv_len = recv_buf_timeout(cdata->fd, buffer, sizeof(buffer), 0, 5000); + if (recv_len <= 0) { + if (recv_len == 0) { + // try again + continue; + } else { + fprintf(stderr, "recv failed: %s\n", strerror(errno)); + break; + } + } else { +// printf("pulled %d bytes from client\n", recv_len); + // send to local socket + sent = send_buf(cdata->sfd, buffer, recv_len); + if (sent < recv_len) { + if (sent <= 0) { + fprintf(stderr, "send failed: %s\n", strerror(errno)); + break; + } else { + fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); + } + } else { + // sending succeeded, receive from device +// printf("sent %d bytes to server\n", sent); + } + } + } + close(cdata->fd); + cdata->fd = -1; + cdata->stop_stoc = 1; + + pthread_join(stoc, NULL); + + return NULL; +} + +void *acceptor_thread(void *arg) +{ + struct client_data *cdata; + usbmuxd_scan_result *dev_list = NULL; + pthread_t ctos; + int count; + + if (!arg) { + fprintf(stderr, "invalid client_data provided!\n"); + return NULL; + } + + cdata = (struct client_data*)arg; + + if ((count = usbmuxd_scan(&dev_list)) < 0) { + printf("Connecting to usbmuxd failed, terminating.\n"); + free(dev_list); + return NULL; + } + + fprintf(stdout, "Number of available devices == %d\n", count); + + if (dev_list == NULL || dev_list[0].handle == 0) { + printf("No connected device found, terminating.\n"); + free(dev_list); + return NULL; + } + + fprintf(stdout, "Requesting connecion to device handle == %d (serial: %s), port %d\n", dev_list[0].handle, dev_list[0].serial_number, device_port); + + cdata->sfd = usbmuxd_connect(dev_list[0].handle, device_port); + free(dev_list); + if (cdata->sfd < 0) { + fprintf(stderr, "Error connecting to device!\n"); + } else { + cdata->stop_ctos = 0; + pthread_create(&ctos, NULL, run_ctos_loop, cdata); + pthread_join(ctos, NULL); + } + + if (cdata->fd > 0) { + close(cdata->fd); + } + if (cdata->sfd > 0) { + close(cdata->sfd); + } + + return NULL; +} + +int main(int argc, char **argv) +{ + int mysock = -1; + + if (argc != 3) { + printf("usage: %s LOCAL_TCP_PORT DEVICE_TCP_PORT\n", argv[0]); + return 0; + } + + listen_port = atoi(argv[1]); + device_port = atoi(argv[2]); + + if (!listen_port) { + fprintf(stderr, "Invalid listen_port specified!\n"); + return -EINVAL; + } + + if (!device_port) { + fprintf(stderr, "Invalid device_port specified!\n"); + return -EINVAL; + } + + // first create the listening socket endpoint waiting for connections. + mysock = create_socket(listen_port); + if (mysock < 0) { + fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); + return -errno; + } else { + pthread_t acceptor; + struct sockaddr_in c_addr; + socklen_t len = sizeof(struct sockaddr_in); + struct client_data cdata; + int c_sock; + while (1) { + printf("waiting for connection\n"); + c_sock = accept(mysock, (struct sockaddr*)&c_addr, &len); + if (c_sock) { + printf("accepted connection, fd = %d\n", c_sock); + cdata.fd = c_sock; + pthread_create(&acceptor, NULL, acceptor_thread, &cdata); + pthread_join(acceptor, NULL); + } else { + break; + } + } + close(c_sock); + close(mysock); + } + + return 0; +} diff --git a/udev/85-usbmuxd.rules b/udev/85-usbmuxd.rules new file mode 100644 index 0000000..69ddef8 --- /dev/null +++ b/udev/85-usbmuxd.rules @@ -0,0 +1,37 @@ +# usbmuxd (iPhone "Apple Mobile Device" MUXer listening on /var/run/usbmuxd) +#if +#SUBSYSTEMS=="usb_interface", SYMLINK+="usbmux/interface" + +#SUBSYSTEMS!="usb", GOTO="usbmuxd_rules_end" # stops the whole script working +ATTR{idVendor}!="05ac", GOTO="usbmuxd_rules_end" + +# If it's plug insertion, flip it into dual "PTP + Apple Mobile Device" configuration +# This allows another application to switch it later without it getting switched back (hopefully) +# TODO: check iPod Touch/3G +SUBSYSTEM=="usb", ACTION=="add", ATTR{product}=="iPhone", ATTR{bConfigurationValue}!="3", ATTR{bConfigurationValue}="3", GOTO="usbmuxd_rules_end" + +# SYMLINK the usbmux endpoints, if we get them +# TODO: Multiple devices +# TODO: work out how to make nice, incrementing usbmux/{0,1,2,3}-in for +LABEL="usbmuxd_rules_usbmux" + +# ff/fe/02 == usbmux +# +#ACTION=="add", + +# Try to symlink the interface, containing the endpoints. +# ...But it doesn't work +#KERNELS=="7-3:3.1", SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="01", ATTRS{bAlternateSetting}==" 0", ATTRS{bNumEndpoints}=="02", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="fe", ATTRS{bInterfaceProtocol}=="02", SYMLINK+="usbmux/prettyplease", RUN+="/bin/ls -l /dev/usbmux/prettyplease" + +#ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="fe", ATTRS{bInterfaceProtocol}=="02", SYMLINK+="usbmux/interface" + +# Cute names, really they should have nice numerically increasing names. +ACTION=="add", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep04", SYMLINK+="usbmux/in" +ACTION=="add", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep85", SYMLINK+="usbmux/out" + +# Start and stop 'usbmuxd' as required. +ACTION=="add", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep85", RUN+="/sbin/start-stop-daemon --start --oknodo --exec /usr/local/sbin/usbmuxd" +ACTION=="remove", SUBSYSTEM=="usb_endpoint", KERNEL=="usbdev*_ep85", RUN+="/sbin/start-stop-daemon --stop --signal 2 --exec /usr/local/sbin/usbmuxd" + +# skip +LABEL="usbmuxd_rules_end" diff --git a/udev/Makefile.am b/udev/Makefile.am new file mode 100644 index 0000000..321008a --- /dev/null +++ b/udev/Makefile.am @@ -0,0 +1,4 @@ +udevdir=$(sysconfdir)/udev/rules.d/ +udev_DATA=85-usbmuxd.rules + +EXTRA_DIST = 85-usbmuxd.rules diff --git a/usbmux.c b/usbmux.c deleted file mode 100644 index e86e3bc..0000000 --- a/usbmux.c +++ /dev/null @@ -1,1259 +0,0 @@ -/* - * Copyright (c) 2008 Jing Su. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; 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 "usbmux.h" - -#define BULKIN 0x85 -#define BULKOUT 0x04 -#define HEADERLEN 28 - -static const uint8_t TCP_FIN = 1; -static const uint8_t TCP_SYN = 1 << 1; -static const uint8_t TCP_RST = 1 << 2; -static const uint8_t TCP_PSH = 1 << 3; -static const uint8_t TCP_ACK = 1 << 4; -static const uint8_t TCP_URG = 1 << 5; - -// I have trouble figuring out how to properly manage the windowing to -// the device. It keeps sending back 512 and seems to drop off a cliff -// when the device gets overwhelmed. In addition, the device likes to -// panic and send out RESETS before the window hits zero. Also, waiting -// for responses seems to not be a winning strategy. -// -// Since I'm not sure how in the hell to interpret the window sizes that -// the device is sending back to us, I've figured out some magic number -// constants which seem to work okay. -static const uint32_t WINDOW_MAX = 5 * 1024; -static const uint32_t WINDOW_INCREMENT = 512; - -typedef struct { - char *buffer; - int leftover; - int capacity; -} receivebuf_t; - -struct usbmux_device_int { - struct usb_dev_handle *usbdev; - struct usb_device *__device; - receivebuf_t usbReceive; -}; - -typedef struct { - uint32_t type, length, major, minor, allnull; -} usbmux_version_header; - -typedef struct { - uint32_t type, length; - uint16_t sport, dport; - uint32_t scnt, ocnt; - uint8_t offset, tcp_flags; - uint16_t window, nullnull, length16; -} usbmux_tcp_header; - -struct usbmux_client_int { - usbmux_tcp_header *header; - usbmux_device_t device; - - char *recv_buffer; - int r_len; - pthread_cond_t wait; - - // this contains a conditional variable which usb-writers can wait - // on while waiting for window updates from the device. - pthread_cond_t wr_wait; - // I'm going to do something really cheesy here. We are going to - // just record the most recent scnt that we are expecting to hear - // back on. We will actually halt progress by limiting the number - // of outstanding un-acked bulk sends that we have beamed out. - uint32_t wr_pending_scnt; - long wr_window; - - pthread_mutex_t mutex; - - // this variable is not protected by the mutex. This will always - // be E_SUCCESS, unless an error of some kind breaks this stream. - // this will then be set to the error that caused the broken stream. - // no further operations other than free_client will be allowed. - int error; - - int cleanup; -}; - - -static pthread_mutex_t usbmuxmutex = PTHREAD_MUTEX_INITIALIZER; -static usbmux_client_t *connlist = NULL; -static int clients = 0; - - -/** - */ -int toto_debug = 0; - -void usbmux_set_debug(int e) -{ - toto_debug = e; -} - -void log_debug_msg(const char *format, ...) -{ -#ifndef STRIP_DEBUG_CODE - va_list args; - /* run the real fprintf */ - va_start(args, format); - - if (toto_debug) - vfprintf(stderr, format, args); - - va_end(args); -#endif -} - -#ifdef DEBUG -/** - * for debugging purposes. - */ -static void print_buffer(const char *data, const int length) -{ - if (toto_debug <= 0) { - return; - } - int i; - int j; - unsigned char c; - - for (i = 0; i < length; i += 16) { - printf("%04x: ", i); - for (j = 0; j < 16; j++) { - if (i + j >= 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 - -void hton_header(usbmux_tcp_header * hdr) -{ - if (hdr) { - hdr->length = htonl(hdr->length); - hdr->scnt = htonl(hdr->scnt); - hdr->ocnt = htonl(hdr->ocnt); - hdr->length16 = htons(hdr->length16); - } -} - -void ntoh_header(usbmux_tcp_header * hdr) -{ - if (hdr) { - hdr->length = ntohl(hdr->length); - hdr->scnt = ntohl(hdr->scnt); - hdr->ocnt = ntohl(hdr->ocnt); - hdr->length16 = ntohs(hdr->length16); - } -} - -/** Creates a USBMux header containing version information - * - * @return A USBMux header - */ -usbmux_version_header *version_header() -{ - usbmux_version_header *version = - (usbmux_version_header *) malloc(sizeof(usbmux_version_header)); - version->type = 0; - version->length = htonl(20); - version->major = htonl(1); - version->minor = 0; - version->allnull = 0; - return version; -} - -/** - * This function sets the configuration of the given device to 3 - * and claims the interface 1. If usb_set_configuration fails, it detaches - * the kernel driver that blocks the device, and retries configuration. - * - * @param device which device to configure - */ -static int usbmux_config_usb_device(usbmux_device_t device) -{ - int ret; - int bytes; - char buf[512]; - -#if 0 - log_debug_msg("checking configuration...\n"); - if (device->__device->config->bConfigurationValue != 3) { - log_debug_msg - ("WARNING: usb device configuration is not 3 as expected!\n"); - } - - log_debug_msg("setting configuration...\n"); - ret = usb_set_configuration(device->device, 3); - if (ret != 0) { - log_debug_msg("Hm, usb_set_configuration returned %d: %s\n", ret, - strerror(-ret)); -#if LIBUSB_HAS_GET_DRIVER_NP - log_debug_msg("trying to fix:\n"); - log_debug_msg("-> detaching kernel driver... "); - ret = - usb_detach_kernel_driver_np(device->device, - device->__device->config-> - interface->altsetting-> - bInterfaceNumber); - if (ret != 0) { - log_debug_msg("usb_detach_kernel_driver_np returned %d: %s\n", - ret, strerror(-ret)); - } else { - log_debug_msg("done.\n"); - log_debug_msg("setting configuration again... "); - ret = usb_set_configuration(device->device, 3); - if (ret != 0) { - log_debug_msg - ("Error: usb_set_configuration returned %d: %s\n", ret, - strerror(-ret)); - log_debug_msg("--> trying to continue anyway...\n"); - } else { - log_debug_msg("done.\n"); - } - } -#else - log_debug_msg("--> trying to continue anyway...\n"); -#endif - } else { - log_debug_msg("done.\n"); - } -#endif - - log_debug_msg("claiming interface... "); - ret = usb_claim_interface(device->usbdev, 1); - if (ret != 0) { - log_debug_msg("Error: usb_claim_interface returned %d: %s\n", ret, - strerror(-ret)); - return -ENODEV; - } else { - log_debug_msg("done.\n"); - } - - do { - bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800); - } while (bytes > 0); - - return 0; -} - -/** - * Given a USB bus and device number, returns a device handle to the device on - * that bus. To aid compatibility with future devices, this function does not - * check the vendor and device IDs! To do that, you should use - * usbmux_get_device() or a system-specific API (e.g. HAL). - * - * @param bus_n The USB bus number. - * @param dev_n The USB device number. - * @param device A pointer to a usbmux_device_t, which must be set to NULL upon - * calling usbmux_get_specific_device, which will be filled with a device - * descriptor on return. - * @return 0 if ok, otherwise a negative errno value. - */ -int usbmux_get_specific_device(int bus_n, int dev_n, - usbmux_device_t * device) -{ - struct usb_bus *bus; - struct usb_device *dev; - usbmux_version_header *version; - int bytes = 0; - - //check we can actually write in device - if (!device || (device && *device)) - return -EINVAL; - - usbmux_device_t newdevice = - (usbmux_device_t) malloc(sizeof(struct usbmux_device_int)); - - // Initialize the struct - newdevice->usbdev = NULL; - newdevice->__device = NULL; - - // don't forget these: - newdevice->usbReceive.buffer = NULL; - newdevice->usbReceive.leftover = 0; - newdevice->usbReceive.capacity = 0; - - // Initialize libusb - usb_init(); - usb_find_busses(); - usb_find_devices(); - - // Set the device configuration - for (bus = usb_get_busses(); bus; bus = bus->next) - //if (bus->location == bus_n) - for (dev = bus->devices; dev != NULL; dev = dev->next) - if (dev->devnum == dev_n) { - newdevice->__device = dev; - newdevice->usbdev = usb_open(newdevice->__device); - if (usbmux_config_usb_device(newdevice) == 0) { - goto found; - } - } - - usbmux_free_device(newdevice); - - log_debug_msg("usbmux_get_specific_device: device not found\n"); - return -ENODEV; - - found: - // Send the version command to the device - version = version_header(); - bytes = - usb_bulk_write(newdevice->usbdev, BULKOUT, (char *) version, - sizeof(*version), 800); - if (bytes < 20) { - log_debug_msg("%s: libusb did NOT send enough!\n", __func__); - if (bytes < 0) { - log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n", - __func__, bytes, usb_strerror(), - strerror(-bytes)); - } - } - // Read the device's response - bytes = - usb_bulk_read(newdevice->usbdev, BULKIN, (char *) version, - sizeof(*version), 800); - - // Check for bad response - if (bytes < 20) { - free(version); - usbmux_free_device(newdevice); - log_debug_msg("%s: Invalid version message -- header too short.\n", - __func__); - if (bytes < 0) { - log_debug_msg("%s: libusb error message %d: %s (%s)\n", - __func__, bytes, usb_strerror(), - strerror(-bytes)); - return bytes; - } - return -EBADMSG; - } - // Check for correct version - if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) { - // We're all ready to roll. - log_debug_msg("%s: success\n", __func__); - free(version); - *device = newdevice; - return 0; - } else { - // Bad header - usbmux_free_device(newdevice); - free(version); - log_debug_msg("%s: Received a bad header/invalid version number.", - __func__); - return -EBADMSG; - } - - // If it got to this point it's gotta be bad - log_debug_msg("%s: Unknown error.\n", __func__); - usbmux_free_device(newdevice); - free(version); - return -EBADMSG; // if it got to this point it's gotta be bad -} - -/** Cleans up an usbmux_device_t structure, then frees the structure itself. - * This is a library-level function; deals directly with the device to tear - * down relations, but otherwise is mostly internal. - * - * @param device A pointer to an usbmux_device_t structure. - */ -int usbmux_free_device(usbmux_device_t device) -{ - char buf[512]; - int bytes; - - if (!device) - return -EINVAL; - int ret = 0; - - if (device->usbdev) { - do { - bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800); - } while (bytes > 0); - } - - if (bytes < 0) { - ret = bytes; - } - - if (device->usbReceive.buffer) { - free(device->usbReceive.buffer); - } - if (device->usbdev) { - usb_release_interface(device->usbdev, 1); - usb_close(device->usbdev); - ret = 0; - } - free(device); - - return ret; -} - - - -/** Sends data to the device - * This is a low-level (i.e. directly to device) function. - * - * @param device The device to send data to - * @param data The data to send - * @param datalen The length of the data - * @return The number of bytes sent, or -ERRNO on error - */ -int send_to_device(usbmux_device_t device, char *data, int datalen) -{ - if (!device) - return -EINVAL; - - int timeout = 1000; - int retrycount = 0; - int bytes = 0; - -#ifdef DEBUG -#ifdef DEBUG_MORE - printf("===============================\n%s: trying to send\n", - __func__); - print_buffer(data, datalen); - printf("===============================\n"); -#endif -#endif - do { - if (retrycount > 3) { - log_debug_msg - ("EPIC FAIL! aborting on retry count overload.\n"); - return -ECOMM; - } - - bytes = - usb_bulk_write(device->usbdev, BULKOUT, data, datalen, - timeout); - if (bytes == -ETIMEDOUT) { - // timed out waiting for write. - log_debug_msg("usb_bulk_write timeout error.\n"); - return bytes; - } else if (bytes < 0) { - log_debug_msg - ("usb_bulk_write failed with error. err:%d (%s)(%s)\n", - bytes, usb_strerror(), strerror(-bytes)); - return bytes; - } else if (bytes == 0) { - log_debug_msg("usb_bulk_write sent nothing. retrying.\n"); - timeout = timeout * 4; - retrycount++; - continue; - } else if (bytes < datalen) { - log_debug_msg - ("usb_bulk_write failed to send full dataload. %d of %d\n", - bytes, datalen); - timeout = timeout * 4; - retrycount++; - data += bytes; - datalen -= bytes; - continue; - } - } while (0); // fall out - -#ifdef DEBUG - if (bytes > 0) { - if (toto_debug > 0) { - printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - printf("%s: sent to device\n", __func__); - print_buffer(data, bytes); - printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - } - } -#endif - return bytes; -} - -/** Receives data from the device - * This function is a low-level (i.e. direct from device) function. - * - * @param device The device to receive data from - * @param data Where to put data read - * @param datalen How much data to read in - * @param timeout How many milliseconds to wait for data - * - * @return How many bytes were read in, or -1 on error. - */ -int recv_from_device_timeout(usbmux_device_t device, char *data, - int datalen, int timeoutmillis) -{ - if (!device) - return -EINVAL; - //log_debug_msg("%s: attempting to receive %i bytes\n", __func__, datalen); - - int bytes = - usb_bulk_read(device->usbdev, BULKIN, data, datalen, - timeoutmillis); - // There are some things which are errors, others which are no problem. - // It's not documented in libUSB, but it seems that the error values - // returned are just negated ERRNO values. - if (bytes < 0) { - if (bytes == -ETIMEDOUT) { - // ignore this. it just means timeout reached before we - // picked up any data. no problem. - return 0; - } else { - fprintf(stderr, "%s: libusb gave me the error %d: %s (%s)\n", - __func__, bytes, usb_strerror(), strerror(-bytes)); - log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n", - __func__, bytes, usb_strerror(), - strerror(-bytes)); - } - return bytes; - } -#ifdef DEBUG - if (bytes > 0) { - if (toto_debug > 0) { - printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - printf("%s: received from device:\n", __func__); - print_buffer(data, bytes); - printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - } - } -#endif - - return bytes; -} - -/** Creates a USBMux packet for the given set of ports. - * - * @param s_port The source port for the connection. - * @param d_port The destination port for the connection. - * - * @return A USBMux packet - */ -usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port) -{ - usbmux_tcp_header *conn = - (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); - conn->type = htonl(6); - conn->length = HEADERLEN; - conn->sport = htons(s_port); - conn->dport = htons(d_port); - conn->scnt = 0; - conn->ocnt = 0; - conn->offset = 0x50; - conn->window = htons(0x0200); - conn->nullnull = 0x0000; - conn->length16 = HEADERLEN; - return conn; -} - - -/** Removes a connection from the list of connections made. - * The list of connections is necessary for buffering. - * - * @param connection The connection to delete from the tracking list. - */ -static void delete_connection(usbmux_client_t connection) -{ - usbmux_client_t *newlist = NULL; - - pthread_mutex_lock(&usbmuxmutex); - - // update the global list of connections - if (clients > 1) { - newlist = - (usbmux_client_t *) malloc(sizeof(usbmux_client_t) * - (clients - 1)); - int i = 0, j = 0; - for (i = 0; i < clients; i++) { - if (connlist[i] == connection) - continue; - else { - newlist[j] = connlist[i]; - j++; - } - } - } - if (connlist) { - free(connlist); - } - connlist = newlist; - clients--; - - // free up this connection - pthread_mutex_lock(&connection->mutex); - if (connection->recv_buffer) { - free(connection->recv_buffer); - connection->recv_buffer = NULL; - } - if (connection->header) { - free(connection->header); - connection->header = NULL; - } - connection->r_len = 0; - pthread_mutex_unlock(&connection->mutex); - pthread_mutex_destroy(&connection->mutex); - free(connection); - - pthread_mutex_unlock(&usbmuxmutex); -} - -/** Adds a connection to the list of connections made. - * The connection list is necessary for buffering. - * - * @param connection The connection to add to the global list of connections. - */ - -static void add_connection(usbmux_client_t connection) -{ - pthread_mutex_lock(&usbmuxmutex); - usbmux_client_t *newlist = - (usbmux_client_t *) realloc(connlist, - sizeof(usbmux_client_t) * (clients + - 1)); - newlist[clients] = connection; - connlist = newlist; - clients++; - pthread_mutex_unlock(&usbmuxmutex); -} - -/** - * Get a source port number that is not used by one of our connections - * This is needed for us to make sure we are not sending on another - * connection. - */ -static uint16_t get_free_port() -{ - int i; - uint16_t newport = 30000; - int cnt = 0; - - pthread_mutex_lock(&usbmuxmutex); - while (1) { - cnt = 0; - for (i = 0; i < clients; i++) { - if (ntohs(connlist[i]->header->sport) == newport) { - cnt++; - } - } - if (cnt == 0) { - // newport is not used in our list of connections! - break; - } else { - newport++; - if (newport < 30000) { - // if all ports from 30000 to 65535 are in use, - // the value wraps (16-bit overflow) - // return 0, no port is available. - // This should not happen, but just in case ;) - newport = 0; - break; - } - } - } - pthread_mutex_unlock(&usbmuxmutex); - - return newport; -} - -/** Initializes a connection to 'device' with source port s_port and destination port d_port - * - * @param device The device to initialize a connection on. - * @param src_port The source port - * @param dst_port The destination port -- 0xf27e for lockdownd. - * @param client A mux TCP header for the connection which is used for tracking and data transfer. - * @return 0 on success, a negative errno value otherwise. - */ -int usbmux_new_client(usbmux_device_t device, uint16_t src_port, - uint16_t dst_port, usbmux_client_t * client) -{ - if (!device || !dst_port) - return -EINVAL; - - src_port = get_free_port(); - - if (!src_port) { - // this is a special case, if we get 0, this is not good, so - return -EISCONN; // TODO: error code suitable? - } - // Initialize connection stuff - usbmux_client_t new_connection = - (usbmux_client_t) malloc(sizeof(struct usbmux_client_int)); - new_connection->header = new_mux_packet(src_port, dst_port); - - // send TCP syn - if (new_connection && new_connection->header) { - int err = 0; - new_connection->header->tcp_flags = TCP_SYN; - new_connection->header->length = new_connection->header->length; - new_connection->header->length16 = - new_connection->header->length16; - new_connection->header->scnt = 0; - new_connection->header->ocnt = 0; - new_connection->device = device; - new_connection->recv_buffer = NULL; - new_connection->r_len = 0; - pthread_cond_init(&new_connection->wait, NULL); - pthread_mutex_init(&new_connection->mutex, NULL); - pthread_cond_init(&new_connection->wr_wait, NULL); - new_connection->wr_pending_scnt = 0; - new_connection->wr_window = 0; - add_connection(new_connection); - new_connection->error = 0; - new_connection->cleanup = 0; - hton_header(new_connection->header); - log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__, - ntohs(new_connection->header->sport), - ntohs(new_connection->header->dport)); - err = - send_to_device(device, (char *) new_connection->header, - sizeof(usbmux_tcp_header)); - if (err >= 0) { - *client = new_connection; - return 0; - } else { - delete_connection(new_connection); - return err; - } - } - // if we get to this point it's probably bad - return -ENOMEM; -} - -/** Cleans up the given USBMux connection. - * @note Once a connection is closed it may not be used again. - * - * @param connection The connection to close. - * - * @return 0 on success or a negative errno value on error. - */ -int usbmux_free_client(usbmux_client_t client) -{ - if (!client || !client->device) - return -EINVAL; - - int err = 0; - int result = 0; - pthread_mutex_lock(&client->mutex); - client->header->tcp_flags = TCP_FIN; - client->header->length = 0x1C; - client->header->window = 0; - client->header->length16 = 0x1C; - hton_header(client->header); - - err = - send_to_device(client->device, (char *) client->header, - sizeof(usbmux_tcp_header)); - if (err < 0) { - log_debug_msg("%s: error sending TCP_FIN\n", __func__); - result = err; - } - - client->cleanup = 1; - - // make sure we don't have any last-minute laggards waiting on this. - // I put it after the mutex unlock because we have cases where the - // conditional wait is dependent on re-grabbing that mutex. - pthread_cond_broadcast(&client->wait); - pthread_cond_destroy(&client->wait); - pthread_cond_broadcast(&client->wr_wait); - pthread_cond_destroy(&client->wr_wait); - - pthread_mutex_unlock(&client->mutex); - - return result; -} - -/** Sends the given data over the selected connection. - * - * @param client The client we're sending data on. - * @param data A pointer to the data to send. - * @param datalen How much data we're sending. - * @param sent_bytes The number of bytes sent, minus the header (28) - * - * @return 0 on success or a negative errno value on error. - */ -int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen, - uint32_t * sent_bytes) -{ - if (!client->device || !client || !sent_bytes) - return -EINVAL; - - if (client->error < 0) { - return client->error; - } - - *sent_bytes = 0; - pthread_mutex_lock(&client->mutex); - - int sendresult = 0; - uint32_t blocksize = 0; - if (client->wr_window <= 0) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - //ts.tv_sec += 1; - ts.tv_nsec += 750 * 1000; - if (pthread_cond_timedwait(&client->wait, &client->mutex, &ts) == - ETIMEDOUT) { - // timed out. optimistically grow the window and try to make progress - client->wr_window += WINDOW_INCREMENT; - } - } - - blocksize = sizeof(usbmux_tcp_header) + datalen; - - // client->scnt and client->ocnt should already be in host notation... - // we don't need to change them juuuust yet. - char *buffer = (char *) malloc(blocksize + 2); // allow 2 bytes of safety padding - // Set the length - client->header->length = blocksize; - client->header->length16 = blocksize; - - // Put header into big-endian notation - hton_header(client->header); - // Concatenation of stuff in the buffer. - memcpy(buffer, client->header, sizeof(usbmux_tcp_header)); - memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen); - - log_debug_msg("%s: send_to_device(%d --> %d)\n", __func__, - ntohs(client->header->sport), - ntohs(client->header->dport)); - sendresult = send_to_device(client->device, buffer, blocksize); - // Now that we've sent it off, we can clean up after our sloppy selves. - if (buffer) - free(buffer); - - // revert header fields that have been swapped before trying to send - ntoh_header(client->header); - - // update counts ONLY if the send succeeded. - if ((uint32_t) sendresult == blocksize) { - // Re-calculate scnt - client->header->scnt += datalen; - client->wr_window -= blocksize; - } - - pthread_mutex_unlock(&client->mutex); - - if (sendresult == -ETIMEDOUT || sendresult == 0) { - // no problem for now... - *sent_bytes = 0; - return -ETIMEDOUT; - } else if (sendresult < 0) { - return sendresult; - } else if ((uint32_t) sendresult == blocksize) { - // actual number of data bytes sent. - *sent_bytes = sendresult - HEADERLEN; - return 0; - } else { - fprintf(stderr, - "usbsend managed to dump a packet that is not full size. %d of %d\n", - sendresult, blocksize); - return -EBADMSG; - } -} - -/** append the packet's DATA to the receive buffer for the client. - * - * this has a few other corner-case functions: - * 1. this will properly handle the handshake syn+ack. - * 2. for all receives, this will appropriately update the ocnt. - * - * @return number of bytes consumed (header + data) - */ -uint32_t append_receive_buffer(usbmux_client_t client, char *packet) -{ - if (client == NULL || packet == NULL) - return 0; - - usbmux_tcp_header *header = (usbmux_tcp_header *) packet; - char *data = &packet[HEADERLEN]; - uint32_t packetlen = ntohl(header->length); - uint32_t datalen = packetlen - HEADERLEN; - - int dobroadcast = 0; - - pthread_mutex_lock(&client->mutex); - - // we need to handle a few corner case tasks and book-keeping which - // falls on our responsibility because we are the ones reading in - // feedback. - if (client->header->scnt == 0 && client->header->ocnt == 0) { - log_debug_msg("client is still waiting for handshake.\n"); - if (header->tcp_flags == (TCP_SYN | TCP_ACK)) { - log_debug_msg("yes, got syn+ack ; replying with ack.\n"); - client->header->tcp_flags = TCP_ACK; - client->header->length = sizeof(usbmux_tcp_header); - client->header->length16 = sizeof(usbmux_tcp_header); - client->header->scnt += 1; - client->header->ocnt = header->ocnt; - hton_header(client->header); - // push it to USB - // TODO: need to check for error in the send here.... :( - log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__, - ntohs(client->header->sport), - ntohs(client->header->dport)); - if (send_to_device - (client->device, (char *) client->header, - sizeof(usbmux_tcp_header)) <= 0) { - log_debug_msg("%s: error when pushing to usb...\n", - __func__); - } - // need to revert some of the fields back to host notation. - ntoh_header(client->header); - } else { - client->error = -ECONNABORTED; - // woah... this connection failed us. - // TODO: somehow signal that this stream is a no-go. - log_debug_msg("WOAH! client failed to get proper syn+ack.\n"); - } - } - // update TCP counters and windows. - // - // save the window that we're getting from the USB device. - // apparently the window is bigger than just the 512 that's typically - // advertised. iTunes apparently shifts this value by 8 to get a much - // larger number. - if (header->tcp_flags & TCP_RST) { - client->error = -ECONNRESET; - - if (datalen > 0) { - char e_msg[128]; - e_msg[0] = 0; - if (datalen > 1) { - memcpy(e_msg, data + 1, datalen - 1); - e_msg[datalen - 1] = 0; - } - // fetch the message - switch (data[0]) { - case 0: - // this is not an error, it's just a status message. - log_debug_msg("received status message: %s\n", e_msg); - datalen = 0; - break; - case 1: - log_debug_msg("received error message: %s\n", e_msg); - datalen = 0; - break; - default: - log_debug_msg - ("received unknown message (type 0x%02x): %s\n", - data[0], e_msg); - //datalen = 0; // <-- we let this commented out for testing - break; - } - } else { - log_debug_msg - ("peer sent connection reset. setting error: %d\n", - client->error); - } - } - // the packet's ocnt tells us how much of our data the device has received. - if (header->tcp_flags & TCP_ACK) { - // this is a hacky magic number condition. it seems that once - // the window reported by the device starts to drop below this - // number, we quickly fall into connection reset problems. - // Once we see the reported window size start falling off, - // ut off and wait for solid acks to come back. - if (ntohs(header->window) < 256) - client->wr_window = 0; - - // check what just got acked. - if (ntohl(header->ocnt) < client->header->scnt) { - // we got some kind of ack, but it hasn't caught up - // with the pending that have been sent. - pthread_cond_broadcast(&client->wr_wait); - } else if (ntohl(header->ocnt) > - /*client->wr_pending_scnt */ client->header->scnt) { - fprintf(stderr, - "WTF?! acks overtook pending outstanding. %u,%u\n", - ntohl(header->ocnt), client->wr_pending_scnt); - } else { - // reset the window - client->wr_window = WINDOW_MAX; - pthread_cond_broadcast(&client->wr_wait); - } - } - // the packet's scnt will be our new ocnt. - client->header->ocnt = ntohl(header->scnt); - - // ensure there is enough space, either by first malloc or realloc - if (datalen > 0) { - log_debug_msg("%s: putting %d bytes into client's recv_buffer\n", - __func__, datalen); - if (client->r_len == 0) - dobroadcast = 1; - - if (client->recv_buffer == NULL) { - client->recv_buffer = malloc(datalen); - client->r_len = 0; - } else { - client->recv_buffer = - realloc(client->recv_buffer, client->r_len + datalen); - } - - memcpy(&client->recv_buffer[client->r_len], data, datalen); - client->r_len += datalen; - } - - pthread_mutex_unlock(&client->mutex); - - // I put this outside the mutex unlock just so that when the threads - // wake, we don't have to do another round of unlock+try to grab. - if (dobroadcast) - pthread_cond_broadcast(&client->wait); - - return packetlen; -} - -/** - * @note THERE IS NO MUTEX LOCK IN THIS FUNCTION! - * because we're only called from one location, pullbulk, where the lock - * is already held. - */ -usbmux_client_t find_client(usbmux_tcp_header * recv_header) -{ - // remember, as we're looking for the client, the receive header is - // coming from the USB into our client. This means that when we check - // the src/dst ports, we need to reverse them. - usbmux_client_t retval = NULL; - - // just for debugging check, I'm going to convert the numbers to host-endian. - uint16_t hsport = ntohs(recv_header->sport); - uint16_t hdport = ntohs(recv_header->dport); - - pthread_mutex_lock(&usbmuxmutex); - int i; - for (i = 0; i < clients; i++) { - uint16_t csport = ntohs(connlist[i]->header->sport); - uint16_t cdport = ntohs(connlist[i]->header->dport); - - if (hsport == cdport && hdport == csport) { - retval = connlist[i]; - break; - } - } - pthread_mutex_unlock(&usbmuxmutex); - - return retval; -} - -/** pull in a big USB bulk packet and distribute it to queues appropriately. - */ -int usbmux_pullbulk(usbmux_device_t device) -{ - if (!device) - return -EINVAL; - - int res = 0; - static const int DEFAULT_CAPACITY = 128 * 1024; - if (device->usbReceive.buffer == NULL) { - device->usbReceive.capacity = DEFAULT_CAPACITY; - device->usbReceive.buffer = malloc(device->usbReceive.capacity); - device->usbReceive.leftover = 0; - } - // start the cursor off just ahead of the leftover. - char *cursor = &device->usbReceive.buffer[device->usbReceive.leftover]; - // pull in content, note that the amount we can pull is capacity minus leftover - int readlen = - recv_from_device_timeout(device, cursor, - device->usbReceive.capacity - - device->usbReceive.leftover, 3000); - if (readlen < 0) { - res = readlen; - //fprintf(stderr, "recv_from_device_timeout gave us an error.\n"); - readlen = 0; - } - if (readlen > 0) { - //fprintf(stdout, "recv_from_device_timeout pulled an extra %d bytes\n", readlen); - } - // the amount of content we have to work with is the remainder plus - // what we managed to read - device->usbReceive.leftover += readlen; - - // reset the cursor to the front of that buffer and work through - // trying to decode packets out of them. - cursor = device->usbReceive.buffer; - while (1) { - // check if there's even sufficient data to decode a header - if (device->usbReceive.leftover < HEADERLEN) - break; - usbmux_tcp_header *header = (usbmux_tcp_header *) cursor; - - log_debug_msg("%s: recv_from_device_timeout (%d --> %d)\n", - __func__, ntohs(header->sport), - ntohs(header->dport)); - - // now that we have a header, check if there is sufficient data - // to construct a full packet, including its data - uint32_t packetlen = ntohl(header->length); - if ((uint32_t) device->usbReceive.leftover < packetlen) { - fprintf(stderr, - "%s: not enough data to construct a full packet\n", - __func__); - break; - } - // ok... find the client this packet will get stuffed to. - usbmux_client_t client = find_client(header); - if (client == NULL) { - log_debug_msg - ("WARNING: client for packet cannot be found. dropping packet.\n"); - } else { - // stuff the data - log_debug_msg - ("%s: found client, calling append_receive_buffer\n", - __func__); - append_receive_buffer(client, cursor); - - // perhaps this is too general, == -ECONNRESET - // might be a better check here - if (client->error < 0) { - pthread_mutex_lock(&client->mutex); - if (client->cleanup) { - pthread_mutex_unlock(&client->mutex); - log_debug_msg("freeing up connection (%d->%d)\n", - ntohs(client->header->sport), - ntohs(client->header->dport)); - delete_connection(client); - } else { - pthread_mutex_unlock(&client->mutex); - } - } - } - - // move the cursor and account for the consumption - cursor += packetlen; - device->usbReceive.leftover -= packetlen; - } - - // now, we need to manage any leftovers. - // I'm going to manage the leftovers by alloc'ing a new block and - // copyingthe leftovers to it. This is just to prevent problems with - // memory moves where there may be overlap. Besides, the leftovers - // should be small enough that this copy is minimal in overhead. - // - // if there are no leftovers, we just leave the datastructure as is, - // and re-use the block next time. - if (device->usbReceive.leftover > 0 - && cursor != device->usbReceive.buffer) { - log_debug_msg("%s: we got a leftover, so handle it\n", __func__); - char *newbuff = malloc(DEFAULT_CAPACITY); - memcpy(newbuff, cursor, device->usbReceive.leftover); - free(device->usbReceive.buffer); - device->usbReceive.buffer = newbuff; - device->usbReceive.capacity = DEFAULT_CAPACITY; - } - - return res; -} - -/** - * return the error code stored in usbmux_client_t structure, - * e.g. non-zero when an usb read error occurs. - * - * @param client the usbmux client - * - * @return 0 or a negative errno value. - */ -int usbmux_get_error(usbmux_client_t client) -{ - if (!client) { - return 0; - } - return client->error; -} - -/** This function reads from the client's recv_buffer. - * - * @param client The client to receive data from. - * @param data Where to put the data we receive. - * @param datalen How much data to read. - * @param timeout How many milliseconds to wait for data - * - * @return 0 on success or a negative errno value on failure. - */ -int usbmux_recv_timeout(usbmux_client_t client, char *data, - uint32_t datalen, uint32_t * recv_bytes, - int timeout) -{ - - if (!client || !data || datalen == 0 || !recv_bytes) - return -EINVAL; - - if (client->error < 0) - return client->error; - - pthread_mutex_lock(&client->mutex); - - if (timeout > 0 && (client->recv_buffer == NULL || client->r_len == 0)) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout / 1000; - ts.tv_nsec += (timeout - ((int) (timeout / 1000)) * 1000) * 1000; - pthread_cond_timedwait(&client->wait, &client->mutex, &ts); - } - - *recv_bytes = 0; - if (client->recv_buffer != NULL && client->r_len > 0) { - uint32_t foolen = datalen; - if ((int) foolen > client->r_len) - foolen = client->r_len; - memcpy(data, client->recv_buffer, foolen); - *recv_bytes = foolen; - - // preserve any left-over unread amounts. - int remainder = client->r_len - foolen; - if (remainder > 0) { - char *newbuf = malloc(remainder); - memcpy(newbuf, client->recv_buffer + foolen, remainder); - client->r_len = remainder; - free(client->recv_buffer); - client->recv_buffer = newbuf; - } else { - free(client->recv_buffer); - client->recv_buffer = NULL; - client->r_len = 0; - } - } - - pthread_mutex_unlock(&client->mutex); - - return 0; -} diff --git a/usbmux.h b/usbmux.h deleted file mode 100644 index 2bcdb15..0000000 --- a/usbmux.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2008 Jing Su. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __USBMUX_H__ -#define __USBMUX_H__ - -#include -#include -//#include - - -void usbmux_set_debug(int e); - -struct usbmux_device_int; -typedef struct usbmux_device_int *usbmux_device_t; - -struct usbmux_client_int; -typedef struct usbmux_client_int *usbmux_client_t; - -int usbmux_get_device ( usbmux_device_t *device ); -int usbmux_get_specific_device(int bus_n, int dev_n, usbmux_device_t * device); -int usbmux_free_device ( usbmux_device_t device ); - - -int usbmux_new_client ( usbmux_device_t device, uint16_t src_port, uint16_t dst_port, usbmux_client_t *client ); -int usbmux_free_client ( usbmux_client_t client ); - -int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes); - -int usbmux_recv_timeout(usbmux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout); - -int usbmux_pullbulk(usbmux_device_t device); - -int usbmux_get_error(usbmux_client_t client); - -#endif diff --git a/usbmuxd-proto.h b/usbmuxd-proto.h deleted file mode 100644 index 7f8c2d6..0000000 --- a/usbmuxd-proto.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Protocol defintion for usbmuxd proxy protocol */ - -#ifndef __USBMUXD_PROTO_H -#define __USBMUXD_PROTO_H - -#include - -#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 usbmuxd_result { - struct usbmuxd_header header; - uint32_t result; -} __attribute__((__packed__)); - -struct usbmuxd_connect_request { - struct usbmuxd_header header; - uint32_t device_id; - uint16_t tcp_dport; // TCP port number - uint16_t reserved; // set to zero -} __attribute__((__packed__)); - -struct usbmuxd_device { - uint32_t device_id; - uint16_t product_id; - char serial_number[40]; -} __attribute__((__packed__)); - -struct usbmuxd_device_info_record { - struct usbmuxd_header header; - struct usbmuxd_device device; - char padding[222]; -} __attribute__((__packed__)); - -struct usbmuxd_scan_request { - struct usbmuxd_header header; -} __attribute__((__packed__)); - -enum { - USBMUXD_RESULT = 1, - USBMUXD_CONNECT = 2, - USBMUXD_SCAN = 3, - USBMUXD_DEVICE_INFO = 4, -}; - -#endif /* __USBMUXD_PROTO_H */ diff --git a/usbmuxd.h b/usbmuxd.h deleted file mode 100644 index 15e97ee..0000000 --- a/usbmuxd.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef __USBMUXD_H -#define __USBMUXD_H - -/** - * Array entry returned by 'usbmuxd_scan()' scanning. - * - * If more than one device is available, 'product_id' and - * 'serial_number' and be analysed to help make a selection. - * The relevant 'handle' should be passed to 'usbmuxd_connect()', to - * start a proxy connection. The value 'handle' should be considered - * opaque and no presumption made about the meaning of its value. - */ -typedef struct { - int handle; - int product_id; - char serial_number[41]; -} usbmuxd_scan_result; - -/** - * Contacts usbmuxd and performs a scan for connected devices. - * - * @param available_devices pointer to array of usbmuxd_scan_result. - * Array of available devices. The required 'handle' - * should be passed to 'usbmuxd_connect()'. The returned array - * is zero-terminated for convenience; the final (unused) - * entry containing handle == 0. The returned array pointer - * should be freed by passing to 'free()' after use. - * - * @return number of available devices, zero on no devices, or negative on error - */ -int usbmuxd_scan(usbmuxd_scan_result **available_devices); - -/** - * Request proxy connect to - * - * @param handle returned by 'usbmuxd_scan()' - * - * @param tcp_port TCP port number on device, in range 0-65535. - * common values are 62078 for lockdown, and 22 for SSH. - * - * @return file descriptor socket of the connection, or -1 on error - */ -int usbmuxd_connect(const int handle, const unsigned short tcp_port); - -#endif /* __USBMUXD_H */ -- 1.6.0.2