├── AUTHORS ├── CMakeLists.txt ├── COPYING.GPLv2 ├── COPYING.GPLv3 ├── COPYING.LGPLv2.1 ├── Modules ├── CheckConstantExists.cmake ├── FindInotify.cmake ├── FindPLIST.cmake ├── FindUSB.cmake ├── LibFindMacros.cmake ├── VersionTag.cmake ├── cmake_uninstall.cmake.in └── describe.sh ├── README ├── README.devel ├── common ├── utils.c └── utils.h ├── daemon ├── CMakeLists.txt ├── client.c ├── client.h ├── device.c ├── device.h ├── log.c ├── log.h ├── main.c ├── usb-linux.c └── usb.h ├── libusbmuxd.pc.in ├── libusbmuxd ├── CMakeLists.txt ├── libusbmuxd.c ├── sock_stuff.c ├── sock_stuff.h ├── usbmuxd-proto.h └── usbmuxd.h ├── python-client ├── tcprelay.py └── usbmux.py ├── stuff ├── README └── com.openssh.sftp.plist ├── tools ├── CMakeLists.txt └── iproxy.c └── udev ├── 85-usbmuxd.rules.in └── CMakeLists.txt /AUTHORS: -------------------------------------------------------------------------------- 1 | Nikias Bassen 2 | Hector Martin 3 | Bastien Nocera 4 | Paul Sladen 5 | Martin Szulecki 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(usbmuxd) 2 | 3 | cmake_minimum_required(VERSION 2.6) 4 | 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/Modules/") 6 | 7 | include(VersionTag) 8 | 9 | set(USBMUXD_VERSION "${VERSION_TAG}") 10 | set(LIBUSBMUXD_VERSION "${VERSION_TAG}") 11 | set(LIBUSBMUXD_SOVERSION "2") 12 | 13 | message("-- Configuring usbmuxd v${VERSION_TAG}") 14 | 15 | if(NOT DEFINED LIB_SUFFIX) 16 | if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND ${CMAKE_SYSTEM_NAME} MATCHES "Linux") 17 | set(LIB_SUFFIX "64" CACHE STRING "Define suffix of library directory name (32/64)" ) 18 | else() 19 | set(LIB_SUFFIX "" CACHE STRING "Define suffix of library directory name (32/64)" ) 20 | endif() 21 | endif() 22 | 23 | # let CFLAGS env override this 24 | if(CMAKE_C_FLAGS STREQUAL "") 25 | set(CMAKE_C_FLAGS "-O2") 26 | endif() 27 | 28 | option(WANT_PLIST "Build with protocol version 1 support using libplist" ON) 29 | 30 | set(OPT_INCLUDES "") 31 | set(OPT_LIBS "") 32 | if(WANT_PLIST) 33 | find_package(PLIST) 34 | if(PLIST_FOUND) 35 | set(HAVE_PLIST ON) 36 | set(OPT_INCLUDES ${OPT_INCLUDES} ${PLIST_INCLUDE_DIRS}) 37 | set(OPT_LIBS ${OPT_LIBS} ${PLIST_LIBRARIES}) 38 | else() 39 | message("* NOTE: libplist was not found!") 40 | message("* libusbmuxd/usbmuxd will be build WITHOUT support for version 1") 41 | message("* of the usbmux protocol (plist based).") 42 | endif() 43 | endif() 44 | 45 | option(WITH_USBMUXD "Build usbmux daemon (usbmuxd)" ON) 46 | if(WIN32 AND WITH_USBMUXD) 47 | message("** NOTE: usbmuxd cannot be built on WIN32 due to missing libusb-1.0 support!") 48 | message(" If you need your own usbmuxd you have to use usbmuxd-legacy which works") 49 | message(" with libusb-0.1; otherwise just use the one that ships with iTunes.") 50 | message(" Building of usbmuxd has been disabled.") 51 | set(WITH_USBMUXD OFF) 52 | endif() 53 | if(WITH_USBMUXD) 54 | message("-- Will build usbmuxd: YES") 55 | else() 56 | message("-- Will build usbmuxd: NO") 57 | message("** NOTE: will NOT build usbmuxd **") 58 | if(WIN32 OR APPLE) 59 | message("** Make sure iTunes is installed, otherwise this software will not work! **") 60 | else() 61 | message("** You'll need a working usbmuxd implementation for this software to work! **") 62 | endif() 63 | endif() 64 | 65 | add_definitions(-Wall) 66 | 67 | add_subdirectory (libusbmuxd) 68 | if (WITH_USBMUXD) 69 | add_subdirectory (daemon) 70 | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 71 | add_subdirectory (udev) 72 | endif() 73 | endif() 74 | add_subdirectory (tools) 75 | 76 | # pkg-config 77 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libusbmuxd.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libusbmuxd.pc") 78 | # install pkg-config file 79 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libusbmuxd.pc" DESTINATION lib${LIB_SUFFIX}/pkgconfig/) 80 | 81 | # add uninstall target 82 | configure_file("${CMAKE_SOURCE_DIR}/Modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) 83 | add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") 84 | -------------------------------------------------------------------------------- /COPYING.GPLv2: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /Modules/CheckConstantExists.cmake: -------------------------------------------------------------------------------- 1 | # - Check if the given constant exists (as an enum, define, or whatever) 2 | # CHECK_CONSTANT_EXISTS (CONSTANT HEADER VARIABLE) 3 | # 4 | # CONSTANT - the name of the constant you are interested in 5 | # HEADER - the header(s) where the prototype should be declared 6 | # VARIABLE - variable to store the result 7 | # 8 | # The following variables may be set before calling this macro to 9 | # modify the way the check is run: 10 | # 11 | # CMAKE_REQUIRED_FLAGS = string of compile command line flags 12 | # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) 13 | # CMAKE_REQUIRED_INCLUDES = list of include directories 14 | # 15 | # Example: CHECK_CONSTANT_EXISTS(O_NOFOLLOW fcntl.h HAVE_O_NOFOLLOW) 16 | 17 | 18 | INCLUDE(CheckCSourceCompiles) 19 | 20 | MACRO (CHECK_CONSTANT_EXISTS _CONSTANT _HEADER _RESULT) 21 | SET(_INCLUDE_FILES) 22 | FOREACH (it ${_HEADER}) 23 | SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n") 24 | ENDFOREACH (it) 25 | 26 | SET(_CHECK_CONSTANT_SOURCE_CODE " 27 | ${_INCLUDE_FILES} 28 | void cmakeRequireConstant(int dummy,...){(void)dummy;} 29 | int main() 30 | { 31 | cmakeRequireConstant(0,${_CONSTANT}); 32 | return 0; 33 | } 34 | ") 35 | CHECK_C_SOURCE_COMPILES("${_CHECK_CONSTANT_SOURCE_CODE}" ${_RESULT}) 36 | 37 | ENDMACRO (CHECK_CONSTANT_EXISTS) 38 | 39 | -------------------------------------------------------------------------------- /Modules/FindInotify.cmake: -------------------------------------------------------------------------------- 1 | set(INOTIFY_H "NOTFOUND") 2 | find_file(INOTIFY_H 3 | "sys/inotify.h" 4 | PATHS ENV INCLUDE 5 | ) 6 | 7 | if (INOTIFY_H) 8 | set(INOTIFY_FOUND TRUE) 9 | else() 10 | set(INOTIFY_FOUND FALSE) 11 | endif() 12 | -------------------------------------------------------------------------------- /Modules/FindPLIST.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libplist 2 | # Once done, this will define 3 | # 4 | # PLIST_FOUND - system has libplist 5 | # PLIST_INCLUDE_DIRS - the libplist include directories 6 | # PLIST_LIBRARIES - link these to use libplist 7 | 8 | include(LibFindMacros) 9 | 10 | # Dependencies 11 | 12 | # Use pkg-config to get hints about paths 13 | libfind_pkg_check_modules(PLIST_PKGCONF libplist >= 0.15) 14 | 15 | # Include dir 16 | find_path(PLIST_INCLUDE_DIR 17 | NAMES plist/plist.h 18 | PATHS ${PLIST_PKGCONF_INCLUDE_DIRS} 19 | ) 20 | 21 | # Finally the library itself 22 | find_library(PLIST_LIBRARY 23 | NAMES plist 24 | PATHS ${PLIST_PKGCONF_LIBRARY_DIRS} 25 | ) 26 | 27 | # Set the include dir variables and the libraries and let libfind_process do the rest. 28 | # NOTE: Singular variables for this library, plural for libraries that this lib depends on. 29 | set(PLIST_PROCESS_INCLUDES PLIST_INCLUDE_DIR) 30 | set(PLIST_PROCESS_LIBS PLIST_LIBRARY) 31 | libfind_process(PLIST) 32 | -------------------------------------------------------------------------------- /Modules/FindUSB.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libusb-1.0 2 | # Once done, this will define 3 | # 4 | # USB_FOUND - system has libusb-1.0 5 | # USB_INCLUDE_DIRS - the libusb-1.0 include directories 6 | # USB_LIBRARIES - link these to use libusb-1.0 7 | 8 | include(LibFindMacros) 9 | 10 | # Dependencies 11 | 12 | # pkg-config + libusb fails on FreeBSD, though libusb is in base 13 | if(NOT(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")) 14 | # Use pkg-config to get hints about paths 15 | libfind_pkg_check_modules(USB_PKGCONF libusb-1.0>=1.0.3) 16 | # We want to look for libusb-1.0 17 | set(USB_LIBRARY_NAME usb-1.0) 18 | else() 19 | set(USB_PKGCONF_INCLUDE_DIRS /usr/include) 20 | set(USB_PKGCONF_LIBRARY_DIRS /usr/lib) 21 | set(USB_LIBRARY_NAME usb) 22 | endif() 23 | 24 | # Include dir 25 | find_path(USB_INCLUDE_DIR 26 | NAMES libusb.h 27 | PATHS ${USB_PKGCONF_INCLUDE_DIRS} 28 | ) 29 | 30 | # Finally the library itself 31 | find_library(USB_LIBRARY 32 | NAMES ${USB_LIBRARY_NAME} 33 | PATHS ${USB_PKGCONF_LIBRARY_DIRS} 34 | ) 35 | 36 | # Set the include dir variables and the libraries and let libfind_process do the rest. 37 | # NOTE: Singular variables for this library, plural for libraries this this lib depends on. 38 | set(USB_PROCESS_INCLUDES USB_INCLUDE_DIR) 39 | set(USB_PROCESS_LIBS USB_LIBRARY) 40 | libfind_process(USB) 41 | -------------------------------------------------------------------------------- /Modules/LibFindMacros.cmake: -------------------------------------------------------------------------------- 1 | # Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments 2 | # used for the current package. For this to work, the first parameter must be the 3 | # prefix of the current package, then the prefix of the new package etc, which are 4 | # passed to find_package. 5 | macro (libfind_package PREFIX) 6 | set (LIBFIND_PACKAGE_ARGS ${ARGN}) 7 | if (${PREFIX}_FIND_QUIETLY) 8 | set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) 9 | endif (${PREFIX}_FIND_QUIETLY) 10 | if (${PREFIX}_FIND_REQUIRED) 11 | set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) 12 | endif (${PREFIX}_FIND_REQUIRED) 13 | find_package(${LIBFIND_PACKAGE_ARGS}) 14 | endmacro (libfind_package) 15 | 16 | # Damn CMake developers made the UsePkgConfig system deprecated in the same release (2.6) 17 | # where they added pkg_check_modules. Consequently I need to support both in my scripts 18 | # to avoid those deprecated warnings. Here's a helper that does just that. 19 | # Works identically to pkg_check_modules, except that no checks are needed prior to use. 20 | macro (libfind_pkg_check_modules PREFIX PKGNAME) 21 | if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 22 | include(UsePkgConfig) 23 | pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) 24 | else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 25 | find_package(PkgConfig) 26 | if (PKG_CONFIG_FOUND) 27 | pkg_check_modules(${PREFIX} ${PKGNAME}) 28 | endif (PKG_CONFIG_FOUND) 29 | endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 30 | endmacro (libfind_pkg_check_modules) 31 | 32 | # Do the final processing once the paths have been detected. 33 | # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain 34 | # all the variables, each of which contain one include directory. 35 | # Ditto for ${PREFIX}_PROCESS_LIBS and library files. 36 | # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. 37 | # Also handles errors in case library detection was required, etc. 38 | macro (libfind_process PREFIX) 39 | # Skip processing if already processed during this run 40 | if (NOT ${PREFIX}_FOUND) 41 | # Start with the assumption that the library was found 42 | set (${PREFIX}_FOUND TRUE) 43 | 44 | # Process all includes and set _FOUND to false if any are missing 45 | foreach (i ${${PREFIX}_PROCESS_INCLUDES}) 46 | if (${i}) 47 | set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) 48 | mark_as_advanced(${i}) 49 | else (${i}) 50 | set (${PREFIX}_FOUND FALSE) 51 | endif (${i}) 52 | endforeach (i) 53 | 54 | # Process all libraries and set _FOUND to false if any are missing 55 | foreach (i ${${PREFIX}_PROCESS_LIBS}) 56 | if (${i}) 57 | set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) 58 | mark_as_advanced(${i}) 59 | else (${i}) 60 | set (${PREFIX}_FOUND FALSE) 61 | endif (${i}) 62 | endforeach (i) 63 | 64 | # Print message and/or exit on fatal error 65 | if (${PREFIX}_FOUND) 66 | if (NOT ${PREFIX}_FIND_QUIETLY) 67 | message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") 68 | endif (NOT ${PREFIX}_FIND_QUIETLY) 69 | else (${PREFIX}_FOUND) 70 | if (${PREFIX}_FIND_REQUIRED) 71 | foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) 72 | message("${i}=${${i}}") 73 | endforeach (i) 74 | message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") 75 | endif (${PREFIX}_FIND_REQUIRED) 76 | endif (${PREFIX}_FOUND) 77 | endif (NOT ${PREFIX}_FOUND) 78 | endmacro (libfind_process) 79 | 80 | macro(libfind_library PREFIX basename) 81 | set(TMP "") 82 | if(MSVC80) 83 | set(TMP -vc80) 84 | endif(MSVC80) 85 | if(MSVC90) 86 | set(TMP -vc90) 87 | endif(MSVC90) 88 | set(${PREFIX}_LIBNAMES ${basename}${TMP}) 89 | if(${ARGC} GREATER 2) 90 | set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) 91 | string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) 92 | set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) 93 | endif(${ARGC} GREATER 2) 94 | find_library(${PREFIX}_LIBRARY 95 | NAMES ${${PREFIX}_LIBNAMES} 96 | PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} 97 | ) 98 | endmacro(libfind_library) 99 | 100 | -------------------------------------------------------------------------------- /Modules/VersionTag.cmake: -------------------------------------------------------------------------------- 1 | execute_process( 2 | COMMAND "sh" "${CMAKE_SOURCE_DIR}/Modules/describe.sh" 3 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" 4 | OUTPUT_VARIABLE DESCRIBE 5 | OUTPUT_STRIP_TRAILING_WHITESPACE 6 | ) 7 | 8 | if(DESCRIBE STREQUAL "") 9 | set (VERSION_TAG "UNKNOWN") 10 | else() 11 | string(REGEX REPLACE "^v" "" VERSION_TAG "${DESCRIBE}") 12 | endif() 13 | 14 | -------------------------------------------------------------------------------- /Modules/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 3 | ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | 5 | FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | STRING(REGEX REPLACE "\n" ";" files "${files}") 7 | FOREACH(file ${files}) 8 | MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 9 | IF(EXISTS "$ENV{DESTDIR}${file}") 10 | EXEC_PROGRAM( 11 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 12 | OUTPUT_VARIABLE rm_out 13 | RETURN_VALUE rm_retval 14 | ) 15 | IF(NOT "${rm_retval}" STREQUAL 0) 16 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 17 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 18 | ELSE(EXISTS "$ENV{DESTDIR}${file}") 19 | MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 20 | ENDIF(EXISTS "$ENV{DESTDIR}${file}") 21 | ENDFOREACH(file) 22 | -------------------------------------------------------------------------------- /Modules/describe.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check for git and a git repo. 4 | if head=`git rev-parse --verify HEAD 2>/dev/null`; then 5 | /bin/echo -n `git describe` 6 | 7 | # Are there uncommitted changes? 8 | git update-index --refresh --unmerged > /dev/null 9 | git diff-index --quiet HEAD || /bin/echo -n -dirty 10 | else 11 | # Check for version tag 12 | if [ -e version.tag ]; then 13 | /bin/echo -n `cat version.tag` 14 | fi 15 | fi 16 | 17 | echo 18 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Background 2 | ========== 3 | 4 | 'usbmuxd' stands for "USB multiplexing daemon". This daemon is in charge of 5 | multiplexing connections over USB to an iPhone or iPod touch. To users, it means 6 | you can sync your music, contacts, photos, etc. over USB. To developers, it 7 | means you can connect to any listening localhost socket on the device. usbmuxd 8 | is not used for tethering data transfer, which uses a dedicated USB interface as 9 | a virtual network device. 10 | 11 | Multiple connections to different TCP ports can happen in parallel. An example 12 | (and useful) tool called 'iproxy' is included that allows you to forward 13 | localhost ports to the device---allows SSH over USB on jailbroken devices, or 14 | allowing access the lockdown daemon (and then to all of the file access, sync, 15 | notification and backup services running on the device). 16 | 17 | The higher-level layers are handled by libimobiledevice. 'ifuse' is then able 18 | to sit on top of this and mount your device's AFC filesystem share. 19 | 20 | There is also a Python implementation of the client library in the python-client 21 | library, and an example tcprelay.py which performs a similar function to iproxy. 22 | This implementation supports OSX and Windows and the new iTunes plist-based 23 | usbmuxd protocol, so it is portable and will run on those operating systems with 24 | no modification, using Apple's native usbmuxd. This is useful if you need to 25 | tunnel to your device from another OS in a pinch. Run python tcpclient.py --help 26 | for usage information. 27 | 28 | License 29 | ======= 30 | 31 | The contents of this package are licensed under the GNU General Public License, 32 | versions 2 or 3 (see COPYING.GPLv2 and COPYING.GPLv3), except for libuxbmuxd 33 | which is licensed under the GNU Lesser General Public License, version 2.1 or, 34 | at your option, any later version (see COPYING.LGPLv2.1). If a more permissive 35 | license is specified at the top of a source file, it takes precedence over this. 36 | 37 | Legal 38 | ===== 39 | 40 | Apple, iPhone, and iPod touch are trademarks of Apple Inc., registered in the 41 | U.S. and other countries. 42 | 43 | Building 44 | ======== 45 | 46 | mkdir build 47 | cd build 48 | cmake .. 49 | make 50 | sudo make install 51 | 52 | You should also create a 'usbmux' user that has access to USB devices on your 53 | system. Alternatively, you can pass a different username after the -U argument. 54 | 55 | Running (with magic) 56 | ==================== 57 | 58 | (Unplug + replug your jailbroken device) 59 | ./iproxy 2222 22 & 60 | ssh -p 2222 root@localhost 61 | 62 | Hopefully you get the normal SSH login prompt. You may still lots of debugging 63 | output for the moment. If this is getting in the way of your ssh login, then 64 | run the 'ssh' command from a different xterminal or virtual console. Of course, 65 | you need to have OpenSSH installed on your jailbroken device for this to work. 66 | 67 | If you have iFuse, you can run "ifuse . This doesn't require 68 | iproxy and works on all devices, jailbroken or not. 69 | 70 | Running (without magic) 71 | ======================= 72 | 73 | If 'udev' is _not_ automatically running on your machine and picking up the new 74 | .rules file, you will need to start usbmuxd by hand first. Check it's running 75 | and that there is only one copy with 'ps aux | grep 76 | usbmuxd'. 77 | 78 | sudo usbmuxd -U -v -v & 79 | ./iproxy 2222 22 & 80 | ssh -p 2222 root@localhost 81 | 82 | Tip: Starting SSH if disabled 83 | ============================= 84 | 85 | If your device is rooted, but SSH isn't started and you _cannot_ (for instance, 86 | cracked/broken screen) get to the Services control panel on the device, then you 87 | can start the SSH service over the USB by mounting the (jailbroken) filesystem. 88 | 89 | You will need to mount it using 'ifuse --afc2' (to access the root directory of 90 | the device), and then edit: 91 | 92 | /Library/LaunchDaemons/com.openssh.sshd.plist 93 | 94 | to _remove_ the lines: 95 | 96 | Disabled 97 | 98 | 99 | Reboot the device and then sshd should be running. 100 | 101 | TODO 102 | ==== 103 | 104 | The server currently assumes that the device is well-behaved and does not do a 105 | bunch of checks like looking for the expected SEQ and ACK numbers from it. This 106 | is normally not an issue, but it's annoying for debugging because lost packets 107 | (which shouldn't happen, but can happen if the code is buggy) mean that stuff 108 | gets out of sync and then might crash and burn dozens of packets later. 109 | 110 | The server needs more testing, and some optimizing. 111 | 112 | Someone should probably do some edge-case testing on the TCP stuff. 113 | 114 | The outgoing ACK handling on the server probably needs some thought. Currently, 115 | when there's an outstanding ACK, we send it after a timeout (to avoid sending 116 | a no-payload ACK packet for everything the phone sends us). However, there's 117 | probably a better way of doing this. 118 | 119 | Architecture information 120 | ======================== 121 | 122 | The iPhone / iPod Touch basically implements a rather strange USB networking 123 | system that operates at a higher level. It is of course completely proprietary. 124 | Generally speaking, this is what goes on in a typical usage scenario: 125 | 126 | 0. iTunes opens a connection to usbmuxd and asks it for device notifications 127 | 1. User inserts phone into computer 128 | 2. usbmuxd notices the phone and pings it with a version packet 129 | 3. phone replies 130 | 4. usbmuxd now considers the phone to be connected and tells iTunes 131 | 5. iTunes opens another separate connection to usbmuxd and asks it to connect 132 | to, say, the afc port on the device 133 | 6. usbmuxd sends a pseudo-TCP SYN packet to the phone 134 | 7. the phone's kernel driver receives the SYN packet and itself opens a 135 | TCP connection to localhost on the afc port 136 | 8. the phone replies with a pseudo-TCP SYN/ACK indicating that the port is open 137 | and the connection can proceed 138 | 7. usbmuxd sends a final ACK to the phone 139 | 8. usbmuxd replies to iTunes with a "connection successful" message 140 | 9. any data that iTunes writes to the usbmuxd socket from now on is forwarded, 141 | through pseudo-TCP, through USB, back into a more regular TCP connection to 142 | localhost, to the afc daemon on the phone, and vice versa 143 | 144 | The usbmuxd protocol is a relatively simple binary message protocol documented 145 | here: 146 | 147 | http://wikee.iphwn.org/usb:usbmux 148 | 149 | Note that once a connection is established the UNIX socket essentially becomes 150 | a dedicated pipe to the TCP connction and no more high-level control is 151 | possible (closing the socket closes the TCP connection). Ditto for the "listen 152 | for devices" mode - usbmuxd will reject any commands in such mode, and the 153 | socket essentially becomes a dedicated device notification pipe. This means 154 | that you need, at minimum, TWO connections to usbmuxd to do anything useful. 155 | 156 | On Windows, usbmuxd works the same way but a TCP connection to localhost port 157 | 27015 replaces the UNIX socket. On OSX, the UNIX socket is /var/run/usbmuxd. The 158 | server and client implemented here default the same /var/run/usbmuxd socket. 159 | 160 | The phone protocol operates over a pair of USB bulk endpoints. There is an outer 161 | layer used for packet size info and a "protocol" (version and TCP are the only 162 | two options), and that header is followed by TCP headers for actual data comms. 163 | However, the protocol isn't actual TCP, just a custom protocol which for some 164 | reason uses a standard TCP header and leaves most fields unused. 165 | 166 | There is no reordering or retransmission. There are no checksums, no URG, no 167 | PSH, no non-ACK, no FIN. What there *is* is the SEQ/ACK/window mechanism used 168 | for flow control, and RST is used as the only connection teardown mechanism (and 169 | also for "connection refused"), and the connection startup is SYN/SYNACK/ACK. 170 | 171 | Windows are constant-scaled by 8 bits. This is legal TCP as long as the 172 | corresponding option is negotiated. Of course, no such negotiation happens on 173 | this protocol. 174 | 175 | Note that, since there are no retransmissions, there is some overlap between ACK 176 | and window for flow control. For example, the server doesn't ever touch its 177 | window size, and just refuses to ACK stuff if its buffers are full and the 178 | client isn't reading data. The phone happily seems to stop sending stuff. 179 | 180 | Also, if the phone RSTs you out of nowhere, look at the packet payload for a 181 | textual error message. Note: if it claims to suffer from amnesia, that probably 182 | means you overflowed its input buffer by ignoring its flow control / window 183 | size. Go figure. Probably a logic bug in the kernel code. 184 | 185 | Note that all normal packets need to have flags set to ACK (and only ACK). There 186 | is no support for, erm, not-acking. Keep the ack number field valid at all 187 | times. 188 | 189 | The usbmuxd CONNECT request port field is byte-swapped (network-endian). This is 190 | even more annoying for the plist based protocol, since it's even true there 191 | (where the field is plain text). So even for the plain text int, you need to 192 | swap the bytes (port 22 becomes 5632). I have no clue if this 193 | is the case on the new plist protocol on PPC macs (is the newer iTunes available 194 | for those?) 195 | 196 | There are a bunch of gotchas due to the USB framing, and this is even worse 197 | because implementations tend to get it wrong (i.e. libusb, and this is the 198 | reason for the patch). Basically, USB Bulk offers, at the low level, the ability 199 | to transfer packets from 0 to wMaxPacketSize (512 here) bytes, period. There is 200 | no other support for higher level framing of transfers. The way you do those is 201 | by breaking them up into packets, and the final shorter packet marks the end of 202 | the transfer. The critical bit is that, if the transfer happens to be divisible 203 | by 512, you send a zero-length packet (ZLP) to indicate the end of the transfer. 204 | Libusb doesn't set this option by default and the iPhone gets packets stuck to 205 | each other, which it doesn't like. Actually, this framing is sort of redundant 206 | because the usbmux packet header includes a length field, but the phone still 207 | wants the ZLPs or else it breaks. To make matters worse, usbdevfs imposes a max 208 | transfer size of 16k, so libusb breaks transfers into that size. This is okay 209 | for sending as long as the ZLP is only added to the last transfer (the patch 210 | does that), but it can easily cause nasty race conditions on RX due to libusb 211 | doing multiple outstanding reads at the same time and then cancelling the rest 212 | when shorter data arrives (but what if some data got into the other requests 213 | already?), so we only do 16k reads and stick them together ourselves by looking 214 | at the packet size header. We still depend on ZLPs being sent to end transfers 215 | at non-16k boundaries that are multiples of 512, but that seems to work fine. I 216 | guess the ZLPs might cause spurious 0-byte transfers to show up on RX if things 217 | line up right, but we ignore those. By the way, the maximum packet/transfer size 218 | is 65535 bytes due to the 16-bit length header of the usbmux protocol. 219 | -------------------------------------------------------------------------------- /README.devel: -------------------------------------------------------------------------------- 1 | Background 2 | ========== 3 | 4 | 'libusbmuxd' makes it really simple to talk to a running 'usbmuxd' and 5 | hides all the details for you. There are two function calls: 6 | 7 | usbmuxd_scan() 8 | -------------- 9 | 10 | This returns a list of all available iPhone-like devices that are 11 | available for talking to. The returned array contains the USB 12 | product_id, hex formatted serial_number of any iPhones/iTouches and a 13 | non-descript 'handle' for all those devices that are within range (as 14 | of March 2009, that means a device directly plugged into the 15 | computer's USB port). 16 | 17 | Once you have found the device you want to communicate with, take its 18 | 'handle' and pass this to usbmuxd_connect(). 19 | 20 | usbmuxd_connect() 21 | ----------------- 22 | 23 | This takes a handle, a destination port number and tries to setup 24 | a proxy a connection. It returns a file-descriptor which you should 25 | be able to read(), write() and select() on like any other active network 26 | socket connection. 27 | 28 | 29 | Technical details 30 | ================= 31 | 32 | When usbmuxd is running (normally started, or stopped as a result of 33 | 'udev' auto-insertion messages), it provides a socket interface in 34 | '/var/run/usbmuxd' that is designed to be compatible with the socket 35 | interface that is provided on MacOSX. 36 | 37 | The structures for communicating over this device are documented 38 | in the 'usbmuxd-proto.h', but you shouldn't need to view them 39 | directly if you are using the libusbmuxd.so library for easy access. 40 | 41 | 42 | Example 43 | ======= 44 | 45 | #include 46 | 47 | ... 48 | 49 | gcc -o leetphone leetphone.c -lusbmuxd 50 | 51 | -------------------------------------------------------------------------------- /common/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | 7 | This library is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as 9 | published by the Free Software Foundation, either version 2.1 of the 10 | License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | */ 22 | 23 | #ifdef HAVE_CONFIG_H 24 | #include 25 | #endif 26 | 27 | #include 28 | #include 29 | #include 30 | #include "utils.h" 31 | 32 | #ifdef USBMUXD_DAEMON 33 | # include "log.h" 34 | # define util_error(...) usbmuxd_log(LL_ERROR, __VA_ARGS__) 35 | #else 36 | # define util_error(...) fprintf(stderr, __VA_ARGS__) 37 | #endif 38 | 39 | #ifdef USBMUXD_DAEMON 40 | void fdlist_create(struct fdlist *list) 41 | { 42 | list->count = 0; 43 | list->capacity = 4; 44 | list->owners = malloc(sizeof(*list->owners) * list->capacity); 45 | list->fds = malloc(sizeof(*list->fds) * list->capacity); 46 | } 47 | void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events) 48 | { 49 | if(list->count == list->capacity) { 50 | list->capacity *= 2; 51 | list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity); 52 | list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity); 53 | } 54 | list->owners[list->count] = owner; 55 | list->fds[list->count].fd = fd; 56 | list->fds[list->count].events = events; 57 | list->fds[list->count].revents = 0; 58 | list->count++; 59 | } 60 | 61 | void fdlist_free(struct fdlist *list) 62 | { 63 | list->count = 0; 64 | list->capacity = 0; 65 | free(list->owners); 66 | list->owners = NULL; 67 | free(list->fds); 68 | list->fds = NULL; 69 | } 70 | 71 | void fdlist_reset(struct fdlist *list) 72 | { 73 | list->count = 0; 74 | } 75 | #endif 76 | 77 | void collection_init(struct collection *col) 78 | { 79 | col->list = malloc(sizeof(void *)); 80 | memset(col->list, 0, sizeof(void *)); 81 | col->capacity = 1; 82 | } 83 | 84 | void collection_free(struct collection *col) 85 | { 86 | free(col->list); 87 | col->list = NULL; 88 | col->capacity = 0; 89 | } 90 | 91 | void collection_add(struct collection *col, void *element) 92 | { 93 | int i; 94 | for(i=0; icapacity; i++) { 95 | if(!col->list[i]) { 96 | col->list[i] = element; 97 | return; 98 | } 99 | } 100 | col->list = realloc(col->list, sizeof(void*) * col->capacity * 2); 101 | memset(&col->list[col->capacity], 0, sizeof(void *) * col->capacity); 102 | col->list[col->capacity] = element; 103 | col->capacity *= 2; 104 | } 105 | 106 | void collection_remove(struct collection *col, void *element) 107 | { 108 | int i; 109 | for(i=0; icapacity; i++) { 110 | if(col->list[i] == element) { 111 | col->list[i] = NULL; 112 | return; 113 | } 114 | } 115 | util_error("collection_remove: element %p not present in collection %p (cap %d)", element, col, col->capacity); 116 | } 117 | 118 | int collection_count(struct collection *col) 119 | { 120 | int i, cnt = 0; 121 | for(i=0; icapacity; i++) { 122 | if(col->list[i]) 123 | cnt++; 124 | } 125 | return cnt; 126 | } 127 | -------------------------------------------------------------------------------- /common/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | 7 | This library is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as 9 | published by the Free Software Foundation, either version 2.1 of the 10 | License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | */ 22 | 23 | #ifndef __UTILS_H__ 24 | #define __UTILS_H__ 25 | 26 | #ifdef USBMUXD_DAEMON 27 | #include 28 | 29 | enum fdowner { 30 | FD_LISTEN, 31 | FD_CLIENT, 32 | FD_USB 33 | }; 34 | 35 | struct fdlist { 36 | int count; 37 | int capacity; 38 | enum fdowner *owners; 39 | struct pollfd *fds; 40 | }; 41 | 42 | void fdlist_create(struct fdlist *list); 43 | void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events); 44 | void fdlist_free(struct fdlist *list); 45 | void fdlist_reset(struct fdlist *list); 46 | #endif 47 | 48 | struct collection { 49 | void **list; 50 | int capacity; 51 | }; 52 | 53 | void collection_init(struct collection *col); 54 | void collection_add(struct collection *col, void *element); 55 | void collection_remove(struct collection *col, void *element); 56 | int collection_count(struct collection *col); 57 | void collection_free(struct collection *col); 58 | 59 | #define FOREACH(var, col) \ 60 | do { \ 61 | int _iter; \ 62 | for(_iter=0; _iter<(col)->capacity; _iter++) { \ 63 | if(!(col)->list[_iter]) continue; \ 64 | var = (col)->list[_iter]; 65 | 66 | #define ENDFOREACH \ 67 | } \ 68 | } while(0); 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /daemon/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(USB REQUIRED) 2 | include_directories(${USB_INCLUDE_DIRS}) 3 | include_directories(${OPT_INCLUDES}) 4 | set(LIBS ${LIBS} ${USB_LIBRARIES} ${OPT_LIBS}) 5 | if(HAVE_PLIST) 6 | add_definitions("-DHAVE_PLIST") 7 | message("-- usbmuxd will be built with protocol version 1 support") 8 | endif() 9 | include_directories (${CMAKE_SOURCE_DIR}/common) 10 | include_directories (${CMAKE_SOURCE_DIR}/daemon) 11 | include_directories (${CMAKE_SOURCE_DIR}/libusbmuxd) 12 | 13 | add_definitions(-DUSBMUXD_DAEMON -DUSBMUXD_VERSION="${USBMUXD_VERSION}") 14 | add_executable(usbmuxd main.c usb-linux.c log.c ${CMAKE_SOURCE_DIR}/common/utils.c device.c client.c) 15 | target_link_libraries(usbmuxd ${LIBS}) 16 | 17 | install(TARGETS usbmuxd RUNTIME DESTINATION sbin) 18 | 19 | message(" 20 | * REMINDER 21 | * Remember to add a user named 'usbmux' with USB access permissions 22 | * for the udev hotplugging feature to work out of the box. 23 | ") 24 | -------------------------------------------------------------------------------- /daemon/client.c: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 or version 3. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifdef HAVE_PLIST 36 | #include 37 | #endif 38 | 39 | #include "log.h" 40 | #include "usb.h" 41 | #include "client.h" 42 | #include "device.h" 43 | 44 | #ifdef HAVE_PLIST 45 | #define CMD_BUF_SIZE 1024 46 | #else 47 | #define CMD_BUF_SIZE 256 48 | #endif 49 | #define REPLY_BUF_SIZE 1024 50 | 51 | enum client_state { 52 | CLIENT_COMMAND, // waiting for command 53 | CLIENT_LISTEN, // listening for devices 54 | CLIENT_CONNECTING1, // issued connection request 55 | CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent 56 | CLIENT_CONNECTED, // connected 57 | CLIENT_DEAD 58 | }; 59 | 60 | struct mux_client { 61 | int fd; 62 | unsigned char *ob_buf; 63 | uint32_t ob_size; 64 | uint32_t ob_capacity; 65 | unsigned char *ib_buf; 66 | uint32_t ib_size; 67 | uint32_t ib_capacity; 68 | short events, devents; 69 | uint32_t connect_tag; 70 | int connect_device; 71 | enum client_state state; 72 | uint32_t proto_version; 73 | }; 74 | 75 | static struct collection client_list; 76 | 77 | int client_read(struct mux_client *client, void *buffer, uint32_t len) 78 | { 79 | usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len); 80 | if(client->state != CLIENT_CONNECTED) { 81 | usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd); 82 | return -1; 83 | } 84 | return recv(client->fd, buffer, len, 0); 85 | } 86 | 87 | int client_write(struct mux_client *client, void *buffer, uint32_t len) 88 | { 89 | usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len); 90 | if(client->state != CLIENT_CONNECTED) { 91 | usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd); 92 | return -1; 93 | } 94 | return send(client->fd, buffer, len, 0); 95 | } 96 | 97 | int client_set_events(struct mux_client *client, short events) 98 | { 99 | if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) { 100 | usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd); 101 | return -1; 102 | } 103 | client->devents = events; 104 | if(client->state == CLIENT_CONNECTED) 105 | client->events = events; 106 | return 0; 107 | } 108 | 109 | int client_accept(int listenfd) 110 | { 111 | struct sockaddr_un addr; 112 | int cfd; 113 | socklen_t len = sizeof(struct sockaddr_un); 114 | cfd = accept(listenfd, (struct sockaddr *)&addr, &len); 115 | if (cfd < 0) { 116 | usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno)); 117 | return cfd; 118 | } 119 | 120 | struct mux_client *client; 121 | client = malloc(sizeof(struct mux_client)); 122 | memset(client, 0, sizeof(struct mux_client)); 123 | 124 | client->fd = cfd; 125 | client->ob_buf = malloc(REPLY_BUF_SIZE); 126 | client->ob_size = 0; 127 | client->ob_capacity = REPLY_BUF_SIZE; 128 | client->ib_buf = malloc(CMD_BUF_SIZE); 129 | client->ib_size = 0; 130 | client->ib_capacity = CMD_BUF_SIZE; 131 | client->state = CLIENT_COMMAND; 132 | client->events = POLLIN; 133 | 134 | collection_add(&client_list, client); 135 | 136 | usbmuxd_log(LL_INFO, "New client on fd %d", client->fd); 137 | return client->fd; 138 | } 139 | 140 | void client_close(struct mux_client *client) 141 | { 142 | usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd); 143 | if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) { 144 | usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device); 145 | client->state = CLIENT_DEAD; 146 | device_abort_connect(client->connect_device, client); 147 | } 148 | close(client->fd); 149 | if(client->ob_buf) 150 | free(client->ob_buf); 151 | if(client->ib_buf) 152 | free(client->ib_buf); 153 | collection_remove(&client_list, client); 154 | free(client); 155 | } 156 | 157 | void client_get_fds(struct fdlist *list) 158 | { 159 | FOREACH(struct mux_client *client, &client_list) { 160 | fdlist_add(list, FD_CLIENT, client->fd, client->events); 161 | } ENDFOREACH 162 | } 163 | 164 | static int send_pkt(struct mux_client *client, uint32_t tag, enum usbmuxd_msgtype msg, void *payload, int payload_length) 165 | { 166 | struct usbmuxd_header hdr; 167 | hdr.version = client->proto_version; 168 | hdr.length = sizeof(hdr) + payload_length; 169 | hdr.message = msg; 170 | hdr.tag = tag; 171 | usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length); 172 | if((client->ob_capacity - client->ob_size) < hdr.length) { 173 | usbmuxd_log(LL_ERROR, "Client %d output buffer full (%d bytes) while sending message %d (%d bytes)", client->fd, client->ob_capacity, hdr.message, hdr.length); 174 | client_close(client); 175 | return -1; 176 | } 177 | memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr)); 178 | if(payload && payload_length) 179 | memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length); 180 | client->ob_size += hdr.length; 181 | client->events |= POLLOUT; 182 | return hdr.length; 183 | } 184 | 185 | static int send_result(struct mux_client *client, uint32_t tag, uint32_t result) 186 | { 187 | int res = -1; 188 | #ifdef HAVE_PLIST 189 | if (client->proto_version == 1) { 190 | /* XML plist packet */ 191 | char *xml = NULL; 192 | uint32_t xmlsize = 0; 193 | plist_t dict = plist_new_dict(); 194 | plist_dict_insert_item(dict, "MessageType", plist_new_string("Result")); 195 | plist_dict_insert_item(dict, "Number", plist_new_uint(result)); 196 | plist_to_xml(dict, &xml, &xmlsize); 197 | plist_free(dict); 198 | if (xml) { 199 | res = send_pkt(client, tag, MESSAGE_PLIST, xml, xmlsize); 200 | free(xml); 201 | } else { 202 | usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__); 203 | } 204 | } else 205 | #endif 206 | { 207 | /* binary packet */ 208 | res = send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t)); 209 | } 210 | return res; 211 | } 212 | 213 | int client_notify_connect(struct mux_client *client, enum usbmuxd_result result) 214 | { 215 | usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result); 216 | if(client->state == CLIENT_DEAD) 217 | return -1; 218 | if(client->state != CLIENT_CONNECTING1) { 219 | usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd); 220 | return -1; 221 | } 222 | if(send_result(client, client->connect_tag, result) < 0) 223 | return -1; 224 | if(result == RESULT_OK) { 225 | client->state = CLIENT_CONNECTING2; 226 | client->events = POLLOUT; // wait for the result packet to go through 227 | // no longer need this 228 | free(client->ib_buf); 229 | client->ib_buf = NULL; 230 | } else { 231 | client->state = CLIENT_COMMAND; 232 | } 233 | return 0; 234 | } 235 | 236 | static int notify_device_add(struct mux_client *client, struct device_info *dev) 237 | { 238 | int res = -1; 239 | #ifdef HAVE_PLIST 240 | if (client->proto_version == 1) { 241 | /* XML plist packet */ 242 | char *xml = NULL; 243 | uint32_t xmlsize = 0; 244 | plist_t dict = plist_new_dict(); 245 | plist_dict_insert_item(dict, "MessageType", plist_new_string("Attached")); 246 | plist_t props = plist_new_dict(); 247 | // TODO: get current usb speed 248 | plist_dict_insert_item(props, "ConnectionSpeed", plist_new_uint(480000000)); 249 | plist_dict_insert_item(props, "ConnectionType", plist_new_string("USB")); 250 | plist_dict_insert_item(props, "DeviceID", plist_new_uint(dev->id)); 251 | plist_dict_insert_item(props, "LocationID", plist_new_uint(dev->location)); 252 | plist_dict_insert_item(props, "ProductID", plist_new_uint(dev->pid)); 253 | plist_dict_insert_item(props, "SerialNumber", plist_new_string(dev->serial)); 254 | plist_dict_insert_item(dict, "Properties", props); 255 | plist_to_xml(dict, &xml, &xmlsize); 256 | plist_free(dict); 257 | if (xml) { 258 | res = send_pkt(client, 0, MESSAGE_PLIST, xml, xmlsize); 259 | free(xml); 260 | } else { 261 | usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__); 262 | } 263 | } else 264 | #endif 265 | { 266 | /* binary packet */ 267 | struct usbmuxd_device_record dmsg; 268 | memset(&dmsg, 0, sizeof(dmsg)); 269 | dmsg.device_id = dev->id; 270 | strncpy(dmsg.serial_number, dev->serial, 256); 271 | dmsg.serial_number[255] = 0; 272 | dmsg.location = dev->location; 273 | dmsg.product_id = dev->pid; 274 | res = send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg)); 275 | } 276 | return res; 277 | } 278 | 279 | static int notify_device_remove(struct mux_client *client, uint32_t device_id) 280 | { 281 | int res = -1; 282 | #ifdef HAVE_PLIST 283 | if (client->proto_version == 1) { 284 | /* XML plist packet */ 285 | char *xml = NULL; 286 | uint32_t xmlsize = 0; 287 | plist_t dict = plist_new_dict(); 288 | plist_dict_insert_item(dict, "MessageType", plist_new_string("Detached")); 289 | plist_dict_insert_item(dict, "DeviceID", plist_new_uint(device_id)); 290 | plist_to_xml(dict, &xml, &xmlsize); 291 | plist_free(dict); 292 | if (xml) { 293 | res = send_pkt(client, 0, MESSAGE_PLIST, xml, xmlsize); 294 | free(xml); 295 | } else { 296 | usbmuxd_log(LL_ERROR, "%s: Could not convert plist to xml", __func__); 297 | } 298 | } else 299 | #endif 300 | { 301 | /* binary packet */ 302 | res = send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &device_id, sizeof(uint32_t)); 303 | } 304 | return res; 305 | } 306 | 307 | static int start_listen(struct mux_client *client) 308 | { 309 | struct device_info *devs; 310 | struct device_info *dev; 311 | int count, i; 312 | 313 | client->state = CLIENT_LISTEN; 314 | count = device_get_count(); 315 | if(!count) 316 | return 0; 317 | devs = malloc(sizeof(struct device_info) * count); 318 | count = device_get_list(devs); 319 | 320 | // going to need a larger buffer for many devices 321 | int needed_buffer = count * (sizeof(struct usbmuxd_device_record) + sizeof(struct usbmuxd_header)) + REPLY_BUF_SIZE; 322 | if(client->ob_capacity < needed_buffer) { 323 | usbmuxd_log(LL_DEBUG, "Enlarging client %d reply buffer %d -> %d to make space for device notifications", client->fd, client->ob_capacity, needed_buffer); 324 | client->ob_buf = realloc(client->ob_buf, needed_buffer); 325 | client->ob_capacity = needed_buffer; 326 | } 327 | dev = devs; 328 | for(i=0; ifd, hdr->length, hdr->version, hdr->message, hdr->tag); 342 | 343 | if(client->state != CLIENT_COMMAND) { 344 | usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd); 345 | if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) 346 | return -1; 347 | client_close(client); 348 | return -1; 349 | } 350 | 351 | struct usbmuxd_connect_request *ch; 352 | #ifdef HAVE_PLIST 353 | char *payload; 354 | uint32_t payload_size; 355 | #endif 356 | 357 | switch(hdr->message) { 358 | #ifdef HAVE_PLIST 359 | case MESSAGE_PLIST: 360 | client->proto_version = 1; 361 | payload = (char*)(hdr) + sizeof(struct usbmuxd_header); 362 | payload_size = hdr->length - sizeof(struct usbmuxd_header); 363 | plist_t dict = NULL; 364 | plist_from_xml(payload, payload_size, &dict); 365 | if (!dict) { 366 | usbmuxd_log(LL_ERROR, "Could not parse plist from payload!"); 367 | return -1; 368 | } else { 369 | char *message = NULL; 370 | plist_t node = plist_dict_get_item(dict, "MessageType"); 371 | plist_get_string_val(node, &message); 372 | if (!message) { 373 | usbmuxd_log(LL_ERROR, "Could not extract MessageType from plist!"); 374 | plist_free(dict); 375 | return -1; 376 | } 377 | if (!strcmp(message, "Listen")) { 378 | free(message); 379 | plist_free(dict); 380 | if (send_result(client, hdr->tag, 0) < 0) 381 | return -1; 382 | usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd); 383 | return start_listen(client); 384 | } else if (!strcmp(message, "Connect")) { 385 | uint64_t val; 386 | uint16_t portnum = 0; 387 | uint32_t device_id = 0; 388 | free(message); 389 | // get device id 390 | node = plist_dict_get_item(dict, "DeviceID"); 391 | if (!node) { 392 | usbmuxd_log(LL_ERROR, "Received connect request without device_id!"); 393 | plist_free(dict); 394 | if (send_result(client, hdr->tag, RESULT_BADDEV) < 0) 395 | return -1; 396 | return 0; 397 | } 398 | val = 0; 399 | plist_get_uint_val(node, &val); 400 | device_id = (uint32_t)val; 401 | 402 | // get port number 403 | node = plist_dict_get_item(dict, "PortNumber"); 404 | if (!node) { 405 | usbmuxd_log(LL_ERROR, "Received connect request without port number!"); 406 | plist_free(dict); 407 | if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) 408 | return -1; 409 | return 0; 410 | } 411 | val = 0; 412 | plist_get_uint_val(node, &val); 413 | portnum = (uint16_t)val; 414 | 415 | usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, device_id, ntohs(portnum)); 416 | res = device_start_connect(device_id, ntohs(portnum), client); 417 | if(res < 0) { 418 | if (send_result(client, hdr->tag, -res) < 0) 419 | return -1; 420 | } else { 421 | client->connect_tag = hdr->tag; 422 | client->connect_device = device_id; 423 | client->state = CLIENT_CONNECTING1; 424 | } 425 | return 0; 426 | } else { 427 | usbmuxd_log(LL_ERROR, "Unexpected command '%s' received!", message); 428 | free(message); 429 | plist_free(dict); 430 | if (send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) 431 | return -1; 432 | return 0; 433 | } 434 | } 435 | // should not be reached?! 436 | return -1; 437 | #endif 438 | case MESSAGE_LISTEN: 439 | if(send_result(client, hdr->tag, 0) < 0) 440 | return -1; 441 | usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd); 442 | return start_listen(client); 443 | case MESSAGE_CONNECT: 444 | ch = (void*)hdr; 445 | usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port)); 446 | res = device_start_connect(ch->device_id, ntohs(ch->port), client); 447 | if(res < 0) { 448 | if(send_result(client, hdr->tag, -res) < 0) 449 | return -1; 450 | } else { 451 | client->connect_tag = hdr->tag; 452 | client->connect_device = ch->device_id; 453 | client->state = CLIENT_CONNECTING1; 454 | } 455 | return 0; 456 | default: 457 | usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message); 458 | if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0) 459 | return -1; 460 | return 0; 461 | } 462 | return -1; 463 | } 464 | 465 | static void process_send(struct mux_client *client) 466 | { 467 | int res; 468 | if(!client->ob_size) { 469 | usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd); 470 | client->events &= ~POLLOUT; 471 | return; 472 | } 473 | res = send(client->fd, client->ob_buf, client->ob_size, 0); 474 | if(res <= 0) { 475 | usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno)); 476 | client_close(client); 477 | return; 478 | } 479 | if(res == client->ob_size) { 480 | client->ob_size = 0; 481 | client->events &= ~POLLOUT; 482 | if(client->state == CLIENT_CONNECTING2) { 483 | usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd); 484 | client->state = CLIENT_CONNECTED; 485 | client->events = client->devents; 486 | // no longer need this 487 | free(client->ob_buf); 488 | client->ob_buf = NULL; 489 | } 490 | } else { 491 | client->ob_size -= res; 492 | memmove(client->ob_buf, client->ob_buf + res, client->ob_size); 493 | } 494 | } 495 | static void process_recv(struct mux_client *client) 496 | { 497 | int res; 498 | int did_read = 0; 499 | if(client->ib_size < sizeof(struct usbmuxd_header)) { 500 | res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct usbmuxd_header) - client->ib_size, 0); 501 | if(res <= 0) { 502 | if(res < 0) 503 | usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno)); 504 | else 505 | usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd); 506 | client_close(client); 507 | return; 508 | } 509 | client->ib_size += res; 510 | if(client->ib_size < sizeof(struct usbmuxd_header)) 511 | return; 512 | did_read = 1; 513 | } 514 | struct usbmuxd_header *hdr = (void*)client->ib_buf; 515 | #ifdef HAVE_PLIST 516 | if((hdr->version != 0) && (hdr->version != 1)) { 517 | usbmuxd_log(LL_INFO, "Client %d version mismatch: expected 0 or 1, got %d", client->fd, hdr->version); 518 | #else 519 | if(hdr->version != USBMUXD_PROTOCOL_VERSION) { 520 | usbmuxd_log(LL_INFO, "Client %d version mismatch: expected %d, got %d", client->fd, USBMUXD_PROTOCOL_VERSION, hdr->version); 521 | #endif 522 | client_close(client); 523 | return; 524 | } 525 | if(hdr->length > client->ib_capacity) { 526 | usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length); 527 | client_close(client); 528 | return; 529 | } 530 | if(hdr->length < sizeof(struct usbmuxd_header)) { 531 | usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length); 532 | client_close(client); 533 | return; 534 | } 535 | if(client->ib_size < hdr->length) { 536 | if(did_read) 537 | return; //maybe we would block, so defer to next loop 538 | res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0); 539 | if(res < 0) { 540 | usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno)); 541 | client_close(client); 542 | return; 543 | } else if(res == 0) { 544 | usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd); 545 | client_close(client); 546 | return; 547 | } 548 | client->ib_size += res; 549 | if(client->ib_size < hdr->length) 550 | return; 551 | } 552 | client_command(client, hdr); 553 | client->ib_size = 0; 554 | } 555 | 556 | void client_process(int fd, short events) 557 | { 558 | struct mux_client *client = NULL; 559 | FOREACH(struct mux_client *lc, &client_list) { 560 | if(lc->fd == fd) { 561 | client = lc; 562 | break; 563 | } 564 | } ENDFOREACH 565 | 566 | if(!client) { 567 | usbmuxd_log(LL_INFO, "client_process: fd %d not found in client list", fd); 568 | return; 569 | } 570 | 571 | if(client->state == CLIENT_CONNECTED) { 572 | usbmuxd_log(LL_SPEW, "client_process in CONNECTED state"); 573 | device_client_process(client->connect_device, client, events); 574 | } else { 575 | if(events & POLLIN) { 576 | process_recv(client); 577 | } else if(events & POLLOUT) { //not both in case client died as part of process_recv 578 | process_send(client); 579 | } 580 | } 581 | 582 | } 583 | 584 | void client_device_add(struct device_info *dev) 585 | { 586 | usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial); 587 | FOREACH(struct mux_client *client, &client_list) { 588 | if(client->state == CLIENT_LISTEN) 589 | notify_device_add(client, dev); 590 | } ENDFOREACH 591 | } 592 | void client_device_remove(int device_id) 593 | { 594 | uint32_t id = device_id; 595 | usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id); 596 | FOREACH(struct mux_client *client, &client_list) { 597 | if(client->state == CLIENT_LISTEN) 598 | notify_device_remove(client, id); 599 | } ENDFOREACH 600 | } 601 | 602 | 603 | void client_init(void) 604 | { 605 | usbmuxd_log(LL_DEBUG, "client_init"); 606 | collection_init(&client_list); 607 | } 608 | 609 | void client_shutdown(void) 610 | { 611 | usbmuxd_log(LL_DEBUG, "client_shutdown"); 612 | FOREACH(struct mux_client *client, &client_list) { 613 | client_close(client); 614 | } ENDFOREACH 615 | collection_free(&client_list); 616 | } 617 | -------------------------------------------------------------------------------- /daemon/client.h: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 or version 3. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | */ 21 | 22 | #ifndef __CLIENT_H__ 23 | #define __CLIENT_H__ 24 | 25 | #include 26 | #include "usbmuxd-proto.h" 27 | 28 | struct device_info; 29 | struct mux_client; 30 | 31 | int client_read(struct mux_client *client, void *buffer, uint32_t len); 32 | int client_write(struct mux_client *client, void *buffer, uint32_t len); 33 | int client_set_events(struct mux_client *client, short events); 34 | void client_close(struct mux_client *client); 35 | int client_notify_connect(struct mux_client *client, enum usbmuxd_result result); 36 | 37 | void client_device_add(struct device_info *dev); 38 | void client_device_remove(int device_id); 39 | 40 | int client_accept(int fd); 41 | void client_get_fds(struct fdlist *list); 42 | void client_process(int fd, short events); 43 | 44 | void client_init(void); 45 | void client_shutdown(void); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /daemon/device.c: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 or version 3. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | */ 20 | 21 | #define _BSD_SOURCE 22 | 23 | #ifdef HAVE_CONFIG_H 24 | #include 25 | #endif 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "device.h" 35 | #include "client.h" 36 | #include "usb.h" 37 | #include "log.h" 38 | 39 | int next_device_id; 40 | 41 | #define DEV_MRU 65536 42 | 43 | #define CONN_INBUF_SIZE 262144 44 | #define CONN_OUTBUF_SIZE 65536 45 | 46 | #define ACK_TIMEOUT 30 47 | 48 | enum mux_protocol { 49 | MUX_PROTO_VERSION = 0, 50 | MUX_PROTO_TCP = IPPROTO_TCP, 51 | }; 52 | 53 | enum mux_dev_state { 54 | MUXDEV_INIT, // sent version packet 55 | MUXDEV_ACTIVE, // received version packet, active 56 | MUXDEV_DEAD // dead 57 | }; 58 | 59 | enum mux_conn_state { 60 | CONN_CONNECTING, // SYN 61 | CONN_CONNECTED, // SYN/SYNACK/ACK -> active 62 | CONN_REFUSED, // RST received during SYN 63 | CONN_DYING, // RST received 64 | CONN_DEAD // being freed; used to prevent infinite recursion between client<->device freeing 65 | }; 66 | 67 | struct mux_header 68 | { 69 | uint32_t protocol; 70 | uint32_t length; 71 | }; 72 | 73 | struct version_header 74 | { 75 | uint32_t major; 76 | uint32_t minor; 77 | uint32_t padding; 78 | }; 79 | 80 | struct mux_device; 81 | 82 | #define CONN_ACK_PENDING 1 83 | 84 | struct mux_connection 85 | { 86 | struct mux_device *dev; 87 | struct mux_client *client; 88 | enum mux_conn_state state; 89 | uint16_t sport, dport; 90 | uint32_t tx_seq, tx_ack, tx_acked, tx_win; 91 | uint32_t rx_seq, rx_recvd, rx_ack, rx_win; 92 | uint32_t max_payload; 93 | uint32_t sendable; 94 | int flags; 95 | unsigned char *ib_buf; 96 | uint32_t ib_size; 97 | uint32_t ib_capacity; 98 | unsigned char *ob_buf; 99 | uint32_t ob_capacity; 100 | short events; 101 | uint64_t last_ack_time; 102 | }; 103 | 104 | struct mux_device 105 | { 106 | struct usb_device *usbdev; 107 | int id; 108 | enum mux_dev_state state; 109 | struct collection connections; 110 | uint16_t next_sport; 111 | unsigned char *pktbuf; 112 | uint32_t pktlen; 113 | }; 114 | 115 | static struct collection device_list; 116 | 117 | uint64_t mstime64(void) 118 | { 119 | struct timeval tv; 120 | gettimeofday(&tv, NULL); 121 | return tv.tv_sec * 1000 + tv.tv_usec / 1000; 122 | } 123 | 124 | static int get_next_device_id(void) 125 | { 126 | while(1) { 127 | int ok = 1; 128 | FOREACH(struct mux_device *dev, &device_list) { 129 | if(dev->id == next_device_id) { 130 | next_device_id++; 131 | ok = 0; 132 | break; 133 | } 134 | } ENDFOREACH 135 | if(ok) 136 | return next_device_id++; 137 | } 138 | } 139 | 140 | static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, const void *data, int length) 141 | { 142 | unsigned char *buffer; 143 | int hdrlen; 144 | int res; 145 | 146 | switch(proto) { 147 | case MUX_PROTO_VERSION: 148 | hdrlen = sizeof(struct version_header); 149 | break; 150 | case MUX_PROTO_TCP: 151 | hdrlen = sizeof(struct tcphdr); 152 | break; 153 | default: 154 | usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length); 155 | return -1; 156 | } 157 | usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length); 158 | 159 | int total = sizeof(struct mux_header) + hdrlen + length; 160 | 161 | if(total > USB_MTU) { 162 | usbmuxd_log(LL_ERROR, "Tried to send packet larger than USB MTU (hdr %d data %d total %d) to device %d", hdrlen, length, total, dev->id); 163 | return -1; 164 | } 165 | 166 | buffer = malloc(total); 167 | struct mux_header *mhdr = (struct mux_header *)buffer; 168 | mhdr->protocol = htonl(proto); 169 | mhdr->length = htonl(total); 170 | memcpy(buffer + sizeof(struct mux_header), header, hdrlen); 171 | if(data && length) 172 | memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length); 173 | 174 | if((res = usb_send(dev->usbdev, buffer, total)) < 0) { 175 | usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res); 176 | free(buffer); 177 | return res; 178 | } 179 | return total; 180 | } 181 | 182 | static uint16_t find_sport(struct mux_device *dev) 183 | { 184 | if(collection_count(&dev->connections) >= 65535) 185 | return 0; //insanity 186 | 187 | while(1) { 188 | int ok = 1; 189 | FOREACH(struct mux_connection *conn, &dev->connections) { 190 | if(dev->next_sport == conn->sport) { 191 | dev->next_sport++; 192 | ok = 0; 193 | break; 194 | } 195 | } ENDFOREACH 196 | if(ok) 197 | return dev->next_sport++; 198 | } 199 | } 200 | 201 | static int send_anon_rst(struct mux_device *dev, uint16_t sport, uint16_t dport, uint32_t ack) 202 | { 203 | struct tcphdr th; 204 | memset(&th, 0, sizeof(th)); 205 | th.th_sport = htons(sport); 206 | th.th_dport = htons(dport); 207 | th.th_ack = htonl(ack); 208 | th.th_flags = TH_RST; 209 | th.th_off = sizeof(th) / 4; 210 | 211 | usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d flags=0x%x", dev->id, sport, dport, th.th_flags); 212 | 213 | int res = send_packet(dev, MUX_PROTO_TCP, &th, NULL, 0); 214 | return res; 215 | } 216 | 217 | static int send_tcp(struct mux_connection *conn, uint8_t flags, const unsigned char *data, int length) 218 | { 219 | struct tcphdr th; 220 | memset(&th, 0, sizeof(th)); 221 | th.th_sport = htons(conn->sport); 222 | th.th_dport = htons(conn->dport); 223 | th.th_seq = htonl(conn->tx_seq); 224 | th.th_ack = htonl(conn->tx_ack); 225 | th.th_flags = flags; 226 | th.th_off = sizeof(th) / 4; 227 | th.th_win = htons(conn->tx_win >> 8); 228 | 229 | usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d", 230 | conn->dev->id, conn->sport, conn->dport, conn->tx_seq, conn->tx_ack, flags, conn->tx_win, conn->tx_win >> 8, length); 231 | 232 | int res = send_packet(conn->dev, MUX_PROTO_TCP, &th, data, length); 233 | if(res >= 0) { 234 | conn->tx_acked = conn->tx_ack; 235 | conn->last_ack_time = mstime64(); 236 | conn->flags &= ~CONN_ACK_PENDING; 237 | } 238 | return res; 239 | } 240 | 241 | static void connection_teardown(struct mux_connection *conn) 242 | { 243 | int res; 244 | if(conn->state == CONN_DEAD) 245 | return; 246 | usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport); 247 | if(conn->dev->state != MUXDEV_DEAD && conn->state != CONN_DYING && conn->state != CONN_REFUSED) { 248 | res = send_tcp(conn, TH_RST, NULL, 0); 249 | if(res < 0) 250 | usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, conn->sport, conn->dport); 251 | } 252 | if(conn->client) { 253 | if(conn->state == CONN_REFUSED || conn->state == CONN_CONNECTING) { 254 | client_notify_connect(conn->client, RESULT_CONNREFUSED); 255 | } else { 256 | conn->state = CONN_DEAD; 257 | client_close(conn->client); 258 | } 259 | } 260 | if(conn->ib_buf) 261 | free(conn->ib_buf); 262 | if(conn->ob_buf) 263 | free(conn->ob_buf); 264 | collection_remove(&conn->dev->connections, conn); 265 | free(conn); 266 | } 267 | 268 | int device_start_connect(int device_id, uint16_t dport, struct mux_client *client) 269 | { 270 | struct mux_device *dev = NULL; 271 | FOREACH(struct mux_device *cdev, &device_list) { 272 | if(cdev->id == device_id) { 273 | dev = cdev; 274 | break; 275 | } 276 | } ENDFOREACH 277 | if(!dev) { 278 | usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id); 279 | return -RESULT_BADDEV; 280 | } 281 | 282 | uint16_t sport = find_sport(dev); 283 | if(!sport) { 284 | usbmuxd_log(LL_WARNING, "Unable to allocate port for device %d", device_id); 285 | return -RESULT_BADDEV; 286 | } 287 | 288 | struct mux_connection *conn; 289 | conn = malloc(sizeof(struct mux_connection)); 290 | memset(conn, 0, sizeof(struct mux_connection)); 291 | 292 | conn->dev = dev; 293 | conn->client = client; 294 | conn->state = CONN_CONNECTING; 295 | conn->sport = sport; 296 | conn->dport = dport; 297 | conn->tx_seq = 0; 298 | conn->tx_ack = 0; 299 | conn->tx_acked = 0; 300 | conn->tx_win = 131072; 301 | conn->rx_recvd = 0; 302 | conn->flags = 0; 303 | conn->max_payload = USB_MTU - sizeof(struct mux_header) - sizeof(struct tcphdr); 304 | 305 | conn->ob_buf = malloc(CONN_OUTBUF_SIZE); 306 | conn->ob_capacity = CONN_OUTBUF_SIZE; 307 | conn->ib_buf = malloc(CONN_INBUF_SIZE); 308 | conn->ib_capacity = CONN_INBUF_SIZE; 309 | conn->ib_size = 0; 310 | 311 | int res; 312 | 313 | res = send_tcp(conn, TH_SYN, NULL, 0); 314 | if(res < 0) { 315 | usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport); 316 | free(conn); 317 | return -RESULT_CONNREFUSED; //bleh 318 | } 319 | collection_add(&dev->connections, conn); 320 | return 0; 321 | } 322 | 323 | static void update_connection(struct mux_connection *conn) 324 | { 325 | uint32_t sent = conn->tx_seq - conn->rx_ack; 326 | 327 | if(conn->rx_win > sent) 328 | conn->sendable = conn->rx_win - sent; 329 | else 330 | conn->sendable = 0; 331 | 332 | if(conn->sendable > conn->ob_capacity) 333 | conn->sendable = conn->ob_capacity; 334 | if(conn->sendable > conn->max_payload) 335 | conn->sendable = conn->max_payload; 336 | 337 | if(conn->sendable > 0) 338 | conn->events |= POLLIN; 339 | else 340 | conn->events &= ~POLLIN; 341 | 342 | if(conn->ib_size) 343 | conn->events |= POLLOUT; 344 | else 345 | conn->events &= ~POLLOUT; 346 | 347 | if(conn->tx_acked != conn->tx_ack) 348 | conn->flags |= CONN_ACK_PENDING; 349 | else 350 | conn->flags &= ~CONN_ACK_PENDING; 351 | 352 | usbmuxd_log(LL_SPEW, "update_connection: sendable %d, events %d, flags %d", conn->sendable, conn->events, conn->flags); 353 | client_set_events(conn->client, conn->events); 354 | } 355 | 356 | void device_client_process(int device_id, struct mux_client *client, short events) 357 | { 358 | struct mux_connection *conn = NULL; 359 | FOREACH(struct mux_device *dev, &device_list) { 360 | if(dev->id == device_id) { 361 | FOREACH(struct mux_connection *lconn, &dev->connections) { 362 | if(lconn->client == client) { 363 | conn = lconn; 364 | break; 365 | } 366 | } ENDFOREACH 367 | break; 368 | } 369 | } ENDFOREACH 370 | 371 | if(!conn) { 372 | usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client); 373 | return; 374 | } 375 | usbmuxd_log(LL_SPEW, "device_client_process (%d)", events); 376 | 377 | int res; 378 | int size; 379 | if(events & POLLOUT) { 380 | size = client_write(conn->client, conn->ib_buf, conn->ib_size); 381 | if(size <= 0) { 382 | usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size); 383 | connection_teardown(conn); 384 | return; 385 | } 386 | conn->tx_ack += size; 387 | if(size == conn->ib_size) { 388 | conn->ib_size = 0; 389 | } else { 390 | conn->ib_size -= size; 391 | memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size); 392 | } 393 | } 394 | if(events & POLLIN) { 395 | size = client_read(conn->client, conn->ob_buf, conn->sendable); 396 | if(size <= 0) { 397 | usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size); 398 | connection_teardown(conn); 399 | return; 400 | } 401 | res = send_tcp(conn, TH_ACK, conn->ob_buf, size); 402 | if(res < 0) { 403 | connection_teardown(conn); 404 | return; 405 | } 406 | conn->tx_seq += size; 407 | } 408 | 409 | update_connection(conn); 410 | } 411 | 412 | static void connection_device_input(struct mux_connection *conn, unsigned char *payload, uint32_t payload_length) 413 | { 414 | if((conn->ib_size + payload_length) > conn->ib_capacity) { 415 | usbmuxd_log(LL_ERROR, "Input buffer overflow on device %d connection %d->%d (space=%d, payload=%d)", conn->dev->id, conn->sport, conn->dport, conn->ib_capacity-conn->ib_size, payload_length); 416 | connection_teardown(conn); 417 | return; 418 | } 419 | memcpy(conn->ib_buf + conn->ib_size, payload, payload_length); 420 | conn->ib_size += payload_length; 421 | conn->rx_recvd += payload_length; 422 | update_connection(conn); 423 | } 424 | 425 | void device_abort_connect(int device_id, struct mux_client *client) 426 | { 427 | FOREACH(struct mux_device *dev, &device_list) { 428 | if(dev->id == device_id) { 429 | FOREACH(struct mux_connection *conn, &dev->connections) { 430 | if(conn->client == client) { 431 | connection_teardown(conn); 432 | return; 433 | } 434 | } ENDFOREACH 435 | usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id); 436 | return; 437 | } 438 | } ENDFOREACH 439 | usbmuxd_log(LL_WARNING, "Attempted to abort connection for nonexistent device %d", device_id); 440 | } 441 | 442 | static void device_version_input(struct mux_device *dev, struct version_header *vh) 443 | { 444 | if(dev->state != MUXDEV_INIT) { 445 | usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id); 446 | return; 447 | } 448 | vh->major = ntohl(vh->major); 449 | vh->minor = ntohl(vh->minor); 450 | if(vh->major != 1 || vh->minor != 0) { 451 | usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor); 452 | collection_remove(&device_list, dev); 453 | free(dev); 454 | return; 455 | } 456 | usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", vh->major, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev)); 457 | dev->state = MUXDEV_ACTIVE; 458 | collection_init(&dev->connections); 459 | struct device_info info; 460 | info.id = dev->id; 461 | info.location = usb_get_location(dev->usbdev); 462 | info.serial = usb_get_serial(dev->usbdev); 463 | info.pid = usb_get_pid(dev->usbdev); 464 | client_device_add(&info); 465 | } 466 | 467 | static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, uint32_t payload_length) 468 | { 469 | uint16_t sport = ntohs(th->th_dport); 470 | uint16_t dport = ntohs(th->th_sport); 471 | struct mux_connection *conn = NULL; 472 | 473 | usbmuxd_log(LL_DEBUG, "[IN] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d", 474 | dev->id, dport, sport, ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length); 475 | 476 | if(dev->state != MUXDEV_ACTIVE) { 477 | usbmuxd_log(LL_ERROR, "Received TCP packet from device %d but the device isn't active yet, discarding\n", dev->id); 478 | return; 479 | } 480 | 481 | FOREACH(struct mux_connection *lconn, &dev->connections) { 482 | if(lconn->sport == sport && lconn->dport == dport) { 483 | conn = lconn; 484 | break; 485 | } 486 | } ENDFOREACH 487 | 488 | if(!conn) { 489 | usbmuxd_log(LL_INFO, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport); 490 | if(!(th->th_flags & TH_RST)) { 491 | if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0) 492 | usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport); 493 | } 494 | return; 495 | } 496 | 497 | conn->rx_seq = ntohl(th->th_seq); 498 | conn->rx_ack = ntohl(th->th_ack); 499 | conn->rx_win = ntohs(th->th_win) << 8; 500 | 501 | if(th->th_flags & TH_RST) { 502 | char *buf = malloc(payload_length+1); 503 | memcpy(buf, payload, payload_length); 504 | if(payload_length && (buf[payload_length-1] == '\n')) 505 | buf[payload_length-1] = 0; 506 | buf[payload_length] = 0; 507 | usbmuxd_log(LL_DEBUG, "RST reason: %s", buf); 508 | free(buf); 509 | } 510 | 511 | if(conn->state == CONN_CONNECTING) { 512 | if(th->th_flags != (TH_SYN|TH_ACK)) { 513 | if(th->th_flags & TH_RST) 514 | conn->state = CONN_REFUSED; 515 | usbmuxd_log(LL_INFO, "Connection refused by device %d (%d->%d)", dev->id, sport, dport); 516 | connection_teardown(conn); //this also sends the notification to the client 517 | } else { 518 | conn->tx_seq++; 519 | conn->tx_ack++; 520 | conn->rx_recvd = conn->rx_seq; 521 | if(send_tcp(conn, TH_ACK, NULL, 0) < 0) { 522 | usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, sport, dport); 523 | connection_teardown(conn); 524 | return; 525 | } 526 | conn->state = CONN_CONNECTED; 527 | if(client_notify_connect(conn->client, RESULT_OK) < 0) { 528 | conn->client = NULL; 529 | connection_teardown(conn); 530 | } 531 | update_connection(conn); 532 | } 533 | } else if(conn->state == CONN_CONNECTED) { 534 | if(th->th_flags != TH_ACK) { 535 | usbmuxd_log(LL_INFO, "Connection reset by device %d (%d->%d)", dev->id, sport, dport); 536 | if(th->th_flags & TH_RST) 537 | conn->state = CONN_DYING; 538 | connection_teardown(conn); 539 | } else { 540 | connection_device_input(conn, payload, payload_length); 541 | } 542 | } 543 | } 544 | 545 | void device_data_input(struct usb_device *usbdev, unsigned char *buffer, uint32_t length) 546 | { 547 | struct mux_device *dev = NULL; 548 | FOREACH(struct mux_device *tdev, &device_list) { 549 | if(tdev->usbdev == usbdev) { 550 | dev = tdev; 551 | break; 552 | } 553 | } ENDFOREACH 554 | if(!dev) { 555 | usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); 556 | return; 557 | } 558 | 559 | if(!length) 560 | return; 561 | 562 | // sanity check (should never happen with current USB implementation) 563 | if((length > USB_MRU) || (length > DEV_MRU)) { 564 | usbmuxd_log(LL_ERROR, "Too much data received from USB (%d), file a bug", length); 565 | return; 566 | } 567 | 568 | usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length); 569 | 570 | // handle broken up transfers 571 | if(dev->pktlen) { 572 | if((length + dev->pktlen) > DEV_MRU) { 573 | usbmuxd_log(LL_ERROR, "Incoming split packet is too large (%d so far), dropping!", length + dev->pktlen); 574 | dev->pktlen = 0; 575 | return; 576 | } 577 | memcpy(dev->pktbuf + dev->pktlen, buffer, length); 578 | struct mux_header *mhdr = (struct mux_header *)dev->pktbuf; 579 | if((length < USB_MRU) || (ntohl(mhdr->length) == (length + dev->pktlen))) { 580 | buffer = dev->pktbuf; 581 | length += dev->pktlen; 582 | dev->pktlen = 0; 583 | usbmuxd_log(LL_SPEW, "Gathered mux data from buffer (total size: %d)", length); 584 | } else { 585 | dev->pktlen += length; 586 | usbmuxd_log(LL_SPEW, "Appended mux data to buffer (total size: %d)", dev->pktlen); 587 | return; 588 | } 589 | } else { 590 | struct mux_header *mhdr = (struct mux_header *)buffer; 591 | if((length == USB_MRU) && (length < ntohl(mhdr->length))) { 592 | memcpy(dev->pktbuf, buffer, length); 593 | dev->pktlen = length; 594 | usbmuxd_log(LL_SPEW, "Copied mux data to buffer (size: %d)", dev->pktlen); 595 | return; 596 | } 597 | } 598 | 599 | struct mux_header *mhdr = (struct mux_header *)buffer; 600 | 601 | if(ntohl(mhdr->length) != length) { 602 | usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length); 603 | return; 604 | } 605 | 606 | struct tcphdr *th; 607 | unsigned char *payload; 608 | uint32_t payload_length; 609 | 610 | switch(ntohl(mhdr->protocol)) { 611 | case MUX_PROTO_VERSION: 612 | if(length < (sizeof(struct mux_header) + sizeof(struct version_header))) { 613 | usbmuxd_log(LL_ERROR, "Incoming version packet is too small (%d)", length); 614 | return; 615 | } 616 | device_version_input(dev, (struct version_header *)(mhdr+1)); 617 | break; 618 | case MUX_PROTO_TCP: 619 | if(length < (sizeof(struct mux_header) + sizeof(struct tcphdr))) { 620 | usbmuxd_log(LL_ERROR, "Incoming TCP packet is too small (%d)", length); 621 | return; 622 | } 623 | th = (struct tcphdr *)(mhdr+1); 624 | payload = (unsigned char *)(th+1); 625 | payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header); 626 | device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length); 627 | break; 628 | default: 629 | usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol)); 630 | break; 631 | } 632 | 633 | } 634 | 635 | int device_add(struct usb_device *usbdev) 636 | { 637 | int res; 638 | int id = get_next_device_id(); 639 | struct mux_device *dev; 640 | usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(usbdev), id); 641 | dev = malloc(sizeof(struct mux_device)); 642 | dev->id = id; 643 | dev->usbdev = usbdev; 644 | dev->state = MUXDEV_INIT; 645 | dev->next_sport = 1; 646 | dev->pktbuf = malloc(DEV_MRU); 647 | dev->pktlen = 0; 648 | struct version_header vh; 649 | vh.major = htonl(1); 650 | vh.minor = htonl(0); 651 | vh.padding = 0; 652 | if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) { 653 | usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d", id); 654 | free(dev); 655 | return res; 656 | } 657 | collection_add(&device_list, dev); 658 | return 0; 659 | } 660 | 661 | void device_remove(struct usb_device *usbdev) 662 | { 663 | FOREACH(struct mux_device *dev, &device_list) { 664 | if(dev->usbdev == usbdev) { 665 | usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev)); 666 | if(dev->state == MUXDEV_ACTIVE) { 667 | dev->state = MUXDEV_DEAD; 668 | FOREACH(struct mux_connection *conn, &dev->connections) { 669 | connection_teardown(conn); 670 | } ENDFOREACH 671 | client_device_remove(dev->id); 672 | collection_free(&dev->connections); 673 | } 674 | collection_remove(&device_list, dev); 675 | free(dev->pktbuf); 676 | free(dev); 677 | return; 678 | } 679 | } ENDFOREACH 680 | usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev)); 681 | } 682 | 683 | int device_get_count(void) 684 | { 685 | int count = 0; 686 | FOREACH(struct mux_device *dev, &device_list) { 687 | if(dev->state == MUXDEV_ACTIVE) 688 | count++; 689 | } ENDFOREACH 690 | return count; 691 | } 692 | 693 | int device_get_list(struct device_info *p) 694 | { 695 | int count = 0; 696 | FOREACH(struct mux_device *dev, &device_list) { 697 | if(dev->state == MUXDEV_ACTIVE) { 698 | p->id = dev->id; 699 | p->serial = usb_get_serial(dev->usbdev); 700 | p->location = usb_get_location(dev->usbdev); 701 | p->pid = usb_get_pid(dev->usbdev); 702 | count++; 703 | p++; 704 | } 705 | } ENDFOREACH 706 | return count; 707 | } 708 | 709 | int device_get_timeout(void) 710 | { 711 | uint64_t oldest = (uint64_t)-1; 712 | FOREACH(struct mux_device *dev, &device_list) { 713 | if(dev->state == MUXDEV_ACTIVE) { 714 | FOREACH(struct mux_connection *conn, &dev->connections) { 715 | if((conn->state == CONN_CONNECTED) && (conn->flags & CONN_ACK_PENDING) && conn->last_ack_time < oldest) 716 | oldest = conn->last_ack_time; 717 | } ENDFOREACH 718 | } 719 | } ENDFOREACH 720 | uint64_t ct = mstime64(); 721 | if(oldest == -1) 722 | return 100000; //meh 723 | if((ct - oldest) > ACK_TIMEOUT) 724 | return 0; 725 | return ACK_TIMEOUT - (ct - oldest); 726 | } 727 | 728 | void device_check_timeouts(void) 729 | { 730 | uint64_t ct = mstime64(); 731 | FOREACH(struct mux_device *dev, &device_list) { 732 | if(dev->state == MUXDEV_ACTIVE) { 733 | FOREACH(struct mux_connection *conn, &dev->connections) { 734 | if((conn->state == CONN_CONNECTED) && 735 | (conn->flags & CONN_ACK_PENDING) && 736 | (ct - conn->last_ack_time) > ACK_TIMEOUT) { 737 | usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct); 738 | if(send_tcp(conn, TH_ACK, NULL, 0) < 0) { 739 | usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, conn->sport, conn->dport); 740 | connection_teardown(conn); 741 | } 742 | } 743 | } ENDFOREACH 744 | } 745 | } ENDFOREACH 746 | } 747 | 748 | void device_init(void) 749 | { 750 | usbmuxd_log(LL_DEBUG, "device_init"); 751 | collection_init(&device_list); 752 | next_device_id = 1; 753 | } 754 | 755 | void device_kill_connections(void) 756 | { 757 | usbmuxd_log(LL_DEBUG, "device_kill_connections"); 758 | FOREACH(struct mux_device *dev, &device_list) { 759 | if(dev->state != MUXDEV_INIT) { 760 | FOREACH(struct mux_connection *conn, &dev->connections) { 761 | connection_teardown(conn); 762 | } ENDFOREACH 763 | } 764 | } ENDFOREACH 765 | // give USB a while to send the final connection RSTs and the like 766 | usb_process_timeout(100); 767 | } 768 | 769 | void device_shutdown(void) 770 | { 771 | usbmuxd_log(LL_DEBUG, "device_shutdown"); 772 | FOREACH(struct mux_device *dev, &device_list) { 773 | FOREACH(struct mux_connection *conn, &dev->connections) { 774 | connection_teardown(conn); 775 | } ENDFOREACH 776 | collection_free(&dev->connections); 777 | collection_remove(&device_list, dev); 778 | free(dev); 779 | } ENDFOREACH 780 | collection_free(&device_list); 781 | } 782 | -------------------------------------------------------------------------------- /daemon/device.h: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 or version 3. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | */ 20 | 21 | #ifndef __DEVICE_H__ 22 | #define __DEVICE_H__ 23 | 24 | #include "usb.h" 25 | #include "client.h" 26 | 27 | struct device_info { 28 | int id; 29 | const char *serial; 30 | uint32_t location; 31 | uint16_t pid; 32 | }; 33 | 34 | void device_data_input(struct usb_device *dev, unsigned char *buf, uint32_t length); 35 | 36 | int device_add(struct usb_device *dev); 37 | void device_remove(struct usb_device *dev); 38 | 39 | int device_start_connect(int device_id, uint16_t port, struct mux_client *client); 40 | void device_client_process(int device_id, struct mux_client *client, short events); 41 | void device_abort_connect(int device_id, struct mux_client *client); 42 | 43 | int device_get_count(void); 44 | int device_get_list(struct device_info *p); 45 | 46 | int device_get_timeout(void); 47 | void device_check_timeouts(void); 48 | 49 | void device_init(void); 50 | void device_kill_connections(void); 51 | void device_shutdown(void); 52 | #endif 53 | -------------------------------------------------------------------------------- /daemon/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 or version 3. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "log.h" 35 | 36 | int log_level = LL_WARNING; 37 | 38 | int log_syslog = 0; 39 | 40 | void log_enable_syslog() 41 | { 42 | if (!log_syslog) { 43 | openlog("usbmuxd", LOG_PID, 0); 44 | log_syslog = 1; 45 | } 46 | } 47 | 48 | void log_disable_syslog() 49 | { 50 | if (log_syslog) { 51 | closelog(); 52 | } 53 | } 54 | 55 | static int level_to_syslog_level(int level) 56 | { 57 | int result = level + LOG_CRIT; 58 | if (result > LOG_DEBUG) { 59 | result = LOG_DEBUG; 60 | } 61 | return result; 62 | } 63 | 64 | void usbmuxd_log(enum loglevel level, const char *fmt, ...) 65 | { 66 | va_list ap; 67 | char *fs; 68 | struct timeval ts; 69 | struct tm *tp; 70 | 71 | if(level > log_level) 72 | return; 73 | 74 | gettimeofday(&ts, NULL); 75 | tp = localtime(&ts.tv_sec); 76 | 77 | fs = malloc(20 + strlen(fmt)); 78 | 79 | if(log_syslog) { 80 | sprintf(fs, "[%d] %s\n", level, fmt); 81 | } else { 82 | strftime(fs, 10, "[%H:%M:%S", tp); 83 | sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt); 84 | } 85 | 86 | va_start(ap, fmt); 87 | if (log_syslog) { 88 | vsyslog(level_to_syslog_level(level), fs, ap); 89 | } else { 90 | vfprintf(stderr, fs, ap); 91 | } 92 | va_end(ap); 93 | 94 | free(fs); 95 | } 96 | -------------------------------------------------------------------------------- /daemon/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 or version 3. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | 20 | */ 21 | 22 | #ifndef __LOG_H__ 23 | #define __LOG_H__ 24 | 25 | enum loglevel { 26 | LL_FATAL = 0, 27 | LL_ERROR, 28 | LL_WARNING, 29 | LL_NOTICE, 30 | LL_INFO, 31 | LL_DEBUG, 32 | LL_SPEW, 33 | LL_FLOOD, 34 | }; 35 | 36 | extern int log_level; 37 | 38 | void log_enable_syslog(); 39 | void log_disable_syslog(); 40 | 41 | void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); 42 | 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /daemon/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | Copyright (C) 2009 Paul Sladen 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 or version 3. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | */ 22 | 23 | #define _BSD_SOURCE 24 | #define _GNU_SOURCE 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include "log.h" 46 | #include "usb.h" 47 | #include "device.h" 48 | #include "client.h" 49 | 50 | static const char *socket_path = "/var/run/usbmuxd"; 51 | static const char *lockfile = "/var/run/usbmuxd.pid"; 52 | 53 | int should_exit; 54 | int should_discover; 55 | 56 | static int verbose = 0; 57 | static int foreground = 0; 58 | static int drop_privileges = 0; 59 | static const char *drop_user = NULL; 60 | static int opt_udev = 0; 61 | static int opt_exit = 0; 62 | static int exit_signal = 0; 63 | static int daemon_pipe; 64 | 65 | static int report_to_parent = 0; 66 | 67 | int create_socket(void) { 68 | struct sockaddr_un bind_addr; 69 | int listenfd; 70 | 71 | if(unlink(socket_path) == -1 && errno != ENOENT) { 72 | usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno)); 73 | return -1; 74 | } 75 | 76 | listenfd = socket(AF_UNIX, SOCK_STREAM, 0); 77 | if (listenfd == -1) { 78 | usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno)); 79 | return -1; 80 | } 81 | 82 | bzero(&bind_addr, sizeof(bind_addr)); 83 | bind_addr.sun_family = AF_UNIX; 84 | strcpy(bind_addr.sun_path, socket_path); 85 | if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) { 86 | usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno)); 87 | return -1; 88 | } 89 | 90 | // Start listening 91 | if (listen(listenfd, 5) != 0) { 92 | usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno)); 93 | return -1; 94 | } 95 | 96 | chmod(socket_path, 0666); 97 | 98 | return listenfd; 99 | } 100 | 101 | void handle_signal(int sig) 102 | { 103 | if (sig != SIGUSR1 && sig != SIGUSR2) { 104 | usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig); 105 | should_exit = 1; 106 | } else { 107 | if(opt_udev) { 108 | if (sig == SIGUSR1) { 109 | usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)..."); 110 | if (device_get_count() > 0) { 111 | // we can't quit, there are still devices attached. 112 | usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit."); 113 | } else { 114 | // it's safe to quit 115 | should_exit = 1; 116 | } 117 | } else if (sig == SIGUSR2) { 118 | usbmuxd_log(LL_INFO, "Caught SIGUSR2, scheduling device discovery"); 119 | should_discover = 1; 120 | } 121 | } else { 122 | usbmuxd_log(LL_INFO, "Caught SIGUSR1/2 but we weren't started in --udev mode, ignoring"); 123 | } 124 | } 125 | } 126 | 127 | void set_signal_handlers(void) 128 | { 129 | struct sigaction sa; 130 | sigset_t set; 131 | 132 | // Mask all signals we handle. They will be unmasked by ppoll(). 133 | sigemptyset(&set); 134 | sigaddset(&set, SIGINT); 135 | sigaddset(&set, SIGQUIT); 136 | sigaddset(&set, SIGTERM); 137 | sigaddset(&set, SIGUSR1); 138 | sigaddset(&set, SIGUSR2); 139 | sigprocmask(SIG_SETMASK, &set, NULL); 140 | 141 | memset(&sa, 0, sizeof(struct sigaction)); 142 | sa.sa_handler = handle_signal; 143 | sigaction(SIGINT, &sa, NULL); 144 | sigaction(SIGQUIT, &sa, NULL); 145 | sigaction(SIGTERM, &sa, NULL); 146 | sigaction(SIGUSR1, &sa, NULL); 147 | sigaction(SIGUSR2, &sa, NULL); 148 | } 149 | 150 | #if defined(__FreeBSD__) || defined(__APPLE__) 151 | static int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask) 152 | { 153 | int ready; 154 | sigset_t origmask; 155 | int to = timeout->tv_sec*1000 + timeout->tv_nsec/1000000; 156 | 157 | sigprocmask(SIG_SETMASK, sigmask, &origmask); 158 | ready = poll(fds, nfds, to); 159 | sigprocmask(SIG_SETMASK, &origmask, NULL); 160 | 161 | return ready; 162 | } 163 | #endif 164 | 165 | int main_loop(int listenfd) 166 | { 167 | int to, cnt, i, dto; 168 | struct fdlist pollfds; 169 | struct timespec tspec; 170 | 171 | sigset_t empty_sigset; 172 | sigemptyset(&empty_sigset); // unmask all signals 173 | 174 | fdlist_create(&pollfds); 175 | while(!should_exit) { 176 | usbmuxd_log(LL_FLOOD, "main_loop iteration"); 177 | to = usb_get_timeout(); 178 | usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to); 179 | dto = device_get_timeout(); 180 | usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to); 181 | if(dto < to) 182 | to = dto; 183 | 184 | fdlist_reset(&pollfds); 185 | fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN); 186 | usb_get_fds(&pollfds); 187 | client_get_fds(&pollfds); 188 | usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count); 189 | 190 | tspec.tv_sec = to / 1000; 191 | tspec.tv_nsec = (to % 1000) * 1000000; 192 | cnt = ppoll(pollfds.fds, pollfds.count, &tspec, &empty_sigset); 193 | usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt); 194 | if(cnt == -1) { 195 | if(errno == EINTR) { 196 | if(should_exit) { 197 | usbmuxd_log(LL_INFO, "Event processing interrupted"); 198 | break; 199 | } 200 | if(should_discover) { 201 | should_discover = 0; 202 | usbmuxd_log(LL_INFO, "Device discovery triggered by udev"); 203 | usb_discover(); 204 | } 205 | } 206 | } else if(cnt == 0) { 207 | if(usb_process() < 0) { 208 | usbmuxd_log(LL_FATAL, "usb_process() failed"); 209 | fdlist_free(&pollfds); 210 | return -1; 211 | } 212 | device_check_timeouts(); 213 | } else { 214 | int done_usb = 0; 215 | for(i=0; i 0) { 269 | // exit parent process 270 | int status; 271 | close(pfd[1]); 272 | 273 | if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) { 274 | fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n"); 275 | exit(1); 276 | } 277 | if(status != 0) 278 | fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status); 279 | exit(status); 280 | } 281 | // At this point we are executing as the child process 282 | // but we need to do one more fork 283 | 284 | daemon_pipe = pfd[1]; 285 | close(pfd[0]); 286 | report_to_parent = 1; 287 | 288 | // Change the file mode mask 289 | umask(0); 290 | 291 | // Create a new SID for the child process 292 | sid = setsid(); 293 | if (sid < 0) { 294 | usbmuxd_log(LL_FATAL, "setsid() failed."); 295 | return -1; 296 | } 297 | 298 | pid = fork(); 299 | if (pid < 0) { 300 | usbmuxd_log(LL_FATAL, "fork() failed (second)."); 301 | return pid; 302 | } 303 | 304 | if (pid > 0) { 305 | // exit parent process 306 | close(daemon_pipe); 307 | exit(0); 308 | } 309 | 310 | // Change the current working directory. 311 | if ((chdir("/")) < 0) { 312 | usbmuxd_log(LL_FATAL, "chdir() failed"); 313 | return -2; 314 | } 315 | // Redirect standard files to /dev/null 316 | if (!freopen("/dev/null", "r", stdin)) { 317 | usbmuxd_log(LL_FATAL, "Redirection of stdin failed."); 318 | return -3; 319 | } 320 | if (!freopen("/dev/null", "w", stdout)) { 321 | usbmuxd_log(LL_FATAL, "Redirection of stdout failed."); 322 | return -3; 323 | } 324 | 325 | return 0; 326 | } 327 | 328 | static int notify_parent(int status) 329 | { 330 | int res; 331 | 332 | report_to_parent = 0; 333 | if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) { 334 | usbmuxd_log(LL_FATAL, "Could not notify parent!"); 335 | if(res >= 0) 336 | return -2; 337 | else 338 | return res; 339 | } 340 | close(daemon_pipe); 341 | if (!freopen("/dev/null", "w", stderr)) { 342 | usbmuxd_log(LL_FATAL, "Redirection of stderr failed."); 343 | return -1; 344 | } 345 | return 0; 346 | } 347 | 348 | static void usage() 349 | { 350 | printf("usage: usbmuxd [options]\n"); 351 | printf("\t-h|--help Print this message.\n"); 352 | printf("\t-v|--verbose Be verbose (use twice or more to increase).\n"); 353 | printf("\t-f|--foreground Do not daemonize (implies one -v).\n"); 354 | printf("\t-U|--user USER Change to this user after startup (needs usb privileges).\n"); 355 | printf("\t-u|--udev Run in udev operation mode.\n"); 356 | printf("\t-x|--exit Tell a running instance to exit if there are no devices\n"); 357 | printf("\t connected (must be in udev mode).\n"); 358 | printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n"); 359 | printf("\t devices connected (always works).\n"); 360 | printf("\n"); 361 | } 362 | 363 | static void parse_opts(int argc, char **argv) 364 | { 365 | static struct option longopts[] = { 366 | {"help", 0, NULL, 'h'}, 367 | {"foreground", 0, NULL, 'f'}, 368 | {"verbose", 0, NULL, 'v'}, 369 | {"user", 2, NULL, 'U'}, 370 | {"udev", 0, NULL, 'u'}, 371 | {"exit", 0, NULL, 'x'}, 372 | {"force-exit", 0, NULL, 'X'}, 373 | {NULL, 0, NULL, 0} 374 | }; 375 | int c; 376 | 377 | while (1) { 378 | c = getopt_long(argc, argv, "hfvuU:xX", longopts, (int *) 0); 379 | if (c == -1) { 380 | break; 381 | } 382 | 383 | switch (c) { 384 | case 'h': 385 | usage(); 386 | exit(0); 387 | case 'f': 388 | foreground = 1; 389 | break; 390 | case 'v': 391 | ++verbose; 392 | break; 393 | case 'U': 394 | drop_privileges = 1; 395 | drop_user = optarg; 396 | break; 397 | case 'u': 398 | opt_udev = 1; 399 | break; 400 | case 'x': 401 | opt_exit = 1; 402 | exit_signal = SIGUSR1; 403 | break; 404 | case 'X': 405 | opt_exit = 1; 406 | exit_signal = SIGTERM; 407 | break; 408 | default: 409 | usage(); 410 | exit(2); 411 | } 412 | } 413 | } 414 | 415 | int main(int argc, char *argv[]) 416 | { 417 | int listenfd; 418 | int res = 0; 419 | int lfd; 420 | struct flock lock; 421 | char pids[10]; 422 | 423 | parse_opts(argc, argv); 424 | 425 | argc -= optind; 426 | argv += optind; 427 | 428 | if (!foreground) { 429 | verbose += LL_WARNING; 430 | log_enable_syslog(); 431 | } else { 432 | verbose += LL_NOTICE; 433 | } 434 | 435 | /* set log level to specified verbosity */ 436 | log_level = verbose; 437 | 438 | usbmuxd_log(LL_NOTICE, "usbmuxd v%s starting up", USBMUXD_VERSION); 439 | should_exit = 0; 440 | should_discover = 0; 441 | 442 | set_signal_handlers(); 443 | signal(SIGPIPE, SIG_IGN); 444 | 445 | res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644); 446 | if(res == -1) { 447 | usbmuxd_log(LL_FATAL, "Could not open lockfile"); 448 | goto terminate; 449 | } 450 | lock.l_type = F_WRLCK; 451 | lock.l_whence = SEEK_SET; 452 | lock.l_start = 0; 453 | lock.l_len = 0; 454 | fcntl(lfd, F_GETLK, &lock); 455 | close(lfd); 456 | if (lock.l_type != F_UNLCK) { 457 | if (opt_exit) { 458 | if (lock.l_pid && !kill(lock.l_pid, 0)) { 459 | usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid); 460 | res = 0; 461 | if (kill(lock.l_pid, exit_signal) < 0) { 462 | usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid); 463 | res = -1; 464 | } 465 | goto terminate; 466 | } else { 467 | usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!"); 468 | res = -1; 469 | goto terminate; 470 | } 471 | } else { 472 | if (!opt_udev) { 473 | usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid); 474 | res = -1; 475 | } else { 476 | usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). Telling it to check for devices.", lock.l_pid); 477 | if (lock.l_pid && !kill(lock.l_pid, 0)) { 478 | usbmuxd_log(LL_NOTICE, "Sending signal SIGUSR2 to instance with pid %d", lock.l_pid); 479 | res = 0; 480 | if (kill(lock.l_pid, SIGUSR2) < 0) { 481 | usbmuxd_log(LL_FATAL, "Could not deliver SIGUSR2 to pid %d", lock.l_pid); 482 | res = -1; 483 | } 484 | } else { 485 | usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!"); 486 | res = -1; 487 | } 488 | } 489 | goto terminate; 490 | } 491 | } 492 | unlink(lockfile); 493 | 494 | if (opt_exit) { 495 | usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting."); 496 | goto terminate; 497 | } 498 | 499 | if (!foreground) { 500 | if ((res = daemonize()) < 0) { 501 | fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n"); 502 | usbmuxd_log(LL_FATAL, "Could not daemonize!"); 503 | goto terminate; 504 | } 505 | } 506 | 507 | // now open the lockfile and place the lock 508 | res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); 509 | if(res < 0) { 510 | usbmuxd_log(LL_FATAL, "Could not open lockfile"); 511 | goto terminate; 512 | } 513 | lock.l_type = F_WRLCK; 514 | lock.l_whence = SEEK_SET; 515 | lock.l_start = 0; 516 | lock.l_len = 0; 517 | if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) { 518 | usbmuxd_log(LL_FATAL, "Lockfile locking failed!"); 519 | goto terminate; 520 | } 521 | sprintf(pids, "%d", getpid()); 522 | if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) { 523 | usbmuxd_log(LL_FATAL, "Could not write pidfile!"); 524 | if(res >= 0) 525 | res = -2; 526 | goto terminate; 527 | } 528 | 529 | usbmuxd_log(LL_INFO, "Creating socket"); 530 | res = listenfd = create_socket(); 531 | if(listenfd < 0) 532 | goto terminate; 533 | 534 | // drop elevated privileges 535 | if (drop_privileges && (getuid() == 0 || geteuid() == 0)) { 536 | struct passwd *pw; 537 | if (!drop_user) { 538 | usbmuxd_log(LL_FATAL, "No user to drop privileges to?"); 539 | res = -1; 540 | goto terminate; 541 | } 542 | pw = getpwnam(drop_user); 543 | if (!pw) { 544 | usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user); 545 | res = -1; 546 | goto terminate; 547 | } 548 | if (pw->pw_uid == 0) { 549 | usbmuxd_log(LL_INFO, "Not dropping privileges to root"); 550 | } else { 551 | if ((res = initgroups(drop_user, pw->pw_gid)) < 0) { 552 | usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)"); 553 | goto terminate; 554 | } 555 | if ((res = setgid(pw->pw_gid)) < 0) { 556 | usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid); 557 | goto terminate; 558 | } 559 | if ((res = setuid(pw->pw_uid)) < 0) { 560 | usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid); 561 | goto terminate; 562 | } 563 | 564 | // security check 565 | if (setuid(0) != -1) { 566 | usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!"); 567 | res = -1; 568 | goto terminate; 569 | } 570 | if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) { 571 | usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!"); 572 | res = -1; 573 | goto terminate; 574 | } 575 | usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user); 576 | } 577 | } 578 | 579 | client_init(); 580 | device_init(); 581 | usbmuxd_log(LL_INFO, "Initializing USB"); 582 | if((res = usb_init()) < 0) 583 | goto terminate; 584 | 585 | usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s"); 586 | 587 | usbmuxd_log(LL_NOTICE, "Initialization complete"); 588 | 589 | if (report_to_parent) 590 | if((res = notify_parent(0)) < 0) 591 | goto terminate; 592 | 593 | if(opt_udev) 594 | usb_autodiscover(0); // discovery triggered by udev 595 | 596 | res = main_loop(listenfd); 597 | if(res < 0) 598 | usbmuxd_log(LL_FATAL, "main_loop failed"); 599 | 600 | usbmuxd_log(LL_NOTICE, "usbmuxd shutting down"); 601 | device_kill_connections(); 602 | usb_shutdown(); 603 | device_shutdown(); 604 | client_shutdown(); 605 | usbmuxd_log(LL_NOTICE, "Shutdown complete"); 606 | 607 | terminate: 608 | log_disable_syslog(); 609 | 610 | if (res < 0) 611 | res = -res; 612 | else 613 | res = 0; 614 | if (report_to_parent) 615 | notify_parent(res); 616 | 617 | return res; 618 | } 619 | -------------------------------------------------------------------------------- /daemon/usb-linux.c: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | Copyright (C) 2009 Martin Szulecki 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 or version 3. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | */ 22 | 23 | #ifdef HAVE_CONFIG_H 24 | #include 25 | #endif 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | #include "usb.h" 35 | #include "log.h" 36 | #include "device.h" 37 | 38 | // interval for device connection/disconnection polling, in milliseconds 39 | // we need this because there is currently no asynchronous device discovery mechanism in libusb 40 | #define DEVICE_POLL_TIME 1000 41 | 42 | struct usb_device { 43 | libusb_device_handle *dev; 44 | uint8_t bus, address; 45 | uint16_t vid, pid; 46 | char serial[256]; 47 | int alive; 48 | uint8_t interface, ep_in, ep_out; 49 | struct libusb_transfer *rx_xfer; 50 | struct collection tx_xfers; 51 | int wMaxPacketSize; 52 | }; 53 | 54 | static struct collection device_list; 55 | 56 | static struct timeval next_dev_poll_time; 57 | 58 | static int devlist_failures; 59 | static int device_polling; 60 | 61 | static void usb_disconnect(struct usb_device *dev) 62 | { 63 | if(!dev->dev) { 64 | return; 65 | } 66 | 67 | // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device 68 | if(dev->rx_xfer) { 69 | usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer"); 70 | libusb_cancel_transfer(dev->rx_xfer); 71 | } 72 | FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) { 73 | usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer); 74 | libusb_cancel_transfer(xfer); 75 | } ENDFOREACH 76 | 77 | while(dev->rx_xfer || collection_count(&dev->tx_xfers)) { 78 | struct timeval tv; 79 | int res; 80 | 81 | tv.tv_sec = 0; 82 | tv.tv_usec = 1000; 83 | if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) { 84 | usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res); 85 | break; 86 | } 87 | } 88 | collection_free(&dev->tx_xfers); 89 | libusb_release_interface(dev->dev, dev->interface); 90 | libusb_close(dev->dev); 91 | dev->dev = NULL; 92 | collection_remove(&device_list, dev); 93 | free(dev); 94 | } 95 | 96 | static void tx_callback(struct libusb_transfer *xfer) 97 | { 98 | struct usb_device *dev = xfer->user_data; 99 | usbmuxd_log(LL_SPEW, "TX callback dev %d-%d len %d -> %d status %d", dev->bus, dev->address, xfer->length, xfer->actual_length, xfer->status); 100 | if(xfer->status != LIBUSB_TRANSFER_COMPLETED) { 101 | switch(xfer->status) { 102 | case LIBUSB_TRANSFER_COMPLETED: //shut up compiler 103 | case LIBUSB_TRANSFER_ERROR: 104 | // funny, this happens when we disconnect the device while waiting for a transfer, sometimes 105 | usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address); 106 | break; 107 | case LIBUSB_TRANSFER_TIMED_OUT: 108 | usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address); 109 | break; 110 | case LIBUSB_TRANSFER_CANCELLED: 111 | usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address); 112 | break; 113 | case LIBUSB_TRANSFER_STALL: 114 | usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address); 115 | break; 116 | case LIBUSB_TRANSFER_NO_DEVICE: 117 | // other times, this happens, and also even when we abort the transfer after device removal 118 | usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address); 119 | break; 120 | case LIBUSB_TRANSFER_OVERFLOW: 121 | usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address); 122 | break; 123 | // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad) 124 | } 125 | // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events 126 | // we'll do device_remove there too 127 | dev->alive = 0; 128 | } 129 | if(xfer->buffer) 130 | free(xfer->buffer); 131 | collection_remove(&dev->tx_xfers, xfer); 132 | libusb_free_transfer(xfer); 133 | } 134 | 135 | int usb_send(struct usb_device *dev, const unsigned char *buf, int length) 136 | { 137 | int res; 138 | struct libusb_transfer *xfer = libusb_alloc_transfer(0); 139 | libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, (void*)buf, length, tx_callback, dev, 0); 140 | if((res = libusb_submit_transfer(xfer)) < 0) { 141 | usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res); 142 | libusb_free_transfer(xfer); 143 | return res; 144 | } 145 | collection_add(&dev->tx_xfers, xfer); 146 | if (length % dev->wMaxPacketSize == 0) { 147 | usbmuxd_log(LL_DEBUG, "Send ZLP"); 148 | // Send Zero Length Packet 149 | xfer = libusb_alloc_transfer(0); 150 | void *buffer = malloc(1); 151 | libusb_fill_bulk_transfer(xfer, dev->dev, dev->ep_out, buffer, 0, tx_callback, dev, 0); 152 | if((res = libusb_submit_transfer(xfer)) < 0) { 153 | usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res); 154 | libusb_free_transfer(xfer); 155 | return res; 156 | } 157 | collection_add(&dev->tx_xfers, xfer); 158 | } 159 | return 0; 160 | } 161 | 162 | static void rx_callback(struct libusb_transfer *xfer) 163 | { 164 | struct usb_device *dev = xfer->user_data; 165 | usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status); 166 | if(xfer->status == LIBUSB_TRANSFER_COMPLETED) { 167 | device_data_input(dev, xfer->buffer, xfer->actual_length); 168 | libusb_submit_transfer(xfer); 169 | } else { 170 | switch(xfer->status) { 171 | case LIBUSB_TRANSFER_COMPLETED: //shut up compiler 172 | case LIBUSB_TRANSFER_ERROR: 173 | // funny, this happens when we disconnect the device while waiting for a transfer, sometimes 174 | usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address); 175 | break; 176 | case LIBUSB_TRANSFER_TIMED_OUT: 177 | usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address); 178 | break; 179 | case LIBUSB_TRANSFER_CANCELLED: 180 | usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address); 181 | break; 182 | case LIBUSB_TRANSFER_STALL: 183 | usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address); 184 | break; 185 | case LIBUSB_TRANSFER_NO_DEVICE: 186 | // other times, this happens, and also even when we abort the transfer after device removal 187 | usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address); 188 | break; 189 | case LIBUSB_TRANSFER_OVERFLOW: 190 | usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address); 191 | break; 192 | // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad) 193 | } 194 | free(xfer->buffer); 195 | dev->rx_xfer = NULL; 196 | libusb_free_transfer(xfer); 197 | // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events 198 | // we'll do device_remove there too 199 | dev->alive = 0; 200 | } 201 | } 202 | 203 | static int start_rx(struct usb_device *dev) 204 | { 205 | int res; 206 | void *buf; 207 | dev->rx_xfer = libusb_alloc_transfer(0); 208 | buf = malloc(USB_MRU); 209 | libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, dev->ep_in, buf, USB_MRU, rx_callback, dev, 0); 210 | if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) { 211 | usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res); 212 | libusb_free_transfer(dev->rx_xfer); 213 | dev->rx_xfer = NULL; 214 | return res; 215 | } 216 | return 0; 217 | } 218 | 219 | int usb_discover(void) 220 | { 221 | int cnt, i, j, res; 222 | int valid_count = 0; 223 | libusb_device **devs; 224 | 225 | cnt = libusb_get_device_list(NULL, &devs); 226 | if(cnt < 0) { 227 | usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt); 228 | devlist_failures++; 229 | // sometimes libusb fails getting the device list if you've just removed something 230 | if(devlist_failures > 5) { 231 | usbmuxd_log(LL_FATAL, "Too many errors getting device list\n"); 232 | return cnt; 233 | } else { 234 | gettimeofday(&next_dev_poll_time, NULL); 235 | next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000; 236 | next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000; 237 | next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000; 238 | return 0; 239 | } 240 | } 241 | devlist_failures = 0; 242 | 243 | usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt); 244 | 245 | FOREACH(struct usb_device *usbdev, &device_list) { 246 | usbdev->alive = 0; 247 | } ENDFOREACH 248 | 249 | for(i=0; ibus == bus && usbdev->address == address) { 258 | valid_count++; 259 | usbdev->alive = 1; 260 | found = 1; 261 | break; 262 | } 263 | } ENDFOREACH 264 | if(found) 265 | continue; //device already found 266 | if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) { 267 | usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res); 268 | continue; 269 | } 270 | if(devdesc.idVendor != VID_APPLE) 271 | continue; 272 | if((devdesc.idProduct < PID_RANGE_LOW) || 273 | (devdesc.idProduct > PID_RANGE_MAX)) 274 | continue; 275 | libusb_device_handle *handle; 276 | usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address); 277 | // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable 278 | if((res = libusb_open(dev, &handle)) != 0) { 279 | usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res); 280 | continue; 281 | } 282 | int current_config = 0; 283 | if((res = libusb_get_configuration(handle, ¤t_config)) != 0) { 284 | usbmuxd_log(LL_WARNING, "Could not get configuration for device %d-%d: %d", bus, address, res); 285 | libusb_close(handle); 286 | continue; 287 | } 288 | if (current_config != devdesc.bNumConfigurations) { 289 | struct libusb_config_descriptor *config; 290 | if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) { 291 | usbmuxd_log(LL_NOTICE, "Could not get old configuration descriptor for device %d-%d: %d", bus, address, res); 292 | } else { 293 | for(j=0; jbNumInterfaces; j++) { 294 | const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; 295 | if((res = libusb_kernel_driver_active(handle, intf->bInterfaceNumber)) < 0) { 296 | usbmuxd_log(LL_NOTICE, "Could not check kernel ownership of interface %d for device %d-%d: %d", intf->bInterfaceNumber, bus, address, res); 297 | continue; 298 | } 299 | if(res == 1) { 300 | usbmuxd_log(LL_INFO, "Detaching kernel driver for device %d-%d, interface %d", bus, address, intf->bInterfaceNumber); 301 | if((res = libusb_detach_kernel_driver(handle, intf->bInterfaceNumber)) < 0) { 302 | usbmuxd_log(LL_WARNING, "Could not detach kernel driver (%d), configuration change will probably fail!", res); 303 | continue; 304 | } 305 | } 306 | } 307 | libusb_free_config_descriptor(config); 308 | } 309 | if((res = libusb_set_configuration(handle, devdesc.bNumConfigurations)) != 0) { 310 | usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", devdesc.bNumConfigurations, bus, address, res); 311 | libusb_close(handle); 312 | continue; 313 | } 314 | } 315 | 316 | struct libusb_config_descriptor *config; 317 | if((res = libusb_get_active_config_descriptor(dev, &config)) != 0) { 318 | usbmuxd_log(LL_WARNING, "Could not get configuration descriptor for device %d-%d: %d", bus, address, res); 319 | libusb_close(handle); 320 | continue; 321 | } 322 | 323 | struct usb_device *usbdev; 324 | usbdev = malloc(sizeof(struct usb_device)); 325 | memset(usbdev, 0, sizeof(*usbdev)); 326 | 327 | for(j=0; jbNumInterfaces; j++) { 328 | const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; 329 | if(intf->bInterfaceClass != INTERFACE_CLASS || 330 | intf->bInterfaceSubClass != INTERFACE_SUBCLASS || 331 | intf->bInterfaceProtocol != INTERFACE_PROTOCOL) 332 | continue; 333 | if(intf->bNumEndpoints != 2) { 334 | usbmuxd_log(LL_WARNING, "Endpoint count mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address); 335 | continue; 336 | } 337 | if((intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && 338 | (intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { 339 | usbdev->interface = intf->bInterfaceNumber; 340 | usbdev->ep_out = intf->endpoint[0].bEndpointAddress; 341 | usbdev->ep_in = intf->endpoint[1].bEndpointAddress; 342 | usbmuxd_log(LL_INFO, "Found interface %d with endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address); 343 | break; 344 | } else if((intf->endpoint[1].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_OUT && 345 | (intf->endpoint[0].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) { 346 | usbdev->interface = intf->bInterfaceNumber; 347 | usbdev->ep_out = intf->endpoint[1].bEndpointAddress; 348 | usbdev->ep_in = intf->endpoint[0].bEndpointAddress; 349 | usbmuxd_log(LL_INFO, "Found interface %d with swapped endpoints %02x/%02x for device %d-%d", usbdev->interface, usbdev->ep_out, usbdev->ep_in, bus, address); 350 | break; 351 | } else { 352 | usbmuxd_log(LL_WARNING, "Endpoint type mismatch for interface %d of device %d-%d", intf->bInterfaceNumber, bus, address); 353 | } 354 | } 355 | 356 | if(j == config->bNumInterfaces) { 357 | usbmuxd_log(LL_WARNING, "Could not find a suitable USB interface for device %d-%d", bus, address); 358 | libusb_free_config_descriptor(config); 359 | libusb_close(handle); 360 | free(usbdev); 361 | continue; 362 | } 363 | 364 | libusb_free_config_descriptor(config); 365 | 366 | if((res = libusb_claim_interface(handle, usbdev->interface)) != 0) { 367 | usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", usbdev->interface, bus, address, res); 368 | libusb_close(handle); 369 | free(usbdev); 370 | continue; 371 | } 372 | 373 | if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) { 374 | usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res); 375 | libusb_release_interface(handle, usbdev->interface); 376 | libusb_close(handle); 377 | free(usbdev); 378 | continue; 379 | } 380 | usbdev->serial[res] = 0; 381 | usbdev->bus = bus; 382 | usbdev->address = address; 383 | usbdev->vid = devdesc.idVendor; 384 | usbdev->pid = devdesc.idProduct; 385 | usbdev->dev = handle; 386 | usbdev->alive = 1; 387 | usbdev->wMaxPacketSize = libusb_get_max_packet_size(dev, usbdev->ep_out); 388 | if (usbdev->wMaxPacketSize <= 0) { 389 | usbmuxd_log(LL_ERROR, "Could not determine wMaxPacketSize for device %d-%d, setting to 64", usbdev->bus, usbdev->address); 390 | usbdev->wMaxPacketSize = 64; 391 | } else { 392 | usbmuxd_log(LL_INFO, "Using wMaxPacketSize=%d for device %d-%d", usbdev->wMaxPacketSize, usbdev->bus, usbdev->address); 393 | } 394 | 395 | collection_init(&usbdev->tx_xfers); 396 | 397 | collection_add(&device_list, usbdev); 398 | 399 | if(device_add(usbdev) < 0) { 400 | usb_disconnect(usbdev); 401 | continue; 402 | } 403 | if(start_rx(usbdev) < 0) { 404 | device_remove(usbdev); 405 | usb_disconnect(usbdev); 406 | continue; 407 | } 408 | valid_count++; 409 | } 410 | FOREACH(struct usb_device *usbdev, &device_list) { 411 | if(!usbdev->alive) { 412 | device_remove(usbdev); 413 | usb_disconnect(usbdev); 414 | } 415 | } ENDFOREACH 416 | 417 | libusb_free_device_list(devs, 1); 418 | 419 | gettimeofday(&next_dev_poll_time, NULL); 420 | next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000; 421 | next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000; 422 | next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000; 423 | 424 | return valid_count; 425 | } 426 | 427 | const char *usb_get_serial(struct usb_device *dev) 428 | { 429 | if(!dev->dev) { 430 | return NULL; 431 | } 432 | return dev->serial; 433 | } 434 | 435 | uint32_t usb_get_location(struct usb_device *dev) 436 | { 437 | if(!dev->dev) { 438 | return 0; 439 | } 440 | return (dev->bus << 16) | dev->address; 441 | } 442 | 443 | uint16_t usb_get_pid(struct usb_device *dev) 444 | { 445 | if(!dev->dev) { 446 | return 0; 447 | } 448 | return dev->pid; 449 | } 450 | 451 | void usb_get_fds(struct fdlist *list) 452 | { 453 | const struct libusb_pollfd **usbfds; 454 | const struct libusb_pollfd **p; 455 | usbfds = libusb_get_pollfds(NULL); 456 | if(!usbfds) { 457 | usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed"); 458 | return; 459 | } 460 | p = usbfds; 461 | while(*p) { 462 | fdlist_add(list, FD_USB, (*p)->fd, (*p)->events); 463 | p++; 464 | } 465 | free(usbfds); 466 | } 467 | 468 | void usb_autodiscover(int enable) 469 | { 470 | usbmuxd_log(LL_DEBUG, "usb polling enable: %d", enable); 471 | device_polling = enable; 472 | } 473 | 474 | static int dev_poll_remain_ms(void) 475 | { 476 | int msecs; 477 | struct timeval tv; 478 | if(!device_polling) 479 | return 100000; // devices will never be polled if this is > 0 480 | gettimeofday(&tv, NULL); 481 | msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000; 482 | msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000; 483 | if(msecs < 0) 484 | return 0; 485 | return msecs; 486 | } 487 | 488 | int usb_get_timeout(void) 489 | { 490 | struct timeval tv; 491 | int msec; 492 | int res; 493 | int pollrem; 494 | pollrem = dev_poll_remain_ms(); 495 | res = libusb_get_next_timeout(NULL, &tv); 496 | if(res == 0) 497 | return pollrem; 498 | if(res < 0) { 499 | usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res); 500 | return pollrem; 501 | } 502 | msec = tv.tv_sec * 1000; 503 | msec += tv.tv_usec / 1000; 504 | if(msec > pollrem) 505 | return pollrem; 506 | return msec; 507 | } 508 | 509 | int usb_process(void) 510 | { 511 | int res; 512 | struct timeval tv; 513 | tv.tv_sec = tv.tv_usec = 0; 514 | res = libusb_handle_events_timeout(NULL, &tv); 515 | if(res < 0) { 516 | usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res); 517 | return res; 518 | } 519 | // reap devices marked dead due to an RX error 520 | FOREACH(struct usb_device *usbdev, &device_list) { 521 | if(!usbdev->alive) { 522 | device_remove(usbdev); 523 | usb_disconnect(usbdev); 524 | } 525 | } ENDFOREACH 526 | 527 | if(dev_poll_remain_ms() <= 0) { 528 | res = usb_discover(); 529 | if(res < 0) { 530 | usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res); 531 | return res; 532 | } 533 | } 534 | return 0; 535 | } 536 | 537 | int usb_process_timeout(int msec) 538 | { 539 | int res; 540 | struct timeval tleft, tcur, tfin; 541 | gettimeofday(&tcur, NULL); 542 | tfin.tv_sec = tcur.tv_sec + (msec / 1000); 543 | tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000; 544 | tfin.tv_sec += tfin.tv_usec / 1000000; 545 | tfin.tv_usec %= 1000000; 546 | while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) { 547 | tleft.tv_sec = tfin.tv_sec - tcur.tv_sec; 548 | tleft.tv_usec = tfin.tv_usec - tcur.tv_usec; 549 | if(tleft.tv_usec < 0) { 550 | tleft.tv_usec += 1000000; 551 | tleft.tv_sec -= 1; 552 | } 553 | res = libusb_handle_events_timeout(NULL, &tleft); 554 | if(res < 0) { 555 | usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res); 556 | return res; 557 | } 558 | // reap devices marked dead due to an RX error 559 | FOREACH(struct usb_device *usbdev, &device_list) { 560 | if(!usbdev->alive) { 561 | device_remove(usbdev); 562 | usb_disconnect(usbdev); 563 | } 564 | } ENDFOREACH 565 | gettimeofday(&tcur, NULL); 566 | } 567 | return 0; 568 | } 569 | 570 | int usb_init(void) 571 | { 572 | int res; 573 | usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0"); 574 | 575 | devlist_failures = 0; 576 | device_polling = 1; 577 | res = libusb_init(NULL); 578 | //libusb_set_debug(NULL, 3); 579 | if(res != 0) { 580 | usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res); 581 | return -1; 582 | } 583 | 584 | collection_init(&device_list); 585 | 586 | return usb_discover(); 587 | } 588 | 589 | void usb_shutdown(void) 590 | { 591 | usbmuxd_log(LL_DEBUG, "usb_shutdown"); 592 | FOREACH(struct usb_device *usbdev, &device_list) { 593 | device_remove(usbdev); 594 | usb_disconnect(usbdev); 595 | } ENDFOREACH 596 | collection_free(&device_list); 597 | libusb_exit(NULL); 598 | } 599 | -------------------------------------------------------------------------------- /daemon/usb.h: -------------------------------------------------------------------------------- 1 | /* 2 | usbmuxd - iPhone/iPod Touch USB multiplex server daemon 3 | 4 | Copyright (C) 2009 Hector Martin "marcan" 5 | Copyright (C) 2009 Nikias Bassen 6 | Copyright (C) 2009 Martin Szulecki 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 or version 3. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | */ 22 | 23 | #ifndef __USB_H__ 24 | #define __USB_H__ 25 | 26 | #include 27 | #include "utils.h" 28 | 29 | #define INTERFACE_CLASS 255 30 | #define INTERFACE_SUBCLASS 254 31 | #define INTERFACE_PROTOCOL 2 32 | 33 | // libusb fragments packets larger than this (usbfs limitation) 34 | // on input, this creates race conditions and other issues 35 | #define USB_MRU 16384 36 | 37 | // max transmission packet size 38 | // libusb fragments these too, but doesn't send ZLPs so we're safe 39 | // but we need to send a ZLP ourselves at the end (see usb-linux.c) 40 | // we're using 3 * 16384 to optimize for the fragmentation 41 | // this results in three URBs per full transfer, 32 USB packets each 42 | // if there are ZLP issues this should make them show up easily too 43 | #define USB_MTU (3 * 16384) 44 | 45 | #define USB_PACKET_SIZE 512 46 | 47 | #define VID_APPLE 0x5ac 48 | #define PID_RANGE_LOW 0x1290 49 | #define PID_RANGE_MAX 0x12af 50 | 51 | struct usb_device; 52 | 53 | int usb_init(void); 54 | void usb_shutdown(void); 55 | const char *usb_get_serial(struct usb_device *dev); 56 | uint32_t usb_get_location(struct usb_device *dev); 57 | uint16_t usb_get_pid(struct usb_device *dev); 58 | void usb_get_fds(struct fdlist *list); 59 | int usb_get_timeout(void); 60 | int usb_send(struct usb_device *dev, const unsigned char *buf, int length); 61 | int usb_discover(void); 62 | void usb_autodiscover(int enable); 63 | int usb_process(void); 64 | int usb_process_timeout(int msec); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /libusbmuxd.pc.in: -------------------------------------------------------------------------------- 1 | prefix=${CMAKE_INSTALL_PREFIX} 2 | exec_prefix=${CMAKE_INSTALL_PREFIX} 3 | libdir=${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX} 4 | includedir=${CMAKE_INSTALL_PREFIX}/include 5 | 6 | Name: libusbmuxd 7 | Description: A library to communicate with the usbmux daemon 8 | Version: ${USBMUXD_VERSION} 9 | Libs: -L${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX} -lusbmuxd 10 | Cflags: -I${CMAKE_INSTALL_PREFIX}/include 11 | 12 | -------------------------------------------------------------------------------- /libusbmuxd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories (${CMAKE_SOURCE_DIR}/common) 2 | find_package(Threads) 3 | 4 | option(WANT_INOTIFY "Build with inotify support" ON) 5 | if (WANT_INOTIFY) 6 | find_package(Inotify) 7 | if (INOTIFY_FOUND) 8 | add_definitions("-DHAVE_INOTIFY") 9 | message("-- libusbmuxd will be built with inotify support") 10 | endif() 11 | endif(WANT_INOTIFY) 12 | 13 | add_library (libusbmuxd SHARED libusbmuxd.c sock_stuff.c ${CMAKE_SOURCE_DIR}/common/utils.c) 14 | find_library (PTHREAD pthread) 15 | 16 | if (HAVE_PLIST) 17 | add_definitions("-DHAVE_PLIST") 18 | message("-- libusbmuxd will be built with protocol version 1 support") 19 | endif() 20 | if(WIN32) 21 | set(OPT_LIBS ${OPT_LIBS} ws2_32) 22 | endif() 23 | include_directories(${OPT_INCLUDES}) 24 | target_link_libraries (libusbmuxd ${CMAKE_THREAD_LIBS_INIT} ${OPT_LIBS}) 25 | 26 | # 'lib' is a UNIXism, the proper CMake target is usbmuxd 27 | # But we can't use that due to the conflict with the usbmuxd daemon, 28 | # so instead change the library output base name to usbmuxd here 29 | set_target_properties(libusbmuxd PROPERTIES OUTPUT_NAME usbmuxd) 30 | set_target_properties(libusbmuxd PROPERTIES VERSION ${LIBUSBMUXD_VERSION}) 31 | set_target_properties(libusbmuxd PROPERTIES SOVERSION ${LIBUSBMUXD_SOVERSION}) 32 | 33 | if(APPLE) 34 | set_target_properties(libusbmuxd PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}") 35 | endif() 36 | if(WIN32) 37 | set_target_properties(libusbmuxd PROPERTIES PREFIX "lib" IMPORT_PREFIX "lib") 38 | endif() 39 | 40 | install(TARGETS libusbmuxd 41 | RUNTIME DESTINATION bin 42 | ARCHIVE DESTINATION lib${LIB_SUFFIX} 43 | LIBRARY DESTINATION lib${LIB_SUFFIX} 44 | ) 45 | install(FILES usbmuxd.h usbmuxd-proto.h DESTINATION include) 46 | -------------------------------------------------------------------------------- /libusbmuxd/libusbmuxd.c: -------------------------------------------------------------------------------- 1 | /* 2 | libusbmuxd - client library to talk to usbmuxd 3 | 4 | Copyright (C) 2009-2010 Nikias Bassen 5 | Copyright (C) 2009 Paul Sladen 6 | Copyright (C) 2009 Martin Szulecki 7 | 8 | This library is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as 10 | published by the Free Software Foundation, either version 2.1 of the 11 | License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #ifdef WIN32 30 | #include 31 | #include 32 | #define sleep(x) Sleep(x*1000) 33 | #define EPROTO 71 34 | #define EBADMSG 77 35 | #else 36 | #include 37 | #include 38 | #include 39 | #endif 40 | 41 | #ifdef HAVE_INOTIFY 42 | #include 43 | #define EVENT_SIZE (sizeof (struct inotify_event)) 44 | #define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16)) 45 | #define USBMUXD_DIRNAME "/var/run" 46 | #define USBMUXD_SOCKET_NAME "usbmuxd" 47 | #endif /* HAVE_INOTIFY */ 48 | 49 | #include 50 | #include 51 | 52 | #ifdef HAVE_PLIST 53 | #include 54 | #define PLIST_BUNDLE_ID "com.marcansoft.usbmuxd" 55 | #define PLIST_CLIENT_VERSION_STRING "usbmuxd built for freedom" 56 | #define PLIST_PROGNAME "libusbmuxd" 57 | #endif 58 | 59 | // usbmuxd public interface 60 | #include "usbmuxd.h" 61 | // usbmuxd protocol 62 | #include "usbmuxd-proto.h" 63 | // socket utility functions 64 | #include "sock_stuff.h" 65 | // misc utility functions 66 | #include "utils.h" 67 | 68 | static int libusbmuxd_debug = 0; 69 | #define DEBUG(x, y, ...) if (x <= libusbmuxd_debug) fprintf(stderr, (y), __VA_ARGS__); 70 | 71 | static struct collection devices; 72 | static usbmuxd_event_cb_t event_cb = NULL; 73 | #ifdef WIN32 74 | HANDLE devmon = NULL; 75 | CRITICAL_SECTION mutex; 76 | static int mutex_initialized = 0; 77 | #define LOCK if (!mutex_initialized) { InitializeCriticalSection(&mutex); mutex_initialized = 1; } EnterCriticalSection(&mutex); 78 | #define UNLOCK LeaveCriticalSection(&mutex); 79 | #else 80 | pthread_t devmon; 81 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 82 | #define LOCK pthread_mutex_lock(&mutex) 83 | #define UNLOCK pthread_mutex_unlock(&mutex) 84 | #endif 85 | static int listenfd = -1; 86 | 87 | static int use_tag = 0; 88 | static int proto_version = 0; 89 | 90 | /** 91 | * Finds a device info record by its handle. 92 | * if the record is not found, NULL is returned. 93 | */ 94 | static usbmuxd_device_info_t *devices_find(int handle) 95 | { 96 | FOREACH(usbmuxd_device_info_t *dev, &devices) { 97 | if (dev && dev->handle == handle) { 98 | return dev; 99 | } 100 | } ENDFOREACH 101 | return NULL; 102 | } 103 | 104 | /** 105 | * Creates a socket connection to usbmuxd. 106 | * For Mac/Linux it is a unix domain socket, 107 | * for Windows it is a tcp socket. 108 | */ 109 | static int connect_usbmuxd_socket() 110 | { 111 | #if defined(WIN32) || defined(__CYGWIN__) 112 | return connect_socket("127.0.0.1", USBMUXD_SOCKET_PORT); 113 | #else 114 | return connect_unix_socket(USBMUXD_SOCKET_FILE); 115 | #endif 116 | } 117 | 118 | static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload, int timeout) 119 | { 120 | int recv_len; 121 | struct usbmuxd_header hdr; 122 | char *payload_loc = NULL; 123 | 124 | header->length = 0; 125 | header->version = 0; 126 | header->message = 0; 127 | header->tag = 0; 128 | 129 | recv_len = recv_buf_timeout(sfd, &hdr, sizeof(hdr), 0, timeout); 130 | if (recv_len < 0) { 131 | return recv_len; 132 | } else if (recv_len < sizeof(hdr)) { 133 | return recv_len; 134 | } 135 | 136 | uint32_t payload_size = hdr.length - sizeof(hdr); 137 | if (payload_size > 0) { 138 | payload_loc = (char*)malloc(payload_size); 139 | if (recv_buf_timeout(sfd, payload_loc, payload_size, 0, 5000) != payload_size) { 140 | DEBUG(1, "%s: Error receiving payload of size %d\n", __func__, payload_size); 141 | free(payload_loc); 142 | return -EBADMSG; 143 | } 144 | } 145 | 146 | #ifdef HAVE_PLIST 147 | if (hdr.message == MESSAGE_PLIST) { 148 | char *message = NULL; 149 | plist_t plist = NULL; 150 | plist_from_xml(payload_loc, payload_size, &plist); 151 | free(payload_loc); 152 | 153 | if (!plist) { 154 | DEBUG(1, "%s: Error getting plist from payload!\n", __func__); 155 | return -EBADMSG; 156 | } 157 | 158 | plist_t node = plist_dict_get_item(plist, "MessageType"); 159 | if (plist_get_node_type(node) != PLIST_STRING) { 160 | DEBUG(1, "%s: Error getting message type from plist!\n", __func__); 161 | free(plist); 162 | return -EBADMSG; 163 | } 164 | 165 | plist_get_string_val(node, &message); 166 | if (message) { 167 | uint64_t val = 0; 168 | if (strcmp(message, "Result") == 0) { 169 | /* result message */ 170 | uint32_t dwval = 0; 171 | plist_t n = plist_dict_get_item(plist, "Number"); 172 | plist_get_uint_val(n, &val); 173 | *payload = malloc(sizeof(uint32_t)); 174 | dwval = val; 175 | memcpy(*payload, &dwval, sizeof(dwval)); 176 | hdr.length = sizeof(hdr) + sizeof(dwval); 177 | hdr.message = MESSAGE_RESULT; 178 | } else if (strcmp(message, "Attached") == 0) { 179 | /* device add message */ 180 | struct usbmuxd_device_record *dev = NULL; 181 | plist_t props = plist_dict_get_item(plist, "Properties"); 182 | if (!props) { 183 | DEBUG(1, "%s: Could not get properties for message '%s' from plist!\n", __func__, message); 184 | plist_free(plist); 185 | return -EBADMSG; 186 | } 187 | dev = (struct usbmuxd_device_record*)malloc(sizeof(struct usbmuxd_device_record)); 188 | memset(dev, 0, sizeof(struct usbmuxd_device_record)); 189 | 190 | plist_t n = plist_dict_get_item(props, "DeviceID"); 191 | plist_get_uint_val(n, &val); 192 | dev->device_id = (uint32_t)val; 193 | 194 | n = plist_dict_get_item(props, "ProductID"); 195 | plist_get_uint_val(n, &val); 196 | dev->product_id = (uint32_t)val; 197 | 198 | n = plist_dict_get_item(props, "SerialNumber"); 199 | char *strval = NULL; 200 | plist_get_string_val(n, &strval); 201 | if (strval) { 202 | strncpy(dev->serial_number, strval, 255); 203 | free(strval); 204 | } 205 | n = plist_dict_get_item(props, "LocationID"); 206 | plist_get_uint_val(n, &val); 207 | dev->location = (uint32_t)val; 208 | *payload = (void*)dev; 209 | hdr.length = sizeof(hdr) + sizeof(struct usbmuxd_device_record); 210 | hdr.message = MESSAGE_DEVICE_ADD; 211 | } else if (strcmp(message, "Detached") == 0) { 212 | /* device remove message */ 213 | uint32_t dwval = 0; 214 | plist_t n = plist_dict_get_item(plist, "DeviceID"); 215 | if (n) { 216 | plist_get_uint_val(n, &val); 217 | *payload = malloc(sizeof(uint32_t)); 218 | dwval = val; 219 | memcpy(*payload, &dwval, sizeof(dwval)); 220 | hdr.length = sizeof(hdr) + sizeof(dwval); 221 | hdr.message = MESSAGE_DEVICE_REMOVE; 222 | } 223 | } else { 224 | DEBUG(1, "%s: Unexpected message '%s' in plist!\n", __func__, message); 225 | plist_free(plist); 226 | return -EBADMSG; 227 | } 228 | } 229 | plist_free(plist); 230 | } else 231 | #endif 232 | { 233 | *payload = payload_loc; 234 | } 235 | 236 | memcpy(header, &hdr, sizeof(hdr)); 237 | 238 | return hdr.length; 239 | } 240 | 241 | /** 242 | * Retrieves the result code to a previously sent request. 243 | */ 244 | static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result) 245 | { 246 | struct usbmuxd_header hdr; 247 | int recv_len; 248 | uint32_t *res = NULL; 249 | 250 | if (!result) { 251 | return -EINVAL; 252 | } 253 | *result = -1; 254 | 255 | if ((recv_len = receive_packet(sfd, &hdr, (void**)&res, 5000)) < 0) { 256 | DEBUG(1, "%s: Error receiving packet: %d\n", __func__, errno); 257 | if (res) 258 | free(res); 259 | return -errno; 260 | } 261 | if (recv_len < sizeof(hdr)) { 262 | DEBUG(1, "%s: Received packet is too small!\n", __func__); 263 | if (res) 264 | free(res); 265 | return -EPROTO; 266 | } 267 | 268 | if (hdr.message == MESSAGE_RESULT) { 269 | int ret = 0; 270 | if (res && (hdr.tag == tag)) { 271 | memcpy(result, res, sizeof(uint32_t)); 272 | ret = 1; 273 | } 274 | if (res) 275 | free(res); 276 | return ret; 277 | } 278 | DEBUG(1, "%s: Unexpected message of type %d received!\n", __func__, hdr.message); 279 | if (res) 280 | free(res); 281 | return -EPROTO; 282 | } 283 | 284 | static int send_packet(int sfd, uint32_t message, uint32_t tag, void *payload, uint32_t payload_size) 285 | { 286 | struct usbmuxd_header header; 287 | 288 | header.length = sizeof(struct usbmuxd_header); 289 | header.version = proto_version; 290 | header.message = message; 291 | header.tag = tag; 292 | if (payload && (payload_size > 0)) { 293 | header.length += payload_size; 294 | } 295 | int sent = send_buf(sfd, &header, sizeof(header)); 296 | if (sent != sizeof(header)) { 297 | DEBUG(1, "%s: ERROR: could not send packet header\n", __func__); 298 | return -1; 299 | } 300 | if (payload && (payload_size > 0)) { 301 | sent += send_buf(sfd, payload, payload_size); 302 | } 303 | if (sent != (int)header.length) { 304 | DEBUG(1, "%s: ERROR: could not send whole packet\n", __func__); 305 | close_socket(sfd); 306 | return -1; 307 | } 308 | return sent; 309 | } 310 | 311 | static int send_listen_packet(int sfd, uint32_t tag) 312 | { 313 | int res = 0; 314 | #ifdef HAVE_PLIST 315 | if (proto_version == 1) { 316 | /* plist packet */ 317 | char *payload = NULL; 318 | uint32_t payload_size = 0; 319 | plist_t plist; 320 | 321 | /* construct message plist */ 322 | plist = plist_new_dict(); 323 | plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID)); 324 | plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING)); 325 | plist_dict_insert_item(plist, "MessageType", plist_new_string("Listen")); 326 | plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME)); 327 | plist_to_xml(plist, &payload, &payload_size); 328 | plist_free(plist); 329 | 330 | res = send_packet(sfd, MESSAGE_PLIST, tag, payload, payload_size); 331 | free(payload); 332 | } else 333 | #endif 334 | { 335 | /* binary packet */ 336 | res = send_packet(sfd, MESSAGE_LISTEN, tag, NULL, 0); 337 | } 338 | return res; 339 | } 340 | 341 | static int send_connect_packet(int sfd, uint32_t tag, uint32_t device_id, uint16_t port) 342 | { 343 | int res = 0; 344 | #ifdef HAVE_PLIST 345 | if (proto_version == 1) { 346 | /* plist packet */ 347 | char *payload = NULL; 348 | uint32_t payload_size = 0; 349 | plist_t plist; 350 | 351 | /* construct message plist */ 352 | plist = plist_new_dict(); 353 | plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID)); 354 | plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING)); 355 | plist_dict_insert_item(plist, "MessageType", plist_new_string("Connect")); 356 | plist_dict_insert_item(plist, "DeviceID", plist_new_uint(device_id)); 357 | plist_dict_insert_item(plist, "PortNumber", plist_new_uint(htons(port))); 358 | plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME)); 359 | plist_to_xml(plist, &payload, &payload_size); 360 | plist_free(plist); 361 | 362 | res = send_packet(sfd, MESSAGE_PLIST, tag, (void*)payload, payload_size); 363 | free(payload); 364 | } else 365 | #endif 366 | { 367 | /* binary packet */ 368 | struct { 369 | uint32_t device_id; 370 | uint16_t port; 371 | uint16_t reserved; 372 | } conninfo; 373 | 374 | conninfo.device_id = device_id; 375 | conninfo.port = htons(port); 376 | conninfo.reserved = 0; 377 | 378 | res = send_packet(sfd, MESSAGE_CONNECT, tag, &conninfo, sizeof(conninfo)); 379 | } 380 | return res; 381 | } 382 | 383 | /** 384 | * Generates an event, i.e. calls the callback function. 385 | * A reference to a populated usbmuxd_event_t with information about the event 386 | * and the corresponding device will be passed to the callback function. 387 | */ 388 | static void generate_event(usbmuxd_event_cb_t callback, const usbmuxd_device_info_t *dev, enum usbmuxd_event_type event, void *user_data) 389 | { 390 | usbmuxd_event_t ev; 391 | 392 | if (!callback || !dev) { 393 | return; 394 | } 395 | 396 | ev.event = event; 397 | memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t)); 398 | 399 | callback(&ev, user_data); 400 | } 401 | 402 | static int usbmuxd_listen_poll() 403 | { 404 | int sfd; 405 | 406 | sfd = connect_usbmuxd_socket(); 407 | if (sfd < 0) { 408 | while (event_cb) { 409 | if ((sfd = connect_usbmuxd_socket()) > 0) { 410 | break; 411 | } 412 | sleep(1); 413 | } 414 | } 415 | 416 | return sfd; 417 | } 418 | 419 | #ifdef HAVE_INOTIFY 420 | static int use_inotify = 1; 421 | 422 | static int usbmuxd_listen_inotify() 423 | { 424 | int inot_fd; 425 | int watch_d; 426 | int sfd; 427 | 428 | if (!use_inotify) { 429 | return -2; 430 | } 431 | 432 | sfd = connect_usbmuxd_socket(); 433 | if (sfd >= 0) 434 | return sfd; 435 | 436 | sfd = -1; 437 | inot_fd = inotify_init (); 438 | if (inot_fd < 0) { 439 | DEBUG(1, "%s: Failed to setup inotify\n", __func__); 440 | return -2; 441 | } 442 | 443 | /* inotify is setup, listen for events that concern us */ 444 | watch_d = inotify_add_watch (inot_fd, USBMUXD_DIRNAME, IN_CREATE); 445 | if (watch_d < 0) { 446 | DEBUG(1, "%s: Failed to setup watch descriptor for socket dir\n", __func__); 447 | close (inot_fd); 448 | return -2; 449 | } 450 | 451 | while (1) { 452 | ssize_t len, i; 453 | char buff[EVENT_BUF_LEN] = {0}; 454 | 455 | i = 0; 456 | len = read (inot_fd, buff, EVENT_BUF_LEN -1); 457 | if (len < 0) 458 | goto end; 459 | while (i < len) { 460 | struct inotify_event *pevent = (struct inotify_event *) & buff[i]; 461 | 462 | /* check that it's ours */ 463 | if (pevent->mask & IN_CREATE && 464 | pevent->len && 465 | pevent->name != NULL && 466 | strcmp(pevent->name, USBMUXD_SOCKET_NAME) == 0) { 467 | sfd = connect_usbmuxd_socket (); 468 | goto end; 469 | } 470 | i += EVENT_SIZE + pevent->len; 471 | } 472 | } 473 | 474 | end: 475 | inotify_rm_watch(inot_fd, watch_d); 476 | close(inot_fd); 477 | 478 | return sfd; 479 | } 480 | #endif /* HAVE_INOTIFY */ 481 | 482 | /** 483 | * Tries to connect to usbmuxd and wait if it is not running. 484 | */ 485 | static int usbmuxd_listen() 486 | { 487 | int sfd; 488 | uint32_t res = -1; 489 | 490 | #ifdef HAVE_PLIST 491 | retry: 492 | #endif 493 | 494 | #ifdef HAVE_INOTIFY 495 | sfd = usbmuxd_listen_inotify(); 496 | if (sfd == -2) 497 | sfd = usbmuxd_listen_poll(); 498 | #else 499 | sfd = usbmuxd_listen_poll(); 500 | #endif 501 | 502 | if (sfd < 0) { 503 | DEBUG(1, "%s: ERROR: usbmuxd was supposed to be running here...\n", __func__); 504 | return sfd; 505 | } 506 | 507 | use_tag++; 508 | LOCK; 509 | if (send_listen_packet(sfd, use_tag) <= 0) { 510 | UNLOCK; 511 | DEBUG(1, "%s: ERROR: could not send listen packet\n", __func__); 512 | close_socket(sfd); 513 | return -1; 514 | } 515 | if (usbmuxd_get_result(sfd, use_tag, &res) && (res != 0)) { 516 | UNLOCK; 517 | close_socket(sfd); 518 | #ifdef HAVE_PLIST 519 | if ((res == RESULT_BADVERSION) && (proto_version != 1)) { 520 | proto_version = 1; 521 | goto retry; 522 | } 523 | #endif 524 | DEBUG(1, "%s: ERROR: did not get OK but %d\n", __func__, res); 525 | return -1; 526 | } 527 | UNLOCK; 528 | 529 | return sfd; 530 | } 531 | 532 | /** 533 | * Waits for an event to occur, i.e. a packet coming from usbmuxd. 534 | * Calls generate_event to pass the event via callback to the client program. 535 | */ 536 | int get_next_event(int sfd, usbmuxd_event_cb_t callback, void *user_data) 537 | { 538 | struct usbmuxd_header hdr; 539 | void *payload = NULL; 540 | 541 | /* block until we receive something */ 542 | if (receive_packet(sfd, &hdr, &payload, 0) < 0) { 543 | // when then usbmuxd connection fails, 544 | // generate remove events for every device that 545 | // is still present so applications know about it 546 | FOREACH(usbmuxd_device_info_t *dev, &devices) { 547 | generate_event(callback, dev, UE_DEVICE_REMOVE, user_data); 548 | collection_remove(&devices, dev); 549 | free(dev); 550 | } ENDFOREACH 551 | return -EIO; 552 | } 553 | 554 | if ((hdr.length > sizeof(hdr)) && !payload) { 555 | DEBUG(1, "%s: Invalid packet received, payload is missing!\n", __func__); 556 | return -EBADMSG; 557 | } 558 | 559 | if (hdr.message == MESSAGE_DEVICE_ADD) { 560 | struct usbmuxd_device_record *dev = payload; 561 | usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); 562 | if (!devinfo) { 563 | DEBUG(1, "%s: Out of memory!\n", __func__); 564 | free(payload); 565 | return -1; 566 | } 567 | 568 | devinfo->handle = dev->device_id; 569 | devinfo->product_id = dev->product_id; 570 | memset(devinfo->udid, '\0', sizeof(devinfo->udid)); 571 | memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)); 572 | 573 | if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { 574 | sprintf(devinfo->udid + 32, "%08x", devinfo->handle); 575 | } 576 | 577 | collection_add(&devices, devinfo); 578 | generate_event(callback, devinfo, UE_DEVICE_ADD, user_data); 579 | } else if (hdr.message == MESSAGE_DEVICE_REMOVE) { 580 | uint32_t handle; 581 | usbmuxd_device_info_t *devinfo; 582 | 583 | memcpy(&handle, payload, sizeof(uint32_t)); 584 | 585 | devinfo = devices_find(handle); 586 | if (!devinfo) { 587 | DEBUG(1, "%s: WARNING: got device remove message for handle %d, but couldn't find the corresponding handle in the device list. This event will be ignored.\n", __func__, handle); 588 | } else { 589 | generate_event(callback, devinfo, UE_DEVICE_REMOVE, user_data); 590 | collection_remove(&devices, devinfo); 591 | free(devinfo); 592 | } 593 | } else if (hdr.length > 0) { 594 | DEBUG(1, "%s: Unexpected message type %d length %d received!\n", __func__, hdr.message, hdr.length); 595 | } 596 | if (payload) { 597 | free(payload); 598 | } 599 | return 0; 600 | } 601 | 602 | static void device_monitor_cleanup(void* data) 603 | { 604 | collection_free(&devices); 605 | 606 | close_socket(listenfd); 607 | listenfd = -1; 608 | } 609 | 610 | /** 611 | * Device Monitor thread function. 612 | * 613 | * This function sets up a connection to usbmuxd 614 | */ 615 | static void *device_monitor(void *data) 616 | { 617 | collection_init(&devices); 618 | 619 | #ifndef WIN32 620 | pthread_cleanup_push(device_monitor_cleanup, NULL); 621 | #endif 622 | while (event_cb) { 623 | 624 | listenfd = usbmuxd_listen(); 625 | if (listenfd < 0) { 626 | continue; 627 | } 628 | 629 | while (event_cb) { 630 | int res = get_next_event(listenfd, event_cb, data); 631 | if (res < 0) { 632 | break; 633 | } 634 | } 635 | } 636 | 637 | #ifndef WIN32 638 | pthread_cleanup_pop(1); 639 | #else 640 | device_monitor_cleanup(NULL); 641 | #endif 642 | return NULL; 643 | } 644 | 645 | int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data) 646 | { 647 | int res; 648 | 649 | if (!callback) { 650 | return -EINVAL; 651 | } 652 | event_cb = callback; 653 | 654 | #ifdef WIN32 655 | res = 0; 656 | devmon = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)device_monitor, user_data, 0, NULL); 657 | if (devmon == NULL) { 658 | res = GetLastError(); 659 | } 660 | #else 661 | res = pthread_create(&devmon, NULL, device_monitor, user_data); 662 | #endif 663 | if (res != 0) { 664 | DEBUG(1, "%s: ERROR: Could not start device watcher thread!\n", __func__); 665 | return res; 666 | } 667 | return 0; 668 | } 669 | 670 | int usbmuxd_unsubscribe() 671 | { 672 | event_cb = NULL; 673 | 674 | shutdown_socket(listenfd, SHUT_RDWR); 675 | 676 | #ifdef WIN32 677 | if (devmon != NULL) { 678 | WaitForSingleObject(devmon, INFINITE); 679 | } 680 | #else 681 | if (pthread_kill(devmon, 0) == 0) { 682 | pthread_cancel(devmon); 683 | pthread_join(devmon, NULL); 684 | } 685 | #endif 686 | 687 | return 0; 688 | } 689 | 690 | int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list) 691 | { 692 | int sfd; 693 | int listen_success = 0; 694 | uint32_t res; 695 | struct collection tmpdevs; 696 | usbmuxd_device_info_t *newlist = NULL; 697 | struct usbmuxd_header hdr; 698 | struct usbmuxd_device_record *dev; 699 | int dev_cnt = 0; 700 | void *payload = NULL; 701 | 702 | *device_list = NULL; 703 | 704 | #ifdef HAVE_PLIST 705 | retry: 706 | #endif 707 | sfd = connect_usbmuxd_socket(); 708 | if (sfd < 0) { 709 | DEBUG(1, "%s: error opening socket!\n", __func__); 710 | return sfd; 711 | } 712 | 713 | use_tag++; 714 | LOCK; 715 | if (send_listen_packet(sfd, use_tag) > 0) { 716 | res = -1; 717 | // get response 718 | if (usbmuxd_get_result(sfd, use_tag, &res) && (res == 0)) { 719 | listen_success = 1; 720 | } else { 721 | UNLOCK; 722 | close_socket(sfd); 723 | #ifdef HAVE_PLIST 724 | if ((res == RESULT_BADVERSION) && (proto_version != 1)) { 725 | proto_version = 1; 726 | goto retry; 727 | } 728 | #endif 729 | DEBUG(1, "%s: Did not get response to scan request (with result=0)...\n", __func__); 730 | return res; 731 | } 732 | } 733 | 734 | if (!listen_success) { 735 | UNLOCK; 736 | DEBUG(1, "%s: Could not send listen request!\n", __func__); 737 | return -1; 738 | } 739 | 740 | collection_init(&tmpdevs); 741 | 742 | // receive device list 743 | while (1) { 744 | if (receive_packet(sfd, &hdr, &payload, 1000) > 0) { 745 | if (hdr.message == MESSAGE_DEVICE_ADD) { 746 | dev = payload; 747 | usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t)); 748 | if (!devinfo) { 749 | UNLOCK; 750 | DEBUG(1, "%s: Out of memory!\n", __func__); 751 | free(payload); 752 | return -1; 753 | } 754 | 755 | devinfo->handle = dev->device_id; 756 | devinfo->product_id = dev->product_id; 757 | memset(devinfo->udid, '\0', sizeof(devinfo->udid)); 758 | memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid)); 759 | 760 | if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) { 761 | sprintf(devinfo->udid + 32, "%08x", devinfo->handle); 762 | } 763 | 764 | collection_add(&tmpdevs, devinfo); 765 | 766 | } else if (hdr.message == MESSAGE_DEVICE_REMOVE) { 767 | uint32_t handle; 768 | usbmuxd_device_info_t *devinfo = NULL; 769 | 770 | memcpy(&handle, payload, sizeof(uint32_t)); 771 | 772 | FOREACH(usbmuxd_device_info_t *di, &tmpdevs) { 773 | if (di && di->handle == handle) { 774 | devinfo = di; 775 | break; 776 | } 777 | } ENDFOREACH 778 | if (devinfo) { 779 | collection_remove(&tmpdevs, devinfo); 780 | free(devinfo); 781 | } 782 | } else { 783 | DEBUG(1, "%s: Unexpected message %d\n", __func__, hdr.message); 784 | } 785 | if (payload) 786 | free(payload); 787 | } else { 788 | // we _should_ have all of them now. 789 | // or perhaps an error occured. 790 | break; 791 | } 792 | } 793 | UNLOCK; 794 | 795 | // explicitly close connection 796 | close_socket(sfd); 797 | 798 | // create copy of device info entries from collection 799 | newlist = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t) * (collection_count(&tmpdevs) + 1)); 800 | dev_cnt = 0; 801 | FOREACH(usbmuxd_device_info_t *di, &tmpdevs) { 802 | if (di) { 803 | memcpy(&newlist[dev_cnt], di, sizeof(usbmuxd_device_info_t)); 804 | free(di); 805 | dev_cnt++; 806 | } 807 | } ENDFOREACH 808 | collection_free(&tmpdevs); 809 | 810 | memset(&newlist[dev_cnt], 0, sizeof(usbmuxd_device_info_t)); 811 | *device_list = newlist; 812 | 813 | return dev_cnt; 814 | } 815 | 816 | int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list) 817 | { 818 | if (device_list) { 819 | free(*device_list); 820 | } 821 | return 0; 822 | } 823 | 824 | int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device) 825 | { 826 | usbmuxd_device_info_t *dev_list = NULL; 827 | 828 | if (!device) { 829 | return -EINVAL; 830 | } 831 | if (usbmuxd_get_device_list(&dev_list) < 0) { 832 | return -ENODEV; 833 | } 834 | 835 | int i; 836 | int result = 0; 837 | for (i = 0; dev_list[i].handle > 0; i++) { 838 | if (!udid) { 839 | device->handle = dev_list[i].handle; 840 | device->product_id = dev_list[i].product_id; 841 | strcpy(device->udid, dev_list[i].udid); 842 | result = 1; 843 | break; 844 | } 845 | if (!strcmp(udid, dev_list[i].udid)) { 846 | device->handle = dev_list[i].handle; 847 | device->product_id = dev_list[i].product_id; 848 | strcpy(device->udid, dev_list[i].udid); 849 | result = 1; 850 | break; 851 | } 852 | } 853 | 854 | free(dev_list); 855 | 856 | return result; 857 | } 858 | 859 | int usbmuxd_connect(const int handle, const unsigned short port) 860 | { 861 | int sfd; 862 | int connected = 0; 863 | uint32_t res = -1; 864 | 865 | #ifdef HAVE_PLIST 866 | retry: 867 | #endif 868 | sfd = connect_usbmuxd_socket(); 869 | if (sfd < 0) { 870 | DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n", 871 | __func__, strerror(errno)); 872 | return sfd; 873 | } 874 | 875 | use_tag++; 876 | if (send_connect_packet(sfd, use_tag, (uint32_t)handle, (uint16_t)port) <= 0) { 877 | DEBUG(1, "%s: Error sending connect message!\n", __func__); 878 | } else { 879 | // read ACK 880 | DEBUG(2, "%s: Reading connect result...\n", __func__); 881 | if (usbmuxd_get_result(sfd, use_tag, &res)) { 882 | if (res == 0) { 883 | DEBUG(2, "%s: Connect success!\n", __func__); 884 | connected = 1; 885 | } else { 886 | #ifdef HAVE_PLIST 887 | if ((res == RESULT_BADVERSION) && (proto_version == 0)) { 888 | proto_version = 1; 889 | close_socket(sfd); 890 | goto retry; 891 | } 892 | #endif 893 | DEBUG(1, "%s: Connect failed, Error code=%d\n", __func__, res); 894 | } 895 | } 896 | } 897 | 898 | if (connected) { 899 | return sfd; 900 | } 901 | 902 | close_socket(sfd); 903 | 904 | return -1; 905 | } 906 | 907 | int usbmuxd_disconnect(int sfd) 908 | { 909 | return close_socket(sfd); 910 | } 911 | 912 | int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes) 913 | { 914 | int num_sent; 915 | 916 | if (sfd < 0) { 917 | return -EINVAL; 918 | } 919 | 920 | num_sent = send(sfd, (void*)data, len, 0); 921 | if (num_sent < 0) { 922 | *sent_bytes = 0; 923 | DEBUG(1, "%s: Error %d when sending: %s\n", __func__, num_sent, strerror(errno)); 924 | return num_sent; 925 | } else if ((uint32_t)num_sent < len) { 926 | DEBUG(1, "%s: Warning: Did not send enough (only %d of %d)\n", __func__, num_sent, len); 927 | } 928 | 929 | *sent_bytes = num_sent; 930 | 931 | return 0; 932 | } 933 | 934 | int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) 935 | { 936 | int num_recv = recv_buf_timeout(sfd, (void*)data, len, 0, timeout); 937 | if (num_recv < 0) { 938 | *recv_bytes = 0; 939 | return num_recv; 940 | } 941 | 942 | *recv_bytes = num_recv; 943 | 944 | return 0; 945 | } 946 | 947 | int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes) 948 | { 949 | return usbmuxd_recv_timeout(sfd, data, len, recv_bytes, 5000); 950 | } 951 | 952 | void libusbmuxd_set_use_inotify(int set) 953 | { 954 | #ifdef HAVE_INOTIFY 955 | use_inotify = set; 956 | #endif 957 | return; 958 | } 959 | 960 | void libusbmuxd_set_debug_level(int level) 961 | { 962 | libusbmuxd_debug = level; 963 | sock_stuff_set_verbose(level); 964 | } 965 | -------------------------------------------------------------------------------- /libusbmuxd/sock_stuff.c: -------------------------------------------------------------------------------- 1 | /* 2 | libusbmuxd - client library to talk to usbmuxd 3 | 4 | Copyright (C) 2009 Nikias Bassen 5 | Copyright (C) 2009 Paul Sladen 6 | Copyright (C) 2009 Martin Szulecki 7 | 8 | This library is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as 10 | published by the Free Software Foundation, either version 2.1 of the 11 | License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #ifdef WIN32 33 | #include 34 | #include 35 | static int wsa_init = 0; 36 | #else 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #endif 43 | #include "sock_stuff.h" 44 | 45 | #define RECV_TIMEOUT 20000 46 | 47 | static int verbose = 0; 48 | 49 | void sock_stuff_set_verbose(int level) 50 | { 51 | verbose = level; 52 | } 53 | 54 | #ifndef WIN32 55 | int create_unix_socket(const char *filename) 56 | { 57 | struct sockaddr_un name; 58 | int sock; 59 | size_t size; 60 | 61 | // remove if still present 62 | unlink(filename); 63 | 64 | /* Create the socket. */ 65 | sock = socket(PF_LOCAL, SOCK_STREAM, 0); 66 | if (sock < 0) { 67 | perror("socket"); 68 | return -1; 69 | } 70 | 71 | /* Bind a name to the socket. */ 72 | name.sun_family = AF_LOCAL; 73 | strncpy(name.sun_path, filename, sizeof(name.sun_path)); 74 | name.sun_path[sizeof(name.sun_path) - 1] = '\0'; 75 | 76 | /* The size of the address is 77 | the offset of the start of the filename, 78 | plus its length, 79 | plus one for the terminating null byte. 80 | Alternatively you can just do: 81 | size = SUN_LEN (&name); 82 | */ 83 | size = (offsetof(struct sockaddr_un, sun_path) 84 | + strlen(name.sun_path) + 1); 85 | 86 | if (bind(sock, (struct sockaddr *) &name, size) < 0) { 87 | perror("bind"); 88 | close_socket(sock); 89 | return -1; 90 | } 91 | 92 | if (listen(sock, 10) < 0) { 93 | perror("listen"); 94 | close_socket(sock); 95 | return -1; 96 | } 97 | 98 | return sock; 99 | } 100 | 101 | int connect_unix_socket(const char *filename) 102 | { 103 | struct sockaddr_un name; 104 | int sfd = -1; 105 | size_t size; 106 | struct stat fst; 107 | 108 | // check if socket file exists... 109 | if (stat(filename, &fst) != 0) { 110 | if (verbose >= 2) 111 | fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename, 112 | strerror(errno)); 113 | return -1; 114 | } 115 | // ... and if it is a unix domain socket 116 | if (!S_ISSOCK(fst.st_mode)) { 117 | if (verbose >= 2) 118 | fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__, 119 | filename); 120 | return -1; 121 | } 122 | // make a new socket 123 | if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { 124 | if (verbose >= 2) 125 | fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno)); 126 | return -1; 127 | } 128 | // and connect to 'filename' 129 | name.sun_family = AF_LOCAL; 130 | strncpy(name.sun_path, filename, sizeof(name.sun_path)); 131 | name.sun_path[sizeof(name.sun_path) - 1] = 0; 132 | 133 | size = (offsetof(struct sockaddr_un, sun_path) 134 | + strlen(name.sun_path) + 1); 135 | 136 | if (connect(sfd, (struct sockaddr *) &name, size) < 0) { 137 | close_socket(sfd); 138 | if (verbose >= 2) 139 | fprintf(stderr, "%s: connect: %s\n", __func__, 140 | strerror(errno)); 141 | return -1; 142 | } 143 | 144 | return sfd; 145 | } 146 | #endif 147 | 148 | int create_socket(uint16_t port) 149 | { 150 | int sfd = -1; 151 | int yes = 1; 152 | #ifdef WIN32 153 | WSADATA wsa_data; 154 | if (!wsa_init) { 155 | if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) { 156 | fprintf(stderr, "WSAStartup failed!\n"); 157 | ExitProcess(-1); 158 | } 159 | wsa_init = 1; 160 | } 161 | #endif 162 | struct sockaddr_in saddr; 163 | 164 | if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { 165 | perror("socket()"); 166 | return -1; 167 | } 168 | 169 | if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) { 170 | perror("setsockopt()"); 171 | close_socket(sfd); 172 | return -1; 173 | } 174 | 175 | memset((void *) &saddr, 0, sizeof(saddr)); 176 | saddr.sin_family = AF_INET; 177 | saddr.sin_addr.s_addr = htonl(INADDR_ANY); 178 | saddr.sin_port = htons(port); 179 | 180 | if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) { 181 | perror("bind()"); 182 | close_socket(sfd); 183 | return -1; 184 | } 185 | 186 | if (listen(sfd, 1) == -1) { 187 | perror("listen()"); 188 | close_socket(sfd); 189 | return -1; 190 | } 191 | 192 | return sfd; 193 | } 194 | 195 | #if defined(WIN32) || defined(__CYGWIN__) 196 | int connect_socket(const char *addr, uint16_t port) 197 | { 198 | int sfd = -1; 199 | int yes = 1; 200 | struct hostent *hp; 201 | struct sockaddr_in saddr; 202 | #ifdef WIN32 203 | WSADATA wsa_data; 204 | if (!wsa_init) { 205 | if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) { 206 | fprintf(stderr, "WSAStartup failed!\n"); 207 | ExitProcess(-1); 208 | } 209 | wsa_init = 1; 210 | } 211 | #endif 212 | 213 | if (!addr) { 214 | errno = EINVAL; 215 | return -1; 216 | } 217 | 218 | if ((hp = gethostbyname(addr)) == NULL) { 219 | if (verbose >= 2) 220 | fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr); 221 | return -1; 222 | } 223 | 224 | if (!hp->h_addr) { 225 | if (verbose >= 2) 226 | fprintf(stderr, "%s: gethostbyname returned NULL address!\n", 227 | __func__); 228 | return -1; 229 | } 230 | 231 | if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) { 232 | perror("socket()"); 233 | return -1; 234 | } 235 | 236 | if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) { 237 | perror("setsockopt()"); 238 | close_socket(sfd); 239 | return -1; 240 | } 241 | 242 | memset((void *) &saddr, 0, sizeof(saddr)); 243 | saddr.sin_family = AF_INET; 244 | saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr; 245 | saddr.sin_port = htons(port); 246 | 247 | if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { 248 | perror("connect"); 249 | close_socket(sfd); 250 | return -2; 251 | } 252 | 253 | return sfd; 254 | } 255 | #endif /* WIN32 || __CYGWIN__ */ 256 | 257 | int check_fd(int fd, fd_mode fdm, unsigned int timeout) 258 | { 259 | fd_set fds; 260 | int sret; 261 | int eagain; 262 | struct timeval to; 263 | struct timeval *pto; 264 | 265 | if (fd <= 0) { 266 | if (verbose >= 2) 267 | fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd); 268 | return -1; 269 | } 270 | 271 | FD_ZERO(&fds); 272 | FD_SET(fd, &fds); 273 | 274 | if (timeout > 0) { 275 | to.tv_sec = (time_t) (timeout / 1000); 276 | to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000); 277 | pto = &to; 278 | } else { 279 | pto = NULL; 280 | } 281 | 282 | sret = -1; 283 | 284 | do { 285 | eagain = 0; 286 | switch (fdm) { 287 | case FDM_READ: 288 | sret = select(fd + 1, &fds, NULL, NULL, pto); 289 | break; 290 | case FDM_WRITE: 291 | sret = select(fd + 1, NULL, &fds, NULL, pto); 292 | break; 293 | case FDM_EXCEPT: 294 | sret = select(fd + 1, NULL, NULL, &fds, pto); 295 | break; 296 | default: 297 | return -1; 298 | } 299 | 300 | if (sret < 0) { 301 | switch (errno) { 302 | case EINTR: 303 | // interrupt signal in select 304 | if (verbose >= 2) 305 | fprintf(stderr, "%s: EINTR\n", __func__); 306 | eagain = 1; 307 | break; 308 | case EAGAIN: 309 | if (verbose >= 2) 310 | fprintf(stderr, "%s: EAGAIN\n", __func__); 311 | break; 312 | default: 313 | if (verbose >= 2) 314 | fprintf(stderr, "%s: select failed: %s\n", __func__, 315 | strerror(errno)); 316 | return -1; 317 | } 318 | } 319 | } while (eagain); 320 | 321 | return sret; 322 | } 323 | 324 | int shutdown_socket(int fd, int how) 325 | { 326 | return shutdown(fd, how); 327 | } 328 | 329 | int close_socket(int fd) { 330 | #ifdef WIN32 331 | return closesocket(fd); 332 | #else 333 | return close(fd); 334 | #endif 335 | } 336 | 337 | int recv_buf(int fd, void *data, size_t length) 338 | { 339 | return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT); 340 | } 341 | 342 | int peek_buf(int fd, void *data, size_t length) 343 | { 344 | return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT); 345 | } 346 | 347 | int recv_buf_timeout(int fd, void *data, size_t length, int flags, 348 | unsigned int timeout) 349 | { 350 | int res; 351 | int result; 352 | 353 | // check if data is available 354 | res = check_fd(fd, FDM_READ, timeout); 355 | if (res <= 0) { 356 | return res; 357 | } 358 | // if we get here, there _is_ data available 359 | result = recv(fd, data, length, flags); 360 | if (res > 0 && result == 0) { 361 | // but this is an error condition 362 | if (verbose >= 3) 363 | fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd); 364 | return -EAGAIN; 365 | } 366 | if (result < 0) { 367 | return -errno; 368 | } 369 | return result; 370 | } 371 | 372 | int send_buf(int fd, void *data, size_t length) 373 | { 374 | return send(fd, data, length, 0); 375 | } 376 | -------------------------------------------------------------------------------- /libusbmuxd/sock_stuff.h: -------------------------------------------------------------------------------- 1 | /* 2 | libusbmuxd - client library to talk to usbmuxd 3 | 4 | Copyright (C) 2009 Nikias Bassen 5 | Copyright (C) 2009 Paul Sladen 6 | Copyright (C) 2009 Martin Szulecki 7 | 8 | This library is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as 10 | published by the Free Software Foundation, either version 2.1 of the 11 | License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | 22 | */ 23 | 24 | #ifndef __SOCK_STUFF_H 25 | #define __SOCK_STUFF_H 26 | 27 | #include 28 | 29 | enum fd_mode { 30 | FDM_READ, 31 | FDM_WRITE, 32 | FDM_EXCEPT 33 | }; 34 | typedef enum fd_mode fd_mode; 35 | 36 | #ifdef WIN32 37 | #include 38 | #define SHUT_RD SD_READ 39 | #define SHUT_WR SD_WRITE 40 | #define SHUT_RDWR SD_BOTH 41 | #endif 42 | 43 | #ifndef WIN32 44 | int create_unix_socket(const char *filename); 45 | int connect_unix_socket(const char *filename); 46 | #endif 47 | int create_socket(uint16_t port); 48 | #if defined(WIN32) || defined(__CYGWIN__) 49 | int connect_socket(const char *addr, uint16_t port); 50 | #endif 51 | int check_fd(int fd, fd_mode fdm, unsigned int timeout); 52 | 53 | int shutdown_socket(int fd, int how); 54 | int close_socket(int fd); 55 | 56 | int recv_buf(int fd, void *data, size_t size); 57 | int peek_buf(int fd, void *data, size_t size); 58 | int recv_buf_timeout(int fd, void *data, size_t size, int flags, 59 | unsigned int timeout); 60 | 61 | int send_buf(int fd, void *data, size_t size); 62 | 63 | void sock_stuff_set_verbose(int level); 64 | 65 | #endif /* __SOCK_STUFF_H */ 66 | -------------------------------------------------------------------------------- /libusbmuxd/usbmuxd-proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | libusbmuxd - client library to talk to usbmuxd 3 | 4 | Copyright (C) 2009 Paul Sladen 5 | Copyright (C) 2009 Nikias Bassen 6 | Copyright (C) 2009 Hector Martin "marcan" 7 | 8 | This library is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as 10 | published by the Free Software Foundation, either version 2.1 of the 11 | License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | 22 | */ 23 | 24 | /* Protocol defintion for usbmuxd proxy protocol */ 25 | #ifndef __USBMUXD_PROTO_H 26 | #define __USBMUXD_PROTO_H 27 | 28 | #include 29 | #define USBMUXD_PROTOCOL_VERSION 0 30 | 31 | #if defined(WIN32) || defined(__CYGWIN__) 32 | #define USBMUXD_SOCKET_PORT 27015 33 | #else 34 | #define USBMUXD_SOCKET_FILE "/var/run/usbmuxd" 35 | #endif 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | enum usbmuxd_result { 42 | RESULT_OK = 0, 43 | RESULT_BADCOMMAND = 1, 44 | RESULT_BADDEV = 2, 45 | RESULT_CONNREFUSED = 3, 46 | // ??? 47 | // ??? 48 | RESULT_BADVERSION = 6, 49 | }; 50 | 51 | enum usbmuxd_msgtype { 52 | MESSAGE_RESULT = 1, 53 | MESSAGE_CONNECT = 2, 54 | MESSAGE_LISTEN = 3, 55 | MESSAGE_DEVICE_ADD = 4, 56 | MESSAGE_DEVICE_REMOVE = 5, 57 | //??? 58 | //??? 59 | MESSAGE_PLIST = 8, 60 | }; 61 | 62 | struct usbmuxd_header { 63 | uint32_t length; // length of message, including header 64 | uint32_t version; // protocol version 65 | uint32_t message; // message type 66 | uint32_t tag; // responses to this query will echo back this tag 67 | } __attribute__((__packed__)); 68 | 69 | struct usbmuxd_result_msg { 70 | struct usbmuxd_header header; 71 | uint32_t result; 72 | } __attribute__((__packed__)); 73 | 74 | struct usbmuxd_connect_request { 75 | struct usbmuxd_header header; 76 | uint32_t device_id; 77 | uint16_t port; // TCP port number 78 | uint16_t reserved; // set to zero 79 | } __attribute__((__packed__)); 80 | 81 | struct usbmuxd_listen_request { 82 | struct usbmuxd_header header; 83 | } __attribute__((__packed__)); 84 | 85 | struct usbmuxd_device_record { 86 | uint32_t device_id; 87 | uint16_t product_id; 88 | char serial_number[256]; 89 | uint16_t padding; 90 | uint32_t location; 91 | } __attribute__((__packed__)); 92 | 93 | #ifdef __cplusplus 94 | } 95 | #endif 96 | 97 | #endif /* __USBMUXD_PROTO_H */ 98 | -------------------------------------------------------------------------------- /libusbmuxd/usbmuxd.h: -------------------------------------------------------------------------------- 1 | /* 2 | libusbmuxd - client library to talk to usbmuxd 3 | 4 | Copyright (C) 2009 Nikias Bassen 5 | Copyright (C) 2009 Paul Sladen 6 | Copyright (C) 2009 Martin Szulecki 7 | 8 | This library is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as 10 | published by the Free Software Foundation, either version 2.1 of the 11 | License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | 22 | */ 23 | 24 | #ifndef __USBMUXD_H 25 | #define __USBMUXD_H 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * Device information structure holding data to identify the device. 34 | * The relevant 'handle' should be passed to 'usbmuxd_connect()', to 35 | * start a proxy connection. The value 'handle' should be considered 36 | * opaque and no presumption made about the meaning of its value. 37 | */ 38 | typedef struct { 39 | int handle; 40 | int product_id; 41 | char udid[41]; 42 | } usbmuxd_device_info_t; 43 | 44 | /** 45 | * event types for event callback function 46 | */ 47 | enum usbmuxd_event_type { 48 | UE_DEVICE_ADD = 1, 49 | UE_DEVICE_REMOVE 50 | }; 51 | 52 | /** 53 | * Event structure that will be passed to the callback function. 54 | * 'event' will contains the type of the event, and 'device' will contains 55 | * information about the device. 56 | */ 57 | typedef struct { 58 | int event; 59 | usbmuxd_device_info_t device; 60 | } usbmuxd_event_t; 61 | 62 | /** 63 | * Callback function prototype. 64 | */ 65 | typedef void (*usbmuxd_event_cb_t) (const usbmuxd_event_t *event, void *user_data); 66 | 67 | /** 68 | * Subscribe a callback function so that applications get to know about 69 | * device add/remove events. 70 | * 71 | * @param callback A callback function that is executed when an event occurs. 72 | * 73 | * @return 0 on success or negative on error. 74 | */ 75 | int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data); 76 | 77 | /** 78 | * Unsubscribe callback. 79 | * 80 | * @return only 0 for now. 81 | */ 82 | int usbmuxd_unsubscribe(); 83 | 84 | /** 85 | * Contacts usbmuxd and retrieves a list of connected devices. 86 | * 87 | * @param device_list A pointer to an array of usbmuxd_device_info_t 88 | * that will hold records of the connected devices. The last record 89 | * is a null-terminated record with all fields set to 0/NULL. 90 | * @note The user has to free the list returned. 91 | * 92 | * @return number of attached devices, zero on no devices, or negative 93 | * if an error occured. 94 | */ 95 | int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list); 96 | 97 | /** 98 | * Frees the device list returned by an usbmuxd_get_device_list call 99 | * 100 | * @param device_list A pointer to an array of usbmuxd_device_info_t to free. 101 | * 102 | * @return 0 on success, -1 on error. 103 | */ 104 | int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list); 105 | 106 | /** 107 | * Gets device information for the device specified by udid. 108 | * 109 | * @param udid A device UDID of the device to look for. If udid is NULL, 110 | * This function will return the first device found. 111 | * @param device Pointer to a previously allocated (or static) 112 | * usbmuxd_device_info_t that will be filled with the device info. 113 | * 114 | * @return 0 if no matching device is connected, 1 if the device was found, 115 | * or a negative value on error. 116 | */ 117 | int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device); 118 | 119 | /** 120 | * Request proxy connect to 121 | * 122 | * @param handle returned by 'usbmuxd_scan()' 123 | * 124 | * @param tcp_port TCP port number on device, in range 0-65535. 125 | * common values are 62078 for lockdown, and 22 for SSH. 126 | * 127 | * @return file descriptor socket of the connection, or -1 on error 128 | */ 129 | int usbmuxd_connect(const int handle, const unsigned short tcp_port); 130 | 131 | /** 132 | * Disconnect. For now, this just closes the socket file descriptor. 133 | * 134 | * @param sfd socker file descriptor returned by usbmuxd_connect() 135 | * 136 | * @return 0 on success, -1 on error. 137 | */ 138 | int usbmuxd_disconnect(int sfd); 139 | 140 | /** 141 | * Send data to the specified socket. 142 | * 143 | * @param sfd socket file descriptor returned by usbmuxd_connect() 144 | * @param data buffer to send 145 | * @param len size of buffer to send 146 | * @param sent_bytes how many bytes sent 147 | * 148 | * @return 0 on success, a negative errno value otherwise. 149 | */ 150 | int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes); 151 | 152 | /** 153 | * Receive data from the specified socket. 154 | * 155 | * @param sfd socket file descriptor returned by usbmuxd_connect() 156 | * @param data buffer to put the data to 157 | * @param len number of bytes to receive 158 | * @param recv_bytes number of bytes received 159 | * @param timeout how many milliseconds to wait for data 160 | * 161 | * @return 0 on success, a negative errno value otherwise. 162 | */ 163 | int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout); 164 | 165 | /** 166 | * Receive data from the specified socket with a default timeout. 167 | * 168 | * @param sfd socket file descriptor returned by usbmuxd_connect() 169 | * @param data buffer to put the data to 170 | * @param len number of bytes to receive 171 | * @param recv_bytes number of bytes received 172 | * 173 | * @return 0 on success, a negative errno value otherwise. 174 | */ 175 | int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes); 176 | 177 | /** 178 | * Enable or disable the use of inotify extension. Enabled by default. 179 | * Use 0 to disable and 1 to enable inotify support. 180 | * This only has an effect on linux systems if inotify support has been built 181 | * in. Otherwise and on all other platforms this function has no effect. 182 | */ 183 | void libusbmuxd_set_use_inotify(int set); 184 | 185 | void libusbmuxd_set_debug_level(int level); 186 | 187 | #ifdef __cplusplus 188 | } 189 | #endif 190 | 191 | #endif /* __USBMUXD_H */ 192 | -------------------------------------------------------------------------------- /python-client/tcprelay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # tcprelay.py - TCP connection relay for usbmuxd 5 | # 6 | # Copyright (C) 2009 Hector Martin "marcan" 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 or version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | import usbmux 22 | import SocketServer 23 | import select 24 | from optparse import OptionParser 25 | import sys 26 | import threading 27 | 28 | class SocketRelay(object): 29 | def __init__(self, a, b, maxbuf=65535): 30 | self.a = a 31 | self.b = b 32 | self.atob = "" 33 | self.btoa = "" 34 | self.maxbuf = maxbuf 35 | def handle(self): 36 | while True: 37 | rlist = [] 38 | wlist = [] 39 | xlist = [self.a, self.b] 40 | if self.atob: 41 | wlist.append(self.b) 42 | if self.btoa: 43 | wlist.append(self.a) 44 | if len(self.atob) < self.maxbuf: 45 | rlist.append(self.a) 46 | if len(self.btoa) < self.maxbuf: 47 | rlist.append(self.b) 48 | rlo, wlo, xlo = select.select(rlist, wlist, xlist) 49 | if xlo: 50 | return 51 | if self.a in wlo: 52 | n = self.a.send(self.btoa) 53 | self.btoa = self.btoa[n:] 54 | if self.b in wlo: 55 | n = self.b.send(self.atob) 56 | self.atob = self.atob[n:] 57 | if self.a in rlo: 58 | s = self.a.recv(self.maxbuf - len(self.atob)) 59 | if not s: 60 | return 61 | self.atob += s 62 | if self.b in rlo: 63 | s = self.b.recv(self.maxbuf - len(self.btoa)) 64 | if not s: 65 | return 66 | self.btoa += s 67 | #print "Relay iter: %8d atob, %8d btoa, lists: %r %r %r"%(len(self.atob), len(self.btoa), rlo, wlo, xlo) 68 | 69 | class TCPRelay(SocketServer.BaseRequestHandler): 70 | def handle(self): 71 | print "Incoming connection to %d"%self.server.server_address[1] 72 | mux = usbmux.USBMux(options.sockpath) 73 | print "Waiting for devices..." 74 | if not mux.devices: 75 | mux.process(1.0) 76 | if not mux.devices: 77 | print "No device found" 78 | self.request.close() 79 | return 80 | dev = mux.devices[0] 81 | print "Connecting to device %s"%str(dev) 82 | dsock = mux.connect(dev, self.server.rport) 83 | lsock = self.request 84 | print "Connection established, relaying data" 85 | try: 86 | fwd = SocketRelay(dsock, lsock, self.server.bufsize * 1024) 87 | fwd.handle() 88 | finally: 89 | dsock.close() 90 | lsock.close() 91 | print "Connection closed" 92 | 93 | class TCPServer(SocketServer.TCPServer): 94 | allow_reuse_address = True 95 | 96 | class ThreadedTCPServer(SocketServer.ThreadingMixIn, TCPServer): 97 | pass 98 | 99 | HOST = "localhost" 100 | 101 | parser = OptionParser(usage="usage: %prog [OPTIONS] RemotePort[:LocalPort] [RemotePort[:LocalPort]]...") 102 | parser.add_option("-t", "--threaded", dest='threaded', action='store_true', default=False, help="use threading to handle multiple connections at once") 103 | parser.add_option("-b", "--bufsize", dest='bufsize', action='store', metavar='KILOBYTES', type='int', default=128, help="specify buffer size for socket forwarding") 104 | parser.add_option("-s", "--socket", dest='sockpath', action='store', metavar='PATH', type='str', default=None, help="specify the path of the usbmuxd socket") 105 | 106 | options, args = parser.parse_args() 107 | 108 | serverclass = TCPServer 109 | if options.threaded: 110 | serverclass = ThreadedTCPServer 111 | 112 | if len(args) == 0: 113 | parser.print_help() 114 | sys.exit(1) 115 | 116 | ports = [] 117 | 118 | for arg in args: 119 | try: 120 | if ':' in arg: 121 | rport, lport = arg.split(":") 122 | rport = int(rport) 123 | lport = int(lport) 124 | ports.append((rport, lport)) 125 | else: 126 | ports.append((int(arg), int(arg))) 127 | except: 128 | parser.print_help() 129 | sys.exit(1) 130 | 131 | servers=[] 132 | 133 | for rport, lport in ports: 134 | print "Forwarding local port %d to remote port %d"%(lport, rport) 135 | server = serverclass((HOST, lport), TCPRelay) 136 | server.rport = rport 137 | server.bufsize = options.bufsize 138 | servers.append(server) 139 | 140 | alive = True 141 | 142 | while alive: 143 | try: 144 | rl, wl, xl = select.select(servers, [], []) 145 | for server in rl: 146 | server.handle_request() 147 | except: 148 | alive = False 149 | -------------------------------------------------------------------------------- /python-client/usbmux.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # usbmux.py - usbmux client library for Python 5 | # 6 | # Copyright (C) 2009 Hector Martin "marcan" 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 or version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | import socket, struct, select, sys 22 | 23 | try: 24 | import plistlib 25 | haveplist = True 26 | except: 27 | haveplist = False 28 | 29 | class MuxError(Exception): 30 | pass 31 | 32 | class MuxVersionError(MuxError): 33 | pass 34 | 35 | class SafeStreamSocket: 36 | def __init__(self, address, family): 37 | self.sock = socket.socket(family, socket.SOCK_STREAM) 38 | self.sock.connect(address) 39 | def send(self, msg): 40 | totalsent = 0 41 | while totalsent < len(msg): 42 | sent = self.sock.send(msg[totalsent:]) 43 | if sent == 0: 44 | raise MuxError("socket connection broken") 45 | totalsent = totalsent + sent 46 | def recv(self, size): 47 | msg = '' 48 | while len(msg) < size: 49 | chunk = self.sock.recv(size-len(msg)) 50 | if chunk == '': 51 | raise MuxError("socket connection broken") 52 | msg = msg + chunk 53 | return msg 54 | 55 | class MuxDevice(object): 56 | def __init__(self, devid, usbprod, serial, location): 57 | self.devid = devid 58 | self.usbprod = usbprod 59 | self.serial = serial 60 | self.location = location 61 | def __str__(self): 62 | return ""%(self.devid, self.usbprod, self.serial, self.location) 63 | 64 | class BinaryProtocol(object): 65 | TYPE_RESULT = 1 66 | TYPE_CONNECT = 2 67 | TYPE_LISTEN = 3 68 | TYPE_DEVICE_ADD = 4 69 | TYPE_DEVICE_REMOVE = 5 70 | VERSION = 0 71 | def __init__(self, socket): 72 | self.socket = socket 73 | self.connected = False 74 | 75 | def _pack(self, req, payload): 76 | if req == self.TYPE_CONNECT: 77 | return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + "\x00\x00" 78 | elif req == self.TYPE_LISTEN: 79 | return "" 80 | else: 81 | raise ValueError("Invalid outgoing request type %d"%req) 82 | 83 | def _unpack(self, resp, payload): 84 | if resp == self.TYPE_RESULT: 85 | return {'Number':struct.unpack("I", payload)[0]} 86 | elif resp == self.TYPE_DEVICE_ADD: 87 | devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload) 88 | serial = serial.split("\0")[0] 89 | return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}} 90 | elif resp == self.TYPE_DEVICE_REMOVE: 91 | devid = struct.unpack("I", payload)[0] 92 | return {'DeviceID': devid} 93 | else: 94 | raise MuxError("Invalid incoming request type %d"%req) 95 | 96 | def sendpacket(self, req, tag, payload={}): 97 | payload = self._pack(req, payload) 98 | if self.connected: 99 | raise MuxError("Mux is connected, cannot issue control packets") 100 | length = 16 + len(payload) 101 | data = struct.pack("IIII", length, self.VERSION, req, tag) + payload 102 | self.socket.send(data) 103 | def getpacket(self): 104 | if self.connected: 105 | raise MuxError("Mux is connected, cannot issue control packets") 106 | dlen = self.socket.recv(4) 107 | dlen = struct.unpack("I", dlen)[0] 108 | body = self.socket.recv(dlen - 4) 109 | version, resp, tag = struct.unpack("III",body[:0xc]) 110 | if version != self.VERSION: 111 | raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version)) 112 | payload = self._unpack(resp, body[0xc:]) 113 | return (resp, tag, payload) 114 | 115 | class PlistProtocol(BinaryProtocol): 116 | TYPE_RESULT = "Result" 117 | TYPE_CONNECT = "Connect" 118 | TYPE_LISTEN = "Listen" 119 | TYPE_DEVICE_ADD = "Attached" 120 | TYPE_DEVICE_REMOVE = "Detached" #??? 121 | TYPE_PLIST = 8 122 | VERSION = 1 123 | def __init__(self, socket): 124 | if not haveplist: 125 | raise Exception("You need the plistlib module") 126 | BinaryProtocol.__init__(self, socket) 127 | 128 | def _pack(self, req, payload): 129 | return payload 130 | 131 | def _unpack(self, resp, payload): 132 | return payload 133 | 134 | def sendpacket(self, req, tag, payload={}): 135 | payload['ClientVersionString'] = 'usbmux.py by marcan' 136 | if isinstance(req, int): 137 | req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2] 138 | payload['MessageType'] = req 139 | payload['ProgName'] = 'tcprelay' 140 | BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.writePlistToString(payload)) 141 | def getpacket(self): 142 | resp, tag, payload = BinaryProtocol.getpacket(self) 143 | if resp != self.TYPE_PLIST: 144 | raise MuxError("Received non-plist type %d"%resp) 145 | payload = plistlib.readPlistFromString(payload) 146 | return payload['MessageType'], tag, payload 147 | 148 | class MuxConnection(object): 149 | def __init__(self, socketpath, protoclass): 150 | self.socketpath = socketpath 151 | if sys.platform in ['win32', 'cygwin']: 152 | family = socket.AF_INET 153 | address = ('127.0.0.1', 27015) 154 | else: 155 | family = socket.AF_UNIX 156 | address = self.socketpath 157 | self.socket = SafeStreamSocket(address, family) 158 | self.proto = protoclass(self.socket) 159 | self.pkttag = 1 160 | self.devices = [] 161 | 162 | def _getreply(self): 163 | while True: 164 | resp, tag, data = self.proto.getpacket() 165 | if resp == self.proto.TYPE_RESULT: 166 | return tag, data 167 | else: 168 | raise MuxError("Invalid packet type received: %d"%resp) 169 | def _processpacket(self): 170 | resp, tag, data = self.proto.getpacket() 171 | if resp == self.proto.TYPE_DEVICE_ADD: 172 | self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID'])) 173 | elif resp == self.proto.TYPE_DEVICE_REMOVE: 174 | for dev in self.devices: 175 | if dev.devid == data['DeviceID']: 176 | self.devices.remove(dev) 177 | elif resp == self.proto.TYPE_RESULT: 178 | raise MuxError("Unexpected result: %d"%resp) 179 | else: 180 | raise MuxError("Invalid packet type received: %d"%resp) 181 | def _exchange(self, req, payload={}): 182 | mytag = self.pkttag 183 | self.pkttag += 1 184 | self.proto.sendpacket(req, mytag, payload) 185 | recvtag, data = self._getreply() 186 | if recvtag != mytag: 187 | raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag)) 188 | return data['Number'] 189 | 190 | def listen(self): 191 | ret = self._exchange(self.proto.TYPE_LISTEN) 192 | if ret != 0: 193 | raise MuxError("Listen failed: error %d"%ret) 194 | def process(self, timeout=None): 195 | if self.proto.connected: 196 | raise MuxError("Socket is connected, cannot process listener events") 197 | rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout) 198 | if xlo: 199 | self.socket.sock.close() 200 | raise MuxError("Exception in listener socket") 201 | if rlo: 202 | self._processpacket() 203 | def connect(self, device, port): 204 | ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)}) 205 | if ret != 0: 206 | raise MuxError("Connect failed: error %d"%ret) 207 | self.proto.connected = True 208 | return self.socket.sock 209 | def close(self): 210 | self.socket.sock.close() 211 | 212 | class USBMux(object): 213 | def __init__(self, socketpath=None): 214 | if socketpath is None: 215 | if sys.platform == 'darwin': 216 | socketpath = "/var/run/usbmuxd" 217 | else: 218 | socketpath = "/var/run/usbmuxd" 219 | self.socketpath = socketpath 220 | self.listener = MuxConnection(socketpath, BinaryProtocol) 221 | try: 222 | self.listener.listen() 223 | self.version = 0 224 | self.protoclass = BinaryProtocol 225 | except MuxVersionError: 226 | self.listener = MuxConnection(socketpath, PlistProtocol) 227 | self.listener.listen() 228 | self.protoclass = PlistProtocol 229 | self.version = 1 230 | self.devices = self.listener.devices 231 | def process(self, timeout=None): 232 | self.listener.process(timeout) 233 | def connect(self, device, port): 234 | connector = MuxConnection(self.socketpath, self.protoclass) 235 | return connector.connect(device, port) 236 | 237 | if __name__ == "__main__": 238 | mux = USBMux() 239 | print "Waiting for devices..." 240 | if not mux.devices: 241 | mux.process(0.1) 242 | while True: 243 | print "Devices:" 244 | for dev in mux.devices: 245 | print dev 246 | mux.process() 247 | -------------------------------------------------------------------------------- /stuff/README: -------------------------------------------------------------------------------- 1 | *** NOTE: 2 | *** Doing this is mostly obsolete. The preferred method to sync music to an 3 | *** iPhone now is to use ifuse or some other afc-based client, which does not 4 | *** require jailbreaking or adding this service. Please take a look at the 5 | *** libiphone, ifuse, and libgpod projects. 6 | 7 | com.openssh.sft.plist is a launchd configuration to set up a bare SFTP server 8 | on TCP port 2299 localhost-only for USB use. It's nice for relatively fast music 9 | syncing and file transfer under Linux (and it avoids encryption). Con: it gives 10 | anyone with usb access root FS access on the phone, as well as anything running 11 | on the phone itself. 12 | 13 | Use it with a command like this: 14 | 15 | IPATH=/var/mobile/Media 16 | MOUNTPOINT=$HOME/media/iphone 17 | $ sshfs localhost:$IPATH $MOUNTPOINT -o workaround=rename -o directport=2299 \ 18 | -o kernel_cache -o entry_timeout=30 -o attr_timeout=30 19 | 20 | Make sure you run tcprelay.py: 21 | $ python tcprelay.py -t 2299 22 | 23 | Remember that to bypass the stupid new iTunesDB hash you need to edit 24 | /System/Library/Lockdown/Checkpoint.xml and change DBVersion to 2. 25 | 26 | -------------------------------------------------------------------------------- /stuff/com.openssh.sftp.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Label 7 | com.openssh.sftpd 8 | 9 | Program 10 | /usr/libexec/sftp-server 11 | 12 | ProgramArguments 13 | 14 | /usr/libexec/sftp-server 15 | 16 | 17 | SessionCreate 18 | 19 | 20 | Sockets 21 | 22 | Listeners 23 | 24 | SockServiceName 25 | 2299 26 | SockNodeName 27 | 127.0.0.1 28 | 29 | 30 | 31 | StandardErrorPath 32 | /dev/null 33 | 34 | inetdCompatibility 35 | 36 | Wait 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories (${CMAKE_SOURCE_DIR}/libusbmuxd) 2 | link_directories (${CMAKE_BINARY_DIR}/libusbmuxd) 3 | 4 | add_executable(iproxy iproxy.c) 5 | if(WIN32) 6 | target_link_libraries(iproxy libusbmuxd pthread ws2_32) 7 | else() 8 | target_link_libraries(iproxy libusbmuxd pthread) 9 | endif() 10 | 11 | install(TARGETS iproxy RUNTIME DESTINATION bin) 12 | -------------------------------------------------------------------------------- /tools/iproxy.c: -------------------------------------------------------------------------------- 1 | /* 2 | iproxy -- proxy that enables tcp service access to iPhone/iPod 3 | 4 | Copyright (C) 2009 Nikias Bassen 5 | Copyright (C) 2009 Paul Sladen 6 | 7 | Based upon iTunnel source code, Copyright (c) 2008 Jing Su. 8 | http://www.cs.toronto.edu/~jingsu/itunnel/ 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | TODO: improve code... 25 | 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #ifdef WIN32 36 | #include 37 | #include 38 | typedef unsigned int socklen_t; 39 | #else 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #endif 46 | #include "sock_stuff.h" 47 | #include "usbmuxd.h" 48 | 49 | static uint16_t listen_port = 0; 50 | static uint16_t device_port = 0; 51 | 52 | struct client_data { 53 | int fd; 54 | int sfd; 55 | volatile int stop_ctos; 56 | volatile int stop_stoc; 57 | }; 58 | 59 | void *run_stoc_loop(void *arg) 60 | { 61 | struct client_data *cdata = (struct client_data*)arg; 62 | int recv_len; 63 | int sent; 64 | char buffer[131072]; 65 | 66 | printf("%s: fd = %d\n", __func__, cdata->fd); 67 | 68 | while (!cdata->stop_stoc && cdata->fd>0 && cdata->sfd>0) { 69 | recv_len = recv_buf_timeout(cdata->sfd, buffer, sizeof(buffer), 0, 5000); 70 | if (recv_len <= 0) { 71 | if (recv_len == 0) { 72 | // try again 73 | continue; 74 | } else { 75 | fprintf(stderr, "recv failed: %s\n", strerror(errno)); 76 | break; 77 | } 78 | } else { 79 | // printf("received %d bytes from server\n", recv_len); 80 | // send to socket 81 | sent = send_buf(cdata->fd, buffer, recv_len); 82 | if (sent < recv_len) { 83 | if (sent <= 0) { 84 | fprintf(stderr, "send failed: %s\n", strerror(errno)); 85 | break; 86 | } else { 87 | fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); 88 | } 89 | } else { 90 | // sending succeeded, receive from device 91 | // printf("pushed %d bytes to client\n", sent); 92 | } 93 | } 94 | } 95 | close(cdata->fd); 96 | cdata->fd = -1; 97 | cdata->stop_ctos = 1; 98 | 99 | return NULL; 100 | } 101 | 102 | void *run_ctos_loop(void *arg) 103 | { 104 | struct client_data *cdata = (struct client_data*)arg; 105 | int recv_len; 106 | int sent; 107 | char buffer[131072]; 108 | #ifdef WIN32 109 | HANDLE stoc = NULL; 110 | #else 111 | pthread_t stoc; 112 | #endif 113 | 114 | printf("%s: fd = %d\n", __func__, cdata->fd); 115 | 116 | cdata->stop_stoc = 0; 117 | #ifdef WIN32 118 | stoc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)run_stoc_loop, cdata, 0, NULL); 119 | #else 120 | pthread_create(&stoc, NULL, run_stoc_loop, cdata); 121 | #endif 122 | 123 | while (!cdata->stop_ctos && cdata->fd>0 && cdata->sfd>0) { 124 | recv_len = recv_buf_timeout(cdata->fd, buffer, sizeof(buffer), 0, 5000); 125 | if (recv_len <= 0) { 126 | if (recv_len == 0) { 127 | // try again 128 | continue; 129 | } else { 130 | fprintf(stderr, "recv failed: %s\n", strerror(errno)); 131 | break; 132 | } 133 | } else { 134 | // printf("pulled %d bytes from client\n", recv_len); 135 | // send to local socket 136 | sent = send_buf(cdata->sfd, buffer, recv_len); 137 | if (sent < recv_len) { 138 | if (sent <= 0) { 139 | fprintf(stderr, "send failed: %s\n", strerror(errno)); 140 | break; 141 | } else { 142 | fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); 143 | } 144 | } else { 145 | // sending succeeded, receive from device 146 | // printf("sent %d bytes to server\n", sent); 147 | } 148 | } 149 | } 150 | close(cdata->fd); 151 | cdata->fd = -1; 152 | cdata->stop_stoc = 1; 153 | 154 | #ifdef WIN32 155 | WaitForSingleObject(stoc, INFINITE); 156 | #else 157 | pthread_join(stoc, NULL); 158 | #endif 159 | 160 | return NULL; 161 | } 162 | 163 | void *acceptor_thread(void *arg) 164 | { 165 | struct client_data *cdata; 166 | usbmuxd_device_info_t *dev_list = NULL; 167 | #ifdef WIN32 168 | HANDLE ctos = NULL; 169 | #else 170 | pthread_t ctos; 171 | #endif 172 | int count; 173 | 174 | if (!arg) { 175 | fprintf(stderr, "invalid client_data provided!\n"); 176 | return NULL; 177 | } 178 | 179 | cdata = (struct client_data*)arg; 180 | 181 | if ((count = usbmuxd_get_device_list(&dev_list)) < 0) { 182 | printf("Connecting to usbmuxd failed, terminating.\n"); 183 | free(dev_list); 184 | return NULL; 185 | } 186 | 187 | fprintf(stdout, "Number of available devices == %d\n", count); 188 | 189 | if (dev_list == NULL || dev_list[0].handle == 0) { 190 | printf("No connected device found, terminating.\n"); 191 | free(dev_list); 192 | return NULL; 193 | } 194 | 195 | fprintf(stdout, "Requesting connecion to device handle == %d (serial: %s), port %d\n", dev_list[0].handle, dev_list[0].udid, device_port); 196 | 197 | cdata->sfd = usbmuxd_connect(dev_list[0].handle, device_port); 198 | free(dev_list); 199 | if (cdata->sfd < 0) { 200 | fprintf(stderr, "Error connecting to device!\n"); 201 | } else { 202 | cdata->stop_ctos = 0; 203 | #ifdef WIN32 204 | ctos = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)run_ctos_loop, cdata, 0, NULL); 205 | WaitForSingleObject(ctos, INFINITE); 206 | #else 207 | pthread_create(&ctos, NULL, run_ctos_loop, cdata); 208 | pthread_join(ctos, NULL); 209 | #endif 210 | } 211 | 212 | if (cdata->fd > 0) { 213 | close(cdata->fd); 214 | } 215 | if (cdata->sfd > 0) { 216 | close(cdata->sfd); 217 | } 218 | 219 | return NULL; 220 | } 221 | 222 | int main(int argc, char **argv) 223 | { 224 | setlinebuf(stdout); 225 | 226 | int mysock = -1; 227 | 228 | if (argc != 3) { 229 | printf("usage: %s LOCAL_TCP_PORT DEVICE_TCP_PORT\n", argv[0]); 230 | return 0; 231 | } 232 | 233 | listen_port = atoi(argv[1]); 234 | device_port = atoi(argv[2]); 235 | 236 | if (!listen_port) { 237 | fprintf(stderr, "Invalid listen_port specified!\n"); 238 | return -EINVAL; 239 | } 240 | 241 | if (!device_port) { 242 | fprintf(stderr, "Invalid device_port specified!\n"); 243 | return -EINVAL; 244 | } 245 | 246 | // first create the listening socket endpoint waiting for connections. 247 | mysock = create_socket(listen_port); 248 | if (mysock < 0) { 249 | fprintf(stderr, "Error creating socket: %s\n", strerror(errno)); 250 | return -errno; 251 | } else { 252 | #ifdef WIN32 253 | HANDLE acceptor = NULL; 254 | #else 255 | pthread_t acceptor; 256 | #endif 257 | struct sockaddr_in c_addr; 258 | socklen_t len = sizeof(struct sockaddr_in); 259 | struct client_data cdata; 260 | int c_sock; 261 | while (1) { 262 | printf("waiting for connection\n"); 263 | c_sock = accept(mysock, (struct sockaddr*)&c_addr, &len); 264 | if (c_sock) { 265 | printf("accepted connection, fd = %d\n", c_sock); 266 | cdata.fd = c_sock; 267 | #ifdef WIN32 268 | acceptor = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)acceptor_thread, &cdata, 0, NULL); 269 | WaitForSingleObject(acceptor, INFINITE); 270 | #else 271 | pthread_create(&acceptor, NULL, acceptor_thread, &cdata); 272 | pthread_join(acceptor, NULL); 273 | #endif 274 | } else { 275 | break; 276 | } 277 | } 278 | close(c_sock); 279 | close(mysock); 280 | } 281 | 282 | return 0; 283 | } 284 | -------------------------------------------------------------------------------- /udev/85-usbmuxd.rules.in: -------------------------------------------------------------------------------- 1 | # usbmuxd ("Apple Mobile Device" muxer listening on /var/run/usbmuxd) 2 | 3 | # Forces iDevices to the last USB configuration and runs usbmuxd 4 | ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="12[9a][0-9a-f]", ENV{USBMUX_SUPPORTED}="1", ATTR{bConfigurationValue}!="$attr{bNumConfigurations}", ATTR{bConfigurationValue}="$attr{bNumConfigurations}", OWNER="usbmux", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -u -U usbmux" 5 | 6 | # Exit usbmuxd when the last device is removed 7 | ACTION=="remove", SUBSYSTEM=="usb", ENV{PRODUCT}=="5ac/12[9a][0-9a-f]/*", ENV{INTERFACE}=="255/*", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -x" 8 | -------------------------------------------------------------------------------- /udev/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/85-usbmuxd.rules.in ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules @ONLY) 2 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules DESTINATION /lib/udev/rules.d/) 3 | --------------------------------------------------------------------------------