summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--AUTHORS1
-rw-r--r--CMakeLists.txt10
-rw-r--r--COPYING.GPLv2 (renamed from COPYING)0
-rw-r--r--COPYING.GPLv3674
-rw-r--r--COPYING.LGPLv2.1 (renamed from COPYING.LESSER)0
-rw-r--r--Makefile.am9
-rw-r--r--Modules/FindUSB.cmake31
-rw-r--r--Modules/LibFindMacros.cmake99
-rw-r--r--README185
-rwxr-xr-xautogen.sh11
-rw-r--r--configure.ac30
-rw-r--r--libusbmuxd.pc.in12
-rw-r--r--libusbmuxd/CMakeLists.txt12
-rw-r--r--libusbmuxd/libusbmuxd.c (renamed from src/libusbmuxd.c)4
-rw-r--r--libusbmuxd/sock_stuff.c (renamed from src/sock_stuff.c)0
-rw-r--r--libusbmuxd/sock_stuff.h (renamed from src/sock_stuff.h)0
-rw-r--r--libusbmuxd/usbmuxd-proto.h (renamed from src/usbmuxd-proto.h)0
-rw-r--r--libusbmuxd/usbmuxd.h (renamed from src/usbmuxd.h)0
-rw-r--r--python-client/.gitignore3
-rw-r--r--python-client/tcprelay.py147
-rw-r--r--python-client/usbmux.py245
-rw-r--r--src/Makefile.am31
-rw-r--r--src/main.c1447
-rw-r--r--src/usbmux.c1264
-rw-r--r--src/usbmux.h53
-rw-r--r--stuff/README19
-rw-r--r--stuff/com.openssh.sftp.plist41
-rw-r--r--tools/CMakeLists.txt8
-rw-r--r--tools/Makefile.am5
-rw-r--r--udev/85-usbmuxd.rules.in4
-rw-r--r--udev/CMakeLists.txt2
-rw-r--r--udev/Makefile.am4
-rw-r--r--usbmuxd/CMakeLists.txt9
-rw-r--r--usbmuxd/client.c430
-rw-r--r--usbmuxd/client.h92
-rw-r--r--usbmuxd/device.c751
-rw-r--r--usbmuxd/device.h52
-rw-r--r--usbmuxd/log.c94
-rw-r--r--usbmuxd/log.h43
-rw-r--r--usbmuxd/main.c548
-rw-r--r--usbmuxd/usb-linux.c503
-rw-r--r--usbmuxd/usb.h67
-rw-r--r--usbmuxd/utils.c110
-rw-r--r--usbmuxd/utils.h65
45 files changed, 4217 insertions, 2905 deletions
diff --git a/.gitignore b/.gitignore
index e6a7834..0784410 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,3 @@
*~
-*.o
-*.so
-.*.swp
-iproxy
-usbmuxd
+build
+
diff --git a/AUTHORS b/AUTHORS
index f84e02b..c196afd 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,4 +1,5 @@
Nikias Bassen
+Hector Martin
Bastien Nocera
Paul Sladen
Martin Szulecki
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..ba1653f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,10 @@
+PROJECT(usbmuxd)
+
+cmake_minimum_required(VERSION 2.6)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/Modules/")
+
+add_subdirectory (libusbmuxd)
+add_subdirectory (usbmuxd)
+add_subdirectory (tools)
+add_subdirectory (udev)
diff --git a/COPYING b/COPYING.GPLv2
index 08ddefd..08ddefd 100644
--- a/COPYING
+++ b/COPYING.GPLv2
diff --git a/COPYING.GPLv3 b/COPYING.GPLv3
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING.GPLv3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/COPYING.LESSER b/COPYING.LGPLv2.1
index 732811e..732811e 100644
--- a/COPYING.LESSER
+++ b/COPYING.LGPLv2.1
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 403981d..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-SUBDIRS = src udev tools
-
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libusbmuxd.pc
-
-indent:
- indent -kr -ut -ts4 -l120 src/*.c src/*.h tools/*.c
-
-EXTRA_DIST = README.devel
diff --git a/Modules/FindUSB.cmake b/Modules/FindUSB.cmake
new file mode 100644
index 0000000..d562b98
--- /dev/null
+++ b/Modules/FindUSB.cmake
@@ -0,0 +1,31 @@
+# - Try to find libusb-1.0
+# Once done, this will define
+#
+# USB_FOUND - system has libusb-1.0
+# USB_INCLUDE_DIRS - the libusb-1.0 include directories
+# USB_LIBRARIES - link these to use libusb-1.0
+
+include(LibFindMacros)
+
+# Dependencies
+
+# Use pkg-config to get hints about paths
+libfind_pkg_check_modules(USB_PKGCONF libusb-1.0)
+
+# Include dir
+find_path(USB_INCLUDE_DIR
+ NAMES libusb.h
+ PATHS ${USB_PKGCONF_INCLUDE_DIRS}
+)
+
+# Finally the library itself
+find_library(USB_LIBRARY
+ NAMES usb-1.0
+ PATHS ${USB_PKGCONF_LIBRARY_DIRS}
+)
+
+# Set the include dir variables and the libraries and let libfind_process do the rest.
+# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
+set(USB_PROCESS_INCLUDES USB_INCLUDE_DIR)
+set(USB_PROCESS_LIBS USB_LIBRARY)
+libfind_process(USB)
diff --git a/Modules/LibFindMacros.cmake b/Modules/LibFindMacros.cmake
new file mode 100644
index 0000000..795d6b7
--- /dev/null
+++ b/Modules/LibFindMacros.cmake
@@ -0,0 +1,99 @@
+# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments
+# used for the current package. For this to work, the first parameter must be the
+# prefix of the current package, then the prefix of the new package etc, which are
+# passed to find_package.
+macro (libfind_package PREFIX)
+ set (LIBFIND_PACKAGE_ARGS ${ARGN})
+ if (${PREFIX}_FIND_QUIETLY)
+ set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET)
+ endif (${PREFIX}_FIND_QUIETLY)
+ if (${PREFIX}_FIND_REQUIRED)
+ set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED)
+ endif (${PREFIX}_FIND_REQUIRED)
+ find_package(${LIBFIND_PACKAGE_ARGS})
+endmacro (libfind_package)
+
+# Damn CMake developers made the UsePkgConfig system deprecated in the same release (2.6)
+# where they added pkg_check_modules. Consequently I need to support both in my scripts
+# to avoid those deprecated warnings. Here's a helper that does just that.
+# Works identically to pkg_check_modules, except that no checks are needed prior to use.
+macro (libfind_pkg_check_modules PREFIX PKGNAME)
+ if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
+ include(UsePkgConfig)
+ pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS)
+ else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
+ find_package(PkgConfig)
+ if (PKG_CONFIG_FOUND)
+ pkg_check_modules(${PREFIX} ${PKGNAME})
+ endif (PKG_CONFIG_FOUND)
+ endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
+endmacro (libfind_pkg_check_modules)
+
+# Do the final processing once the paths have been detected.
+# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
+# all the variables, each of which contain one include directory.
+# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
+# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
+# Also handles errors in case library detection was required, etc.
+macro (libfind_process PREFIX)
+ # Skip processing if already processed during this run
+ if (NOT ${PREFIX}_FOUND)
+ # Start with the assumption that the library was found
+ set (${PREFIX}_FOUND TRUE)
+
+ # Process all includes and set _FOUND to false if any are missing
+ foreach (i ${${PREFIX}_PROCESS_INCLUDES})
+ if (${i})
+ set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}})
+ mark_as_advanced(${i})
+ else (${i})
+ set (${PREFIX}_FOUND FALSE)
+ endif (${i})
+ endforeach (i)
+
+ # Process all libraries and set _FOUND to false if any are missing
+ foreach (i ${${PREFIX}_PROCESS_LIBS})
+ if (${i})
+ set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}})
+ mark_as_advanced(${i})
+ else (${i})
+ set (${PREFIX}_FOUND FALSE)
+ endif (${i})
+ endforeach (i)
+
+ # Print message and/or exit on fatal error
+ if (${PREFIX}_FOUND)
+ if (NOT ${PREFIX}_FIND_QUIETLY)
+ message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
+ endif (NOT ${PREFIX}_FIND_QUIETLY)
+ else (${PREFIX}_FOUND)
+ if (${PREFIX}_FIND_REQUIRED)
+ foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS})
+ message("${i}=${${i}}")
+ endforeach (i)
+ 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.")
+ endif (${PREFIX}_FIND_REQUIRED)
+ endif (${PREFIX}_FOUND)
+ endif (NOT ${PREFIX}_FOUND)
+endmacro (libfind_process)
+
+macro(libfind_library PREFIX basename)
+ set(TMP "")
+ if(MSVC80)
+ set(TMP -vc80)
+ endif(MSVC80)
+ if(MSVC90)
+ set(TMP -vc90)
+ endif(MSVC90)
+ set(${PREFIX}_LIBNAMES ${basename}${TMP})
+ if(${ARGC} GREATER 2)
+ set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2})
+ string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES})
+ set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP})
+ endif(${ARGC} GREATER 2)
+ find_library(${PREFIX}_LIBRARY
+ NAMES ${${PREFIX}_LIBNAMES}
+ PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}
+ )
+endmacro(libfind_library)
+
diff --git a/README b/README
index ff28e19..0490e1f 100644
--- a/README
+++ b/README
@@ -1,31 +1,37 @@
Background
==========
-'usbmuxd' stands for "USB multiplexing daemon". To the user/developer
-what it actually does is to proxy requests over a USB cable on directly
-to a listening TCP port on the iPhone.
-
-Multiple connections to different TCP ports can happen in parallel.
-An example (and useful) tool called 'iproxy' is included that allows
-you to forward localhost ports to the device---allows SSH over USB on
-jailbroken devices, or allowing access the lockdown daemon (and then
-to all of the file access, sync, notification and backup services
-running on the device).
-
-This higher-level layers are handled by libiphone. The version of
-libiphone compatible with the 'usbmuxd' infrastructure is called
-'libiphone-usbmuxd'. 'ifuse' is then able to sit on top of this.
-
+'usbmuxd' stands for "USB multiplexing daemon". To the user/developer what it
+actually does is to proxy requests over a USB cable on directly to a listening
+TCP port on the iPhone.
+
+Multiple connections to different TCP ports can happen in parallel. An example
+(and useful) tool called 'iproxy' is included that allows you to forward
+localhost ports to the device---allows SSH over USB on jailbroken devices, or
+allowing access the lockdown daemon (and then to all of the file access, sync,
+notification and backup services running on the device).
+
+This higher-level layers are handled by libiphone. The version of libiphone
+compatible with the 'usbmuxd' infrastructure is called 'libiphone-usbmuxd'.
+'ifuse' is then able to sit on top of this.
+
+There is also a Python implementation of the client library in the python-client
+library, and an example tcprelay.py which performs a similar function to iproxy.
+This implementation supports OSX and Windows and the new iTunes plist-based
+usbmuxd protocol, so it is portable and will run on those operating systems with
+no modification, using Apple's native usbmuxd. This is useful if you need to
+tunnel to your phone from another OS in a pinch. Run python tcpclient.py --help
+for usage information.
Building
========
- ./autogen.sh
- ./configure --sysconfdir=/etc
+ mkdir build
+ cd build
+ cmake ..
make
sudo make install
-
Running (with magic)
====================
@@ -33,21 +39,21 @@ Running (with magic)
./iproxy 2222 22 &
ssh -p 2222 root@localhost
-Hopefully you get the normal SSH login prompt. You may still lots of
-debugging output for the moment. If this is getting in the way of
-your ssh login, then run the 'ssh' command from a different xterminal
+Hopefully you get the normal SSH login prompt. You may still lots of debugging
+output for the moment. If this is getting in the way of your ssh login, then
+run the 'ssh' command from a different xterminal
or virtual console.
Running (without magic)
=======================
-If 'udev' is _not_ automatically running on your machine and picking
-up the new .rules file, you will need to start usbmuxd by hand first.
-Check it's running and that there is only one copy with 'ps aux | grep
+If 'udev' is _not_ automatically running on your machine and picking up the new
+.rules file, you will need to start usbmuxd by hand first. Check it's running
+and that there is only one copy with 'ps aux | grep
usbmuxd'.
- sudo usbmuxd &
+ sudo usbmuxd -U -v -v &
./iproxy 2222 22 &
ssh -p 2222 root@localhost
@@ -55,13 +61,13 @@ usbmuxd'.
Tip: Starting SSH if disabled
=============================
-If your iphone is rooted, but SSH isn't started and you _cannot_ (for
-instance, cracked/broken screen) get to the Services control panel on
-the device, then you can start the SSH service over the USB by
+If your iphone is rooted, but SSH isn't started and you _cannot_ (for instance,
+cracked/broken screen) get to the Services control panel on the device, then you
+can start the SSH service over the USB by
mounting the (jailbroken) filesystem.
-You will need to mount it usbing 'ifuse --afc2' (to access the root
-directory of the device), and then edit:
+You will need to mount it usbing 'ifuse --afc2' (to access the root directory of
+the device), and then edit:
/Library/LaunchDaemons/com.openssh.sshd.plist
@@ -71,3 +77,122 @@ to _remove_ the lines:
<true/>
Reboot the device and then sshd should be running.
+
+TODO
+====
+
+The server currently assumes that the phone is well-behaved and does not do a
+bunch of checks like looking for the expected SEQ and ACK numbers from it. This
+is normally not an issue, but it's annoying for debugging because lost packets
+(which shouldn't happen, but can happen if the code is buggy) mean that stuff
+gets out of sync and then might crash and burn dozens of packets later.
+
+The server needs more testing, and some optimizing.
+
+Someone should probably do some edge-case testing on the TCP stuff.
+
+The outgoing ACK handling on the server probably needs some thought. Currently,
+when there's an outstanding ACK, we send it after a timeout (to avoid sending
+a no-payload ACK packet for everything the phone sends us). However, there's
+probably a better way of doing this.
+
+Architecture information
+========================
+
+The iPhone / iPod Touch basically implements a rather strange USB networking
+system that operates at a higher level. It is of course completely proprietary.
+Generally speaking, this is what goes on in a typical usage scenario:
+
+0. iTunes opens a connection to usbmuxd and asks it for device notifications
+1. User inserts phone into computer
+2. usbmuxd notices the phone and pings it with a version packet
+3. phone replies
+4. usbmuxd now considers the phone to be connected and tells iTunes
+5. iTunes opens another separate connection to usbmuxd and asks it to connect
+ to, say, the afc port on the device
+6. usbmuxd sends a pseudo-TCP SYN packet to the phone
+7. the phone's kernel driver receives the SYN packet and itself opens a
+ TCP connection to localhost on the afc port
+8. the phone replies with a pseudo-TCP SYN/ACK indicating that the port is open
+ and the connection can proceed
+7. usbmuxd sends a final ACK to the phone
+8. usbmuxd replies to iTunes with a "connection successful" message
+9. any data that iTunes writes to the usbmuxd socket from now on is forwarded,
+ through pseudo-TCP, through USB, back into a more regular TCP connection to
+ localhost, to the afc daemon on the phone, and vice versa
+
+The usbmuxd protocol is a relatively simple binary message protocol documented
+here:
+
+http://wikee.iphwn.org/usb:usbmux
+
+Note that once a connection is established the UNIX socket essentially becomes
+a dedicated pipe to the TCP connction and no more high-level control is
+possible (closing the socket closes the TCP connection). Ditto for the "listen
+for devices" mode - usbmuxd will reject any commands in such mode, and the
+socket essentially becomes a dedicated device notification pipe. This means
+that you need, at minimum, TWO connections to usbmuxd to do anything useful.
+
+On Windows, usbmuxd works the same way but a TCP connection to localhost port
+27015 replaces the UNIX socket. On OSX, the UNIX socket is /var/run/usbmuxd. The
+server and client implemented here default to /tmp/usbmuxd at the moment.
+
+The phone protocol operates over a pair of USB bulk endpoints. There is an outer
+layer used for packet size info and a "protocol" (version and TCP are the only
+two options), and that header is followed by TCP headers for actual data comms.
+However, the protocol isn't actual TCP, just a custom protocol which for some
+reason uses a standard TCP header and leaves most fields unused.
+
+There is no reordering or retransmission. There are no checksums, no URG, no
+PSH, no non-ACK, no FIN. What there *is* is the SEQ/ACK/window mechanism used
+for flow control, and RST is used as the only connection teardown mechanism (and
+also for "connection refused"), and the connection startup is SYN/SYNACK/ACK.
+
+Windows are constant-scaled by 8 bits. This is legal TCP as long as the
+corresponding option is negotiated. Of course, no such negotiation happens on
+this protocol.
+
+Note that, since there are no retransmissions, there is some overlap between ACK
+and window for flow control. For example, the server doesn't ever touch its
+window size, and just refuses to ACK stuff if its buffers are full and the
+client isn't reading data. The phone happily seems to stop sending stuff.
+
+Also, if the phone RSTs you out of nowhere, look at the packet payload for a
+textual error message. Note: if it claims to suffer from amnesia, that probably
+means you overflowed its input buffer by ignoring its flow control / window
+size. Go figure. Probably a logic bug in the kernel code.
+
+Note that all normal packets need to have flags set to ACK (and only ACK). There
+is no support for, erm, not-acking. Keep the ack number field valid at all
+times.
+
+The usbmuxd CONNECT request port field is byte-swapped (network-endian). This is
+even more annoying for the plist based protocol, since it's even true there
+(where the field is plain text). So even for the plain text int, you need to
+swap the bytes (port 22 becomes <integer>5632</integer>). I have no clue if this
+is the case on the new plist protocol on PPC macs (is the newer iTunes available
+for those?)
+
+There are a bunch of gotchas due to the USB framing, and this is even worse
+because implementations tend to get it wrong (i.e. libusb, and this is the
+reason for the patch). Basically, USB Bulk offers, at the low level, the ability
+to transfer packets from 0 to wMaxPacketSize (512 here) bytes, period. There is
+no other support for higher level framing of transfers. The way you do those is
+by breaking them up into packets, and the final shorter packet marks the end of
+the transfer. The critical bit is that, if the transfer happens to be divisible
+by 512, you send a zero-length packet (ZLP) to indicate the end of the transfer.
+Libusb doesn't set this option by default and the iPhone gets packets stuck to
+each other, which it doesn't like. Actually, this framing is sort of redundant
+because the usbmux packet header includes a length field, but the phone still
+wants the ZLPs or else it breaks. To make matters worse, usbdevfs imposes a max
+transfer size of 16k, so libusb breaks transfers into that size. This is okay
+for sending as long as the ZLP is only added to the last transfer (the patch
+does that), but it can easily cause nasty race conditions on RX due to libusb
+doing multiple outstanding reads at the same time and then cancelling the rest
+when shorter data arrives (but what if some data got into the other requests
+already?), so we only do 16k reads and stick them together ourselves by looking
+at the packet size header. We still depend on ZLPs being sent to end transfers
+at non-16k boundaries that are multiples of 512, but that seems to work fine. I
+guess the ZLPs might cause spurious 0-byte transfers to show up on RX if things
+line up right, but we ignore those. By the way, the maximum packet/transfer size
+is 65535 bytes due to the 16-bit length header of the usbmux protocol.
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755
index 82cc273..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-if [ ! -d m4 ]; then mkdir m4; fi
-aclocal -I m4 || exit 1
-libtoolize || exit 1
-autoheader || exit 1
-automake --add-missing || exit 1
-autoconf || exit 1
-echo
-echo Executing ./configure $@
-echo
-./configure "$@"
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index 029eb1f..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,30 +0,0 @@
-# Inital configuration
-
-AC_PREREQ(2.61)
-
-AC_INIT([usbmuxd], [0.1.5], [nikias@gmx.li])
-AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2])
-
-AC_CONFIG_HEADERS([config.h])
-
-# Check for programs
-
-AC_PROG_CC
-AM_PROG_CC_C_O
-AC_PROG_LIBTOOL
-AC_PROG_INSTALL
-
-# Check for libraries
-
-PKG_CHECK_MODULES(libusb, libusb >= 0.1.12)
-
-# Output files
-
-AC_OUTPUT([
-Makefile
-src/Makefile
-tools/Makefile
-udev/85-usbmuxd.rules
-udev/Makefile
-libusbmuxd.pc
-])
diff --git a/libusbmuxd.pc.in b/libusbmuxd.pc.in
deleted file mode 100644
index 84c5027..0000000
--- a/libusbmuxd.pc.in
+++ /dev/null
@@ -1,12 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libusbmuxd
-Description: A library to communicate with the usbmux daemon
-Version: @VERSION@
-Requires: libusb >= 0.1.12
-Libs: -L${libdir} -lusbmuxd
-Cflags: -I${includedir}
-
diff --git a/libusbmuxd/CMakeLists.txt b/libusbmuxd/CMakeLists.txt
new file mode 100644
index 0000000..61de1a8
--- /dev/null
+++ b/libusbmuxd/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_library (libusbmuxd libusbmuxd.c sock_stuff.c)
+
+# 'lib' is a UNIXism, the proper CMake target is usbmuxd
+# But we can't use that due to the conflict with the usbmuxd daemon,
+# so instead change the library output base name to usbmuxd here
+set_target_properties(libusbmuxd PROPERTIES OUTPUT_NAME usbmuxd)
+
+install(TARGETS libusbmuxd
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+)
+install(FILES usbmuxd.h usbmuxd-proto.h DESTINATION include)
diff --git a/src/libusbmuxd.c b/libusbmuxd/libusbmuxd.c
index 4cd0a6d..090695f 100644
--- a/src/libusbmuxd.c
+++ b/libusbmuxd/libusbmuxd.c
@@ -8,9 +8,9 @@
#include <unistd.h>
// usbmuxd public interface
-#include <usbmuxd.h>
+#include "usbmuxd.h"
// usbmuxd protocol
-#include <usbmuxd-proto.h>
+#include "usbmuxd-proto.h"
// socket utility functions
#include "sock_stuff.h"
diff --git a/src/sock_stuff.c b/libusbmuxd/sock_stuff.c
index 137375d..137375d 100644
--- a/src/sock_stuff.c
+++ b/libusbmuxd/sock_stuff.c
diff --git a/src/sock_stuff.h b/libusbmuxd/sock_stuff.h
index 190f7e1..190f7e1 100644
--- a/src/sock_stuff.h
+++ b/libusbmuxd/sock_stuff.h
diff --git a/src/usbmuxd-proto.h b/libusbmuxd/usbmuxd-proto.h
index 7f8c2d6..7f8c2d6 100644
--- a/src/usbmuxd-proto.h
+++ b/libusbmuxd/usbmuxd-proto.h
diff --git a/src/usbmuxd.h b/libusbmuxd/usbmuxd.h
index ba45ec3..ba45ec3 100644
--- a/src/usbmuxd.h
+++ b/libusbmuxd/usbmuxd.h
diff --git a/python-client/.gitignore b/python-client/.gitignore
new file mode 100644
index 0000000..5da7ef5
--- /dev/null
+++ b/python-client/.gitignore
@@ -0,0 +1,3 @@
+*.pyc
+*.pyo
+
diff --git a/python-client/tcprelay.py b/python-client/tcprelay.py
new file mode 100644
index 0000000..2874ae1
--- /dev/null
+++ b/python-client/tcprelay.py
@@ -0,0 +1,147 @@
+#!/usr/bin/python
+#
+# tcprelay.py - TCP connection relay for usbmuxd
+#
+# Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 or version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import usbmux
+import SocketServer
+import select
+from optparse import OptionParser
+import sys
+import threading
+
+class SocketRelay(object):
+ def __init__(self, a, b, maxbuf=65535):
+ self.a = a
+ self.b = b
+ self.atob = ""
+ self.btoa = ""
+ self.maxbuf = maxbuf
+ def handle(self):
+ while True:
+ rlist = []
+ wlist = []
+ xlist = [self.a, self.b]
+ if self.atob:
+ wlist.append(self.b)
+ if self.btoa:
+ wlist.append(self.a)
+ if len(self.atob) < self.maxbuf:
+ rlist.append(self.a)
+ if len(self.btoa) < self.maxbuf:
+ rlist.append(self.b)
+ rlo, wlo, xlo = select.select(rlist, wlist, xlist)
+ if xlo:
+ return
+ if self.a in wlo:
+ n = self.a.send(self.btoa)
+ self.btoa = self.btoa[n:]
+ if self.b in wlo:
+ n = self.b.send(self.atob)
+ self.atob = self.atob[n:]
+ if self.a in rlo:
+ s = self.a.recv(self.maxbuf - len(self.atob))
+ if not s:
+ return
+ self.atob += s
+ if self.b in rlo:
+ s = self.b.recv(self.maxbuf - len(self.btoa))
+ if not s:
+ return
+ self.btoa += s
+ #print "Relay iter: %8d atob, %8d btoa, lists: %r %r %r"%(len(self.atob), len(self.btoa), rlo, wlo, xlo)
+
+class TCPRelay(SocketServer.BaseRequestHandler):
+ def handle(self):
+ print "Incoming connection to %d"%self.server.server_address[1]
+ mux = usbmux.USBMux(options.sockpath)
+ print "Waiting for devices..."
+ if not mux.devices:
+ mux.process(1.0)
+ if not mux.devices:
+ print "No device found"
+ self.request.close()
+ return
+ dev = mux.devices[0]
+ print "Connecting to device %s"%str(dev)
+ dsock = mux.connect(dev, self.server.rport)
+ lsock = self.request
+ print "Connection established, relaying data"
+ try:
+ fwd = SocketRelay(dsock, lsock, self.server.bufsize * 1024)
+ fwd.handle()
+ finally:
+ dsock.close()
+ lsock.close()
+ print "Connection closed"
+
+class TCPServer(SocketServer.TCPServer):
+ allow_reuse_address = True
+
+class ThreadedTCPServer(SocketServer.ThreadingMixIn, TCPServer):
+ pass
+
+HOST = "localhost"
+
+parser = OptionParser(usage="usage: %prog [OPTIONS] RemotePort[:LocalPort] [RemotePort[:LocalPort]]...")
+parser.add_option("-t", "--threaded", dest='threaded', action='store_true', default=False, help="use threading to handle multiple connections at once")
+parser.add_option("-b", "--bufsize", dest='bufsize', action='store', metavar='KILOBYTES', type='int', default=128, help="specify buffer size for socket forwarding")
+parser.add_option("-s", "--socket", dest='sockpath', action='store', metavar='PATH', type='str', default=None, help="specify the path of the usbmuxd socket")
+
+options, args = parser.parse_args()
+
+serverclass = TCPServer
+if options.threaded:
+ serverclass = ThreadedTCPServer
+
+if len(args) == 0:
+ parser.print_help()
+ sys.exit(1)
+
+ports = []
+
+for arg in args:
+ try:
+ if ':' in arg:
+ rport, lport = arg.split(":")
+ rport = int(rport)
+ lport = int(lport)
+ ports.append((rport, lport))
+ else:
+ ports.append((int(arg), int(arg)))
+ except:
+ parser.print_help()
+ sys.exit(1)
+
+servers=[]
+
+for rport, lport in ports:
+ print "Forwarding local port %d to remote port %d"%(lport, rport)
+ server = serverclass((HOST, lport), TCPRelay)
+ server.rport = rport
+ server.bufsize = options.bufsize
+ servers.append(server)
+
+alive = True
+
+while alive:
+ try:
+ rl, wl, xl = select.select(servers, [], [])
+ for server in rl:
+ server.handle_request()
+ except:
+ alive = False
diff --git a/python-client/usbmux.py b/python-client/usbmux.py
new file mode 100644
index 0000000..47112d3
--- /dev/null
+++ b/python-client/usbmux.py
@@ -0,0 +1,245 @@
+#!/usr/bin/python
+#
+# usbmux.py - usbmux client library for Python
+#
+# Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 or version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import socket, struct, select, sys
+
+try:
+ import plistlib
+ haveplist = True
+except:
+ haveplist = False
+
+class MuxError(Exception):
+ pass
+
+class MuxVersionError(MuxError):
+ pass
+
+class SafeStreamSocket:
+ def __init__(self, address, family):
+ self.sock = socket.socket(family, socket.SOCK_STREAM)
+ self.sock.connect(address)
+ def send(self, msg):
+ totalsent = 0
+ while totalsent < len(msg):
+ sent = self.sock.send(msg[totalsent:])
+ if sent == 0:
+ raise MuxError("socket connection broken")
+ totalsent = totalsent + sent
+ def recv(self, size):
+ msg = ''
+ while len(msg) < size:
+ chunk = self.sock.recv(size-len(msg))
+ if chunk == '':
+ raise MuxError("socket connection broken")
+ msg = msg + chunk
+ return msg
+
+class MuxDevice(object):
+ def __init__(self, devid, usbprod, serial, location):
+ self.devid = devid
+ self.usbprod = usbprod
+ self.serial = serial
+ self.location = location
+ def __str__(self):
+ return "<MuxDevice: ID %d ProdID 0x%04x Serial '%s' Location 0x%x>"%(self.devid, self.usbprod, self.serial, self.location)
+
+class BinaryProtocol(object):
+ TYPE_RESULT = 1
+ TYPE_CONNECT = 2
+ TYPE_LISTEN = 3
+ TYPE_DEVICE_ADD = 4
+ TYPE_DEVICE_REMOVE = 5
+ VERSION = 0
+ def __init__(self, socket):
+ self.socket = socket
+ self.connected = False
+
+ def _pack(self, req, payload):
+ if req == self.TYPE_CONNECT:
+ return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + "\x00\x00"
+ elif req == self.TYPE_LISTEN:
+ return ""
+ else:
+ raise ValueError("Invalid outgoing request type %d"%req)
+
+ def _unpack(self, resp, payload):
+ if resp == self.TYPE_RESULT:
+ return {'Number':struct.unpack("I", payload)[0]}
+ elif resp == self.TYPE_DEVICE_ADD:
+ devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload)
+ serial = serial.split("\0")[0]
+ return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}}
+ elif resp == self.TYPE_DEVICE_REMOVE:
+ devid = struct.unpack("I", payload)[0]
+ return {'DeviceID': devid}
+ else:
+ raise MuxError("Invalid incoming request type %d"%req)
+
+ def sendpacket(self, req, tag, payload={}):
+ payload = self._pack(req, payload)
+ if self.connected:
+ raise MuxError("Mux is connected, cannot issue control packets")
+ length = 16 + len(payload)
+ data = struct.pack("IIII", length, self.VERSION, req, tag) + payload
+ self.socket.send(data)
+ def getpacket(self):
+ if self.connected:
+ raise MuxError("Mux is connected, cannot issue control packets")
+ dlen = self.socket.recv(4)
+ dlen = struct.unpack("I", dlen)[0]
+ body = self.socket.recv(dlen - 4)
+ version, resp, tag = struct.unpack("III",body[:0xc])
+ if version != self.VERSION:
+ raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version))
+ payload = self._unpack(resp, body[0xc:])
+ return (resp, tag, payload)
+
+class PlistProtocol(BinaryProtocol):
+ TYPE_RESULT = "Result"
+ TYPE_CONNECT = "Connect"
+ TYPE_LISTEN = "Listen"
+ TYPE_DEVICE_ADD = "Attached"
+ TYPE_DEVICE_REMOVE = "Detached" #???
+ TYPE_PLIST = 8
+ VERSION = 1
+ def __init__(self, socket):
+ if not haveplist:
+ raise Exception("You need the plistlib module")
+ BinaryProtocol.__init__(self, socket)
+
+ def _pack(self, req, payload):
+ return payload
+
+ def _unpack(self, resp, payload):
+ return payload
+
+ def sendpacket(self, req, tag, payload={}):
+ payload['ClientVersionString'] = 'usbmux.py by marcan'
+ if isinstance(req, int):
+ req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2]
+ payload['MessageType'] = req
+ payload['ProgName'] = 'tcprelay'
+ BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.writePlistToString(payload))
+ def getpacket(self):
+ resp, tag, payload = BinaryProtocol.getpacket(self)
+ if resp != self.TYPE_PLIST:
+ raise MuxError("Received non-plist type %d"%resp)
+ payload = plistlib.readPlistFromString(payload)
+ return payload['MessageType'], tag, payload
+
+class MuxConnection(object):
+ def __init__(self, socketpath, protoclass):
+ self.socketpath = socketpath
+ if sys.platform in ['win32', 'cygwin']:
+ family = socket.AF_INET
+ address = ('127.0.0.1', 27015)
+ else:
+ family = socket.AF_UNIX
+ address = self.socketpath
+ self.socket = SafeStreamSocket(address, family)
+ self.proto = protoclass(self.socket)
+ self.pkttag = 1
+ self.devices = []
+
+ def _getreply(self):
+ while True:
+ resp, tag, data = self.proto.getpacket()
+ if resp == self.proto.TYPE_RESULT:
+ return tag, data
+ else:
+ raise MuxError("Invalid packet type received: %d"%resp)
+ def _processpacket(self):
+ resp, tag, data = self.proto.getpacket()
+ if resp == self.proto.TYPE_DEVICE_ADD:
+ self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID']))
+ elif resp == self.proto.TYPE_DEVICE_REMOVE:
+ for dev in self.devices:
+ if dev.devid == data['DeviceID']:
+ self.devices.remove(dev)
+ elif resp == self.proto.TYPE_RESULT:
+ raise MuxError("Unexpected result: %d"%resp)
+ else:
+ raise MuxError("Invalid packet type received: %d"%resp)
+ def _exchange(self, req, payload={}):
+ mytag = self.pkttag
+ self.pkttag += 1
+ self.proto.sendpacket(req, mytag, payload)
+ recvtag, data = self._getreply()
+ if recvtag != mytag:
+ raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag))
+ return data['Number']
+
+ def listen(self):
+ ret = self._exchange(self.proto.TYPE_LISTEN)
+ if ret != 0:
+ raise MuxError("Listen failed: error %d"%ret)
+ def process(self, timeout=None):
+ if self.proto.connected:
+ raise MuxError("Socket is connected, cannot process listener events")
+ rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout)
+ if xlo:
+ self.socket.sock.close()
+ raise MuxError("Exception in listener socket")
+ if rlo:
+ self._processpacket()
+ def connect(self, device, port):
+ ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)})
+ if ret != 0:
+ raise MuxError("Connect failed: error %d"%ret)
+ self.proto.connected = True
+ return self.socket.sock
+ def close(self):
+ self.socket.sock.close()
+
+class USBMux(object):
+ def __init__(self, socketpath=None):
+ if socketpath is None:
+ if sys.platform == 'darwin':
+ socketpath = "/var/run/usbmuxd"
+ else:
+ socketpath = "/var/run/usbmuxd"
+ self.socketpath = socketpath
+ self.listener = MuxConnection(socketpath, BinaryProtocol)
+ try:
+ self.listener.listen()
+ self.version = 0
+ self.protoclass = BinaryProtocol
+ except MuxVersionError:
+ self.listener = MuxConnection(socketpath, PlistProtocol)
+ self.listener.listen()
+ self.protoclass = PlistProtocol
+ self.version = 1
+ self.devices = self.listener.devices
+ def process(self, timeout=None):
+ self.listener.process(timeout)
+ def connect(self, device, port):
+ connector = MuxConnection(self.socketpath, self.protoclass)
+ return connector.connect(device, port)
+
+if __name__ == "__main__":
+ mux = USBMux()
+ print "Waiting for devices..."
+ if not mux.devices:
+ mux.process(0.1)
+ while True:
+ print "Devices:"
+ for dev in mux.devices:
+ print dev
+ mux.process()
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 547870e..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,31 +0,0 @@
-AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS)
-AM_LDFLAGS = $(libusb_LIBS) -lpthread -lrt
-
-# Libraries
-
-noinst_LTLIBRARIES = libusbmux.la libsock_stuff.la
-libsock_stuff_la_SOURCES = sock_stuff.c \
- sock_stuff.h
-
-libusbmux_la_SOURCES = usbmux.c \
- usbmux.h
-libusbmux_la_CFLAGS = $(AM_CFLAGS)
-libusbmux_la_LDFLAGS = $(AM_LDFLAGS)
-
-lib_LTLIBRARIES = libusbmuxd.la
-libusbmuxd_la_SOURCES = libusbmuxd.c \
- usbmuxd.h \
- usbmuxd-proto.h
-libusbmuxd_la_LIBADD = libsock_stuff.la
-
-include_HEADERS = usbmuxd.h \
- usbmuxd-proto.h
-
-# Programs
-
-sbin_PROGRAMS = usbmuxd
-
-usbmuxd_SOURCES = main.c
-usbmuxd_LDADD = libusbmux.la \
- libsock_stuff.la
-
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index 19649b1..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,1447 +0,0 @@
-/*
- * usbmuxd -- daemon for communication with iPhone/iPod via USB
- *
- * Copyright (c) 2009 Nikias Bassen. All Rights Reserved.
- * Based upon iTunnel source code, Copyright (c) 2008 Jing Su.
- * http://www.cs.toronto.edu/~jingsu/itunnel/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include <stddef.h>
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <stdarg.h>
-#include <syslog.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <signal.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <usb.h>
-#include <pwd.h>
-
-#include "usbmuxd-proto.h"
-#include "sock_stuff.h"
-
-#include "usbmux.h"
-
-#define DEFAULT_TIMEOUT 4000
-#define DEFAULT_CHILDREN_CAPACITY 10
-#define DEBUG_LEVEL 0
-
-#define LOCKFILE "/var/run/usbmuxd.lock"
-
-#define THREAD (unsigned int)pthread_self()
-
-static int quit_flag = 0;
-static int fsock = -1;
-static int verbose = DEBUG_LEVEL;
-static int foreground = 0;
-static int exit_on_no_devices = 0;
-static int drop_privileges = 0;
-static int opt_udev = 0;
-static int opt_exit = 0;
-static int exit_signal = 0;
-
-struct device_info {
- uint32_t device_id;
- usbmux_device_t phone;
- int use_count;
- pthread_t bulk_reader;
- pthread_mutex_t mutex;
- /* mutex for mutual exclusion of calling the usbmux_send function
- * TODO: I don't know if we need really need this? */
- pthread_mutex_t writer_mutex;
-};
-
-struct client_data {
- volatile int dead;
- int socket;
- int tag;
- pthread_t thread;
- pthread_t handler;
- pthread_t reader;
- int reader_quit;
- int reader_dead;
- int handler_dead;
- int connected;
- usbmux_client_t muxclient;
- struct device_info *dev;
-};
-
-static struct device_info **devices = NULL;
-static int device_count = 0;
-static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t usb_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/**
- * logs a message to syslog when running as daemon or to stdout/stderr when
- * running in foreground.
- * @param prio The logging priority.
- * @param format The message to be printed.
- */
-static void logmsg(int prio, const char *format, ...)
-{
- va_list args;
- va_start(args, format);
-
- if (!foreground) {
- // daemon. log using syslog.
- vsyslog(prio, format, args);
- } else {
- // running in foreground. log to stdout/stderr.
- char msgbuf[256];
- FILE *lfp = stdout;
- switch (prio) {
- case LOG_EMERG:
- case LOG_ALERT:
- case LOG_CRIT:
- case LOG_ERR:
- case LOG_WARNING:
- lfp = stderr;
- break;
- default:
- lfp = stdout;
- }
- strcpy(msgbuf, "usbmuxd: ");
- vsnprintf(msgbuf + 9, 244, format, args);
- strcat(msgbuf, "\n");
- fputs(msgbuf, lfp);
- }
-
- va_end(args);
-}
-
-#ifdef DEBUG
-/**
- * for debugging purposes.
- */
-static void print_buffer(FILE * fp, const char *data, const int length)
-{
- int i;
- int j;
- unsigned char c;
-
- for (i = 0; i < length; i += 16) {
- if (verbose >= 4)
- fprintf(fp, "%04x: ", i);
- for (j = 0; j < 16; j++) {
- if (i + j >= length) {
- if (verbose >= 4)
- fprintf(fp, " ");
- continue;
- }
- if (verbose >= 4)
- fprintf(fp, "%02hhx ", *(data + i + j));
- }
- if (verbose >= 4)
- fprintf(fp, " | ");
- for (j = 0; j < 16; j++) {
- if (i + j >= length)
- break;
- c = *(data + i + j);
- if ((c < 32) || (c > 127)) {
- if (verbose >= 4)
- fprintf(fp, ".");
- continue;
- }
- if (verbose >= 4)
- fprintf(fp, "%c", c);
- }
- if (verbose >= 4)
- fprintf(fp, "\n");
- }
- if (verbose >= 4)
- fprintf(fp, "\n");
-}
-#endif
-
-/**
- * Read incoming usbmuxd packet. If the packet is larger than
- * the size specified by len, the data will be truncated.
- *
- * @param fd the file descriptor to read from.
- * @param data pointer to a buffer to store the read data to.
- * @param len the length of the data to be read. The buffer
- * pointed to by data should be at least len bytes in size.
- *
- * @return
- */
-static int usbmuxd_get_request(int fd, void **data, size_t len)
-{
- uint32_t pktlen;
- int recv_len;
-
- if (peek_buf(fd, &pktlen, sizeof(pktlen)) < (int) sizeof(pktlen)) {
- return -errno;
- }
-
- if (len == 0) {
- // allocate buffer space
- *data = malloc(pktlen);
- } else if (len < pktlen) {
- // target buffer is to small to hold this packet! fix it!
- if (verbose >= 2)
- logmsg(LOG_WARNING,
- "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.",
- __func__, pktlen, len);
- pktlen = len;
- }
-
- recv_len = recv_buf(fd, *data, pktlen);
- if ((recv_len > 0) && ((uint32_t) recv_len < pktlen)) {
- if (verbose >= 2)
- logmsg(LOG_WARNING,
- "%s: Uh-oh, we got less than the packet's size, %d instead of %d...",
- __func__, recv_len, pktlen);
- }
-#ifdef DEBUG
- if (*data && (recv_len > 0) && verbose >= 4) {
- fprintf(stderr, "%s: received:\n", __func__);
- print_buffer(stderr, *data, recv_len);
- }
-#endif
-
- return recv_len;
-}
-
-/**
- * Send a usbmuxd result packet with given tag and result_code.
- *
- * @param fd the file descriptor to write to.
- * @param tag the tag value that identifies where this message belongs to.
- * @param result_code the error value (0 = Success, most likely errno values otherwise)
- *
- * @return the return value returned by send_buf (normally the number of bytes sent)
- */
-static int usbmuxd_send_result(int fd, uint32_t tag, uint32_t result_code)
-{
- struct usbmuxd_result res;
- int ret;
-
- res.header.length = sizeof(res);
- res.header.reserved = 0;
- res.header.type = USBMUXD_RESULT;
- res.header.tag = tag;
- res.result = result_code;
-
- if (verbose >= 4)
- logmsg(LOG_NOTICE, "%s: tag=%d result=%d", __func__,
- res.header.tag, res.result);
-
- ret = send_buf(fd, &res, sizeof(res));
- fsync(fd); // let's get it sent
- return ret;
-}
-
-/**
- * this thread reads from the usb connection and writes the
- * data to the connected client.
- *
- * @param arg pointer to a client_data structure.
- *
- * @return NULL in any case
- */
-static void *usbmuxd_client_reader_thread(void *arg)
-{
- struct client_data *cdata;
-
- char rbuffer[512];
- uint32_t rbuffersize = 512;
- uint32_t rlen;
- int err;
- char *cursor;
- ssize_t len;
- int result;
-
- if (!arg) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: invalid client_data supplied!", __func__);
- cdata->reader_dead = 1;
- return NULL;
- }
-
- cdata = (struct client_data *) arg;
-
- cdata->reader_dead = 0;
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: started (device %d:%d, use_count=%d)", __func__, THREAD,
- cdata->dev->device_id >> 16, cdata->dev->device_id & 0xFFFF, cdata->dev->use_count);
-
- while (!quit_flag && !cdata->reader_quit) {
- result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT);
- if (result <= 0) {
- if (result < 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: select error: %s", __func__,
- strerror(errno));
- }
- continue;
- }
-
- rlen = 0;
- err =
- usbmux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize,
- &rlen, DEFAULT_TIMEOUT);
- if (err != 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR,
- "%s[%x]: encountered USB read error: %d",
- __func__, THREAD, err);
- break;
- }
-
- cursor = rbuffer;
- while (rlen > 0) {
- len = send_buf(cdata->socket, cursor, rlen);
- if (len <= 0) {
- logmsg(LOG_ERR, "%s: Error: send returned %d", __func__,
- len);
- err = 1;
- break;
- }
- // calculate remainder
- rlen -= len;
- // advance cursor
- cursor += len;
- }
- if (err != 0) {
- logmsg(LOG_ERR, "%s: Error when writing to client...",
- __func__);
- break;
- }
- fsync(cdata->socket);
- }
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD);
-
- cdata->reader_dead = 1;
-
- return NULL;
-}
-
-/**
- * This function handles the connecting procedure to a previously
- * set up usbmux client.
- * Sends a usbmuxd result packet denoting success or failure.
- * A successful result is mandatory for later communication.
- *
- * @param cdata pointer to a previously initialized client_data structure
- *
- * @return
- */
-static int usbmuxd_handleConnectResult(struct client_data *cdata)
-{
- int result;
- char buffer[512];
- char err_type[64];
- int err_code;
- ssize_t maxlen = 512;
- uint32_t rlen;
- int err;
-
- if (!cdata) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: Invalid client_data provided!", __func__);
- return -EINVAL;
- }
-
- result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT);
- if (result <= 0) {
- if (result < 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: select error: %s", __func__,
- strerror(errno));
- return result;
- }
- } else {
- result = 0;
- err =
- usbmux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen,
- 100);
- if (err < 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: encountered USB read error: %d",
- __func__, err);
- usbmuxd_send_result(cdata->socket, cdata->tag, -err);
- return err;
- } else {
- if (rlen > 0) {
- if ((buffer[0] == 1) && (rlen > 20)
- && !memcmp(buffer + 1, "handleConnectResult:", 20)) {
- // hm... we got an error message!
- buffer[rlen] = 0;
- if (verbose >= 1)
- logmsg(LOG_ERR, "%s: %s\n", __func__, buffer + 22);
-
- if (sscanf
- (buffer + 22, "%s - %d\n", err_type, &err_code)
- == 2) {
- usbmuxd_send_result(cdata->socket, cdata->tag,
- err_code);
- return -err_code;
- } else {
- usbmuxd_send_result(cdata->socket, cdata->tag,
- ENODATA);
- return -ENODATA;
- }
- } else {
- // send success result
- usbmuxd_send_result(cdata->socket, cdata->tag, 0);
- // and the server greeting message
- send_buf(cdata->socket, buffer, rlen);
- }
- } else {
- // no server greeting? this seems to be ok. send success.
- usbmuxd_send_result(cdata->socket, cdata->tag, 0);
- }
- }
- //fsync(cdata->socket);
- }
- return result;
-}
-
-/**
- * This thread handles the communication between the connected iPhone/iPod
- * and the client that created the connection.
- */
-static void *usbmuxd_client_handler_thread(void *arg)
-{
- struct client_data *cdata;
- int result;
- char *cursor;
- char buffer[32740];
- ssize_t len;
- ssize_t maxlen = sizeof(buffer);
- uint32_t wlen;
- int err;
-
- if (!arg) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: invalid client_data provided!", __func__);
- return NULL;
- }
-
- cdata = (struct client_data *) arg;
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: started (device %d:%d, use_count=%d)", __func__, THREAD,
- cdata->dev->device_id >> 16, cdata->dev->device_id & 0xFFFF, cdata->dev->use_count);
-
- if (usbmuxd_handleConnectResult(cdata)) {
- if (verbose >= 3)
- logmsg(LOG_ERR, "handleConnectResult: Error");
- goto leave;
- } else {
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "handleConnectResult: Success");
- }
-
- // starting mux reader thread
- cdata->reader_quit = 0;
- cdata->reader_dead = 0;
- if (pthread_create
- (&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: could not start client_reader thread",
- __func__);
- cdata->reader = 0;
- }
-
- while (!quit_flag && !cdata->reader_dead) {
- result = check_fd(cdata->socket, FD_READ, DEFAULT_TIMEOUT);
- if (result <= 0) {
- if (result < 0) {
- if (verbose >= 3)
- logmsg(LOG_ERR, "%s[%x]: Error: checkfd: %s", __func__, THREAD, strerror(errno));
- }
- continue;
- }
- // check_fd told us there's data available, so read from client
- // and push to USB device.
- len = recv(cdata->socket, buffer, maxlen, 0);
- if (len == 0) {
- break;
- }
- if (len < 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s[%x]: Error: recv: %s", __func__, THREAD, strerror(errno));
- break;
- }
-
- cursor = buffer;
-
- pthread_mutex_lock(&cdata->dev->writer_mutex);
- do {
- wlen = 0;
- err = usbmux_send(cdata->muxclient, cursor, len, &wlen);
- if (err == -ETIMEDOUT) {
- // some kind of timeout... just be patient and retry.
- } else if (err < 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s[%x]: USB write error: %d", __func__, THREAD, err);
- len = -1;
- break;
- }
- // calculate remainder.
- len -= wlen;
- // advance cursor appropiately.
- cursor += wlen;
- }
- while ((len > 0) && !quit_flag);
- pthread_mutex_unlock(&cdata->dev->writer_mutex);
- if (len < 0) {
- break;
- }
- }
-
- leave:
- // cleanup
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: terminating", __func__, THREAD);
- if (cdata->reader != 0) {
- cdata->reader_quit = 1;
- pthread_join(cdata->reader, NULL);
- }
-
- cdata->handler_dead = 1;
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD);
- return NULL;
-}
-
-/**
- * Thread performing usb_bulk_read from the connected device.
- * One thread per device. Lives as long as the device is in use.
- */
-static void *usbmuxd_bulk_reader_thread(void *arg)
-{
- struct device_info *cur_dev;
- int err;
-
- if (!arg) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s: Invalid client_data provided", __func__);
- return NULL;
- }
-
- cur_dev = (struct device_info *) arg;
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s: started", __func__);
-
- while (!quit_flag && cur_dev) {
-
- pthread_mutex_lock(&cur_dev->mutex);
- if (cur_dev->use_count <= 0) {
- pthread_mutex_unlock(&cur_dev->mutex);
- break;
- }
- pthread_mutex_unlock(&cur_dev->mutex);
-
- if ((err = usbmux_pullbulk(cur_dev->phone)) < 0) {
- if (verbose >= 1)
- logmsg(LOG_ERR, "%s: error %d when reading from device",
- __func__, err);
- break;
- }
- }
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s: terminated", __func__);
-
- return NULL;
-}
-
-/**
- * This thread is started when a new connection is accepted.
- * It performs the handshake, then waits for the connect packet and
- * on success it starts the usbmuxd_client_handler thread.
- */
-static void *usbmuxd_client_init_thread(void *arg)
-{
- struct client_data *cdata;
- struct usbmuxd_scan_request *s_req;
- struct usbmuxd_device_info_record dev_info_rec;
- struct usbmuxd_connect_request *c_req = NULL;
-
- struct usb_bus *bus;
- struct usb_device *dev;
-
- int recv_len;
- int found = 0;
- int res;
- int i;
-
- usbmux_device_t phone = NULL;
- struct device_info *cur_dev = NULL;
-
- if (!arg) {
- if (verbose >= 1)
- logmsg(LOG_ERR, "%s[%x]: invalid client_data provided!",
- __func__, THREAD);
- return NULL;
- }
-
- cdata = (struct client_data *) arg;
- cdata->dead = 0;
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: started (fd=%d)", __func__, THREAD,
- cdata->socket);
-
- if ((recv_len =
- usbmuxd_get_request(cdata->socket, (void **) &s_req, 0)) <= 0) {
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s[%x]: No scan packet received, error %s",
- __func__, THREAD, strerror(errno));
- goto leave;
- }
-
- if ((recv_len == sizeof(struct usbmuxd_scan_request))
- && (s_req->header.length == sizeof(struct usbmuxd_scan_request))
- && (s_req->header.reserved == 0)
- && (s_req->header.type == USBMUXD_SCAN)) {
- // send success response
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: Got scan packet!", __func__,
- THREAD);
- usbmuxd_send_result(cdata->socket, s_req->header.tag, 0);
- } else if ((recv_len == sizeof(struct usbmuxd_connect_request))
- && (s_req->header.type == USBMUXD_CONNECT)) {
- c_req = (struct usbmuxd_connect_request *) s_req;
- s_req = NULL;
- goto connect;
- } else {
- // send error response and exit
- if (verbose >= 2)
- logmsg(LOG_ERR, "%s[%x]: Invalid scan packet received.",
- __func__, THREAD);
- // TODO is this required?!
- usbmuxd_send_result(cdata->socket, s_req->header.tag, EINVAL);
- goto leave;
- }
-
- pthread_mutex_lock(&usb_mutex);
- // gather data about all iPhones/iPods attached
-
- if (verbose >= 5)
- logmsg(LOG_DEBUG, "%s[%x]: usb init", __func__, THREAD);
- usb_init();
- if (verbose >= 5)
- logmsg(LOG_DEBUG, "%s[%x]: usb find busses", __func__, THREAD);
- usb_find_busses();
- if (verbose >= 5)
- logmsg(LOG_DEBUG, "%s[%x]: usb find devices", __func__, THREAD);
- usb_find_devices();
-
- if (verbose >= 2)
- logmsg(LOG_NOTICE, "%s[%x]: Looking for attached devices...",
- __func__, THREAD);
-
- for (bus = usb_get_busses(); bus; bus = bus->next) {
- for (dev = bus->devices; dev; dev = dev->next) {
- if (dev->descriptor.idVendor == 0x05ac
- && dev->descriptor.idProduct >= 0x1290
- && dev->descriptor.idProduct <= 0x1294) {
- if (verbose >= 1)
- logmsg(LOG_NOTICE,
- "%s[%x]: Found device on bus %s, id %s",
- __func__, THREAD, bus->dirname, dev->filename);
- found++;
-
- // construct packet
- memset(&dev_info_rec, 0, sizeof(dev_info_rec));
- dev_info_rec.header.length = sizeof(dev_info_rec);
- dev_info_rec.header.type = USBMUXD_DEVICE_INFO;
- uint32_t dev_id =
- strtol(dev->filename, NULL, 10)
- + (strtoul(bus->dirname, NULL, 10) << 16);
- dev_info_rec.device.device_id = dev_id;
- dev_info_rec.device.product_id = dev->descriptor.idProduct;
- if (dev->descriptor.iSerialNumber) {
- usb_dev_handle *udev;
- //pthread_mutex_lock(&usbmux_mutex);
- udev = usb_open(dev);
- if (udev) {
- usb_get_string_simple(udev,
- dev->descriptor.iSerialNumber,
- dev_info_rec.device.serial_number,
- sizeof(dev_info_rec.device.serial_number) + 1);
- usb_close(udev);
- } else {
- logmsg(LOG_ERR, "%s[%x]: Error: usb_open(): %s\n", __func__, THREAD, usb_strerror());
- }
- //pthread_mutex_unlock(&usbmux_mutex);
- }
-#ifdef DEBUG
- if (verbose >= 4)
- print_buffer(stderr, (char *) &dev_info_rec,
- sizeof(dev_info_rec));
-#endif
-
- // send it
- if (send_buf
- (cdata->socket, &dev_info_rec,
- sizeof(dev_info_rec)) <= 0) {
- if (verbose >= 3)
- logmsg(LOG_ERR,
- "%s[%x]: Error: Could not send device info: %s",
- __func__, THREAD, strerror(errno));
- found--;
- }
- }
- }
- }
- pthread_mutex_unlock(&usb_mutex);
-
- if (found <= 0) {
- if (verbose >= 1)
- logmsg(LOG_NOTICE,
- "%s[%x]: No attached iPhone/iPod devices found.",
- __func__, THREAD);
- usbmuxd_send_result(cdata->socket, s_req->header.tag, -ENODEV);
- goto leave;
- }
-
- goto leave;
-
-/* if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: Waiting for connect request", __func__,
- THREAD);
-
- // now wait for connect request
- //memset(&c_req, 0, sizeof(c_req));
- if ((recv_len =
- usbmuxd_get_request(cdata->socket, (void **) &c_req, 0)) <= 0) {
- if (verbose >= 3)
- logmsg(LOG_NOTICE,
- "%s[%x]: Did not receive any connect request.",
- __func__, THREAD);
- goto leave;
- }*/
-
- connect:
-
- if (c_req->header.type != USBMUXD_CONNECT) {
- if (verbose >= 2)
- logmsg(LOG_ERR,
- "%s[%x]: Unexpected packet of type %d received.",
- __func__, THREAD, c_req->header.type);
- goto leave;
- }
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE,
- "%s[%x]: Setting up connection to usb device %d:%d on port %d",
- __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF, ntohs(c_req->tcp_dport));
-
- // find the device, and open usb connection
- pthread_mutex_lock(&usbmux_mutex);
- phone = NULL;
- cur_dev = NULL;
- // first check if we already have an open connection
- if (devices) {
- for (i = 0; i < device_count; i++) {
- if (devices[i]) {
- if (devices[i]->device_id == c_req->device_id) {
- devices[i]->use_count++;
- cur_dev = devices[i];
- phone = cur_dev->phone;
- break;
- }
- }
- }
- }
- if (!phone) {
- // if not found, make a new connection
- if (verbose >= 2)
- logmsg(LOG_NOTICE,
- "%s[%x]: creating new usb connection, device %d:%d",
- __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF);
-
- pthread_mutex_lock(&usb_mutex);
- if (usbmux_get_specific_device(c_req->device_id >> 16, c_req->device_id & 0xFFFF, &phone) < 0) {
- pthread_mutex_unlock(&usb_mutex);
- pthread_mutex_unlock(&usbmux_mutex);
- if (verbose >= 1)
- logmsg(LOG_ERR, "%s[%x]: device %d:%d could not be opened",
- __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF);
- usbmuxd_send_result(cdata->socket, c_req->header.tag, ENODEV);
- goto leave;
- }
- pthread_mutex_unlock(&usb_mutex);
-
- // create device object
- if (verbose >= 3)
- logmsg(LOG_DEBUG, "%s[%x]: add to device list", __func__,
- THREAD);
- cur_dev =
- (struct device_info *) malloc(sizeof(struct device_info));
- memset(cur_dev, 0, sizeof(struct device_info));
- cur_dev->use_count = 1;
- cur_dev->device_id = c_req->device_id;
- cur_dev->phone = phone;
- cur_dev->bulk_reader = 0;
- pthread_mutex_init(&cur_dev->mutex, NULL);
- pthread_mutex_init(&cur_dev->writer_mutex, NULL);
-
- if (verbose >= 3)
- logmsg(LOG_DEBUG, "%s[%x]: device_count = %d", __func__,
- THREAD, device_count);
-
- // add to list of devices
- devices =
- (struct device_info **) realloc(devices,
- sizeof(struct device_info *) *
- (device_count + 1));
- if (devices) {
- devices[device_count] = cur_dev;
- device_count++;
- }
- } else {
- if (verbose >= 2)
- logmsg(LOG_NOTICE,
- "%s[%x]: reusing usb connection, device %d:%d",
- __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF);
- }
- pthread_mutex_unlock(&usbmux_mutex);
-
- // setup connection to iPhone/iPod
- pthread_mutex_lock(&cur_dev->writer_mutex);
- res =
- usbmux_new_client(cur_dev->phone, 0, ntohs(c_req->tcp_dport),
- &(cdata->muxclient));
- pthread_mutex_unlock(&cur_dev->writer_mutex);
-
- if (res != 0) {
- usbmuxd_send_result(cdata->socket, c_req->header.tag, res);
- if (verbose >= 1)
- logmsg(LOG_ERR,
- "%s[%x]: mux_new_client returned %d, aborting.",
- __func__, THREAD, res);
- goto leave;
- }
- // start bulk reader thread (once per device)
- pthread_mutex_lock(&cur_dev->mutex);
- if (cur_dev->bulk_reader == 0) {
- pthread_create(&cur_dev->bulk_reader, NULL,
- usbmuxd_bulk_reader_thread, cur_dev);
- }
- pthread_mutex_unlock(&cur_dev->mutex);
-
- // wait for the initial handshake (SYN->SYN+ACK->ACK) to complete)
- struct timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = 100000000;
-
- i = 0;
- while (i < 10000) {
- if (usbmux_is_connected(cdata->muxclient)) {
- break;
- }
- nanosleep(&ts, NULL);
- i+=100;
- }
- if (!usbmux_is_connected(cdata->muxclient)) {
- usbmuxd_send_result(cdata->socket, c_req->header.tag, -ENOTCONN);
- goto leave;
- }
-
- // start connection handler thread
- cdata->handler_dead = 0;
- cdata->tag = c_req->header.tag;
- cdata->dev = cur_dev;
- if (pthread_create
- (&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0)
- {
- if (verbose >= 1)
- logmsg(LOG_ERR,
- "%s[%x]: could not create usbmuxd_client_handler_thread!",
- __func__, THREAD);
- cdata->handler = 0;
- goto leave;
- }
- // wait for handler thread to finish its work
- if (cdata->handler != 0) {
- pthread_join(cdata->handler, NULL);
- }
-
- if (verbose >= 2)
- logmsg(LOG_NOTICE, "%s[%x]: closing connection", __func__, THREAD);
-
- // time to clean up
- if (cdata && cdata->muxclient) { // should be non-NULL
- pthread_mutex_lock(&cdata->dev->writer_mutex);
- usbmux_free_client(cdata->muxclient);
- pthread_mutex_unlock(&cdata->dev->writer_mutex);
- }
-
- leave:
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: terminating", __func__, THREAD);
-
- if (s_req) {
- free(s_req);
- }
- if (c_req) {
- free(c_req);
- }
- // this has to be freed only if it's not in use anymore as it closes
- // the USB connection
- pthread_mutex_lock(&usbmux_mutex);
- if (cur_dev) {
- pthread_mutex_lock(&cur_dev->mutex);
- if (cur_dev->use_count > 1) {
- if (verbose >= 2)
- logmsg(LOG_NOTICE,
- "%s[%x]: decreasing device use count (from %d to %d)",
- __func__, THREAD, cur_dev->use_count,
- cur_dev->use_count - 1);
- cur_dev->use_count--;
- pthread_mutex_unlock(&cur_dev->mutex);
- } else {
- if (verbose >= 2)
- logmsg(LOG_NOTICE,
- "%s[%x]: last client disconnected, cleaning up",
- __func__, THREAD);
- cur_dev->use_count = 0;
- pthread_mutex_unlock(&cur_dev->mutex);
- if (cur_dev->bulk_reader != 0) {
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: joining bulk_reader...",
- __func__, THREAD);
- pthread_join(cur_dev->bulk_reader, NULL);
- }
- pthread_mutex_lock(&usb_mutex);
- usbmux_free_device(cur_dev->phone);
- pthread_mutex_unlock(&usb_mutex);
- pthread_mutex_destroy(&cur_dev->writer_mutex);
- pthread_mutex_destroy(&cur_dev->mutex);
- free(cur_dev);
- cur_dev = NULL;
- if (device_count > 1) {
- struct device_info **newlist;
- int j = 0;
-
- newlist =
- (struct device_info **)
- malloc(sizeof(struct device_info *)
- * device_count - 1);
- for (i = 0; i < device_count; i++) {
- if (devices[i] != NULL) {
- newlist[j++] = devices[i];
- }
- }
- free(devices);
- devices = newlist;
- device_count--;
- } else {
- free(devices);
- devices = NULL;
- device_count = 0;
- }
- }
- }
- pthread_mutex_unlock(&usbmux_mutex);
-
- cdata->dead = 1;
- close(cdata->socket);
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD);
-
- return NULL;
-}
-
-/**
- * make this program run detached from the current console
- */
-static int daemonize()
-{
- pid_t pid;
- pid_t sid;
-
- // already a daemon
- if (getppid() == 1)
- return 0;
-
- pid = fork();
- if (pid < 0) {
- exit(EXIT_FAILURE);
- }
-
- if (pid > 0) {
- // exit parent process
- exit(EXIT_SUCCESS);
- }
- // At this point we are executing as the child process
-
- // Change the file mode mask
- umask(0);
-
- // Create a new SID for the child process
- sid = setsid();
- if (sid < 0) {
- return -1;
- }
- // Change the current working directory.
- if ((chdir("/")) < 0) {
- return -2;
- }
- // Redirect standard files to /dev/null
- if (!freopen("/dev/null", "r", stdin)) {
- logmsg(LOG_ERR, "ERROR: redirection of stdin failed.\n");
- }
- if (!freopen("/dev/null", "w", stdout)) {
- logmsg(LOG_ERR, "ERROR: redirection of stdout failed.\n");
- }
- if (!freopen("/dev/null", "w", stderr)) {
- logmsg(LOG_ERR, "ERROR: redirection of stderr failed.\n");
- }
-
- return 0;
-}
-
-static void usage()
-{
- printf("usage: usbmuxd [options]\n");
- printf("\t-h|--help print this message\n");
- printf("\t-v|--verbose be verbose (use twice or more to increase)\n");
- printf("\t-f|--foreground do not daemonize\n");
- printf("\t-e|--exit-on-no-devices exit if no device is attached\n");
- printf("\t-d|--drop-privileges drop privileges after startup\n");
- printf("\t-u|--udev udev operation mode\n");
- printf("\t-x|--exit tell a running instance to exit\n");
- printf("\t-X|--force-exit tell a running instance to exit, even if\n");
- printf("\t there are still devices connected\n");
- printf("\n");
-}
-
-static void parse_opts(int argc, char **argv)
-{
- static struct option longopts[] = {
- {"help", 0, NULL, 'h'},
- {"foreground", 0, NULL, 'f'},
- {"verbose", 0, NULL, 'v'},
- {"exit-on-no-devices", 0, NULL, 'e'},
- {"drop-privileges", 0, NULL, 'd'},
- {"udev", 0, NULL, 'u'},
- {"exit", 0, NULL, 'x'},
- {"force-exit", 0, NULL, 'X'},
- {NULL, 0, NULL, 0}
- };
- int c;
-
- while (1) {
- c = getopt_long(argc, argv, "hfveduxX", longopts, (int *) 0);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'h':
- usage();
- exit(0);
- case 'f':
- foreground = 1;
- break;
- case 'v':
- sock_stuff_set_verbose(++verbose);
- break;
- case 'e':
- exit_on_no_devices = 1;
- break;
- case 'd':
- drop_privileges = 1;
- break;
- case 'u':
- opt_udev = 1;
- break;
- case 'x':
- opt_exit = 1;
- exit_signal = SIGQUIT;
- break;
- case 'X':
- opt_exit = 1;
- exit_signal = SIGTERM;
- break;
- default:
- usage();
- exit(2);
- }
- }
- if (opt_udev)
- foreground = 0;
-}
-
-/**
- * checks for attached devices
- *
- * @return number of devices found
- */
-static int devices_attached()
-{
- struct usb_bus *bus;
- struct usb_device *dev;
- int res = 0;
-
- usb_init();
- usb_find_busses();
- usb_find_devices();
-
- for (bus = usb_get_busses(); bus; bus = bus->next) {
- for (dev = bus->devices; dev; dev = dev->next) {
- if (dev->descriptor.idVendor == 0x05ac
- && dev->descriptor.idProduct >= 0x1290
- && dev->descriptor.idProduct <= 0x1294) {
- res++;
- }
- }
- }
-
- return res;
-}
-
-/**
- * signal handler function for cleaning up properly
- */
-static void handle_signal(int sig)
-{
- if (sig == SIGTERM) {
- quit_flag = 1;
- } else {
- if (sig == SIGINT) {
- if (verbose >= 1)
- fprintf(stderr, "CTRL+C pressed\n");
- }
-
- if (verbose >= 1)
- fprintf(stderr, "Checking if we can terminate (no more devices attached)...\n");
-
- if (devices_attached() > 0) {
- // we can't quit, there are still devices attached.
- if (verbose >= 1)
- fprintf(stderr, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.\n");
- } else {
- // it's safe to quit
- quit_flag = 1;
- }
- }
-}
-
-/**
- * main function. Initializes all stuff and then loops waiting in accept.
- */
-int main(int argc, char **argv)
-{
- struct sockaddr_un c_addr;
- socklen_t len = sizeof(struct sockaddr_un);
- struct client_data *cdata = NULL;
- struct client_data **children = NULL;
- int children_capacity = DEFAULT_CHILDREN_CAPACITY;
- int i;
- int result = 0;
- int exit_val = 0;
- int cnt = 0;
- FILE *lfd = NULL;
- struct flock lock;
-
- parse_opts(argc, argv);
-
- argc -= optind;
- argv += optind;
-
- if (!foreground) {
- openlog("usbmuxd", LOG_PID, 0);
- }
-
- if (verbose >= 1)
- logmsg(LOG_NOTICE, "starting");
-
- // signal(SIGHUP, reload_conf); // none yet
- signal(SIGINT, handle_signal);
- signal(SIGQUIT, handle_signal);
- signal(SIGTERM, handle_signal);
- signal(SIGPIPE, SIG_IGN);
-
- // check for other running instance
- lfd = fopen(LOCKFILE, "r");
- if (lfd) {
- lock.l_type = 0;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- fcntl(fileno(lfd), F_GETLK, &lock);
- fclose(lfd);
- if (lock.l_type != F_UNLCK) {
- if (opt_exit) {
- if (lock.l_pid && !kill(lock.l_pid, 0)) {
- logmsg(LOG_NOTICE, "sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
- if (kill(lock.l_pid, exit_signal) < 0) {
- logmsg(LOG_ERR, "Error: could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
- }
- exit_val = 0;
- goto terminate;
- } else {
- logmsg(LOG_ERR, "Error: could not determine pid of the other running instance!");
- exit_val = -1;
- goto terminate;
- }
- } else {
- logmsg(LOG_NOTICE,
- "another instance is already running (pid %d). exiting.", lock.l_pid);
- if (!opt_udev) {
- exit_val = -1;
- }
- goto terminate;
- }
- }
- }
-
- if (opt_exit) {
- logmsg(LOG_NOTICE, "no running instance found, none killed. exiting.");
- goto terminate;
- }
-
- if (exit_on_no_devices) {
- if (devices_attached() <= 0) {
- logmsg(LOG_NOTICE, "no devices attached. exiting.");
- return 0;
- }
- }
-
- fsock = create_unix_socket(USBMUXD_SOCKET_FILE);
- if (fsock < 0) {
- logmsg(LOG_ERR, "Could not create socket, exiting");
- if (!foreground) {
- closelog();
- }
- return -1;
- }
-
- chmod(USBMUXD_SOCKET_FILE, 0666);
-
- if (verbose >= 2)
- usb_set_debug(verbose);
-
- if (verbose >= 4)
- usbmux_set_debug(1);
-
- if (!foreground) {
- if (daemonize() < 0) {
- fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
- syslog(LOG_ERR, "FATAL: Could not daemonize!");
- closelog();
- exit(EXIT_FAILURE);
- }
- }
- // now open the lockfile and place the lock
- lfd = fopen(LOCKFILE, "w");
- if (lfd) {
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- if (fcntl(fileno(lfd), F_SETLK, &lock) == -1) {
- logmsg(LOG_ERR, "ERROR: lockfile locking failed!");
- exit(EXIT_FAILURE);
- }
- }
- // drop elevated privileges
- if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
- struct passwd *pw = getpwnam("nobody");
- if (pw) {
- setuid(pw->pw_uid);
- } else {
- logmsg(LOG_ERR,
- "ERROR: Dropping privileges failed, check if user 'nobody' exists! Will now terminate.");
- exit(EXIT_FAILURE);
- }
-
- // security check
- if (setuid(0) != -1) {
- logmsg(LOG_ERR, "ERROR: Failed to drop privileges properly!");
- exit(EXIT_FAILURE);
- }
- if (verbose >= 2)
- logmsg(LOG_NOTICE, "Successfully dropped privileges");
- }
- // Reserve space for 10 clients which should be enough. If not, the
- // buffer gets enlarged later.
- children =
- (struct client_data **) malloc(sizeof(struct client_data *) *
- children_capacity);
- if (!children) {
- logmsg(LOG_ERR,
- "Out of memory when allocating memory for child threads. Terminating.");
- if (!foreground) {
- closelog();
- }
- exit(EXIT_FAILURE);
- }
- memset(children, 0, sizeof(struct client_data *) * children_capacity);
-
- if (verbose >= 2)
- logmsg(LOG_NOTICE, "waiting for connection");
- while (!quit_flag) {
- // Check the file descriptor before accepting a connection.
- // If no connection attempt is made, just repeat...
- result = check_fd(fsock, FD_READ, 1000);
- if (result <= 0) {
- if (result == 0) {
- // cleanup
- for (i = 0; i < children_capacity; i++) {
- if (children[i]) {
- if (children[i]->dead != 0) {
- pthread_join(children[i]->thread, NULL);
- if (verbose >= 3)
- logmsg(LOG_NOTICE,
- "reclaimed client thread (fd=%d)",
- children[i]->socket);
- free(children[i]);
- children[i] = NULL;
- cnt++;
- } else {
- cnt = 0;
- }
- } else {
- cnt++;
- }
- }
-
- if ((children_capacity > DEFAULT_CHILDREN_CAPACITY)
- && ((children_capacity - cnt) <=
- DEFAULT_CHILDREN_CAPACITY)) {
- children_capacity = DEFAULT_CHILDREN_CAPACITY;
- children =
- realloc(children,
- sizeof(struct client_data *) *
- children_capacity);
- }
- continue;
- } else {
- if (verbose >= 3)
- logmsg(LOG_ERR, "usbmuxd: select error: %s",
- strerror(errno));
- continue;
- }
- }
-
- cdata = (struct client_data *) malloc(sizeof(struct client_data));
- memset(cdata, 0, sizeof(struct client_data));
- if (!cdata) {
- quit_flag = 1;
- logmsg(LOG_ERR, "Error: Out of memory! Terminating.");
- break;
- }
-
- cdata->socket = accept(fsock, (struct sockaddr *) &c_addr, &len);
- if (cdata->socket < 0) {
- free(cdata);
- if (errno == EINTR) {
- continue;
- } else {
- if (verbose >= 3)
- logmsg(LOG_ERR, "Error in accept: %s",
- strerror(errno));
- continue;
- }
- }
-
- if (verbose >= 1)
- logmsg(LOG_NOTICE, "new client connected (fd=%d)",
- cdata->socket);
-
- // create client thread:
- if (pthread_create
- (&cdata->thread, NULL, usbmuxd_client_init_thread, cdata) == 0)
- {
- for (i = 0; i < children_capacity; i++) {
- if (children[i] == NULL)
- break;
- }
- if (i == children_capacity) {
- // enlarge buffer
- children_capacity++;
- children =
- realloc(children,
- sizeof(struct client_data *) *
- children_capacity);
- if (!children) {
- logmsg(LOG_ERR,
- "Out of memory when enlarging child thread buffer");
- }
- }
- children[i] = cdata;
- } else {
- logmsg(LOG_ERR, "Failed to create client_init_thread.");
- close(cdata->socket);
- free(cdata);
- cdata = NULL;
- }
- }
-
- if (verbose >= 3)
- logmsg(LOG_NOTICE, "terminating");
-
- // preparing for shutdown: wait for child threads to terminate (if any)
- if (verbose >= 2)
- logmsg(LOG_NOTICE, "waiting for child threads to terminate...");
- for (i = 0; i < children_capacity; i++) {
- if (children[i] != NULL) {
- pthread_join(children[i]->thread, NULL);
- free(children[i]);
- children[i] = NULL;
- }
- }
-
- // delete the children set.
- free(children);
- children = NULL;
-
-
- if (fsock >= 0) {
- close(fsock);
- }
-
- unlink(USBMUXD_SOCKET_FILE);
-
- // unlock lock file and close it.
- if (lfd) {
- lock.l_type = F_UNLCK;
- fcntl(fileno(lfd), F_SETLK, &lock);
- fclose(lfd);
- }
-
-terminate:
- if (verbose >= 1)
- logmsg(LOG_NOTICE, "terminated");
- if (!foreground) {
- closelog();
- }
-
- return 0;
-}
diff --git a/src/usbmux.c b/src/usbmux.c
deleted file mode 100644
index c5e38dd..0000000
--- a/src/usbmux.c
+++ /dev/null
@@ -1,1264 +0,0 @@
-/*
- * Copyright (c) 2008 Jing Su. All Rights Reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include <stdint.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <usb.h>
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <pthread.h>
-#include "usbmux.h"
-
-#define BULKIN 0x85
-#define BULKOUT 0x04
-
-static const uint8_t TCP_FIN = 1;
-static const uint8_t TCP_SYN = 1 << 1;
-static const uint8_t TCP_RST = 1 << 2;
-static const uint8_t TCP_PSH = 1 << 3;
-static const uint8_t TCP_ACK = 1 << 4;
-static const uint8_t TCP_URG = 1 << 5;
-
-// I have trouble figuring out how to properly manage the windowing to
-// the device. It keeps sending back 512 and seems to drop off a cliff
-// when the device gets overwhelmed. In addition, the device likes to
-// panic and send out RESETS before the window hits zero. Also, waiting
-// for responses seems to not be a winning strategy.
-//
-// Since I'm not sure how in the hell to interpret the window sizes that
-// the device is sending back to us, I've figured out some magic number
-// constants which seem to work okay.
-static const uint32_t WINDOW_MAX = 5 * 1024;
-static const uint32_t WINDOW_INCREMENT = 512;
-
-typedef struct {
- char *buffer;
- int leftover;
- int capacity;
-} receivebuf_t;
-
-struct usbmux_device_int {
- struct usb_dev_handle *usbdev;
- struct usb_device *__device;
- receivebuf_t usbReceive;
- int wMaxPacketSize;
-};
-
-typedef struct {
- uint32_t type, length, major, minor, allnull;
-} usbmux_version_header;
-
-typedef struct {
- uint32_t type, length;
- uint16_t sport, dport;
- uint32_t scnt, ocnt;
- uint8_t offset, tcp_flags;
- uint16_t window, nullnull, length16;
-} usbmux_tcp_header;
-
-struct usbmux_client_int {
- usbmux_tcp_header *header;
- usbmux_device_t device;
-
- char *recv_buffer;
- int r_len;
- pthread_cond_t wait;
-
- // this contains a conditional variable which usb-writers can wait
- // on while waiting for window updates from the device.
- pthread_cond_t wr_wait;
- // I'm going to do something really cheesy here. We are going to
- // just record the most recent scnt that we are expecting to hear
- // back on. We will actually halt progress by limiting the number
- // of outstanding un-acked bulk sends that we have beamed out.
- uint32_t wr_pending_scnt;
- long wr_window;
-
- pthread_mutex_t mutex;
-
- // this variable is not protected by the mutex. This will always
- // be E_SUCCESS, unless an error of some kind breaks this stream.
- // this will then be set to the error that caused the broken stream.
- // no further operations other than free_client will be allowed.
- int error;
-
- int cleanup;
-
- int connected;
-};
-
-
-static pthread_mutex_t usbmuxmutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t printmutex = PTHREAD_MUTEX_INITIALIZER;
-static usbmux_client_t *connlist = NULL;
-static int clients = 0;
-
-
-/**
- */
-int toto_debug = 0;
-
-void usbmux_set_debug(int e)
-{
- toto_debug = e;
-}
-
-static void log_debug_msg(const char *format, ...)
-{
-#ifndef STRIP_DEBUG_CODE
- va_list args;
- /* run the real fprintf */
- va_start(args, format);
-
- if (toto_debug) {
- pthread_mutex_lock(&printmutex);
- vfprintf(stderr, format, args);
- pthread_mutex_unlock(&printmutex);
- }
-
- va_end(args);
-#endif
-}
-
-/**
- * for debugging purposes.
- */
-static void print_buffer(const char *data, const int length)
-{
- if (toto_debug <= 0) {
- return;
- }
- int i;
- int j;
- unsigned char c;
-
- for (i = 0; i < length; i += 16) {
- printf("%04x: ", i);
- for (j = 0; j < 16; j++) {
- if (i + j >= length) {
- printf(" ");
- continue;
- }
- printf("%02hhx ", *(data + i + j));
- }
- printf(" | ");
- for (j = 0; j < 16; j++) {
- if (i + j >= length)
- break;
- c = *(data + i + j);
- if ((c < 32) || (c > 127)) {
- printf(".");
- continue;
- }
- printf("%c", c);
- }
- printf("\n");
- }
- printf("\n");
-}
-
-static void hton_header(usbmux_tcp_header * hdr)
-{
- if (hdr) {
- hdr->length = htonl(hdr->length);
- hdr->scnt = htonl(hdr->scnt);
- hdr->ocnt = htonl(hdr->ocnt);
- hdr->length16 = htons(hdr->length16);
- }
-}
-
-static void ntoh_header(usbmux_tcp_header * hdr)
-{
- if (hdr) {
- hdr->length = ntohl(hdr->length);
- hdr->scnt = ntohl(hdr->scnt);
- hdr->ocnt = ntohl(hdr->ocnt);
- hdr->length16 = ntohs(hdr->length16);
- }
-}
-
-/** Creates a USBMux header containing version information
- *
- * @return A USBMux header
- */
-static usbmux_version_header *version_header()
-{
- usbmux_version_header *version =
- (usbmux_version_header *) malloc(sizeof(usbmux_version_header));
- version->type = 0;
- version->length = htonl(20);
- version->major = htonl(1);
- version->minor = 0;
- version->allnull = 0;
- return version;
-}
-
-/**
- * This function sets the configuration of the given device to 3
- * and claims the interface 1. If usb_set_configuration fails, it detaches
- * the kernel driver that blocks the device, and retries configuration.
- *
- * @param device which device to configure
- */
-static int usbmux_config_usb_device(usbmux_device_t device)
-{
- int ret;
- int bytes;
- char buf[512];
-
- log_debug_msg("claiming interface... ");
- ret = usb_claim_interface(device->usbdev, 1);
- if (ret != 0) {
- log_debug_msg("Error: usb_claim_interface returned %d: %s\n", ret,
- strerror(-ret));
- return -ENODEV;
- } else {
- log_debug_msg("done.\n");
- }
-
- // get the last configuration
- struct usb_config_descriptor *cfg = &device->__device->config[device->__device->descriptor.bNumConfigurations-1];
- if (cfg && cfg->bNumInterfaces >= 2) {
- struct usb_interface *ifp = &cfg->interface[1];
- if (ifp && ifp->num_altsetting >= 1) {
- struct usb_interface_descriptor *as = &ifp->altsetting[0];
- int i;
- for (i = 0; i < as->bNumEndpoints; i++) {
- struct usb_endpoint_descriptor *ep=&as->endpoint[i];
- if (ep->bEndpointAddress == BULKOUT) {
- device->wMaxPacketSize = ep->wMaxPacketSize;
- }
- }
- }
- }
-
- log_debug_msg("Setting wMaxPacketSize to %d\n", device->wMaxPacketSize);
-
- do {
- bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800);
- } while (bytes > 0);
-
- return 0;
-}
-
-/**
- * Given a USB bus and device number, returns a device handle to the device on
- * that bus. To aid compatibility with future devices, this function does not
- * check the vendor and device IDs! To do that, you should use
- * usbmux_get_device() or a system-specific API (e.g. HAL).
- *
- * @param bus_n The USB bus number.
- * @param dev_n The USB device number.
- * @param device A pointer to a usbmux_device_t, which must be set to NULL upon
- * calling usbmux_get_specific_device, which will be filled with a device
- * descriptor on return.
- * @return 0 if ok, otherwise a negative errno value.
- */
-int usbmux_get_specific_device(int bus_n, int dev_n,
- usbmux_device_t * device)
-{
- struct usb_bus *bus;
- struct usb_device *dev;
- usbmux_version_header *version;
- int bytes = 0;
-
- //check we can actually write in device
- if (!device || (device && *device))
- return -EINVAL;
-
- usbmux_device_t newdevice =
- (usbmux_device_t) malloc(sizeof(struct usbmux_device_int));
-
- // Initialize the struct
- newdevice->usbdev = NULL;
- newdevice->__device = NULL;
-
- // don't forget these:
- newdevice->usbReceive.buffer = NULL;
- newdevice->usbReceive.leftover = 0;
- newdevice->usbReceive.capacity = 0;
-
- // wMaxPacketSize
- newdevice->wMaxPacketSize = 64;
-
- // Initialize libusb
- usb_init();
- usb_find_busses();
- usb_find_devices();
-
- // Set the device configuration
- for (bus = usb_get_busses(); bus; bus = bus->next)
- if (strtoul(bus->dirname, NULL, 10) == bus_n)
- for (dev = bus->devices; dev != NULL; dev = dev->next)
- if (strtol(dev->filename, NULL, 10) == dev_n) {
- newdevice->__device = dev;
- newdevice->usbdev = usb_open(newdevice->__device);
- if (!newdevice->usbdev) {
- fprintf(stderr, "%s: Error: usb_open(): %s\n", __func__, usb_strerror());
- }
- if (usbmux_config_usb_device(newdevice) == 0) {
- goto found;
- }
- }
-
- usbmux_free_device(newdevice);
-
- log_debug_msg("usbmux_get_specific_device: device not found\n");
- return -ENODEV;
-
- found:
- // Send the version command to the device
- version = version_header();
- bytes =
- usb_bulk_write(newdevice->usbdev, BULKOUT, (char *) version,
- sizeof(*version), 800);
- if (bytes < 20) {
- log_debug_msg("%s: libusb did NOT send enough!\n", __func__);
- if (bytes < 0) {
- log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n",
- __func__, bytes, usb_strerror(),
- strerror(-bytes));
- }
- }
- // Read the device's response
- bytes =
- usb_bulk_read(newdevice->usbdev, BULKIN, (char *) version,
- sizeof(*version), 800);
-
- // Check for bad response
- if (bytes < 20) {
- free(version);
- usbmux_free_device(newdevice);
- log_debug_msg("%s: Invalid version message -- header too short.\n",
- __func__);
- if (bytes < 0) {
- log_debug_msg("%s: libusb error message %d: %s (%s)\n",
- __func__, bytes, usb_strerror(),
- strerror(-bytes));
- return bytes;
- }
- return -EBADMSG;
- }
- // Check for correct version
- if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) {
- // We're all ready to roll.
- log_debug_msg("%s: success\n", __func__);
- free(version);
- *device = newdevice;
- return 0;
- } else {
- // Bad header
- usbmux_free_device(newdevice);
- free(version);
- log_debug_msg("%s: Received a bad header/invalid version number.",
- __func__);
- return -EBADMSG;
- }
-
- // If it got to this point it's gotta be bad
- log_debug_msg("%s: Unknown error.\n", __func__);
- usbmux_free_device(newdevice);
- free(version);
- return -EBADMSG; // if it got to this point it's gotta be bad
-}
-
-/** Cleans up an usbmux_device_t structure, then frees the structure itself.
- * This is a library-level function; deals directly with the device to tear
- * down relations, but otherwise is mostly internal.
- *
- * @param device A pointer to an usbmux_device_t structure.
- */
-int usbmux_free_device(usbmux_device_t device)
-{
- char buf[512];
- int bytes = -1;
-
- if (!device)
- return -EINVAL;
- int ret = 0;
-
- if (device->usbdev) {
- do {
- bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800);
- } while (bytes > 0);
- }
-
- if (bytes < 0) {
- ret = bytes;
- }
-
- if (device->usbReceive.buffer) {
- free(device->usbReceive.buffer);
- }
- if (device->usbdev) {
- usb_release_interface(device->usbdev, 1);
- usb_close(device->usbdev);
- ret = 0;
- }
- free(device);
-
- return ret;
-}
-
-
-
-/** Sends data to the device
- * This is a low-level (i.e. directly to device) function.
- *
- * @param device The device to send data to
- * @param data The data to send
- * @param datalen The length of the data
- * @return The number of bytes sent, or -ERRNO on error
- */
-static int send_to_device(usbmux_device_t device, char *data, int datalen)
-{
- if (!device)
- return -EINVAL;
-
- int timeout = 1000;
- int retrycount = 0;
- int bytes = 0;
-
-if (toto_debug > 0) {
- pthread_mutex_lock(&printmutex);
- printf("===============================\n%s: trying to send\n",
- __func__);
- print_buffer(data, datalen);
- printf("===============================\n");
- pthread_mutex_unlock(&printmutex);
-}
-
- do {
- if (retrycount > 3) {
- log_debug_msg
- ("EPIC FAIL! aborting on retry count overload.\n");
- return -ECOMM;
- }
-
- bytes =
- usb_bulk_write(device->usbdev, BULKOUT, data, datalen,
- timeout);
- if (bytes == -ETIMEDOUT) {
- // timed out waiting for write.
- log_debug_msg("usb_bulk_write timeout error.\n");
- return bytes;
- } else if (bytes < 0) {
- log_debug_msg
- ("usb_bulk_write failed with error. err:%d (%s)(%s)\n",
- bytes, usb_strerror(), strerror(-bytes));
- return bytes;
- } else if (bytes == 0) {
- log_debug_msg("usb_bulk_write sent nothing. retrying.\n");
- timeout = timeout * 4;
- retrycount++;
- continue;
- } else if (bytes < datalen) {
- log_debug_msg
- ("usb_bulk_write failed to send full dataload. %d of %d\n",
- bytes, datalen);
- timeout = timeout * 4;
- retrycount++;
- data += bytes;
- datalen -= bytes;
- continue;
- }
- if ((bytes % device->wMaxPacketSize) == 0) {
- log_debug_msg("NOTE: sending NULL packet\n");
- char nullp = 0;
- int res = usb_bulk_write(device->usbdev, BULKOUT,
- &nullp, 0, timeout);
- if (res < 0) {
- log_debug_msg("ERROR: NULL packet write returned %d\n", res);
- }
- }
- } while (0); // fall out
-
- if (bytes > 0) {
- if (toto_debug > 0) {
- pthread_mutex_lock(&printmutex);
- printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
- printf("%s: sent to device\n", __func__);
- print_buffer(data, bytes);
- printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
- pthread_mutex_unlock(&printmutex);
- }
- }
- return bytes;
-}
-
-/** Receives data from the device
- * This function is a low-level (i.e. direct from device) function.
- *
- * @param device The device to receive data from
- * @param data Where to put data read
- * @param datalen How much data to read in
- * @param timeout How many milliseconds to wait for data
- *
- * @return How many bytes were read in, or -1 on error.
- */
-static int recv_from_device_timeout(usbmux_device_t device, char *data,
- int datalen, int timeoutmillis)
-{
- if (!device)
- return -EINVAL;
- //log_debug_msg("%s: attempting to receive %i bytes\n", __func__, datalen);
-
- int bytes =
- usb_bulk_read(device->usbdev, BULKIN, data, datalen,
- timeoutmillis);
- // There are some things which are errors, others which are no problem.
- // It's not documented in libUSB, but it seems that the error values
- // returned are just negated ERRNO values.
- if (bytes < 0) {
- if (bytes == -ETIMEDOUT) {
- // ignore this. it just means timeout reached before we
- // picked up any data. no problem.
- return 0;
- } else {
- fprintf(stderr, "%s: libusb gave me the error %d: %s (%s)\n",
- __func__, bytes, usb_strerror(), strerror(-bytes));
- log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n",
- __func__, bytes, usb_strerror(),
- strerror(-bytes));
- }
- return bytes;
- }
- if (bytes > 0) {
- if (toto_debug > 0) {
- pthread_mutex_lock(&printmutex);
- printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
- printf("%s: received from device:\n", __func__);
- print_buffer(data, bytes);
- printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
- pthread_mutex_unlock(&printmutex);
- }
- }
-
- return bytes;
-}
-
-/** Creates a USBMux packet for the given set of ports.
- *
- * @param s_port The source port for the connection.
- * @param d_port The destination port for the connection.
- *
- * @return A USBMux packet
- */
-static usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port)
-{
- usbmux_tcp_header *conn =
- (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header));
- conn->type = htonl(6);
- conn->length = sizeof(usbmux_tcp_header);
- conn->sport = htons(s_port);
- conn->dport = htons(d_port);
- conn->scnt = 0;
- conn->ocnt = 0;
- conn->offset = 0x50;
- conn->window = htons(0x0200);
- conn->nullnull = 0x0000;
- conn->length16 = sizeof(usbmux_tcp_header);
- return conn;
-}
-
-
-/** Removes a connection from the list of connections made.
- * The list of connections is necessary for buffering.
- *
- * @param connection The connection to delete from the tracking list.
- */
-static void delete_connection(usbmux_client_t connection)
-{
- usbmux_client_t *newlist = NULL;
-
- pthread_mutex_lock(&usbmuxmutex);
-
- // update the global list of connections
- if (clients > 1) {
- newlist =
- (usbmux_client_t *) malloc(sizeof(usbmux_client_t) *
- (clients - 1));
- int i = 0, j = 0;
- for (i = 0; i < clients; i++) {
- if (connlist[i] == connection)
- continue;
- else {
- newlist[j] = connlist[i];
- j++;
- }
- }
- }
- if (connlist) {
- free(connlist);
- }
- connlist = newlist;
- clients--;
-
- // free up this connection
- pthread_mutex_lock(&connection->mutex);
- if (connection->recv_buffer) {
- free(connection->recv_buffer);
- connection->recv_buffer = NULL;
- }
- if (connection->header) {
- free(connection->header);
- connection->header = NULL;
- }
- connection->r_len = 0;
- pthread_mutex_unlock(&connection->mutex);
- pthread_mutex_destroy(&connection->mutex);
- free(connection);
-
- pthread_mutex_unlock(&usbmuxmutex);
-}
-
-/** Adds a connection to the list of connections made.
- * The connection list is necessary for buffering.
- *
- * @param connection The connection to add to the global list of connections.
- */
-
-static void add_connection(usbmux_client_t connection)
-{
- pthread_mutex_lock(&usbmuxmutex);
- usbmux_client_t *newlist =
- (usbmux_client_t *) realloc(connlist,
- sizeof(usbmux_client_t) * (clients +
- 1));
- newlist[clients] = connection;
- connlist = newlist;
- clients++;
- pthread_mutex_unlock(&usbmuxmutex);
-}
-
-/**
- * Get a source port number that is not used by one of our connections
- * This is needed for us to make sure we are not sending on another
- * connection.
- */
-static uint16_t get_free_port()
-{
- int i;
- uint16_t newport = 30000;
- int cnt = 0;
-
- pthread_mutex_lock(&usbmuxmutex);
- while (1) {
- cnt = 0;
- for (i = 0; i < clients; i++) {
- if (ntohs(connlist[i]->header->sport) == newport) {
- cnt++;
- }
- }
- if (cnt == 0) {
- // newport is not used in our list of connections!
- break;
- } else {
- newport++;
- if (newport < 30000) {
- // if all ports from 30000 to 65535 are in use,
- // the value wraps (16-bit overflow)
- // return 0, no port is available.
- // This should not happen, but just in case ;)
- newport = 0;
- break;
- }
- }
- }
- pthread_mutex_unlock(&usbmuxmutex);
-
- return newport;
-}
-
-/** Initializes a connection to 'device' with source port s_port and destination port d_port
- *
- * @param device The device to initialize a connection on.
- * @param src_port The source port
- * @param dst_port The destination port -- 0xf27e for lockdownd.
- * @param client A mux TCP header for the connection which is used for tracking and data transfer.
- * @return 0 on success, a negative errno value otherwise.
- */
-int usbmux_new_client(usbmux_device_t device, uint16_t src_port,
- uint16_t dst_port, usbmux_client_t * client)
-{
- if (!device || !dst_port)
- return -EINVAL;
-
- src_port = get_free_port();
-
- if (!src_port) {
- // this is a special case, if we get 0, this is not good, so
- return -EISCONN; // TODO: error code suitable?
- }
- // Initialize connection stuff
- usbmux_client_t new_connection =
- (usbmux_client_t) malloc(sizeof(struct usbmux_client_int));
- new_connection->header = new_mux_packet(src_port, dst_port);
-
- // send TCP syn
- if (new_connection && new_connection->header) {
- int err = 0;
- new_connection->header->tcp_flags = TCP_SYN;
- new_connection->header->length = new_connection->header->length;
- new_connection->header->length16 =
- new_connection->header->length16;
- new_connection->header->scnt = 0;
- new_connection->header->ocnt = 0;
- new_connection->device = device;
- new_connection->recv_buffer = NULL;
- new_connection->r_len = 0;
- pthread_cond_init(&new_connection->wait, NULL);
- pthread_mutex_init(&new_connection->mutex, NULL);
- pthread_cond_init(&new_connection->wr_wait, NULL);
- new_connection->wr_pending_scnt = 0;
- new_connection->wr_window = 0;
- add_connection(new_connection);
- new_connection->error = 0;
- new_connection->cleanup = 0;
- new_connection->connected = 0;
- hton_header(new_connection->header);
- log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__,
- ntohs(new_connection->header->sport),
- ntohs(new_connection->header->dport));
- err =
- send_to_device(device, (char *) new_connection->header,
- sizeof(usbmux_tcp_header));
- if (err >= 0) {
- *client = new_connection;
- return 0;
- } else {
- delete_connection(new_connection);
- return err;
- }
- }
- // if we get to this point it's probably bad
- return -ENOMEM;
-}
-
-/** Cleans up the given USBMux connection.
- * @note Once a connection is closed it may not be used again.
- *
- * @param connection The connection to close.
- *
- * @return 0 on success or a negative errno value on error.
- */
-int usbmux_free_client(usbmux_client_t client)
-{
- if (!client || !client->device)
- return -EINVAL;
-
- int err = 0;
- int result = 0;
- pthread_mutex_lock(&client->mutex);
- client->header->tcp_flags = TCP_RST;
- client->header->length = 0x1C;
- client->header->window = 0;
- client->header->length16 = 0x1C;
- hton_header(client->header);
-
- err =
- send_to_device(client->device, (char *) client->header,
- sizeof(usbmux_tcp_header));
- if (err < 0) {
- log_debug_msg("%s: error sending TCP_FIN\n", __func__);
- result = err;
- }
-
- client->cleanup = 1;
-
- // make sure we don't have any last-minute laggards waiting on this.
- // I put it after the mutex unlock because we have cases where the
- // conditional wait is dependent on re-grabbing that mutex.
- pthread_cond_broadcast(&client->wait);
- pthread_cond_destroy(&client->wait);
- pthread_cond_broadcast(&client->wr_wait);
- pthread_cond_destroy(&client->wr_wait);
-
- pthread_mutex_unlock(&client->mutex);
-
- return result;
-}
-
-/** Sends the given data over the selected connection.
- *
- * @param client The client we're sending data on.
- * @param data A pointer to the data to send.
- * @param datalen How much data we're sending.
- * @param sent_bytes The number of bytes sent, minus the header (28)
- *
- * @return 0 on success or a negative errno value on error.
- */
-int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen,
- uint32_t * sent_bytes)
-{
- if (!client->device || !client || !sent_bytes)
- return -EINVAL;
-
- if (client->error < 0) {
- return client->error;
- }
-
- *sent_bytes = 0;
- pthread_mutex_lock(&client->mutex);
-
- int sendresult = 0;
- uint32_t blocksize = 0;
- if (client->wr_window <= 0) {
- struct timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
- //ts.tv_sec += 1;
- ts.tv_nsec += 750 * 1000;
- if (pthread_cond_timedwait(&client->wait, &client->mutex, &ts) ==
- ETIMEDOUT) {
- // timed out. optimistically grow the window and try to make progress
- client->wr_window += WINDOW_INCREMENT;
- }
- }
-
- blocksize = sizeof(usbmux_tcp_header) + datalen;
-
- // client->scnt and client->ocnt should already be in host notation...
- // we don't need to change them juuuust yet.
- char *buffer = (char *) malloc(blocksize + 2); // allow 2 bytes of safety padding
- // Set the length
- client->header->length = blocksize;
- client->header->length16 = blocksize;
-
- // Put header into big-endian notation
- hton_header(client->header);
- // Concatenation of stuff in the buffer.
- memcpy(buffer, client->header, sizeof(usbmux_tcp_header));
- memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen);
-
- log_debug_msg("%s: send_to_device(%d --> %d)\n", __func__,
- ntohs(client->header->sport),
- ntohs(client->header->dport));
- sendresult = send_to_device(client->device, buffer, blocksize);
- // Now that we've sent it off, we can clean up after our sloppy selves.
- if (buffer)
- free(buffer);
-
- // revert header fields that have been swapped before trying to send
- ntoh_header(client->header);
-
- // update counts ONLY if the send succeeded.
- if ((uint32_t) sendresult == blocksize) {
- // Re-calculate scnt
- client->header->scnt += datalen;
- client->wr_window -= blocksize;
- }
-
- pthread_mutex_unlock(&client->mutex);
-
- if (sendresult == -ETIMEDOUT || sendresult == 0) {
- // no problem for now...
- *sent_bytes = 0;
- return -ETIMEDOUT;
- } else if (sendresult < 0) {
- return sendresult;
- } else if ((uint32_t) sendresult == blocksize) {
- // actual number of data bytes sent.
- *sent_bytes = sendresult - sizeof(usbmux_tcp_header);
- return 0;
- } else {
- fprintf(stderr,
- "usbsend managed to dump a packet that is not full size. %d of %d\n",
- sendresult, blocksize);
- return -EBADMSG;
- }
-}
-
-/** append the packet's DATA to the receive buffer for the client.
- *
- * this has a few other corner-case functions:
- * 1. this will properly handle the handshake syn+ack.
- * 2. for all receives, this will appropriately update the ocnt.
- *
- * @return number of bytes consumed (header + data)
- */
-static uint32_t append_receive_buffer(usbmux_client_t client, char *packet)
-{
- if (client == NULL || packet == NULL)
- return 0;
-
- usbmux_tcp_header *header = (usbmux_tcp_header *) packet;
- char *data = &packet[sizeof(usbmux_tcp_header)];
- uint32_t packetlen = ntohl(header->length);
- uint32_t datalen = packetlen - sizeof(usbmux_tcp_header);
-
- int dobroadcast = 0;
-
- pthread_mutex_lock(&client->mutex);
-
- // we need to handle a few corner case tasks and book-keeping which
- // falls on our responsibility because we are the ones reading in
- // feedback.
- if (client->header->scnt == 0 && client->header->ocnt == 0) {
- log_debug_msg("client is still waiting for handshake.\n");
- if (header->tcp_flags == (TCP_SYN | TCP_ACK)) {
- log_debug_msg("yes, got syn+ack ; replying with ack.\n");
- client->header->tcp_flags = TCP_ACK;
- client->header->length = sizeof(usbmux_tcp_header);
- client->header->length16 = sizeof(usbmux_tcp_header);
- client->header->scnt += 1;
- client->header->ocnt = header->ocnt;
- hton_header(client->header);
- // push it to USB
- // TODO: need to check for error in the send here.... :(
- log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__,
- ntohs(client->header->sport),
- ntohs(client->header->dport));
- if (send_to_device
- (client->device, (char *) client->header,
- sizeof(usbmux_tcp_header)) <= 0) {
- log_debug_msg("%s: error when pushing to usb...\n",
- __func__);
- } else {
- client->connected = 1;
- }
- // need to revert some of the fields back to host notation.
- ntoh_header(client->header);
- } else {
- client->error = -ECONNABORTED;
- // woah... this connection failed us.
- // TODO: somehow signal that this stream is a no-go.
- log_debug_msg("WOAH! client failed to get proper syn+ack.\n");
- }
- }
- // update TCP counters and windows.
- //
- // save the window that we're getting from the USB device.
- // apparently the window is bigger than just the 512 that's typically
- // advertised. iTunes apparently shifts this value by 8 to get a much
- // larger number.
- if (header->tcp_flags & TCP_RST) {
- client->error = -ECONNRESET;
-
- if (datalen > 0) {
- char e_msg[128];
- e_msg[0] = 0;
- if (datalen > 1) {
- memcpy(e_msg, data + 1, datalen - 1);
- e_msg[datalen - 1] = 0;
- }
- // fetch the message
- switch (data[0]) {
- case 0:
- // this is not an error, it's just a status message.
- log_debug_msg("received status message: %s\n", e_msg);
- datalen = 0;
- break;
- case 1:
- log_debug_msg("received error message: %s\n", e_msg);
- datalen = 0;
- break;
- default:
- log_debug_msg
- ("received unknown message (type 0x%02x): %s\n",
- data[0], e_msg);
- //datalen = 0; // <-- we let this commented out for testing
- break;
- }
- } else {
- log_debug_msg
- ("peer sent connection reset. setting error: %d\n",
- client->error);
- }
- }
- // the packet's ocnt tells us how much of our data the device has received.
- if (header->tcp_flags & TCP_ACK) {
- // this is a hacky magic number condition. it seems that once
- // the window reported by the device starts to drop below this
- // number, we quickly fall into connection reset problems.
- // Once we see the reported window size start falling off,
- // ut off and wait for solid acks to come back.
- if (ntohs(header->window) < 256)
- client->wr_window = 0;
-
- // check what just got acked.
- if (ntohl(header->ocnt) < client->header->scnt) {
- // we got some kind of ack, but it hasn't caught up
- // with the pending that have been sent.
- pthread_cond_broadcast(&client->wr_wait);
- } else if (ntohl(header->ocnt) >
- /*client->wr_pending_scnt */ client->header->scnt) {
- fprintf(stderr,
- "WTF?! acks overtook pending outstanding. %u,%u\n",
- ntohl(header->ocnt), client->wr_pending_scnt);
- } else {
- // reset the window
- client->wr_window = WINDOW_MAX;
- pthread_cond_broadcast(&client->wr_wait);
- }
- }
- // the packet's scnt will be our new ocnt.
- client->header->ocnt = ntohl(header->scnt);
-
- // ensure there is enough space, either by first malloc or realloc
- if (datalen > 0) {
- log_debug_msg("%s: putting %d bytes into client's recv_buffer\n",
- __func__, datalen);
- if (client->r_len == 0)
- dobroadcast = 1;
-
- if (client->recv_buffer == NULL) {
- client->recv_buffer = malloc(datalen);
- client->r_len = 0;
- } else {
- client->recv_buffer =
- realloc(client->recv_buffer, client->r_len + datalen);
- }
-
- memcpy(&client->recv_buffer[client->r_len], data, datalen);
- client->r_len += datalen;
- }
-
- pthread_mutex_unlock(&client->mutex);
-
- // I put this outside the mutex unlock just so that when the threads
- // wake, we don't have to do another round of unlock+try to grab.
- if (dobroadcast)
- pthread_cond_broadcast(&client->wait);
-
- return packetlen;
-}
-
-/**
- * @note THERE IS NO MUTEX LOCK IN THIS FUNCTION!
- * because we're only called from one location, pullbulk, where the lock
- * is already held.
- */
-static usbmux_client_t find_client(usbmux_tcp_header * recv_header)
-{
- // remember, as we're looking for the client, the receive header is
- // coming from the USB into our client. This means that when we check
- // the src/dst ports, we need to reverse them.
- usbmux_client_t retval = NULL;
-
- // just for debugging check, I'm going to convert the numbers to host-endian.
- uint16_t hsport = ntohs(recv_header->sport);
- uint16_t hdport = ntohs(recv_header->dport);
-
- pthread_mutex_lock(&usbmuxmutex);
- int i;
- for (i = 0; i < clients; i++) {
- uint16_t csport = ntohs(connlist[i]->header->sport);
- uint16_t cdport = ntohs(connlist[i]->header->dport);
-
- if (hsport == cdport && hdport == csport) {
- retval = connlist[i];
- break;
- }
- }
- pthread_mutex_unlock(&usbmuxmutex);
-
- return retval;
-}
-
-/** pull in a big USB bulk packet and distribute it to queues appropriately.
- */
-int usbmux_pullbulk(usbmux_device_t device)
-{
- if (!device)
- return -EINVAL;
-
- int res = 0;
- static const int DEFAULT_CAPACITY = 128 * 1024;
- if (device->usbReceive.buffer == NULL) {
- device->usbReceive.capacity = DEFAULT_CAPACITY;
- device->usbReceive.buffer = malloc(device->usbReceive.capacity);
- device->usbReceive.leftover = 0;
- }
- // start the cursor off just ahead of the leftover.
- char *cursor = &device->usbReceive.buffer[device->usbReceive.leftover];
- // pull in content, note that the amount we can pull is capacity minus leftover
- int readlen =
- recv_from_device_timeout(device, cursor,
- device->usbReceive.capacity -
- device->usbReceive.leftover, 3000);
- if (readlen < 0) {
- res = readlen;
- //fprintf(stderr, "recv_from_device_timeout gave us an error.\n");
- readlen = 0;
- }
- if (readlen > 0) {
- //fprintf(stdout, "recv_from_device_timeout pulled an extra %d bytes\n", readlen);
- }
- // the amount of content we have to work with is the remainder plus
- // what we managed to read
- device->usbReceive.leftover += readlen;
-
- // reset the cursor to the front of that buffer and work through
- // trying to decode packets out of them.
- cursor = device->usbReceive.buffer;
- while (1) {
- // check if there's even sufficient data to decode a header
- if (device->usbReceive.leftover < sizeof(usbmux_tcp_header))
- break;
- usbmux_tcp_header *header = (usbmux_tcp_header *) cursor;
-
- log_debug_msg("%s: recv_from_device_timeout (%d --> %d)\n",
- __func__, ntohs(header->sport),
- ntohs(header->dport));
-
- // now that we have a header, check if there is sufficient data
- // to construct a full packet, including its data
- uint32_t packetlen = ntohl(header->length);
- if ((uint32_t) device->usbReceive.leftover < packetlen) {
- fprintf(stderr,
- "%s: not enough data to construct a full packet\n",
- __func__);
- break;
- }
- // ok... find the client this packet will get stuffed to.
- usbmux_client_t client = find_client(header);
- if (client == NULL) {
- log_debug_msg
- ("WARNING: client for packet cannot be found. dropping packet.\n");
- } else {
- // stuff the data
- log_debug_msg
- ("%s: found client, calling append_receive_buffer\n",
- __func__);
- append_receive_buffer(client, cursor);
-
- // perhaps this is too general, == -ECONNRESET
- // might be a better check here
- if (client->error < 0) {
- pthread_mutex_lock(&client->mutex);
- if (client->cleanup) {
- pthread_mutex_unlock(&client->mutex);
- log_debug_msg("freeing up connection (%d->%d)\n",
- ntohs(client->header->sport),
- ntohs(client->header->dport));
- delete_connection(client);
- } else {
- pthread_mutex_unlock(&client->mutex);
- }
- }
- }
-
- // move the cursor and account for the consumption
- cursor += packetlen;
- device->usbReceive.leftover -= packetlen;
- }
-
- // now, we need to manage any leftovers.
- // I'm going to manage the leftovers by alloc'ing a new block and
- // copyingthe leftovers to it. This is just to prevent problems with
- // memory moves where there may be overlap. Besides, the leftovers
- // should be small enough that this copy is minimal in overhead.
- //
- // if there are no leftovers, we just leave the datastructure as is,
- // and re-use the block next time.
- if (device->usbReceive.leftover > 0
- && cursor != device->usbReceive.buffer) {
- log_debug_msg("%s: we got a leftover, so handle it\n", __func__);
- char *newbuff = malloc(DEFAULT_CAPACITY);
- memcpy(newbuff, cursor, device->usbReceive.leftover);
- free(device->usbReceive.buffer);
- device->usbReceive.buffer = newbuff;
- device->usbReceive.capacity = DEFAULT_CAPACITY;
- }
-
- return res;
-}
-
-/**
- * return the error code stored in usbmux_client_t structure,
- * e.g. non-zero when an usb read error occurs.
- *
- * @param client the usbmux client
- *
- * @return 0 or a negative errno value.
- */
-int usbmux_get_error(usbmux_client_t client)
-{
- if (!client) {
- return 0;
- }
- return client->error;
-}
-
-/** This function reads from the client's recv_buffer.
- *
- * @param client The client to receive data from.
- * @param data Where to put the data we receive.
- * @param datalen How much data to read.
- * @param timeout How many milliseconds to wait for data
- *
- * @return 0 on success or a negative errno value on failure.
- */
-int usbmux_recv_timeout(usbmux_client_t client, char *data,
- uint32_t datalen, uint32_t * recv_bytes,
- int timeout)
-{
-
- if (!client || !data || datalen == 0 || !recv_bytes)
- return -EINVAL;
-
- if (client->error < 0)
- return client->error;
-
- pthread_mutex_lock(&client->mutex);
-
- if (timeout > 0 && (client->recv_buffer == NULL || client->r_len == 0)) {
- struct timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
- ts.tv_sec += timeout / 1000;
- ts.tv_nsec += (timeout - ((int) (timeout / 1000)) * 1000) * 1000;
- pthread_cond_timedwait(&client->wait, &client->mutex, &ts);
- }
-
- *recv_bytes = 0;
- if (client->recv_buffer != NULL && client->r_len > 0) {
- uint32_t foolen = datalen;
- if ((int) foolen > client->r_len)
- foolen = client->r_len;
- memcpy(data, client->recv_buffer, foolen);
- *recv_bytes = foolen;
-
- // preserve any left-over unread amounts.
- int remainder = client->r_len - foolen;
- if (remainder > 0) {
- char *newbuf = malloc(remainder);
- memcpy(newbuf, client->recv_buffer + foolen, remainder);
- client->r_len = remainder;
- free(client->recv_buffer);
- client->recv_buffer = newbuf;
- } else {
- free(client->recv_buffer);
- client->recv_buffer = NULL;
- client->r_len = 0;
- }
- }
-
- pthread_mutex_unlock(&client->mutex);
-
- return 0;
-}
-
-int usbmux_is_connected(usbmux_client_t client)
-{
- if (!client) {
- return 0;
- }
- return client->connected;
-}
diff --git a/src/usbmux.h b/src/usbmux.h
deleted file mode 100644
index 155316a..0000000
--- a/src/usbmux.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2008 Jing Su. All Rights Reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __USBMUX_H__
-#define __USBMUX_H__
-
-#include <stdint.h>
-#include <sys/types.h>
-//#include <sys/stat.h>
-
-
-void usbmux_set_debug(int e);
-
-struct usbmux_device_int;
-typedef struct usbmux_device_int *usbmux_device_t;
-
-struct usbmux_client_int;
-typedef struct usbmux_client_int *usbmux_client_t;
-
-int usbmux_get_device ( usbmux_device_t *device );
-int usbmux_get_specific_device(int bus_n, int dev_n, usbmux_device_t * device);
-int usbmux_free_device ( usbmux_device_t device );
-
-
-int usbmux_new_client ( usbmux_device_t device, uint16_t src_port, uint16_t dst_port, usbmux_client_t *client );
-int usbmux_free_client ( usbmux_client_t client );
-
-int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes);
-
-int usbmux_recv_timeout(usbmux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout);
-
-int usbmux_pullbulk(usbmux_device_t device);
-
-int usbmux_get_error(usbmux_client_t client);
-
-int usbmux_is_connected(usbmux_client_t client);
-
-#endif
diff --git a/stuff/README b/stuff/README
new file mode 100644
index 0000000..8c5c423
--- /dev/null
+++ b/stuff/README
@@ -0,0 +1,19 @@
+com.openssh.sft.plist is a launchd configuration to set up a bare SFTP server
+on TCP port 2299 localhost-only for USB use. It's nice for relatively fast music
+syncing and file transfer under Linux (and it avoids encryption). Con: it gives
+anyone with usb access root FS access on the phone, as well as anything running
+on the phone itself.
+
+Use it with a command like this:
+
+IPATH=/var/mobile/Media
+MOUNTPOINT=$HOME/media/iphone
+$ sshfs localhost:$IPATH $MOUNTPOINT -o workaround=rename -o directport=2299 \
+ -o kernel_cache -o entry_timeout=30 -o attr_timeout=30
+
+Make sure you run tcprelay.py:
+$ python tcprelay.py -t 2299
+
+Remember that to bypass the stupid new iTunesDB hash you need to edit
+/System/Library/Lockdown/Checkpoint.xml and change DBVersion to 2.
+
diff --git a/stuff/com.openssh.sftp.plist b/stuff/com.openssh.sftp.plist
new file mode 100644
index 0000000..569fabc
--- /dev/null
+++ b/stuff/com.openssh.sftp.plist
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+
+<dict>
+ <key>Label</key>
+ <string>com.openssh.sftpd</string>
+
+ <key>Program</key>
+ <string>/usr/libexec/sftp-server</string>
+
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/libexec/sftp-server</string>
+ </array>
+
+ <key>SessionCreate</key>
+ <true/>
+
+ <key>Sockets</key>
+ <dict>
+ <key>Listeners</key>
+ <dict>
+ <key>SockServiceName</key>
+ <string>2299</string>
+ <key>SockNodeName</key>
+ <string>127.0.0.1</string>
+ </dict>
+ </dict>
+
+ <key>StandardErrorPath</key>
+ <string>/dev/null</string>
+
+ <key>inetdCompatibility</key>
+ <dict>
+ <key>Wait</key>
+ <false/>
+ </dict>
+</dict>
+
+</plist>
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..70ec3b3
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,8 @@
+include_directories (${CMAKE_SOURCE_DIR}/libusbmuxd)
+link_directories (${CMAKE_BINARY_DIR}/libusbmuxd)
+
+add_definitions(-Wall -O2 -g)
+add_executable(iproxy iproxy.c)
+target_link_libraries(iproxy libusbmuxd pthread)
+
+install(TARGETS iproxy RUNTIME DESTINATION bin) \ No newline at end of file
diff --git a/tools/Makefile.am b/tools/Makefile.am
deleted file mode 100644
index 5d20bbf..0000000
--- a/tools/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
-bin_PROGRAMS = iproxy
-
-iproxy_SOURCES = iproxy.c
-iproxy_CFLAGS = -I$(top_srcdir)/src
-iproxy_LDADD = ../src/libusbmuxd.la
diff --git a/udev/85-usbmuxd.rules.in b/udev/85-usbmuxd.rules.in
index c0a3900..3521ccb 100644
--- a/udev/85-usbmuxd.rules.in
+++ b/udev/85-usbmuxd.rules.in
@@ -1,7 +1,7 @@
# usbmuxd (iPhone "Apple Mobile Device" MUXer listening on /var/run/usbmuxd)
# Forces iPhone 1.0, 3G, 3GS and iPodTouch 1 and 2 to USB configuration 3 and run usbmuxd
-ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="129[0-4]", ATTR{bConfigurationValue}!="$attr{bNumConfigurations}", ATTR{bConfigurationValue}="$attr{bNumConfigurations}", RUN+="@prefix@/sbin/usbmuxd -u"
+ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="129[0-4]", ATTR{bConfigurationValue}!="$attr{bNumConfigurations}", ATTR{bConfigurationValue}="$attr{bNumConfigurations}", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -u -U"
# Exit usbmuxd when the last phone is removed
-ACTION=="remove", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="05ac", ENV{ID_MODEL_ID}=="129[0-4]", RUN+="@prefix@/sbin/usbmuxd -x"
+ACTION=="remove", SUBSYSTEM=="usb", ENV{PRODUCT}=="5ac/129[0-4]/*", ENV{INTERFACE}=="255/*", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -x"
diff --git a/udev/CMakeLists.txt b/udev/CMakeLists.txt
new file mode 100644
index 0000000..0f7042d
--- /dev/null
+++ b/udev/CMakeLists.txt
@@ -0,0 +1,2 @@
+CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/85-usbmuxd.rules.in ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules DESTINATION /lib/udev/rules.d/)
diff --git a/udev/Makefile.am b/udev/Makefile.am
deleted file mode 100644
index e44b077..0000000
--- a/udev/Makefile.am
+++ /dev/null
@@ -1,4 +0,0 @@
-udevdir=/lib/udev/rules.d/
-udev_DATA=85-usbmuxd.rules
-
-EXTRA_DIST = 85-usbmuxd.rules
diff --git a/usbmuxd/CMakeLists.txt b/usbmuxd/CMakeLists.txt
new file mode 100644
index 0000000..b982dc0
--- /dev/null
+++ b/usbmuxd/CMakeLists.txt
@@ -0,0 +1,9 @@
+find_package(USB REQUIRED)
+include_directories(${USB_INCLUDE_DIRS})
+set(LIBS ${LIBS} ${USB_LIBRARIES})
+
+add_definitions(-Wall -O2 -g)
+add_executable(usbmuxd main.c usb-linux.c log.c utils.c device.c client.c)
+target_link_libraries(usbmuxd ${LIBS})
+
+install(TARGETS usbmuxd RUNTIME DESTINATION sbin) \ No newline at end of file
diff --git a/usbmuxd/client.c b/usbmuxd/client.c
new file mode 100644
index 0000000..7a3160f
--- /dev/null
+++ b/usbmuxd/client.c
@@ -0,0 +1,430 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#include "log.h"
+#include "usb.h"
+#include "client.h"
+#include "device.h"
+
+#define CMD_BUF_SIZE 256
+#define REPLY_BUF_SIZE 1024
+
+enum client_state {
+ CLIENT_COMMAND, // waiting for command
+ CLIENT_LISTEN, // listening for devices
+ CLIENT_CONNECTING1, // issued connection request
+ CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
+ CLIENT_CONNECTED, // connected
+ CLIENT_DEAD
+};
+
+struct mux_client {
+ int fd;
+ unsigned char *ob_buf;
+ int ob_size;
+ int ob_capacity;
+ unsigned char *ib_buf;
+ int ib_size;
+ int ib_capacity;
+ short events, devents;
+ uint32_t connect_tag;
+ int connect_device;
+ enum client_state state;
+};
+
+static struct collection client_list;
+
+int client_read(struct mux_client *client, void *buffer, int len)
+{
+ usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
+ if(client->state != CLIENT_CONNECTED) {
+ usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
+ return -1;
+ }
+ return recv(client->fd, buffer, len, 0);
+}
+
+int client_write(struct mux_client *client, void *buffer, int len)
+{
+ usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
+ if(client->state != CLIENT_CONNECTED) {
+ usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
+ return -1;
+ }
+ return send(client->fd, buffer, len, 0);
+}
+
+int client_set_events(struct mux_client *client, short events)
+{
+ if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
+ usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
+ return -1;
+ }
+ client->devents = events;
+ if(client->state == CLIENT_CONNECTED)
+ client->events = events;
+ return 0;
+}
+
+int client_accept(int listenfd)
+{
+ struct sockaddr_un addr;
+ int cfd;
+ socklen_t len = sizeof(struct sockaddr_un);
+ cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
+ if (cfd < 0) {
+ usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
+ return cfd;
+ }
+
+ struct mux_client *client;
+ client = malloc(sizeof(struct mux_client));
+ memset(client, 0, sizeof(struct mux_client));
+
+ client->fd = cfd;
+ client->ob_buf = malloc(REPLY_BUF_SIZE);
+ client->ob_size = 0;
+ client->ob_capacity = REPLY_BUF_SIZE;
+ client->ib_buf = malloc(CMD_BUF_SIZE);
+ client->ib_size = 0;
+ client->ib_capacity = CMD_BUF_SIZE;
+ client->state = CLIENT_COMMAND;
+ client->events = POLLIN;
+
+ collection_add(&client_list, client);
+
+ usbmuxd_log(LL_INFO, "New client on fd %d", client->fd);
+ return client->fd;
+}
+
+void client_close(struct mux_client *client)
+{
+ usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd);
+ if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
+ usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
+ client->state = CLIENT_DEAD;
+ device_abort_connect(client->connect_device, client);
+ }
+ close(client->fd);
+ if(client->ob_buf)
+ free(client->ob_buf);
+ if(client->ib_buf)
+ free(client->ib_buf);
+ collection_remove(&client_list, client);
+ free(client);
+}
+
+void client_get_fds(struct fdlist *list)
+{
+ FOREACH(struct mux_client *client, &client_list) {
+ fdlist_add(list, FD_CLIENT, client->fd, client->events);
+ } ENDFOREACH
+}
+
+static int send_pkt(struct mux_client *client, uint32_t tag, enum client_msgtype msg, void *payload, int payload_length)
+{
+ struct client_header hdr;
+ hdr.version = CLIENT_PROTOCOL_VERSION;
+ hdr.length = sizeof(hdr) + payload_length;
+ hdr.message = msg;
+ hdr.tag = tag;
+ usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
+ if((client->ob_capacity - client->ob_size) < hdr.length) {
+ 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);
+ client_close(client);
+ return -1;
+ }
+ memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
+ if(payload && payload_length)
+ memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
+ client->ob_size += hdr.length;
+ client->events |= POLLOUT;
+ return hdr.length;
+}
+
+static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
+{
+ return send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
+}
+
+int client_notify_connect(struct mux_client *client, enum client_result result)
+{
+ usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
+ if(client->state == CLIENT_DEAD)
+ return -1;
+ if(client->state != CLIENT_CONNECTING1) {
+ usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
+ return -1;
+ }
+ if(send_result(client, client->connect_tag, result) < 0)
+ return -1;
+ if(result == RESULT_OK) {
+ client->state = CLIENT_CONNECTING2;
+ client->events = POLLOUT; // wait for the result packet to go through
+ // no longer need this
+ free(client->ib_buf);
+ client->ib_buf = NULL;
+ } else {
+ client->state = CLIENT_COMMAND;
+ }
+ return 0;
+}
+
+static int notify_device(struct mux_client *client, struct device_info *dev)
+{
+ struct client_msg_dev dmsg;
+ memset(&dmsg, 0, sizeof(dmsg));
+ dmsg.device_id = dev->id;
+ strncpy(dmsg.device_serial, dev->serial, 256);
+ dmsg.device_serial[255] = 0;
+ dmsg.location = dev->location;
+ dmsg.device_pid = dev->pid;
+ return send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
+}
+
+static int start_listen(struct mux_client *client)
+{
+ struct device_info *devs;
+ struct device_info *dev;
+ int count, i;
+
+ client->state = CLIENT_LISTEN;
+ count = device_get_count();
+ if(!count)
+ return 0;
+ devs = malloc(sizeof(struct device_info) * count);
+ count = device_get_list(devs);
+
+ // going to need a larger buffer for many devices
+ int needed_buffer = count * (sizeof(struct client_msg_dev) + sizeof(struct client_header)) + REPLY_BUF_SIZE;
+ if(client->ob_capacity < needed_buffer) {
+ usbmuxd_log(LL_DEBUG, "Enlarging client %d reply buffer %d -> %d to make space for device notifications", client->fd, client->ob_capacity, needed_buffer);
+ client->ob_buf = realloc(client->ob_buf, needed_buffer);
+ client->ob_capacity = needed_buffer;
+ }
+ dev = devs;
+ for(i=0; i<count; i++) {
+ if(notify_device(client, dev++) < 0) {
+ free(devs);
+ return -1;
+ }
+ }
+ free(devs);
+ return count;
+}
+
+static int client_command(struct mux_client *client, struct client_header *hdr, const char *payload)
+{
+ int res;
+ usbmuxd_log(LL_DEBUG, "Client command in fd %d len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag);
+
+ if(client->state != CLIENT_COMMAND) {
+ usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd);
+ if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
+ return -1;
+ client_close(client);
+ return -1;
+ }
+
+ struct client_msg_connect *ch;
+ switch(hdr->message) {
+ case MESSAGE_LISTEN:
+ if(send_result(client, hdr->tag, 0) < 0)
+ return -1;
+ usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
+ return start_listen(client);
+ case MESSAGE_CONNECT:
+ ch = (void*)payload;
+ usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
+ res = device_start_connect(ch->device_id, ntohs(ch->port), client);
+ if(res < 0) {
+ if(send_result(client, hdr->tag, -res) < 0)
+ return -1;
+ } else {
+ client->connect_tag = hdr->tag;
+ client->connect_device = ch->device_id;
+ client->state = CLIENT_CONNECTING1;
+ }
+ return 0;
+ default:
+ usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
+ if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
+ return -1;
+ return 0;
+ }
+ return -1;
+}
+
+static void process_send(struct mux_client *client)
+{
+ int res;
+ if(!client->ob_size) {
+ usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
+ client->events &= ~POLLOUT;
+ return;
+ }
+ res = send(client->fd, client->ob_buf, client->ob_size, 0);
+ if(res <= 0) {
+ usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno));
+ client_close(client);
+ return;
+ }
+ if(res == client->ob_size) {
+ client->ob_size = 0;
+ client->events &= ~POLLOUT;
+ if(client->state == CLIENT_CONNECTING2) {
+ usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
+ client->state = CLIENT_CONNECTED;
+ client->events = client->devents;
+ // no longer need this
+ free(client->ob_buf);
+ client->ob_buf = NULL;
+ }
+ } else {
+ client->ob_size -= res;
+ memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
+ }
+}
+static void process_recv(struct mux_client *client)
+{
+ int res;
+ int did_read = 0;
+ if(client->ib_size < sizeof(struct client_header)) {
+ res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct client_header) - client->ib_size, 0);
+ if(res <= 0) {
+ if(res < 0)
+ usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
+ else
+ usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
+ client_close(client);
+ return;
+ }
+ client->ib_size += res;
+ if(client->ib_size < sizeof(struct client_header))
+ return;
+ did_read = 1;
+ }
+ struct client_header *hdr = (void*)client->ib_buf;
+ if(hdr->version != CLIENT_PROTOCOL_VERSION) {
+ usbmuxd_log(LL_INFO, "Client %d version mismatch: expected %d, got %d", client->fd, CLIENT_PROTOCOL_VERSION, hdr->version);
+ client_close(client);
+ }
+ if(hdr->length > client->ib_capacity) {
+ usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
+ client_close(client);
+ }
+ if(hdr->length < sizeof(struct client_header)) {
+ usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
+ client_close(client);
+ }
+ if(client->ib_size < hdr->length) {
+ if(did_read)
+ return; //maybe we would block, so defer to next loop
+ res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
+ client_close(client);
+ return;
+ } else if(res == 0) {
+ usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
+ client_close(client);
+ return;
+ }
+ client->ib_size += res;
+ if(client->ib_size < hdr->length)
+ return;
+ }
+ client_command(client, hdr, (char *)(hdr+1));
+ client->ib_size = 0;
+}
+
+void client_process(int fd, short events)
+{
+ struct mux_client *client = NULL;
+ FOREACH(struct mux_client *lc, &client_list) {
+ if(lc->fd == fd) {
+ client = lc;
+ break;
+ }
+ } ENDFOREACH
+
+ if(!client) {
+ usbmuxd_log(LL_ERROR, "client_process: fd %d not found in client list", fd);
+ return;
+ }
+
+ if(client->state == CLIENT_CONNECTED) {
+ usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
+ device_client_process(client->connect_device, client, events);
+ } else {
+ if(events & POLLIN) {
+ process_recv(client);
+ } else if(events & POLLOUT) { //not both in case client died as part of process_recv
+ process_send(client);
+ }
+ }
+
+}
+
+void client_device_add(struct device_info *dev)
+{
+ usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
+ FOREACH(struct mux_client *client, &client_list) {
+ if(client->state == CLIENT_LISTEN)
+ notify_device(client, dev);
+ } ENDFOREACH
+}
+void client_device_remove(int device_id)
+{
+ uint32_t id = device_id;
+ usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
+ FOREACH(struct mux_client *client, &client_list) {
+ if(client->state == CLIENT_LISTEN)
+ send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &id, sizeof(uint32_t));
+ } ENDFOREACH
+}
+
+
+void client_init(void)
+{
+ usbmuxd_log(LL_DEBUG, "client_init");
+ collection_init(&client_list);
+}
+
+void client_shutdown(void)
+{
+ usbmuxd_log(LL_DEBUG, "client_shutdown");
+ FOREACH(struct mux_client *client, &client_list) {
+ client_close(client);
+ } ENDFOREACH
+ collection_free(&client_list);
+}
diff --git a/usbmuxd/client.h b/usbmuxd/client.h
new file mode 100644
index 0000000..0cda676
--- /dev/null
+++ b/usbmuxd/client.h
@@ -0,0 +1,92 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef __CLIENT_H__
+#define __CLIENT_H__
+
+#include <stdint.h>
+
+struct device_info;
+struct mux_client;
+
+enum client_result {
+ RESULT_OK = 0,
+ RESULT_BADCOMMAND = 1,
+ RESULT_BADDEV = 2,
+ RESULT_CONNREFUSED = 3,
+ // ???
+ // ???
+ RESULT_BADVERSION = 6,
+};
+
+enum client_msgtype {
+ MESSAGE_RESULT = 1,
+ MESSAGE_CONNECT = 2,
+ MESSAGE_LISTEN = 3,
+ MESSAGE_DEVICE_ADD = 4,
+ MESSAGE_DEVICE_REMOVE = 5,
+ //???
+ //???
+ //MESSAGE_PLIST = 8,
+};
+
+#define CLIENT_PROTOCOL_VERSION 0
+
+struct client_header {
+ uint32_t length;
+ uint32_t version;
+ uint32_t message;
+ uint32_t tag;
+};
+
+struct client_msg_result {
+ uint32_t result;
+};
+
+struct client_msg_connect {
+ uint32_t device_id;
+ uint16_t port;
+};
+
+struct client_msg_dev {
+ uint32_t device_id;
+ uint16_t device_pid;
+ char device_serial[256];
+ uint16_t padding;
+ uint32_t location;
+};
+
+int client_read(struct mux_client *client, void *buffer, int len);
+int client_write(struct mux_client *client, void *buffer, int len);
+int client_set_events(struct mux_client *client, short events);
+void client_close(struct mux_client *client);
+int client_notify_connect(struct mux_client *client, enum client_result result);
+
+void client_device_add(struct device_info *dev);
+void client_device_remove(int device_id);
+
+int client_accept(int fd);
+void client_get_fds(struct fdlist *list);
+void client_process(int fd, short events);
+
+void client_init(void);
+void client_shutdown(void);
+
+#endif
diff --git a/usbmuxd/device.c b/usbmuxd/device.c
new file mode 100644
index 0000000..3a5883c
--- /dev/null
+++ b/usbmuxd/device.c
@@ -0,0 +1,751 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#define _BSD_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include "device.h"
+#include "client.h"
+#include "usb.h"
+#include "log.h"
+
+int next_device_id;
+
+#define DEV_PKTBUF_SIZE 65536
+
+#define CONN_INBUF_SIZE 262144
+#define CONN_OUTBUF_SIZE 65536
+
+#define ACK_TIMEOUT 30
+
+enum mux_protocol {
+ MUX_PROTO_VERSION = 0,
+ MUX_PROTO_TCP = IPPROTO_TCP,
+};
+
+enum mux_dev_state {
+ MUXDEV_INIT, // sent version packet
+ MUXDEV_ACTIVE, // received version packet, active
+ MUXDEV_DEAD // dead
+};
+
+enum mux_conn_state {
+ CONN_CONNECTING, // SYN
+ CONN_CONNECTED, // SYN/SYNACK/ACK -> active
+ CONN_REFUSED, // RST received during SYN
+ CONN_DYING, // RST received
+ CONN_DEAD // being freed; used to prevent infinite recursion between client<->device freeing
+};
+
+struct mux_header
+{
+ uint32_t protocol;
+ uint32_t length;
+};
+
+struct version_header
+{
+ uint32_t major;
+ uint32_t minor;
+ uint32_t padding;
+};
+
+struct mux_device;
+
+#define CONN_ACK_PENDING 1
+
+struct mux_connection
+{
+ struct mux_device *dev;
+ struct mux_client *client;
+ enum mux_conn_state state;
+ uint16_t sport, dport;
+ uint32_t tx_seq, tx_ack, tx_acked, tx_win;
+ uint32_t rx_seq, rx_recvd, rx_ack, rx_win;
+ int max_payload;
+ int sendable;
+ int flags;
+ unsigned char *ib_buf;
+ int ib_size;
+ int ib_capacity;
+ unsigned char *ob_buf;
+ int ob_capacity;
+ short events;
+ uint64_t last_ack_time;
+};
+
+struct mux_device
+{
+ struct usb_device *usbdev;
+ int id;
+ enum mux_dev_state state;
+ struct collection connections;
+ uint16_t next_sport;
+ unsigned char *pktbuf;
+ int pktlen;
+};
+
+static struct collection device_list;
+
+uint64_t mstime64(void)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static int get_next_device_id(void)
+{
+ while(1) {
+ int ok = 1;
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->id == next_device_id) {
+ next_device_id++;
+ ok = 0;
+ break;
+ }
+ } ENDFOREACH
+ if(ok)
+ return next_device_id++;
+ }
+}
+
+static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, const void *data, int length)
+{
+ unsigned char *buffer;
+ int hdrlen;
+ int res;
+
+ switch(proto) {
+ case MUX_PROTO_VERSION:
+ hdrlen = sizeof(struct version_header);
+ break;
+ case MUX_PROTO_TCP:
+ hdrlen = sizeof(struct tcphdr);
+ break;
+ default:
+ usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length);
+ return -1;
+ }
+ usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length);
+
+ int total = sizeof(struct mux_header) + hdrlen + length;
+
+ if(total > USB_MTU) {
+ 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);
+ return -1;
+ }
+
+ buffer = malloc(total);
+ struct mux_header *mhdr = (struct mux_header *)buffer;
+ mhdr->protocol = htonl(proto);
+ mhdr->length = htonl(total);;
+ memcpy(buffer + sizeof(struct mux_header), header, hdrlen);
+ if(data && length)
+ memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length);
+
+ if((res = usb_send(dev->usbdev, buffer, total)) < 0) {
+ usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res);
+ free(buffer);
+ return res;
+ }
+ return total;
+}
+
+static uint16_t find_sport(struct mux_device *dev)
+{
+ if(collection_count(&dev->connections) >= 65535)
+ return 0; //insanity
+
+ while(1) {
+ int ok = 1;
+ FOREACH(struct mux_connection *conn, &dev->connections) {
+ if(dev->next_sport == conn->sport) {
+ dev->next_sport++;
+ ok = 0;
+ break;
+ }
+ } ENDFOREACH
+ if(ok)
+ return dev->next_sport++;
+ }
+}
+
+static int send_anon_rst(struct mux_device *dev, uint16_t sport, uint16_t dport, uint32_t ack)
+{
+ struct tcphdr th;
+ memset(&th, 0, sizeof(th));
+ th.th_sport = htons(sport);
+ th.th_dport = htons(dport);
+ th.th_ack = htonl(ack);
+ th.th_flags = TH_RST;
+ th.th_off = sizeof(th) / 4;
+
+ usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d flags=0x%x", dev->id, sport, dport, th.th_flags);
+
+ int res = send_packet(dev, MUX_PROTO_TCP, &th, NULL, 0);
+ return res;
+}
+
+static int send_tcp(struct mux_connection *conn, uint8_t flags, const unsigned char *data, int length)
+{
+ struct tcphdr th;
+ memset(&th, 0, sizeof(th));
+ th.th_sport = htons(conn->sport);
+ th.th_dport = htons(conn->dport);
+ th.th_seq = htonl(conn->tx_seq);
+ th.th_ack = htonl(conn->tx_ack);
+ th.th_flags = flags;
+ th.th_off = sizeof(th) / 4;
+ th.th_win = htons(conn->tx_win >> 8);
+
+ usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
+ conn->dev->id, conn->sport, conn->dport, conn->tx_seq, conn->tx_ack, flags, conn->tx_win, conn->tx_win >> 8, length);
+
+ int res = send_packet(conn->dev, MUX_PROTO_TCP, &th, data, length);
+ if(res >= 0) {
+ conn->tx_acked = conn->tx_ack;
+ conn->last_ack_time = mstime64();
+ conn->flags &= ~CONN_ACK_PENDING;
+ }
+ return res;
+}
+
+static void connection_teardown(struct mux_connection *conn)
+{
+ int res;
+ if(conn->state == CONN_DEAD)
+ return;
+ usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport);
+ if(conn->dev->state != MUXDEV_DEAD && conn->state != CONN_DYING && conn->state != CONN_REFUSED) {
+ res = send_tcp(conn, TH_RST, NULL, 0);
+ if(res < 0)
+ usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, conn->sport, conn->dport);
+ }
+ if(conn->client) {
+ if(conn->state == CONN_REFUSED || conn->state == CONN_CONNECTING) {
+ client_notify_connect(conn->client, RESULT_CONNREFUSED);
+ } else {
+ conn->state = CONN_DEAD;
+ client_close(conn->client);
+ }
+ }
+ if(conn->ib_buf)
+ free(conn->ib_buf);
+ if(conn->ob_buf)
+ free(conn->ob_buf);
+ collection_remove(&conn->dev->connections, conn);
+ free(conn);
+}
+
+int device_start_connect(int device_id, uint16_t dport, struct mux_client *client)
+{
+ struct mux_device *dev = NULL;
+ FOREACH(struct mux_device *cdev, &device_list) {
+ if(cdev->id == device_id) {
+ dev = cdev;
+ break;
+ }
+ } ENDFOREACH
+ if(!dev) {
+ usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id);
+ return -RESULT_BADDEV;
+ }
+
+ uint16_t sport = find_sport(dev);
+ if(!sport) {
+ usbmuxd_log(LL_WARNING, "Unable to allocate port for device %d", device_id);
+ return -RESULT_BADDEV;
+ }
+
+ struct mux_connection *conn;
+ conn = malloc(sizeof(struct mux_connection));
+ memset(conn, 0, sizeof(struct mux_connection));
+
+ conn->dev = dev;
+ conn->client = client;
+ conn->state = CONN_CONNECTING;
+ conn->sport = sport;
+ conn->dport = dport;
+ conn->tx_seq = 0;
+ conn->tx_ack = 0;
+ conn->tx_acked = 0;
+ conn->tx_win = 131072;
+ conn->rx_recvd = 0;
+ conn->flags = 0;
+ conn->max_payload = USB_MTU - sizeof(struct mux_header) - sizeof(struct tcphdr);
+
+ conn->ob_buf = malloc(CONN_OUTBUF_SIZE);
+ conn->ob_capacity = CONN_OUTBUF_SIZE;
+ conn->ib_buf = malloc(CONN_INBUF_SIZE);
+ conn->ib_capacity = CONN_INBUF_SIZE;
+ conn->ib_size = 0;
+
+ int res;
+
+ res = send_tcp(conn, TH_SYN, NULL, 0);
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport);
+ free(conn);
+ return -RESULT_CONNREFUSED; //bleh
+ }
+ collection_add(&dev->connections, conn);
+ return 0;
+}
+
+static void update_connection(struct mux_connection *conn)
+{
+ conn->sendable = conn->rx_win - (conn->tx_seq - conn->rx_ack);
+
+ if(conn->sendable > conn->ob_capacity)
+ conn->sendable = conn->ob_capacity;
+ if(conn->sendable > conn->max_payload)
+ conn->sendable = conn->max_payload;
+
+ if(conn->sendable > 0)
+ conn->events |= POLLIN;
+ else
+ conn->events &= ~POLLIN;
+
+ if(conn->ib_size)
+ conn->events |= POLLOUT;
+ else
+ conn->events &= ~POLLOUT;
+
+ if(conn->tx_acked != conn->tx_ack)
+ conn->flags |= CONN_ACK_PENDING;
+ else
+ conn->flags &= ~CONN_ACK_PENDING;
+
+ usbmuxd_log(LL_SPEW, "update_connection: sendable %d, events %d, flags %d", conn->sendable, conn->events, conn->flags);
+ client_set_events(conn->client, conn->events);
+}
+
+void device_client_process(int device_id, struct mux_client *client, short events)
+{
+ struct mux_connection *conn = NULL;
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->id == device_id) {
+ FOREACH(struct mux_connection *lconn, &dev->connections) {
+ if(lconn->client == client) {
+ conn = lconn;
+ break;
+ }
+ } ENDFOREACH
+ break;
+ }
+ } ENDFOREACH
+
+ if(!conn) {
+ usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client);
+ return;
+ }
+ usbmuxd_log(LL_SPEW, "device_client_process (%d)", events);
+
+ int res;
+ int size;
+ if(events & POLLOUT) {
+ size = client_write(conn->client, conn->ib_buf, conn->ib_size);
+ if(size <= 0) {
+ usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size);
+ connection_teardown(conn);
+ return;
+ }
+ conn->tx_ack += size;
+ if(size == conn->ib_size) {
+ conn->ib_size = 0;
+ } else {
+ conn->ib_size -= size;
+ memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
+ }
+ }
+ if(events & POLLIN) {
+ size = client_read(conn->client, conn->ob_buf, conn->sendable);
+ if(size <= 0) {
+ usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size);
+ connection_teardown(conn);
+ return;
+ }
+ res = send_tcp(conn, TH_ACK, conn->ob_buf, size);
+ if(res < 0) {
+ connection_teardown(conn);
+ return;
+ }
+ conn->tx_seq += size;
+ }
+
+ update_connection(conn);
+}
+
+static void connection_device_input(struct mux_connection *conn, unsigned char *payload, int payload_length)
+{
+ if((conn->ib_size + payload_length) > conn->ib_capacity) {
+ 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);
+ connection_teardown(conn);
+ return;
+ }
+ memcpy(conn->ib_buf + conn->ib_size, payload, payload_length);
+ conn->ib_size += payload_length;
+ conn->rx_recvd += payload_length;
+ update_connection(conn);
+}
+
+void device_abort_connect(int device_id, struct mux_client *client)
+{
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->id == device_id) {
+ FOREACH(struct mux_connection *conn, &dev->connections) {
+ if(conn->client == client) {
+ connection_teardown(conn);
+ return;
+ }
+ } ENDFOREACH
+ usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
+ return;
+ }
+ } ENDFOREACH
+ usbmuxd_log(LL_WARNING, "Attempted to abort connection for nonexistent device %d", device_id);
+}
+
+static void device_version_input(struct mux_device *dev, struct version_header *vh)
+{
+ if(dev->state != MUXDEV_INIT) {
+ usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id);
+ return;
+ }
+ vh->major = ntohl(vh->major);
+ vh->minor = ntohl(vh->minor);
+ if(vh->major != 1 || vh->minor != 0) {
+ usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor);
+ collection_remove(&device_list, dev);
+ free(dev);
+ return;
+ }
+ 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));
+ dev->state = MUXDEV_ACTIVE;
+ collection_init(&dev->connections);
+ struct device_info info;
+ info.id = dev->id;
+ info.location = usb_get_location(dev->usbdev);
+ info.serial = usb_get_serial(dev->usbdev);
+ info.pid = usb_get_pid(dev->usbdev);
+ client_device_add(&info);
+}
+
+static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, int payload_length)
+{
+ usbmuxd_log(LL_DEBUG, "[IN] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
+ dev->id, ntohs(th->th_sport), ntohs(th->th_dport), ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length);
+
+ uint16_t sport = ntohs(th->th_dport);
+ uint16_t dport = ntohs(th->th_sport);
+ struct mux_connection *conn = NULL;
+ FOREACH(struct mux_connection *lconn, &dev->connections) {
+ if(lconn->sport == sport && lconn->dport == dport) {
+ conn = lconn;
+ break;
+ }
+ } ENDFOREACH
+
+ if(!conn) {
+ usbmuxd_log(LL_WARNING, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
+ if(!(th->th_flags & TH_RST)) {
+ if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0)
+ usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport);
+ }
+ return;
+ }
+
+ conn->rx_seq = ntohl(th->th_seq);
+ conn->rx_ack = ntohl(th->th_ack);
+ conn->rx_win = ntohs(th->th_win) << 8;
+
+ if(th->th_flags & TH_RST) {
+ char *buf = malloc(payload_length+1);
+ memcpy(buf, payload, payload_length);
+ if(payload_length && (buf[payload_length-1] == '\n'))
+ buf[payload_length-1] = 0;
+ buf[payload_length] = 0;
+ usbmuxd_log(LL_DEBUG, "RST reason: %s", buf);
+ free(buf);
+ }
+
+ if(conn->state == CONN_CONNECTING) {
+ if(th->th_flags != (TH_SYN|TH_ACK)) {
+ if(th->th_flags & TH_RST)
+ conn->state = CONN_REFUSED;
+ usbmuxd_log(LL_INFO, "Connection refused by device %d (%d->%d)", dev->id, sport, dport);
+ connection_teardown(conn); //this also sends the notification to the client
+ } else {
+ conn->tx_seq++;
+ conn->tx_ack++;
+ conn->rx_recvd = conn->rx_seq;
+ if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
+ usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, sport, dport);
+ connection_teardown(conn);
+ return;
+ }
+ conn->state = CONN_CONNECTED;
+ if(client_notify_connect(conn->client, RESULT_OK) < 0) {
+ conn->client = NULL;
+ connection_teardown(conn);
+ }
+ update_connection(conn);
+ }
+ } else if(conn->state == CONN_CONNECTED) {
+ if(th->th_flags != TH_ACK) {
+ usbmuxd_log(LL_INFO, "Connection reset by device %d (%d->%d)", dev->id, sport, dport);
+ if(th->th_flags & TH_RST)
+ conn->state = CONN_DYING;
+ connection_teardown(conn);
+ } else {
+ connection_device_input(conn, payload, payload_length);
+ }
+ }
+}
+
+void device_data_input(struct usb_device *usbdev, unsigned char *buffer, int length)
+{
+ struct mux_device *dev = NULL;
+ FOREACH(struct mux_device *tdev, &device_list) {
+ if(tdev->usbdev == usbdev) {
+ dev = tdev;
+ break;
+ }
+ } ENDFOREACH
+ if(!dev) {
+ usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
+ return;
+ }
+
+ if(!length)
+ return;
+
+ usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length);
+
+ // handle broken up transfers
+ if(dev->pktlen) {
+ memcpy(dev->pktbuf + dev->pktlen, buffer, length);
+ struct mux_header *mhdr = (struct mux_header *)dev->pktbuf;
+ if((length < USB_MRU) || (ntohl(mhdr->length) == length)) {
+ buffer = dev->pktbuf;
+ length += dev->pktlen;
+ dev->pktlen = 0;
+ usbmuxd_log(LL_SPEW, "Gathered mux data from buffer (total size: %d)", length);
+ } else {
+ dev->pktlen += length;
+ usbmuxd_log(LL_SPEW, "Appended mux data to buffer (total size: %d)", dev->pktlen);
+ return;
+ }
+ } else {
+ struct mux_header *mhdr = (struct mux_header *)buffer;
+ if((length == USB_MRU) && (length < ntohl(mhdr->length))) {
+ memcpy(dev->pktbuf, buffer, length);
+ dev->pktlen = length;
+ usbmuxd_log(LL_SPEW, "Copied mux data to buffer (size: %d)", dev->pktlen);
+ return;
+ }
+ }
+
+ struct mux_header *mhdr = (struct mux_header *)buffer;
+
+ if(ntohl(mhdr->length) != length) {
+ usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length);
+ return;
+ }
+
+ struct tcphdr *th;
+ unsigned char *payload;
+ int payload_length;
+
+ switch(ntohl(mhdr->protocol)) {
+ case MUX_PROTO_VERSION:
+ device_version_input(dev, (struct version_header *)(mhdr+1));
+ break;
+ case MUX_PROTO_TCP:
+ th = (struct tcphdr *)(mhdr+1);
+ payload = (unsigned char *)(th+1);
+ payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header);
+ device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length);
+ break;
+ default:
+ usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol));
+ break;
+ }
+
+}
+
+int device_add(struct usb_device *usbdev)
+{
+ int res;
+ int id = get_next_device_id();
+ struct mux_device *dev;
+ usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(usbdev), id);
+ dev = malloc(sizeof(struct mux_device));
+ dev->id = id;
+ dev->usbdev = usbdev;
+ dev->state = MUXDEV_INIT;
+ dev->next_sport = 1;
+ dev->pktbuf = malloc(DEV_PKTBUF_SIZE);
+ dev->pktlen = 0;
+ struct version_header vh;
+ vh.major = htonl(1);
+ vh.minor = htonl(0);
+ vh.padding = 0;
+ if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) {
+ usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d\n", id);
+ free(dev);
+ return res;
+ }
+ collection_add(&device_list, dev);
+ return 0;
+}
+
+void device_remove(struct usb_device *usbdev)
+{
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->usbdev == usbdev) {
+ usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev));
+ if(dev->state == MUXDEV_ACTIVE) {
+ dev->state = MUXDEV_DEAD;
+ FOREACH(struct mux_connection *conn, &dev->connections) {
+ connection_teardown(conn);
+ } ENDFOREACH
+ client_device_remove(dev->id);
+ collection_free(&dev->connections);
+ }
+ collection_remove(&device_list, dev);
+ free(dev->pktbuf);
+ free(dev);
+ return;
+ }
+ } ENDFOREACH
+ usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
+}
+
+int device_get_count(void)
+{
+ int count = 0;
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->state == MUXDEV_ACTIVE)
+ count++;
+ } ENDFOREACH
+ return count;
+}
+
+int device_get_list(struct device_info *p)
+{
+ int count = 0;
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->state == MUXDEV_ACTIVE) {
+ p->id = dev->id;
+ p->serial = usb_get_serial(dev->usbdev);
+ p->location = usb_get_location(dev->usbdev);
+ p->pid = usb_get_pid(dev->usbdev);
+ count++;
+ p++;
+ }
+ } ENDFOREACH
+ return count;
+}
+
+int device_get_timeout(void)
+{
+ uint64_t oldest = (uint64_t)-1;
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->state == MUXDEV_ACTIVE) {
+ FOREACH(struct mux_connection *conn, &dev->connections) {
+ if((conn->state == CONN_CONNECTED) && (conn->flags & CONN_ACK_PENDING) && conn->last_ack_time < oldest)
+ oldest = conn->last_ack_time;
+ } ENDFOREACH
+ }
+ } ENDFOREACH
+ uint64_t ct = mstime64();
+ if(oldest == -1)
+ return 100000; //meh
+ if((ct - oldest) > ACK_TIMEOUT)
+ return 0;
+ return ACK_TIMEOUT - (ct - oldest);
+}
+
+void device_check_timeouts(void)
+{
+ uint64_t ct = mstime64();
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->state == MUXDEV_ACTIVE) {
+ FOREACH(struct mux_connection *conn, &dev->connections) {
+ if((conn->state == CONN_CONNECTED) &&
+ (conn->flags & CONN_ACK_PENDING) &&
+ (ct - conn->last_ack_time) > ACK_TIMEOUT) {
+ usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct);
+ if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
+ usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, conn->sport, conn->dport);
+ connection_teardown(conn);
+ }
+ }
+ } ENDFOREACH
+ }
+ } ENDFOREACH
+}
+
+void device_init(void)
+{
+ usbmuxd_log(LL_DEBUG, "device_init");
+ collection_init(&device_list);
+ next_device_id = 1;
+}
+
+void device_kill_connections(void)
+{
+ usbmuxd_log(LL_DEBUG, "device_kill_connections");
+ FOREACH(struct mux_device *dev, &device_list) {
+ if(dev->state != MUXDEV_INIT) {
+ FOREACH(struct mux_connection *conn, &dev->connections) {
+ connection_teardown(conn);
+ } ENDFOREACH
+ }
+ } ENDFOREACH
+ // give USB a while to send the final connection RSTs and the like
+ usb_process_timeout(100);
+}
+
+void device_shutdown(void)
+{
+ usbmuxd_log(LL_DEBUG, "device_shutdown");
+ FOREACH(struct mux_device *dev, &device_list) {
+ FOREACH(struct mux_connection *conn, &dev->connections) {
+ connection_teardown(conn);
+ } ENDFOREACH
+ collection_free(&dev->connections);
+ collection_remove(&device_list, dev);
+ free(dev);
+ } ENDFOREACH
+ collection_free(&device_list);
+}
diff --git a/usbmuxd/device.h b/usbmuxd/device.h
new file mode 100644
index 0000000..ce6c50b
--- /dev/null
+++ b/usbmuxd/device.h
@@ -0,0 +1,52 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef __DEVICE_H__
+#define __DEVICE_H__
+
+#include "usb.h"
+#include "client.h"
+
+struct device_info {
+ int id;
+ const char *serial;
+ uint32_t location;
+ uint16_t pid;
+};
+
+void device_data_input(struct usb_device *dev, unsigned char *buf, int length);
+
+int device_add(struct usb_device *dev);
+void device_remove(struct usb_device *dev);
+
+int device_start_connect(int device_id, uint16_t port, struct mux_client *client);
+void device_client_process(int device_id, struct mux_client *client, short events);
+void device_abort_connect(int device_id, struct mux_client *client);
+
+int device_get_count(void);
+int device_get_list(struct device_info *p);
+
+int device_get_timeout(void);
+void device_check_timeouts(void);
+
+void device_init(void);
+void device_kill_connections(void);
+void device_shutdown(void);
+#endif
diff --git a/usbmuxd/log.c b/usbmuxd/log.c
new file mode 100644
index 0000000..2ccb3cc
--- /dev/null
+++ b/usbmuxd/log.c
@@ -0,0 +1,94 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "log.h"
+
+int log_level = LL_WARNING;
+
+int log_syslog = 0;
+
+void log_enable_syslog()
+{
+ if (!log_syslog) {
+ openlog("usbmuxd", LOG_PID, 0);
+ log_syslog = 1;
+ }
+}
+
+void log_disable_syslog()
+{
+ if (log_syslog) {
+ closelog();
+ }
+}
+
+static int level_to_syslog_level(int level)
+{
+ int result = level + LOG_CRIT;
+ if (result > LOG_DEBUG) {
+ result = LOG_DEBUG;
+ }
+ return result;
+}
+
+void usbmuxd_log(enum loglevel level, const char *fmt, ...)
+{
+ va_list ap;
+ char *fs;
+ struct timeval ts;
+ struct tm *tp;
+
+ gettimeofday(&ts, NULL);
+ tp = localtime(&ts.tv_sec);
+
+ if(level > log_level)
+ return;
+
+ fs = malloc(20 + strlen(fmt));
+
+ if(log_syslog) {
+ sprintf(fs, "[%d] %s\n", level, fmt);
+ } else {
+ strftime(fs, 10, "[%H:%M:%S", tp);
+ sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
+ }
+
+ va_start(ap, fmt);
+ if (log_syslog) {
+ vsyslog(level_to_syslog_level(level), fs, ap);
+ } else {
+ vfprintf(stderr, fs, ap);
+ }
+ va_end(ap);
+
+ free(fs);
+}
diff --git a/usbmuxd/log.h b/usbmuxd/log.h
new file mode 100644
index 0000000..4a2ac2e
--- /dev/null
+++ b/usbmuxd/log.h
@@ -0,0 +1,43 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+enum loglevel {
+ LL_FATAL = 0,
+ LL_ERROR,
+ LL_WARNING,
+ LL_NOTICE,
+ LL_INFO,
+ LL_DEBUG,
+ LL_SPEW,
+ LL_FLOOD,
+};
+
+extern int log_level;
+
+void log_enable_syslog();
+void log_disable_syslog();
+
+void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+
+#endif
diff --git a/usbmuxd/main.c b/usbmuxd/main.c
new file mode 100644
index 0000000..dde99c2
--- /dev/null
+++ b/usbmuxd/main.c
@@ -0,0 +1,548 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#define _BSD_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "log.h"
+#include "usb.h"
+#include "device.h"
+#include "client.h"
+
+static const char *socket_path = "/var/run/usbmuxd";
+static const char *lockfile = "/var/run/usbmuxd.pid";
+
+int should_exit;
+
+static int verbose = 0;
+static int foreground = 0;
+static int drop_privileges = 0;
+static const char *drop_user = "usbmux";
+static int opt_udev = 0;
+static int opt_exit = 0;
+static int exit_signal = 0;
+static int daemon_pipe;
+
+static int report_to_parent = 0;
+
+int create_socket(void) {
+ struct sockaddr_un bind_addr;
+ int listenfd;
+
+ if(unlink(socket_path) == -1 && errno != ENOENT) {
+ usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
+ return -1;
+ }
+
+ listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (listenfd == -1) {
+ usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ bzero(&bind_addr, sizeof(bind_addr));
+ bind_addr.sun_family = AF_UNIX;
+ strcpy(bind_addr.sun_path, socket_path);
+ if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
+ usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ // Start listening
+ if (listen(listenfd, 5) != 0) {
+ usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ chmod(socket_path, 0666);
+
+ return listenfd;
+}
+
+void handle_signal(int sig)
+{
+ if (sig != SIGUSR1) {
+ usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
+ should_exit = 1;
+ } else {
+ if(opt_udev) {
+ usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
+ if (device_get_count() > 0) {
+ // we can't quit, there are still devices attached.
+ usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
+ } else {
+ // it's safe to quit
+ should_exit = 1;
+ }
+ } else {
+ usbmuxd_log(LL_INFO, "Caught SIGUSR1 but we weren't started in --udev mode, ignoring");
+ }
+ }
+}
+
+void set_signal_handlers(void)
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = handle_signal;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+}
+
+int main_loop(int listenfd)
+{
+ int to, cnt, i, dto;
+ struct fdlist pollfds;
+
+ while(!should_exit) {
+ usbmuxd_log(LL_FLOOD, "main_loop iteration");
+ to = usb_get_timeout();
+ usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
+ dto = device_get_timeout();
+ usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to);
+ if(dto < to)
+ to = dto;
+
+ fdlist_create(&pollfds);
+ fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
+ usb_get_fds(&pollfds);
+ client_get_fds(&pollfds);
+ usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
+
+ cnt = poll(pollfds.fds, pollfds.count, to);
+ usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
+
+ if(cnt == -1) {
+ if(errno == EINTR && should_exit) {
+ usbmuxd_log(LL_INFO, "event processing interrupted");
+ fdlist_free(&pollfds);
+ return 0;
+ }
+ } else if(cnt == 0) {
+ if(usb_process() < 0) {
+ usbmuxd_log(LL_FATAL, "usb_process() failed");
+ fdlist_free(&pollfds);
+ return -1;
+ }
+ device_check_timeouts();
+ } else {
+ int done_usb = 0;
+ for(i=0; i<pollfds.count; i++) {
+ if(pollfds.fds[i].revents) {
+ if(!done_usb && pollfds.owners[i] == FD_USB) {
+ if(usb_process() < 0) {
+ usbmuxd_log(LL_FATAL, "usb_process() failed");
+ fdlist_free(&pollfds);
+ return -1;
+ }
+ done_usb = 1;
+ }
+ if(pollfds.owners[i] == FD_LISTEN) {
+ if(client_accept(listenfd) < 0) {
+ usbmuxd_log(LL_FATAL, "client_accept() failed");
+ fdlist_free(&pollfds);
+ return -1;
+ }
+ }
+ if(pollfds.owners[i] == FD_CLIENT) {
+ client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
+ }
+ }
+ }
+ }
+ fdlist_free(&pollfds);
+ }
+ return 0;
+}
+
+/**
+ * make this program run detached from the current console
+ */
+static int daemonize(void)
+{
+ pid_t pid;
+ pid_t sid;
+ int pfd[2];
+ int res;
+
+ // already a daemon
+ if (getppid() == 1)
+ return 0;
+
+ if((res = pipe(pfd)) < 0) {
+ usbmuxd_log(LL_FATAL, "pipe() failed.");
+ return res;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ usbmuxd_log(LL_FATAL, "fork() failed.");
+ return pid;
+ }
+
+ if (pid > 0) {
+ // exit parent process
+ int status;
+ close(pfd[1]);
+
+ if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
+ fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
+ exit(1);
+ }
+ if(status != 0)
+ fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
+ exit(status);
+ }
+ // At this point we are executing as the child process
+ // but we need to do one more fork
+
+ daemon_pipe = pfd[1];
+ close(pfd[0]);
+ report_to_parent = 1;
+
+ // Change the file mode mask
+ umask(0);
+
+ // Create a new SID for the child process
+ sid = setsid();
+ if (sid < 0) {
+ usbmuxd_log(LL_FATAL, "setsid() failed.");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ usbmuxd_log(LL_FATAL, "fork() failed (second).");
+ return pid;
+ }
+
+ if (pid > 0) {
+ // exit parent process
+ close(daemon_pipe);
+ exit(0);
+ }
+
+ // Change the current working directory.
+ if ((chdir("/")) < 0) {
+ usbmuxd_log(LL_FATAL, "chdir() failed");
+ return -2;
+ }
+ // Redirect standard files to /dev/null
+ if (!freopen("/dev/null", "r", stdin)) {
+ usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
+ return -3;
+ }
+ if (!freopen("/dev/null", "w", stdout)) {
+ usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
+ return -3;
+ }
+
+ return 0;
+}
+
+static int notify_parent(int status)
+{
+ int res;
+
+ report_to_parent = 0;
+ if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
+ usbmuxd_log(LL_FATAL, "Could not notify parent!");
+ if(res >= 0)
+ return -2;
+ else
+ return res;
+ }
+ close(daemon_pipe);
+ if (!freopen("/dev/null", "w", stderr)) {
+ usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
+ return -1;
+ }
+ return 0;
+}
+
+static void usage()
+{
+ printf("usage: usbmuxd [options]\n");
+ printf("\t-h|--help Print this message.\n");
+ printf("\t-v|--verbose Be verbose (use twice or more to increase).\n");
+ printf("\t-f|--foreground Do not daemonize (implies one -v).\n");
+ printf("\t-U|--user[=USER] Change to this user after startup (needs usb privileges).\n");
+ printf("\t If USER is not specified, defaults to usbmux.\n");
+ printf("\t-u|--udev Run in udev operation mode.\n");
+ printf("\t-x|--exit Tell a running instance to exit if there are no devices\n");
+ printf("\t connected (must be in udev mode).\n");
+ printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n");
+ printf("\t devices connected (always works).\n");
+ printf("\n");
+}
+
+static void parse_opts(int argc, char **argv)
+{
+ static struct option longopts[] = {
+ {"help", 0, NULL, 'h'},
+ {"foreground", 0, NULL, 'f'},
+ {"verbose", 0, NULL, 'v'},
+ {"user", 2, NULL, 'U'},
+ {"udev", 0, NULL, 'u'},
+ {"exit", 0, NULL, 'x'},
+ {"force-exit", 0, NULL, 'X'},
+ {NULL, 0, NULL, 0}
+ };
+ int c;
+
+ while (1) {
+ c = getopt_long(argc, argv, "hfvuU::xX", longopts, (int *) 0);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ case 'f':
+ foreground = 1;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'U':
+ drop_privileges = 1;
+ if(optarg)
+ drop_user = optarg;
+ break;
+ case 'u':
+ opt_udev = 1;
+ break;
+ case 'x':
+ opt_exit = 1;
+ exit_signal = SIGUSR1;
+ break;
+ case 'X':
+ opt_exit = 1;
+ exit_signal = SIGTERM;
+ break;
+ default:
+ usage();
+ exit(2);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int listenfd;
+ int res = 0;
+ int lfd;
+ struct flock lock;
+ char pids[10];
+
+ parse_opts(argc, argv);
+
+ argc -= optind;
+ argv += optind;
+
+ if (!foreground) {
+ verbose += LL_WARNING;
+ log_enable_syslog();
+ } else {
+ verbose += LL_NOTICE;
+ }
+
+ /* set log level to specified verbosity */
+ log_level = verbose;
+
+ usbmuxd_log(LL_NOTICE, "usbmux v0.1 starting up");
+ should_exit = 0;
+
+ set_signal_handlers();
+
+ res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
+ if(res == -1) {
+ usbmuxd_log(LL_FATAL, "Could not open lockfile");
+ goto terminate;
+ }
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ fcntl(lfd, F_GETLK, &lock);
+ close(lfd);
+ if (lock.l_type != F_UNLCK) {
+ if (opt_exit) {
+ if (lock.l_pid && !kill(lock.l_pid, 0)) {
+ usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
+ res = 0;
+ if (kill(lock.l_pid, exit_signal) < 0) {
+ usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
+ res = -1;
+ }
+ goto terminate;
+ } else {
+ usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
+ res = -1;
+ goto terminate;
+ }
+ } else {
+ if (!opt_udev) {
+ usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
+ res = -1;
+ } else {
+ usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). exiting.", lock.l_pid);
+ res = 0;
+ }
+ goto terminate;
+ }
+ }
+ unlink(lockfile);
+
+ if (opt_exit) {
+ usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
+ goto terminate;
+ }
+
+ if (!foreground) {
+ if ((res = daemonize()) < 0) {
+ fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
+ usbmuxd_log(LL_FATAL, "Could not daemonize!");
+ goto terminate;
+ }
+ }
+
+ // now open the lockfile and place the lock
+ res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+ if(res < 0) {
+ usbmuxd_log(LL_FATAL, "Could not open lockfile");
+ goto terminate;
+ }
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
+ usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
+ goto terminate;
+ }
+ sprintf(pids, "%d", getpid());
+ if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
+ usbmuxd_log(LL_FATAL, "Could not write pidfile!");
+ if(res >= 0)
+ res = -2;
+ goto terminate;
+ }
+
+ usbmuxd_log(LL_INFO, "Creating socket");
+ res = listenfd = create_socket();
+ if(listenfd < 0)
+ goto terminate;
+
+ // drop elevated privileges
+ if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
+ struct passwd *pw = getpwnam(drop_user);
+ if (!pw) {
+ usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
+ res = -1;
+ goto terminate;
+ }
+
+ if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
+ usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
+ goto terminate;
+ }
+ if ((res = setgid(pw->pw_gid)) < 0) {
+ usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
+ goto terminate;
+ }
+ if ((res = setuid(pw->pw_uid)) < 0) {
+ usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
+ goto terminate;
+ }
+
+ // security check
+ if (setuid(0) != -1) {
+ usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
+ res = -1;
+ goto terminate;
+ }
+ if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
+ usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
+ res = -1;
+ goto terminate;
+ }
+ usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
+ }
+
+ client_init();
+ device_init();
+ usbmuxd_log(LL_INFO, "Initializing USB");
+ if((res = usb_init()) < 0)
+ goto terminate;
+
+ usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
+
+ usbmuxd_log(LL_NOTICE, "Initialization complete");
+
+ if (report_to_parent)
+ if((res = notify_parent(0)) < 0)
+ goto terminate;
+
+ res = main_loop(listenfd);
+ if(res < 0)
+ usbmuxd_log(LL_FATAL, "main_loop failed");
+
+ usbmuxd_log(LL_NOTICE, "usbmux shutting down");
+ device_kill_connections();
+ usb_shutdown();
+ device_shutdown();
+ client_shutdown();
+ usbmuxd_log(LL_NOTICE, "Shutdown complete");
+
+terminate:
+ log_disable_syslog();
+
+ if (res < 0)
+ res = -res;
+ else
+ res = 0;
+ if (report_to_parent)
+ notify_parent(res);
+
+ return res;
+}
diff --git a/usbmuxd/usb-linux.c b/usbmuxd/usb-linux.c
new file mode 100644
index 0000000..6e99a95
--- /dev/null
+++ b/usbmuxd/usb-linux.c
@@ -0,0 +1,503 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <libusb.h>
+
+#include "usb.h"
+#include "log.h"
+#include "device.h"
+
+// interval for device connection/disconnection polling, in milliseconds
+// we need this because there is currently no asynchronous device discovery mechanism in libusb
+#define DEVICE_POLL_TIME 1000
+
+struct usb_device {
+ libusb_device_handle *dev;
+ uint8_t bus, address;
+ uint16_t vid, pid;
+ char serial[256];
+ int alive;
+ struct libusb_transfer *rx_xfer;
+ struct collection tx_xfers;
+};
+
+static struct collection device_list;
+
+static struct timeval next_dev_poll_time;
+
+static int devlist_failures;
+
+static void usb_disconnect(struct usb_device *dev)
+{
+ if(!dev->dev) {
+ return;
+ }
+
+ // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device
+ if(dev->rx_xfer) {
+ usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer");
+ libusb_cancel_transfer(dev->rx_xfer);
+ }
+ FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
+ usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
+ libusb_cancel_transfer(xfer);
+ } ENDFOREACH
+
+ while(dev->rx_xfer || collection_count(&dev->tx_xfers)) {
+ struct timeval tv;
+ int res;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000;
+ if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
+ usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res);
+ break;
+ }
+ }
+ collection_free(&dev->tx_xfers);
+ libusb_release_interface(dev->dev, USB_INTERFACE);
+ libusb_close(dev->dev);
+ dev->dev = NULL;
+ collection_remove(&device_list, dev);
+ free(dev);
+}
+
+static void tx_callback(struct libusb_transfer *xfer)
+{
+ struct usb_device *dev = xfer->user_data;
+ 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);
+ if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ switch(xfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
+ case LIBUSB_TRANSFER_ERROR:
+ // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
+ usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_CANCELLED:
+ usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ // other times, this happens, and also even when we abort the transfer after device removal
+ usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
+ break;
+ // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
+ }
+ // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
+ // we'll do device_remove there too
+ dev->alive = 0;
+ }
+ if(xfer->buffer)
+ free(xfer->buffer);
+ collection_remove(&dev->tx_xfers, xfer);
+ libusb_free_transfer(xfer);
+}
+
+int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
+{
+ int res;
+ struct libusb_transfer *xfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, (void*)buf, length, tx_callback, dev, 0);
+ xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
+ if((res = libusb_submit_transfer(xfer)) < 0) {
+ usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res);
+ libusb_free_transfer(xfer);
+ return res;
+ }
+ collection_add(&dev->tx_xfers, xfer);
+ if((length % 512) == 0) {
+ usbmuxd_log(LL_DEBUG, "Send ZLP");
+ // Send Zero Length Packet
+ xfer = libusb_alloc_transfer(0);
+ void *buffer = malloc(1);
+ libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, buffer, 0, tx_callback, dev, 0);
+ xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
+ if((res = libusb_submit_transfer(xfer)) < 0) {
+ usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res);
+ libusb_free_transfer(xfer);
+ return res;
+ }
+ collection_add(&dev->tx_xfers, xfer);
+ }
+ return 0;
+}
+
+static void rx_callback(struct libusb_transfer *xfer)
+{
+ struct usb_device *dev = xfer->user_data;
+ usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
+ if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
+ device_data_input(dev, xfer->buffer, xfer->actual_length);
+ libusb_submit_transfer(xfer);
+ } else {
+ switch(xfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
+ case LIBUSB_TRANSFER_ERROR:
+ // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
+ usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_CANCELLED:
+ usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ // other times, this happens, and also even when we abort the transfer after device removal
+ usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
+ break;
+ // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
+ }
+ free(xfer->buffer);
+ dev->rx_xfer = NULL;
+ libusb_free_transfer(xfer);
+ // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
+ // we'll do device_remove there too
+ dev->alive = 0;
+ }
+}
+
+static int start_rx(struct usb_device *dev)
+{
+ int res;
+ void *buf;
+ dev->rx_xfer = libusb_alloc_transfer(0);
+ buf = malloc(USB_MRU);
+ libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, BULK_IN, buf, USB_MRU, rx_callback, dev, 0);
+ if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) {
+ usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
+ libusb_free_transfer(dev->rx_xfer);
+ dev->rx_xfer = NULL;
+ return res;
+ }
+ return 0;
+}
+
+static int usb_discover(void)
+{
+ int cnt, i, res;
+ int valid_count = 0;
+ libusb_device **devs;
+
+ cnt = libusb_get_device_list(NULL, &devs);
+ if(cnt < 0) {
+ usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
+ devlist_failures++;
+ // sometimes libusb fails getting the device list if you've just removed something
+ if(devlist_failures > 5) {
+ usbmuxd_log(LL_FATAL, "Too many errors getting device list\n");
+ return cnt;
+ } else {
+ gettimeofday(&next_dev_poll_time, NULL);
+ next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
+ next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
+ next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
+ return 0;
+ }
+ }
+ devlist_failures = 0;
+
+ usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
+
+ FOREACH(struct usb_device *usbdev, &device_list) {
+ usbdev->alive = 0;
+ } ENDFOREACH
+
+ for(i=0; i<cnt; i++) {
+ // the following are non-blocking operations on the device list
+ libusb_device *dev = devs[i];
+ uint8_t bus = libusb_get_bus_number(dev);
+ uint8_t address = libusb_get_device_address(dev);
+ struct libusb_device_descriptor devdesc;
+ int found = 0;
+ FOREACH(struct usb_device *usbdev, &device_list) {
+ if(usbdev->bus == bus && usbdev->address == address) {
+ valid_count++;
+ usbdev->alive = 1;
+ found = 1;
+ break;
+ }
+ } ENDFOREACH
+ if(found)
+ continue; //device already found
+ if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res);
+ continue;
+ }
+ if(devdesc.idVendor != VID_APPLE)
+ continue;
+ if( (devdesc.idProduct != PID_IPHONE2G) &&
+ (devdesc.idProduct != PID_ITOUCH1G) &&
+ (devdesc.idProduct != PID_IPHONE3G) &&
+ (devdesc.idProduct != PID_ITOUCH2G) &&
+ (devdesc.idProduct != PID_IPHONE3GS))
+ continue;
+ libusb_device_handle *handle;
+ usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
+ // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable
+ if((res = libusb_open(dev, &handle)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res);
+ continue;
+ }
+ if((res = libusb_set_configuration(handle, USB_CONFIGURATION)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", USB_CONFIGURATION, bus, address, res);
+ libusb_close(handle);
+ continue;
+ }
+ if((res = libusb_claim_interface(handle, USB_INTERFACE)) != 0) {
+ usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", USB_INTERFACE, bus, address, res);
+ libusb_close(handle);
+ continue;
+ }
+ struct usb_device *usbdev;
+ usbdev = malloc(sizeof(struct usb_device));
+
+ if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) {
+ usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res);
+ libusb_release_interface(handle, USB_INTERFACE);
+ libusb_close(handle);
+ free(usbdev);
+ continue;
+ }
+ usbdev->serial[res] = 0;
+ usbdev->bus = bus;
+ usbdev->address = address;
+ usbdev->vid = devdesc.idVendor;
+ usbdev->pid = devdesc.idProduct;
+ usbdev->dev = handle;
+ usbdev->alive = 1;
+ collection_init(&usbdev->tx_xfers);
+
+ collection_add(&device_list, usbdev);
+
+ if(device_add(usbdev) < 0) {
+ usb_disconnect(usbdev);
+ continue;
+ }
+ if(start_rx(usbdev) < 0) {
+ device_remove(usbdev);
+ usb_disconnect(usbdev);
+ continue;
+ }
+ valid_count++;
+ }
+ FOREACH(struct usb_device *usbdev, &device_list) {
+ if(!usbdev->alive) {
+ device_remove(usbdev);
+ usb_disconnect(usbdev);
+ }
+ } ENDFOREACH
+
+ libusb_free_device_list(devs, 1);
+
+ gettimeofday(&next_dev_poll_time, NULL);
+ next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
+ next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
+ next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
+
+ return valid_count;
+}
+
+const char *usb_get_serial(struct usb_device *dev)
+{
+ if(!dev->dev) {
+ return NULL;
+ }
+ return dev->serial;
+}
+
+uint32_t usb_get_location(struct usb_device *dev)
+{
+ if(!dev->dev) {
+ return 0;
+ }
+ return (dev->bus << 16) | dev->address;
+}
+
+uint16_t usb_get_pid(struct usb_device *dev)
+{
+ if(!dev->dev) {
+ return 0;
+ }
+ return dev->pid;
+}
+
+void usb_get_fds(struct fdlist *list)
+{
+ const struct libusb_pollfd **usbfds;
+ const struct libusb_pollfd **p;
+ usbfds = libusb_get_pollfds(NULL);
+ if(!usbfds) {
+ usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
+ return;
+ }
+ p = usbfds;
+ while(*p) {
+ fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
+ p++;
+ }
+ free(usbfds);
+}
+
+static int dev_poll_remain_ms(void)
+{
+ int msecs;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
+ msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
+ if(msecs < 0)
+ return 0;
+ return msecs;
+}
+
+int usb_get_timeout(void)
+{
+ struct timeval tv;
+ int msec;
+ int res;
+ int pollrem;
+ pollrem = dev_poll_remain_ms();
+ res = libusb_get_next_timeout(NULL, &tv);
+ if(res == 0)
+ return pollrem;
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res);
+ return pollrem;
+ }
+ msec = tv.tv_sec * 1000;
+ msec += tv.tv_usec / 1000;
+ if(msec > pollrem)
+ return pollrem;
+ return msec;
+}
+
+int usb_process(void)
+{
+ int res;
+ struct timeval tv;
+ tv.tv_sec = tv.tv_usec = 0;
+ res = libusb_handle_events_timeout(NULL, &tv);
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
+ return res;
+ }
+ // reap devices marked dead due to an RX error
+ FOREACH(struct usb_device *usbdev, &device_list) {
+ if(!usbdev->alive) {
+ device_remove(usbdev);
+ usb_disconnect(usbdev);
+ }
+ } ENDFOREACH
+
+ if(dev_poll_remain_ms() <= 0) {
+ res = usb_discover();
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res);
+ return res;
+ }
+ }
+ return 0;
+}
+
+int usb_process_timeout(int msec)
+{
+ int res;
+ struct timeval tleft, tcur, tfin;
+ gettimeofday(&tcur, NULL);
+ tfin.tv_sec = tcur.tv_sec + (msec / 1000);
+ tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
+ tfin.tv_sec += tfin.tv_usec / 1000000;
+ tfin.tv_usec %= 1000000;
+ while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
+ tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
+ tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
+ if(tleft.tv_usec < 0) {
+ tleft.tv_usec += 1000000;
+ tleft.tv_sec -= 1;
+ }
+ res = libusb_handle_events_timeout(NULL, &tleft);
+ if(res < 0) {
+ usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
+ return res;
+ }
+ // reap devices marked dead due to an RX error
+ FOREACH(struct usb_device *usbdev, &device_list) {
+ if(!usbdev->alive) {
+ device_remove(usbdev);
+ usb_disconnect(usbdev);
+ }
+ } ENDFOREACH
+ gettimeofday(&tcur, NULL);
+ }
+ return 0;
+}
+
+int usb_init(void)
+{
+ int res;
+ usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
+
+ devlist_failures = 0;
+ res = libusb_init(NULL);
+ //libusb_set_debug(NULL, 3);
+ if(res != 0) {
+ usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
+ return -1;
+ }
+
+ collection_init(&device_list);
+
+ return usb_discover();
+}
+
+void usb_shutdown(void)
+{
+ usbmuxd_log(LL_DEBUG, "usb_shutdown");
+ FOREACH(struct usb_device *usbdev, &device_list) {
+ device_remove(usbdev);
+ usb_disconnect(usbdev);
+ } ENDFOREACH
+ collection_free(&device_list);
+ libusb_exit(NULL);
+}
diff --git a/usbmuxd/usb.h b/usbmuxd/usb.h
new file mode 100644
index 0000000..d13d9ba
--- /dev/null
+++ b/usbmuxd/usb.h
@@ -0,0 +1,67 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef __USB_H__
+#define __USB_H__
+
+#include <stdint.h>
+#include "utils.h"
+
+#define BULK_IN 0x85
+#define BULK_OUT 0x04
+
+// libusb fragments packets larger than this (usbfs limitation)
+// on input, this creates race conditions and other issues
+#define USB_MRU 16384
+
+// max transmission packet size
+// libusb fragments these too, but doesn't send ZLPs so we're safe
+// but we need to send a ZLP ourselves at the end (see usb-linux.c)
+// we're using 3 * 16384 to optimize for the fragmentation
+// this results in three URBs per full transfer, 32 USB packets each
+// if there are ZLP issues this should make them show up easily too
+#define USB_MTU (3 * 16384)
+
+#define USB_PACKET_SIZE 512
+
+#define VID_APPLE 0x5ac
+#define PID_IPHONE2G 0x1290
+#define PID_ITOUCH1G 0x1291
+#define PID_IPHONE3G 0x1292
+#define PID_ITOUCH2G 0x1293
+#define PID_IPHONE3GS 0x1294
+
+#define USB_CONFIGURATION 3
+#define USB_INTERFACE 1
+
+struct usb_device;
+
+int usb_init(void);
+void usb_shutdown(void);
+const char *usb_get_serial(struct usb_device *dev);
+uint32_t usb_get_location(struct usb_device *dev);
+uint16_t usb_get_pid(struct usb_device *dev);
+void usb_get_fds(struct fdlist *list);
+int usb_get_timeout(void);
+int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
+int usb_process(void);
+int usb_process_timeout(int msec);
+
+#endif
diff --git a/usbmuxd/utils.c b/usbmuxd/utils.c
new file mode 100644
index 0000000..1ffa04a
--- /dev/null
+++ b/usbmuxd/utils.c
@@ -0,0 +1,110 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "utils.h"
+#include "log.h"
+
+void fdlist_create(struct fdlist *list)
+{
+ list->count = 0;
+ list->capacity = 4;
+ list->owners = malloc(sizeof(*list->owners) * list->capacity);
+ list->fds = malloc(sizeof(*list->fds) * list->capacity);
+}
+void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
+{
+ if(list->count == list->capacity) {
+ list->capacity *= 2;
+ list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity);
+ list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity);
+ }
+ list->owners[list->count] = owner;
+ list->fds[list->count].fd = fd;
+ list->fds[list->count].events = events;
+ list->fds[list->count].revents = 0;
+ list->count++;
+}
+
+void fdlist_free(struct fdlist *list)
+{
+ list->count = 0;
+ list->capacity = 0;
+ free(list->owners);
+ list->owners = NULL;
+ free(list->fds);
+ list->fds = NULL;
+}
+
+void collection_init(struct collection *col)
+{
+ col->list = malloc(sizeof(void *));
+ memset(col->list, 0, sizeof(void *));
+ col->capacity = 1;
+}
+
+void collection_free(struct collection *col)
+{
+ free(col->list);
+ col->list = NULL;
+ col->capacity = 0;
+}
+
+void collection_add(struct collection *col, void *element)
+{
+ int i;
+ for(i=0; i<col->capacity; i++) {
+ if(!col->list[i]) {
+ col->list[i] = element;
+ return;
+ }
+ }
+ col->list = realloc(col->list, sizeof(void*) * col->capacity * 2);
+ memset(&col->list[col->capacity], 0, sizeof(void *) * col->capacity);
+ col->list[col->capacity] = element;
+ col->capacity *= 2;
+}
+
+void collection_remove(struct collection *col, void *element)
+{
+ int i;
+ for(i=0; i<col->capacity; i++) {
+ if(col->list[i] == element) {
+ col->list[i] = NULL;
+ return;
+ }
+ }
+ usbmuxd_log(LL_ERROR, "collection_remove: element %p not present in collection %p (cap %d)", element, col, col->capacity);
+}
+
+int collection_count(struct collection *col)
+{
+ int i, cnt = 0;
+ for(i=0; i<col->capacity; i++) {
+ if(col->list[i])
+ cnt++;
+ }
+ return cnt;
+}
diff --git a/usbmuxd/utils.h b/usbmuxd/utils.h
new file mode 100644
index 0000000..ad4ac9d
--- /dev/null
+++ b/usbmuxd/utils.h
@@ -0,0 +1,65 @@
+/*
+ usbmuxd - iPhone/iPod Touch USB multiplex server daemon
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 or version 3.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <poll.h>
+
+enum fdowner {
+ FD_LISTEN,
+ FD_CLIENT,
+ FD_USB
+};
+
+struct fdlist {
+ int count;
+ int capacity;
+ enum fdowner *owners;
+ struct pollfd *fds;
+};
+
+void fdlist_create(struct fdlist *list);
+void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
+void fdlist_free(struct fdlist *list);
+
+struct collection {
+ void **list;
+ int capacity;
+};
+
+void collection_init(struct collection *col);
+void collection_add(struct collection *col, void *element);
+void collection_remove(struct collection *col, void *element);
+int collection_count(struct collection *col);
+void collection_free(struct collection *col);
+
+#define FOREACH(var, col) \
+ do { \
+ int _iter; \
+ for(_iter=0; _iter<(col)->capacity; _iter++) { \
+ if(!(col)->list[_iter]) continue; \
+ var = (col)->list[_iter];
+
+#define ENDFOREACH \
+ } \
+ } while(0);
+
+#endif