summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2013-09-17 11:00:31 +0200
committerGravatar Nikias Bassen2013-09-17 11:00:31 +0200
commitc45ae1f6b6f53995a5bc99591688102d11ad2148 (patch)
tree03e36986e4ad61f6345c64b7b2f673eebee33816
downloadlibusbmuxd-c45ae1f6b6f53995a5bc99591688102d11ad2148.tar.gz
libusbmuxd-c45ae1f6b6f53995a5bc99591688102d11ad2148.tar.bz2
initial commit of adapted source tree.
-rw-r--r--.gitignore39
-rw-r--r--AUTHORS5
-rw-r--r--COPYING.LGPLv2.1502
-rw-r--r--Makefile.am6
-rw-r--r--README214
-rw-r--r--README.devel50
-rwxr-xr-xautogen.sh15
-rw-r--r--configure.ac124
-rw-r--r--include/Makefile.am4
-rw-r--r--include/usbmuxd-proto.h97
-rw-r--r--include/usbmuxd.h191
-rw-r--r--libusbmuxd.pc.in11
-rw-r--r--m4/as-compiler-flag.m462
-rw-r--r--python-client/.gitignore3
-rw-r--r--python-client/tcprelay.py148
-rw-r--r--python-client/usbmux.py246
-rw-r--r--src/Makefile.am16
-rw-r--r--src/collection.c81
-rw-r--r--src/collection.h48
-rw-r--r--src/libusbmuxd.c980
-rw-r--r--src/sock_stuff.c375
-rw-r--r--src/sock_stuff.h65
-rw-r--r--tools/Makefile.am10
-rw-r--r--tools/iproxy.c281
24 files changed, 3573 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6c0a8c9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
+# git-ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+*.[oa]
+*~
+*.po
+*.lo
+*.la
+autom4te.cache/*
+*.in
+*/.deps/*
+m4/*
+swig/*
+*.swp
+*.patch
+aclocal.m4
+config.h
+config.log
+config.sub
+config.guess
+config.status
+configure
+depcomp
+install-sh
+compile
+main
+ltmain.sh
+missing
+mkinstalldirs
+libtool
+*Makefile
+py-compile
+stamp-h1
+src/.libs
+docs/html
+libusbmuxd.pc
+tools/.libs/*
+tools/iproxy
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..c196afd
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+Nikias Bassen
+Hector Martin
+Bastien Nocera
+Paul Sladen
+Martin Szulecki
diff --git a/COPYING.LGPLv2.1 b/COPYING.LGPLv2.1
new file mode 100644
index 0000000..732811e
--- /dev/null
+++ b/COPYING.LGPLv2.1
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..4655b8f
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,6 @@
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = src include tools
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libusbmuxd.pc
diff --git a/README b/README
new file mode 100644
index 0000000..e7e8b9b
--- /dev/null
+++ b/README
@@ -0,0 +1,214 @@
+Background
+==========
+
+'usbmuxd' stands for "USB multiplexing daemon". This daemon is in charge of
+multiplexing connections over USB to an iPhone or iPod touch. To users, it means
+you can sync your music, contacts, photos, etc. over USB. To developers, it
+means you can connect to any listening localhost socket on the device. usbmuxd
+is not used for tethering data transfer, which uses a dedicated USB interface as
+a virtual network device.
+
+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).
+
+The higher-level layers are handled by libimobiledevice. 'ifuse' is then able
+to sit on top of this and mount your device's AFC filesystem share.
+
+This package contains the usbmuxd communication interface library 'libusbmuxd'.
+
+There is also a Python implementation of the client library in the python-client
+directory, 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 device from another OS in a pinch. Run python tcpclient.py --help
+for usage information.
+
+License
+=======
+
+The contents of this package are licensed under the GNU Lesser General Public
+License, version 2.1 or, at your option, any later version (see COPYING.LGPLv2.1).
+If a more permissive license is specified at the top of a source file, it takes
+precedence over this.
+
+Legal
+=====
+
+Apple, iPhone, and iPod touch are trademarks of Apple Inc., registered in the
+U.S. and other countries.
+
+Building
+========
+
+ ./autogen.sh
+ make
+ sudo make install
+
+Running (with magic)
+====================
+
+ (Unplug + replug your jailbroken device)
+ ./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 or virtual console. Of course,
+you need to have OpenSSH installed on your jailbroken device for this to work.
+
+If you have iFuse, you can run "ifuse <mountpoint">. This doesn't require
+iproxy and works on all devices, jailbroken or not.
+
+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
+usbmuxd'.
+
+ sudo usbmuxd -U -v -v &
+ ./iproxy 2222 22 &
+ ssh -p 2222 root@localhost
+
+Tip: Starting SSH if disabled
+=============================
+
+If your device 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 using 'ifuse --afc2' (to access the root directory of
+the device), and then edit:
+
+ /Library/LaunchDaemons/com.openssh.sshd.plist
+
+to _remove_ the lines:
+
+ <key>Disabled</key>
+ <true/>
+
+Reboot the device and then sshd should be running.
+
+TODO
+====
+
+The server currently assumes that the device 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 the same /var/run/usbmuxd socket.
+
+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/README.devel b/README.devel
new file mode 100644
index 0000000..727e095
--- /dev/null
+++ b/README.devel
@@ -0,0 +1,50 @@
+Background
+==========
+
+'libusbmuxd' makes it really simple to talk to a running 'usbmuxd' and
+hides all the details for you. There are two function calls:
+
+usbmuxd_scan()
+--------------
+
+This returns a list of all available iPhone-like devices that are
+available for talking to. The returned array contains the USB
+product_id, hex formatted serial_number of any iPhones/iTouches and a
+non-descript 'handle' for all those devices that are within range (as
+of March 2009, that means a device directly plugged into the
+computer's USB port).
+
+Once you have found the device you want to communicate with, take its
+'handle' and pass this to usbmuxd_connect().
+
+usbmuxd_connect()
+-----------------
+
+This takes a handle, a destination port number and tries to setup
+a proxy a connection. It returns a file-descriptor which you should
+be able to read(), write() and select() on like any other active network
+socket connection.
+
+
+Technical details
+=================
+
+When usbmuxd is running (normally started, or stopped as a result of
+'udev' auto-insertion messages), it provides a socket interface in
+'/var/run/usbmuxd' that is designed to be compatible with the socket
+interface that is provided on MacOSX.
+
+The structures for communicating over this device are documented
+in the 'usbmuxd-proto.h', but you shouldn't need to view them
+directly if you are using the libusbmuxd.so library for easy access.
+
+
+Example
+=======
+
+#include <usbmuxd.h>
+
+...
+
+gcc -o leetphone leetphone.c -lusbmuxd
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..3292973
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+gprefix=`which glibtoolize 2>&1 >/dev/null`
+if [ $? -eq 0 ]; then
+ glibtoolize --force
+else
+ libtoolize --force
+fi
+aclocal -I m4
+autoheader
+automake --add-missing
+autoconf
+
+if [ -z "$NOCONFIGURE" ]; then
+ ./configure "$@"
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..6948d18
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,124 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.61)
+AC_INIT(libusbmuxd, 1.0.8, nospam@nowhere.com)
+AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
+AC_CONFIG_SRCDIR([src/])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl libtool versioning
+# +1 : 0 : +1 == adds new functions to the interface
+# +1 : 0 : 0 == changes or removes functions (changes include both
+# changes to the signature and the semantic)
+# ? :+1 : ? == just internal changes
+# CURRENT : REVISION : AGE
+LIBUSBMUXD_SO_VERSION=2:0:0
+
+AC_SUBST(LIBUSBMUXD_SO_VERSION)
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_CXX
+AM_PROG_CC_C_O
+AC_PROG_LIBTOOL
+
+# Checks for libraries.
+PKG_CHECK_MODULES(libplist, libplist >= 1.9, have_plist=yes, have_plist=no)
+AC_CHECK_HEADERS([sys/inotify.h], have_inotify=yes, have_inotify=no)
+
+AC_ARG_WITH([protov1],
+ [AS_HELP_STRING([--without-protov1],
+ [do not build with protocol v1 support (default is yes)])],
+ [with_protov1=no],
+ [with_protov1=yes])
+
+if test "x$have_plist" = "xyes"; then
+ if test "x$with_protov1" != "xyes"; then
+ have_plist=no
+ echo "*** Note: Protocol V1 support has been disabled ***"
+ else
+ AC_DEFINE(HAVE_PLIST, 1, [Define if you have libplist support])
+ AC_SUBST(libplist_CFLAGS)
+ AC_SUBST(libplist_LIBS)
+ fi
+else
+ if test "x$with_protov1" == "xyes"; then
+ AC_MSG_ERROR([protocol V1 support requested but libplist could not be found])
+ fi
+fi
+
+AC_ARG_WITH([inotify],
+ [AS_HELP_STRING([--without-inotify],
+ [(Linux only) do not build with inotify support (default is yes)])],
+ [with_inotify=no],
+ [with_inotify=yes])
+
+if test "x$have_inotify" = "xyes"; then
+ if test "x$with_inotify" != "xyes"; then
+ have_inotify=no
+ echo "*** Note: inotify support has been disabled ***"
+ else
+ AC_DEFINE(HAVE_INOTIFY, 1, [Define if you have inotify support (linux only)])
+ fi
+fi
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([strcasecmp strdup strerror strndup])
+
+# Check for operating system
+AC_MSG_CHECKING([whether to enable WIN32 build settings])
+case ${host_os} in
+ *mingw32*|*cygwin*)
+ win32=true
+ AC_MSG_RESULT([yes])
+ AC_CHECK_TOOL([WINDRES], [windres], AC_MSG_ERROR([windres not found]))
+ AC_SUBST(WINDRES)
+ ;;
+ *)
+ win32=false
+ AC_MSG_RESULT([no])
+ ;;
+esac
+AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
+
+AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter")
+AC_SUBST(GLOBAL_CFLAGS)
+
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+include/Makefile
+tools/Makefile
+libusbmuxd.pc
+])
+
+echo "
+Configuration for $PACKAGE $VERSION:
+-------------------------------------------
+
+ Install prefix: .........: $prefix
+ Protocol v1 support: ....: $have_plist
+ inotify support (Linux) .: $have_inotify
+
+ Now type 'make' to build $PACKAGE $VERSION,
+ and then 'make install' for installation.
+"
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..f6bc922
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,4 @@
+EXTRA_DIST =
+
+nobase_include_HEADERS = usbmuxd.h \
+ usbmuxd-proto.h
diff --git a/include/usbmuxd-proto.h b/include/usbmuxd-proto.h
new file mode 100644
index 0000000..be9e709
--- /dev/null
+++ b/include/usbmuxd-proto.h
@@ -0,0 +1,97 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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
+
+*/
+
+/* Protocol defintion for usbmuxd proxy protocol */
+#ifndef __USBMUXD_PROTO_H
+#define __USBMUXD_PROTO_H
+
+#include <stdint.h>
+#define USBMUXD_PROTOCOL_VERSION 0
+
+#if defined(WIN32) || defined(__CYGWIN__)
+#define USBMUXD_SOCKET_PORT 27015
+#else
+#define USBMUXD_SOCKET_FILE "/var/run/usbmuxd"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum usbmuxd_result {
+ RESULT_OK = 0,
+ RESULT_BADCOMMAND = 1,
+ RESULT_BADDEV = 2,
+ RESULT_CONNREFUSED = 3,
+ // ???
+ // ???
+ RESULT_BADVERSION = 6,
+};
+
+enum usbmuxd_msgtype {
+ MESSAGE_RESULT = 1,
+ MESSAGE_CONNECT = 2,
+ MESSAGE_LISTEN = 3,
+ MESSAGE_DEVICE_ADD = 4,
+ MESSAGE_DEVICE_REMOVE = 5,
+ //???
+ //???
+ MESSAGE_PLIST = 8,
+};
+
+struct usbmuxd_header {
+ uint32_t length; // length of message, including header
+ uint32_t version; // protocol version
+ uint32_t message; // message type
+ uint32_t tag; // responses to this query will echo back this tag
+} __attribute__((__packed__));
+
+struct usbmuxd_result_msg {
+ struct usbmuxd_header header;
+ uint32_t result;
+} __attribute__((__packed__));
+
+struct usbmuxd_connect_request {
+ struct usbmuxd_header header;
+ uint32_t device_id;
+ uint16_t port; // TCP port number
+ uint16_t reserved; // set to zero
+} __attribute__((__packed__));
+
+struct usbmuxd_listen_request {
+ struct usbmuxd_header header;
+} __attribute__((__packed__));
+
+struct usbmuxd_device_record {
+ uint32_t device_id;
+ uint16_t product_id;
+ char serial_number[256];
+ uint16_t padding;
+ uint32_t location;
+} __attribute__((__packed__));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __USBMUXD_PROTO_H */
diff --git a/include/usbmuxd.h b/include/usbmuxd.h
new file mode 100644
index 0000000..4ae71e2
--- /dev/null
+++ b/include/usbmuxd.h
@@ -0,0 +1,191 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
+Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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
+
+*/
+
+#ifndef __USBMUXD_H
+#define __USBMUXD_H
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Device information structure holding data to identify the device.
+ * The relevant 'handle' should be passed to 'usbmuxd_connect()', to
+ * start a proxy connection. The value 'handle' should be considered
+ * opaque and no presumption made about the meaning of its value.
+ */
+typedef struct {
+ uint32_t handle;
+ int product_id;
+ char udid[41];
+} usbmuxd_device_info_t;
+
+/**
+ * event types for event callback function
+ */
+enum usbmuxd_event_type {
+ UE_DEVICE_ADD = 1,
+ UE_DEVICE_REMOVE
+};
+
+/**
+ * Event structure that will be passed to the callback function.
+ * 'event' will contains the type of the event, and 'device' will contains
+ * information about the device.
+ */
+typedef struct {
+ int event;
+ usbmuxd_device_info_t device;
+} usbmuxd_event_t;
+
+/**
+ * Callback function prototype.
+ */
+typedef void (*usbmuxd_event_cb_t) (const usbmuxd_event_t *event, void *user_data);
+
+/**
+ * Subscribe a callback function so that applications get to know about
+ * device add/remove events.
+ *
+ * @param callback A callback function that is executed when an event occurs.
+ *
+ * @return 0 on success or negative on error.
+ */
+int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data);
+
+/**
+ * Unsubscribe callback.
+ *
+ * @return only 0 for now.
+ */
+int usbmuxd_unsubscribe();
+
+/**
+ * Contacts usbmuxd and retrieves a list of connected devices.
+ *
+ * @param device_list A pointer to an array of usbmuxd_device_info_t
+ * that will hold records of the connected devices. The last record
+ * is a null-terminated record with all fields set to 0/NULL.
+ * @note The user has to free the list returned.
+ *
+ * @return number of attached devices, zero on no devices, or negative
+ * if an error occured.
+ */
+int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list);
+
+/**
+ * Frees the device list returned by an usbmuxd_get_device_list call
+ *
+ * @param device_list A pointer to an array of usbmuxd_device_info_t to free.
+ *
+ * @return 0 on success, -1 on error.
+ */
+int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list);
+
+/**
+ * Gets device information for the device specified by udid.
+ *
+ * @param udid A device UDID of the device to look for. If udid is NULL,
+ * This function will return the first device found.
+ * @param device Pointer to a previously allocated (or static)
+ * usbmuxd_device_info_t that will be filled with the device info.
+ *
+ * @return 0 if no matching device is connected, 1 if the device was found,
+ * or a negative value on error.
+ */
+int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device);
+
+/**
+ * Request proxy connect to
+ *
+ * @param handle returned by 'usbmuxd_scan()'
+ *
+ * @param tcp_port TCP port number on device, in range 0-65535.
+ * common values are 62078 for lockdown, and 22 for SSH.
+ *
+ * @return file descriptor socket of the connection, or -1 on error
+ */
+int usbmuxd_connect(const int handle, const unsigned short tcp_port);
+
+/**
+ * Disconnect. For now, this just closes the socket file descriptor.
+ *
+ * @param sfd socker file descriptor returned by usbmuxd_connect()
+ *
+ * @return 0 on success, -1 on error.
+ */
+int usbmuxd_disconnect(int sfd);
+
+/**
+ * Send data to the specified socket.
+ *
+ * @param sfd socket file descriptor returned by usbmuxd_connect()
+ * @param data buffer to send
+ * @param len size of buffer to send
+ * @param sent_bytes how many bytes sent
+ *
+ * @return 0 on success, a negative errno value otherwise.
+ */
+int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes);
+
+/**
+ * Receive data from the specified socket.
+ *
+ * @param sfd socket file descriptor returned by usbmuxd_connect()
+ * @param data buffer to put the data to
+ * @param len number of bytes to receive
+ * @param recv_bytes number of bytes received
+ * @param timeout how many milliseconds to wait for data
+ *
+ * @return 0 on success, a negative errno value otherwise.
+ */
+int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout);
+
+/**
+ * Receive data from the specified socket with a default timeout.
+ *
+ * @param sfd socket file descriptor returned by usbmuxd_connect()
+ * @param data buffer to put the data to
+ * @param len number of bytes to receive
+ * @param recv_bytes number of bytes received
+ *
+ * @return 0 on success, a negative errno value otherwise.
+ */
+int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes);
+
+/**
+ * Enable or disable the use of inotify extension. Enabled by default.
+ * Use 0 to disable and 1 to enable inotify support.
+ * This only has an effect on linux systems if inotify support has been built
+ * in. Otherwise and on all other platforms this function has no effect.
+ */
+void libusbmuxd_set_use_inotify(int set);
+
+void libusbmuxd_set_debug_level(int level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __USBMUXD_H */
diff --git a/libusbmuxd.pc.in b/libusbmuxd.pc.in
new file mode 100644
index 0000000..551ad44
--- /dev/null
+++ b/libusbmuxd.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libusbmuxd
+Description: A library to communicate with the usbmux daemon
+Version: @VERSION@
+Libs: -L${libdir} -lusbmuxd
+Cflags: -I${includedir}
+
diff --git a/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4
new file mode 100644
index 0000000..0f660cf
--- /dev/null
+++ b/m4/as-compiler-flag.m4
@@ -0,0 +1,62 @@
+dnl as-compiler-flag.m4 0.1.0
+
+dnl autostars m4 macro for detection of compiler flags
+
+dnl David Schleef <ds@schleef.org>
+
+dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $
+
+dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
+dnl Tries to compile with the given CFLAGS.
+dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
+dnl and ACTION-IF-NOT-ACCEPTED otherwise.
+
+AC_DEFUN([AS_COMPILER_FLAG],
+[
+ AC_MSG_CHECKING([to see if compiler understands $1])
+
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+
+ AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
+ CFLAGS="$save_CFLAGS"
+
+ if test "X$flag_ok" = Xyes ; then
+ m4_ifvaln([$2],[$2])
+ true
+ else
+ m4_ifvaln([$3],[$3])
+ true
+ fi
+ AC_MSG_RESULT([$flag_ok])
+])
+
+dnl AS_COMPILER_FLAGS(VAR, FLAGS)
+dnl Tries to compile with the given CFLAGS.
+
+AC_DEFUN([AS_COMPILER_FLAGS],
+[
+ list=$2
+ flags_supported=""
+ flags_unsupported=""
+ AC_MSG_CHECKING([for supported compiler flags])
+ for each in $list
+ do
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $each"
+ AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
+ CFLAGS="$save_CFLAGS"
+
+ if test "X$flag_ok" = Xyes ; then
+ flags_supported="$flags_supported $each"
+ else
+ flags_unsupported="$flags_unsupported $each"
+ fi
+ done
+ AC_MSG_RESULT([$flags_supported])
+ if test "X$flags_unsupported" != X ; then
+ AC_MSG_WARN([unsupported compiler flags: $flags_unsupported])
+ fi
+ $1="$$1 $flags_supported"
+])
+
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..add200c
--- /dev/null
+++ b/python-client/tcprelay.py
@@ -0,0 +1,148 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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..79ec26a
--- /dev/null
+++ b/python-client/usbmux.py
@@ -0,0 +1,246 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
new file mode 100644
index 0000000..bf9198f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,16 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+AM_CFLAGS = $(GLOBAL_CFLAGS) $(libplist_CFLAGS)
+AM_LDFLAGS = $(GLOBAL_LIBS) $(libplist_LIBS)
+
+lib_LTLIBRARIES = libusbmuxd.la
+libusbmuxd_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBUSBMUXD_SO_VERSION) -no-undefined
+libusbmuxd_la_LIBADD =
+libusbmuxd_la_SOURCES = \
+ collection.c collection.h \
+ sock_stuff.c sock_stuff.h \
+ libusbmuxd.c
+
+if WIN32
+libusbmuxd_la_LIBADD += ws2_32
+endif
diff --git a/src/collection.c b/src/collection.c
new file mode 100644
index 0000000..423cc4e
--- /dev/null
+++ b/src/collection.c
@@ -0,0 +1,81 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "collection.h"
+
+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;
+ }
+ }
+ fprintf(stderr, "%s: WARNING: element %p not present in collection %p (cap %d)", __func__, 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/src/collection.h b/src/collection.h
new file mode 100644
index 0000000..e9b6403
--- /dev/null
+++ b/src/collection.h
@@ -0,0 +1,48 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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
+
+*/
+
+#ifndef __COLLECTION_H__
+#define __COLLECTION_H__
+
+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
diff --git a/src/libusbmuxd.c b/src/libusbmuxd.c
new file mode 100644
index 0000000..64b3725
--- /dev/null
+++ b/src/libusbmuxd.c
@@ -0,0 +1,980 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009-2010 Nikias Bassen <nikias@gmx.li>
+Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
+Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+#define sleep(x) Sleep(x*1000)
+#ifndef EPROTO
+#define EPROTO 134
+#endif
+#ifndef EBADMSG
+#define EBADMSG 104
+#endif
+#else
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#define EVENT_SIZE (sizeof (struct inotify_event))
+#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
+#define USBMUXD_DIRNAME "/var/run"
+#define USBMUXD_SOCKET_NAME "usbmuxd"
+#endif /* HAVE_INOTIFY */
+
+#include <unistd.h>
+#include <signal.h>
+
+#ifdef HAVE_PLIST
+#include <plist/plist.h>
+#define PLIST_BUNDLE_ID "com.marcansoft.usbmuxd"
+#define PLIST_CLIENT_VERSION_STRING "usbmuxd built for freedom"
+#define PLIST_PROGNAME "libusbmuxd"
+#endif
+
+// usbmuxd public interface
+#include "usbmuxd.h"
+// usbmuxd protocol
+#include "usbmuxd-proto.h"
+// socket utility functions
+#include "sock_stuff.h"
+// misc utility functions
+#include "collection.h"
+
+static int libusbmuxd_debug = 2;
+#define DEBUG(x, y, ...) if (x <= libusbmuxd_debug) fprintf(stderr, (y), __VA_ARGS__);
+
+static struct collection devices;
+static usbmuxd_event_cb_t event_cb = NULL;
+#ifdef WIN32
+HANDLE devmon = NULL;
+CRITICAL_SECTION mutex;
+static int mutex_initialized = 0;
+#define LOCK if (!mutex_initialized) { InitializeCriticalSection(&mutex); mutex_initialized = 1; } EnterCriticalSection(&mutex);
+#define UNLOCK LeaveCriticalSection(&mutex);
+#else
+pthread_t devmon;
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK pthread_mutex_lock(&mutex)
+#define UNLOCK pthread_mutex_unlock(&mutex)
+#endif
+static int listenfd = -1;
+
+static int use_tag = 0;
+static int proto_version = 0;
+
+/**
+ * Finds a device info record by its handle.
+ * if the record is not found, NULL is returned.
+ */
+static usbmuxd_device_info_t *devices_find(uint32_t handle)
+{
+ FOREACH(usbmuxd_device_info_t *dev, &devices) {
+ if (dev && dev->handle == handle) {
+ return dev;
+ }
+ } ENDFOREACH
+ return NULL;
+}
+
+/**
+ * Creates a socket connection to usbmuxd.
+ * For Mac/Linux it is a unix domain socket,
+ * for Windows it is a tcp socket.
+ */
+static int connect_usbmuxd_socket()
+{
+#if defined(WIN32) || defined(__CYGWIN__)
+ return connect_socket("127.0.0.1", USBMUXD_SOCKET_PORT);
+#else
+ return connect_unix_socket(USBMUXD_SOCKET_FILE);
+#endif
+}
+
+static int receive_packet(int sfd, struct usbmuxd_header *header, void **payload, int timeout)
+{
+ int recv_len;
+ struct usbmuxd_header hdr;
+ char *payload_loc = NULL;
+
+ header->length = 0;
+ header->version = 0;
+ header->message = 0;
+ header->tag = 0;
+
+ recv_len = recv_buf_timeout(sfd, &hdr, sizeof(hdr), 0, timeout);
+ if (recv_len < 0) {
+ return recv_len;
+ } else if ((size_t)recv_len < sizeof(hdr)) {
+ return recv_len;
+ }
+
+ uint32_t payload_size = hdr.length - sizeof(hdr);
+ if (payload_size > 0) {
+ payload_loc = (char*)malloc(payload_size);
+ if (recv_buf_timeout(sfd, payload_loc, payload_size, 0, 5000) != (int)payload_size) {
+ DEBUG(1, "%s: Error receiving payload of size %d\n", __func__, payload_size);
+ free(payload_loc);
+ return -EBADMSG;
+ }
+ }
+
+#ifdef HAVE_PLIST
+ if (hdr.message == MESSAGE_PLIST) {
+ char *message = NULL;
+ plist_t plist = NULL;
+ plist_from_xml(payload_loc, payload_size, &plist);
+ free(payload_loc);
+
+ if (!plist) {
+ DEBUG(1, "%s: Error getting plist from payload!\n", __func__);
+ return -EBADMSG;
+ }
+
+ plist_t node = plist_dict_get_item(plist, "MessageType");
+ if (plist_get_node_type(node) != PLIST_STRING) {
+ DEBUG(1, "%s: Error getting message type from plist!\n", __func__);
+ free(plist);
+ return -EBADMSG;
+ }
+
+ plist_get_string_val(node, &message);
+ if (message) {
+ uint64_t val = 0;
+ if (strcmp(message, "Result") == 0) {
+ /* result message */
+ uint32_t dwval = 0;
+ plist_t n = plist_dict_get_item(plist, "Number");
+ plist_get_uint_val(n, &val);
+ *payload = malloc(sizeof(uint32_t));
+ dwval = val;
+ memcpy(*payload, &dwval, sizeof(dwval));
+ hdr.length = sizeof(hdr) + sizeof(dwval);
+ hdr.message = MESSAGE_RESULT;
+ } else if (strcmp(message, "Attached") == 0) {
+ /* device add message */
+ struct usbmuxd_device_record *dev = NULL;
+ plist_t props = plist_dict_get_item(plist, "Properties");
+ if (!props) {
+ DEBUG(1, "%s: Could not get properties for message '%s' from plist!\n", __func__, message);
+ free(message);
+ plist_free(plist);
+ return -EBADMSG;
+ }
+ dev = (struct usbmuxd_device_record*)malloc(sizeof(struct usbmuxd_device_record));
+ memset(dev, 0, sizeof(struct usbmuxd_device_record));
+
+ plist_t n = plist_dict_get_item(props, "DeviceID");
+ plist_get_uint_val(n, &val);
+ dev->device_id = (uint32_t)val;
+
+ n = plist_dict_get_item(props, "ProductID");
+ plist_get_uint_val(n, &val);
+ dev->product_id = (uint32_t)val;
+
+ n = plist_dict_get_item(props, "SerialNumber");
+ char *strval = NULL;
+ plist_get_string_val(n, &strval);
+ if (strval) {
+ strncpy(dev->serial_number, strval, 255);
+ free(strval);
+ }
+ n = plist_dict_get_item(props, "LocationID");
+ plist_get_uint_val(n, &val);
+ dev->location = (uint32_t)val;
+ *payload = (void*)dev;
+ hdr.length = sizeof(hdr) + sizeof(struct usbmuxd_device_record);
+ hdr.message = MESSAGE_DEVICE_ADD;
+ } else if (strcmp(message, "Detached") == 0) {
+ /* device remove message */
+ uint32_t dwval = 0;
+ plist_t n = plist_dict_get_item(plist, "DeviceID");
+ if (n) {
+ plist_get_uint_val(n, &val);
+ *payload = malloc(sizeof(uint32_t));
+ dwval = val;
+ memcpy(*payload, &dwval, sizeof(dwval));
+ hdr.length = sizeof(hdr) + sizeof(dwval);
+ hdr.message = MESSAGE_DEVICE_REMOVE;
+ }
+ } else {
+ DEBUG(1, "%s: Unexpected message '%s' in plist!\n", __func__, message);
+ free(message);
+ plist_free(plist);
+ return -EBADMSG;
+ }
+ free(message);
+ }
+ plist_free(plist);
+ } else
+#endif
+ {
+ *payload = payload_loc;
+ }
+
+ memcpy(header, &hdr, sizeof(hdr));
+
+ return hdr.length;
+}
+
+/**
+ * Retrieves the result code to a previously sent request.
+ */
+static int usbmuxd_get_result(int sfd, uint32_t tag, uint32_t * result)
+{
+ struct usbmuxd_header hdr;
+ int recv_len;
+ uint32_t *res = NULL;
+
+ if (!result) {
+ return -EINVAL;
+ }
+ *result = -1;
+
+ if ((recv_len = receive_packet(sfd, &hdr, (void**)&res, 5000)) < 0) {
+ DEBUG(1, "%s: Error receiving packet: %d\n", __func__, errno);
+ if (res)
+ free(res);
+ return -errno;
+ }
+ if ((size_t)recv_len < sizeof(hdr)) {
+ DEBUG(1, "%s: Received packet is too small!\n", __func__);
+ if (res)
+ free(res);
+ return -EPROTO;
+ }
+
+ if (hdr.message == MESSAGE_RESULT) {
+ int ret = 0;
+ if (res && (hdr.tag == tag)) {
+ memcpy(result, res, sizeof(uint32_t));
+ ret = 1;
+ }
+ if (res)
+ free(res);
+ return ret;
+ }
+ DEBUG(1, "%s: Unexpected message of type %d received!\n", __func__, hdr.message);
+ if (res)
+ free(res);
+ return -EPROTO;
+}
+
+static int send_packet(int sfd, uint32_t message, uint32_t tag, void *payload, uint32_t payload_size)
+{
+ struct usbmuxd_header header;
+
+ header.length = sizeof(struct usbmuxd_header);
+ header.version = proto_version;
+ header.message = message;
+ header.tag = tag;
+ if (payload && (payload_size > 0)) {
+ header.length += payload_size;
+ }
+ int sent = send_buf(sfd, &header, sizeof(header));
+ if (sent != sizeof(header)) {
+ DEBUG(1, "%s: ERROR: could not send packet header\n", __func__);
+ return -1;
+ }
+ if (payload && (payload_size > 0)) {
+ sent += send_buf(sfd, payload, payload_size);
+ }
+ if (sent != (int)header.length) {
+ DEBUG(1, "%s: ERROR: could not send whole packet\n", __func__);
+ close_socket(sfd);
+ return -1;
+ }
+ return sent;
+}
+
+static int send_listen_packet(int sfd, uint32_t tag)
+{
+ int res = 0;
+#ifdef HAVE_PLIST
+ if (proto_version == 1) {
+ /* plist packet */
+ char *payload = NULL;
+ uint32_t payload_size = 0;
+ plist_t plist;
+
+ /* construct message plist */
+ plist = plist_new_dict();
+ plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID));
+ plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING));
+ plist_dict_insert_item(plist, "MessageType", plist_new_string("Listen"));
+ plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME));
+ plist_to_xml(plist, &payload, &payload_size);
+ plist_free(plist);
+
+ res = send_packet(sfd, MESSAGE_PLIST, tag, payload, payload_size);
+ free(payload);
+ } else
+#endif
+ {
+ /* binary packet */
+ res = send_packet(sfd, MESSAGE_LISTEN, tag, NULL, 0);
+ }
+ return res;
+}
+
+static int send_connect_packet(int sfd, uint32_t tag, uint32_t device_id, uint16_t port)
+{
+ int res = 0;
+#ifdef HAVE_PLIST
+ if (proto_version == 1) {
+ /* plist packet */
+ char *payload = NULL;
+ uint32_t payload_size = 0;
+ plist_t plist;
+
+ /* construct message plist */
+ plist = plist_new_dict();
+ plist_dict_insert_item(plist, "BundleID", plist_new_string(PLIST_BUNDLE_ID));
+ plist_dict_insert_item(plist, "ClientVersionString", plist_new_string(PLIST_CLIENT_VERSION_STRING));
+ plist_dict_insert_item(plist, "MessageType", plist_new_string("Connect"));
+ plist_dict_insert_item(plist, "DeviceID", plist_new_uint(device_id));
+ plist_dict_insert_item(plist, "PortNumber", plist_new_uint(htons(port)));
+ plist_dict_insert_item(plist, "ProgName", plist_new_string(PLIST_PROGNAME));
+ plist_to_xml(plist, &payload, &payload_size);
+ plist_free(plist);
+
+ res = send_packet(sfd, MESSAGE_PLIST, tag, (void*)payload, payload_size);
+ free(payload);
+ } else
+#endif
+ {
+ /* binary packet */
+ struct {
+ uint32_t device_id;
+ uint16_t port;
+ uint16_t reserved;
+ } conninfo;
+
+ conninfo.device_id = device_id;
+ conninfo.port = htons(port);
+ conninfo.reserved = 0;
+
+ res = send_packet(sfd, MESSAGE_CONNECT, tag, &conninfo, sizeof(conninfo));
+ }
+ return res;
+}
+
+/**
+ * Generates an event, i.e. calls the callback function.
+ * A reference to a populated usbmuxd_event_t with information about the event
+ * and the corresponding device will be passed to the callback function.
+ */
+static void generate_event(usbmuxd_event_cb_t callback, const usbmuxd_device_info_t *dev, enum usbmuxd_event_type event, void *user_data)
+{
+ usbmuxd_event_t ev;
+
+ if (!callback || !dev) {
+ return;
+ }
+
+ ev.event = event;
+ memcpy(&ev.device, dev, sizeof(usbmuxd_device_info_t));
+
+ callback(&ev, user_data);
+}
+
+static int usbmuxd_listen_poll()
+{
+ int sfd;
+
+ sfd = connect_usbmuxd_socket();
+ if (sfd < 0) {
+ while (event_cb) {
+ if ((sfd = connect_usbmuxd_socket()) > 0) {
+ break;
+ }
+ sleep(1);
+ }
+ }
+
+ return sfd;
+}
+
+#ifdef HAVE_INOTIFY
+static int use_inotify = 1;
+
+static int usbmuxd_listen_inotify()
+{
+ int inot_fd;
+ int watch_d;
+ int sfd;
+
+ if (!use_inotify) {
+ return -2;
+ }
+
+ sfd = connect_usbmuxd_socket();
+ if (sfd >= 0)
+ return sfd;
+
+ sfd = -1;
+ inot_fd = inotify_init ();
+ if (inot_fd < 0) {
+ DEBUG(1, "%s: Failed to setup inotify\n", __func__);
+ return -2;
+ }
+
+ /* inotify is setup, listen for events that concern us */
+ watch_d = inotify_add_watch (inot_fd, USBMUXD_DIRNAME, IN_CREATE);
+ if (watch_d < 0) {
+ DEBUG(1, "%s: Failed to setup watch descriptor for socket dir\n", __func__);
+ close (inot_fd);
+ return -2;
+ }
+
+ while (1) {
+ ssize_t len, i;
+ char buff[EVENT_BUF_LEN] = {0};
+
+ i = 0;
+ len = read (inot_fd, buff, EVENT_BUF_LEN -1);
+ if (len < 0)
+ goto end;
+ while (i < len) {
+ struct inotify_event *pevent = (struct inotify_event *) & buff[i];
+
+ /* check that it's ours */
+ if (pevent->mask & IN_CREATE &&
+ pevent->len &&
+ pevent->name != NULL &&
+ strcmp(pevent->name, USBMUXD_SOCKET_NAME) == 0) {
+ sfd = connect_usbmuxd_socket ();
+ goto end;
+ }
+ i += EVENT_SIZE + pevent->len;
+ }
+ }
+
+end:
+ inotify_rm_watch(inot_fd, watch_d);
+ close(inot_fd);
+
+ return sfd;
+}
+#endif /* HAVE_INOTIFY */
+
+/**
+ * Tries to connect to usbmuxd and wait if it is not running.
+ */
+static int usbmuxd_listen()
+{
+ int sfd;
+ uint32_t res = -1;
+
+#ifdef HAVE_PLIST
+retry:
+#endif
+
+#ifdef HAVE_INOTIFY
+ sfd = usbmuxd_listen_inotify();
+ if (sfd == -2)
+ sfd = usbmuxd_listen_poll();
+#else
+ sfd = usbmuxd_listen_poll();
+#endif
+
+ if (sfd < 0) {
+ DEBUG(1, "%s: ERROR: usbmuxd was supposed to be running here...\n", __func__);
+ return sfd;
+ }
+
+ use_tag++;
+ LOCK;
+ if (send_listen_packet(sfd, use_tag) <= 0) {
+ UNLOCK;
+ DEBUG(1, "%s: ERROR: could not send listen packet\n", __func__);
+ close_socket(sfd);
+ return -1;
+ }
+ if (usbmuxd_get_result(sfd, use_tag, &res) && (res != 0)) {
+ UNLOCK;
+ close_socket(sfd);
+#ifdef HAVE_PLIST
+ if ((res == RESULT_BADVERSION) && (proto_version != 1)) {
+ proto_version = 1;
+ goto retry;
+ }
+#endif
+ DEBUG(1, "%s: ERROR: did not get OK but %d\n", __func__, res);
+ return -1;
+ }
+ UNLOCK;
+
+ return sfd;
+}
+
+/**
+ * Waits for an event to occur, i.e. a packet coming from usbmuxd.
+ * Calls generate_event to pass the event via callback to the client program.
+ */
+static int get_next_event(int sfd, usbmuxd_event_cb_t callback, void *user_data)
+{
+ struct usbmuxd_header hdr;
+ void *payload = NULL;
+
+ /* block until we receive something */
+ if (receive_packet(sfd, &hdr, &payload, 0) < 0) {
+ // when then usbmuxd connection fails,
+ // generate remove events for every device that
+ // is still present so applications know about it
+ FOREACH(usbmuxd_device_info_t *dev, &devices) {
+ generate_event(callback, dev, UE_DEVICE_REMOVE, user_data);
+ collection_remove(&devices, dev);
+ free(dev);
+ } ENDFOREACH
+ return -EIO;
+ }
+
+ if ((hdr.length > sizeof(hdr)) && !payload) {
+ DEBUG(1, "%s: Invalid packet received, payload is missing!\n", __func__);
+ return -EBADMSG;
+ }
+
+ if (hdr.message == MESSAGE_DEVICE_ADD) {
+ struct usbmuxd_device_record *dev = payload;
+ usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t));
+ if (!devinfo) {
+ DEBUG(1, "%s: Out of memory!\n", __func__);
+ free(payload);
+ return -1;
+ }
+
+ devinfo->handle = dev->device_id;
+ devinfo->product_id = dev->product_id;
+ memset(devinfo->udid, '\0', sizeof(devinfo->udid));
+ memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid));
+
+ if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) {
+ sprintf(devinfo->udid + 32, "%08x", devinfo->handle);
+ }
+
+ collection_add(&devices, devinfo);
+ generate_event(callback, devinfo, UE_DEVICE_ADD, user_data);
+ } else if (hdr.message == MESSAGE_DEVICE_REMOVE) {
+ uint32_t handle;
+ usbmuxd_device_info_t *devinfo;
+
+ memcpy(&handle, payload, sizeof(uint32_t));
+
+ devinfo = devices_find(handle);
+ if (!devinfo) {
+ DEBUG(1, "%s: WARNING: got device remove message for handle %d, but couldn't find the corresponding handle in the device list. This event will be ignored.\n", __func__, handle);
+ } else {
+ generate_event(callback, devinfo, UE_DEVICE_REMOVE, user_data);
+ collection_remove(&devices, devinfo);
+ free(devinfo);
+ }
+ } else if (hdr.length > 0) {
+ DEBUG(1, "%s: Unexpected message type %d length %d received!\n", __func__, hdr.message, hdr.length);
+ }
+ if (payload) {
+ free(payload);
+ }
+ return 0;
+}
+
+static void device_monitor_cleanup(void* data)
+{
+ FOREACH(usbmuxd_device_info_t *dev, &devices) {
+ collection_remove(&devices, dev);
+ free(dev);
+ } ENDFOREACH
+ collection_free(&devices);
+
+ close_socket(listenfd);
+ listenfd = -1;
+}
+
+/**
+ * Device Monitor thread function.
+ *
+ * This function sets up a connection to usbmuxd
+ */
+static void *device_monitor(void *data)
+{
+ collection_init(&devices);
+
+#ifndef WIN32
+ pthread_cleanup_push(device_monitor_cleanup, NULL);
+#endif
+ while (event_cb) {
+
+ listenfd = usbmuxd_listen();
+ if (listenfd < 0) {
+ continue;
+ }
+
+ while (event_cb) {
+ int res = get_next_event(listenfd, event_cb, data);
+ if (res < 0) {
+ break;
+ }
+ }
+ }
+
+#ifndef WIN32
+ pthread_cleanup_pop(1);
+#else
+ device_monitor_cleanup(NULL);
+#endif
+ return NULL;
+}
+
+int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data)
+{
+ int res;
+
+ if (!callback) {
+ return -EINVAL;
+ }
+ event_cb = callback;
+
+#ifdef WIN32
+ res = 0;
+ devmon = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)device_monitor, user_data, 0, NULL);
+ if (devmon == NULL) {
+ res = GetLastError();
+ }
+#else
+ res = pthread_create(&devmon, NULL, device_monitor, user_data);
+#endif
+ if (res != 0) {
+ DEBUG(1, "%s: ERROR: Could not start device watcher thread!\n", __func__);
+ return res;
+ }
+ return 0;
+}
+
+int usbmuxd_unsubscribe()
+{
+ event_cb = NULL;
+
+ shutdown_socket(listenfd, SHUT_RDWR);
+
+#ifdef WIN32
+ if (devmon != NULL) {
+ WaitForSingleObject(devmon, INFINITE);
+ }
+#else
+ if (pthread_kill(devmon, 0) == 0) {
+ pthread_cancel(devmon);
+ pthread_join(devmon, NULL);
+ }
+#endif
+
+ return 0;
+}
+
+int usbmuxd_get_device_list(usbmuxd_device_info_t **device_list)
+{
+ int sfd;
+ int listen_success = 0;
+ uint32_t res;
+ struct collection tmpdevs;
+ usbmuxd_device_info_t *newlist = NULL;
+ struct usbmuxd_header hdr;
+ struct usbmuxd_device_record *dev;
+ int dev_cnt = 0;
+ void *payload = NULL;
+
+ *device_list = NULL;
+
+#ifdef HAVE_PLIST
+retry:
+#endif
+ sfd = connect_usbmuxd_socket();
+ if (sfd < 0) {
+ DEBUG(1, "%s: error opening socket!\n", __func__);
+ return sfd;
+ }
+
+ use_tag++;
+ LOCK;
+ if (send_listen_packet(sfd, use_tag) > 0) {
+ res = -1;
+ // get response
+ if (usbmuxd_get_result(sfd, use_tag, &res) && (res == 0)) {
+ listen_success = 1;
+ } else {
+ UNLOCK;
+ close_socket(sfd);
+#ifdef HAVE_PLIST
+ if ((res == RESULT_BADVERSION) && (proto_version != 1)) {
+ proto_version = 1;
+ goto retry;
+ }
+#endif
+ DEBUG(1, "%s: Did not get response to scan request (with result=0)...\n", __func__);
+ return res;
+ }
+ }
+
+ if (!listen_success) {
+ UNLOCK;
+ DEBUG(1, "%s: Could not send listen request!\n", __func__);
+ return -1;
+ }
+
+ collection_init(&tmpdevs);
+
+ // receive device list
+ while (1) {
+ if (receive_packet(sfd, &hdr, &payload, 1000) > 0) {
+ if (hdr.message == MESSAGE_DEVICE_ADD) {
+ dev = payload;
+ usbmuxd_device_info_t *devinfo = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t));
+ if (!devinfo) {
+ UNLOCK;
+ DEBUG(1, "%s: Out of memory!\n", __func__);
+ free(payload);
+ return -1;
+ }
+
+ devinfo->handle = dev->device_id;
+ devinfo->product_id = dev->product_id;
+ memset(devinfo->udid, '\0', sizeof(devinfo->udid));
+ memcpy(devinfo->udid, dev->serial_number, sizeof(devinfo->udid));
+
+ if (strcasecmp(devinfo->udid, "ffffffffffffffffffffffffffffffffffffffff") == 0) {
+ sprintf(devinfo->udid + 32, "%08x", devinfo->handle);
+ }
+
+ collection_add(&tmpdevs, devinfo);
+
+ } else if (hdr.message == MESSAGE_DEVICE_REMOVE) {
+ uint32_t handle;
+ usbmuxd_device_info_t *devinfo = NULL;
+
+ memcpy(&handle, payload, sizeof(uint32_t));
+
+ FOREACH(usbmuxd_device_info_t *di, &tmpdevs) {
+ if (di && di->handle == handle) {
+ devinfo = di;
+ break;
+ }
+ } ENDFOREACH
+ if (devinfo) {
+ collection_remove(&tmpdevs, devinfo);
+ free(devinfo);
+ }
+ } else {
+ DEBUG(1, "%s: Unexpected message %d\n", __func__, hdr.message);
+ }
+ if (payload)
+ free(payload);
+ } else {
+ // we _should_ have all of them now.
+ // or perhaps an error occured.
+ break;
+ }
+ }
+ UNLOCK;
+
+ // explicitly close connection
+ close_socket(sfd);
+
+ // create copy of device info entries from collection
+ newlist = (usbmuxd_device_info_t*)malloc(sizeof(usbmuxd_device_info_t) * (collection_count(&tmpdevs) + 1));
+ dev_cnt = 0;
+ FOREACH(usbmuxd_device_info_t *di, &tmpdevs) {
+ if (di) {
+ memcpy(&newlist[dev_cnt], di, sizeof(usbmuxd_device_info_t));
+ free(di);
+ dev_cnt++;
+ }
+ } ENDFOREACH
+ collection_free(&tmpdevs);
+
+ memset(&newlist[dev_cnt], 0, sizeof(usbmuxd_device_info_t));
+ *device_list = newlist;
+
+ return dev_cnt;
+}
+
+int usbmuxd_device_list_free(usbmuxd_device_info_t **device_list)
+{
+ if (device_list) {
+ free(*device_list);
+ }
+ return 0;
+}
+
+int usbmuxd_get_device_by_udid(const char *udid, usbmuxd_device_info_t *device)
+{
+ usbmuxd_device_info_t *dev_list = NULL;
+
+ if (!device) {
+ return -EINVAL;
+ }
+ if (usbmuxd_get_device_list(&dev_list) < 0) {
+ return -ENODEV;
+ }
+
+ int i;
+ int result = 0;
+ for (i = 0; dev_list[i].handle > 0; i++) {
+ if (!udid) {
+ device->handle = dev_list[i].handle;
+ device->product_id = dev_list[i].product_id;
+ strcpy(device->udid, dev_list[i].udid);
+ result = 1;
+ break;
+ }
+ if (!strcmp(udid, dev_list[i].udid)) {
+ device->handle = dev_list[i].handle;
+ device->product_id = dev_list[i].product_id;
+ strcpy(device->udid, dev_list[i].udid);
+ result = 1;
+ break;
+ }
+ }
+
+ free(dev_list);
+
+ return result;
+}
+
+int usbmuxd_connect(const int handle, const unsigned short port)
+{
+ int sfd;
+ int connected = 0;
+ uint32_t res = -1;
+
+#ifdef HAVE_PLIST
+retry:
+#endif
+ sfd = connect_usbmuxd_socket();
+ if (sfd < 0) {
+ DEBUG(1, "%s: Error: Connection to usbmuxd failed: %s\n",
+ __func__, strerror(errno));
+ return sfd;
+ }
+
+ use_tag++;
+ if (send_connect_packet(sfd, use_tag, (uint32_t)handle, (uint16_t)port) <= 0) {
+ DEBUG(1, "%s: Error sending connect message!\n", __func__);
+ } else {
+ // read ACK
+ DEBUG(2, "%s: Reading connect result...\n", __func__);
+ if (usbmuxd_get_result(sfd, use_tag, &res)) {
+ if (res == 0) {
+ DEBUG(2, "%s: Connect success!\n", __func__);
+ connected = 1;
+ } else {
+#ifdef HAVE_PLIST
+ if ((res == RESULT_BADVERSION) && (proto_version == 0)) {
+ proto_version = 1;
+ close_socket(sfd);
+ goto retry;
+ }
+#endif
+ DEBUG(1, "%s: Connect failed, Error code=%d\n", __func__, res);
+ }
+ }
+ }
+
+ if (connected) {
+ return sfd;
+ }
+
+ close_socket(sfd);
+
+ return -1;
+}
+
+int usbmuxd_disconnect(int sfd)
+{
+ return close_socket(sfd);
+}
+
+int usbmuxd_send(int sfd, const char *data, uint32_t len, uint32_t *sent_bytes)
+{
+ int num_sent;
+
+ if (sfd < 0) {
+ return -EINVAL;
+ }
+
+ num_sent = send(sfd, (void*)data, len, 0);
+ if (num_sent < 0) {
+ *sent_bytes = 0;
+ DEBUG(1, "%s: Error %d when sending: %s\n", __func__, num_sent, strerror(errno));
+ return num_sent;
+ } else if ((uint32_t)num_sent < len) {
+ DEBUG(1, "%s: Warning: Did not send enough (only %d of %d)\n", __func__, num_sent, len);
+ }
+
+ *sent_bytes = num_sent;
+
+ return 0;
+}
+
+int usbmuxd_recv_timeout(int sfd, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout)
+{
+ int num_recv = recv_buf_timeout(sfd, (void*)data, len, 0, timeout);
+ if (num_recv < 0) {
+ *recv_bytes = 0;
+ return num_recv;
+ }
+
+ *recv_bytes = num_recv;
+
+ return 0;
+}
+
+int usbmuxd_recv(int sfd, char *data, uint32_t len, uint32_t *recv_bytes)
+{
+ return usbmuxd_recv_timeout(sfd, data, len, recv_bytes, 5000);
+}
+
+void libusbmuxd_set_use_inotify(int set)
+{
+#ifdef HAVE_INOTIFY
+ use_inotify = set;
+#endif
+ return;
+}
+
+void libusbmuxd_set_debug_level(int level)
+{
+ libusbmuxd_debug = level;
+ sock_stuff_set_verbose(level);
+}
diff --git a/src/sock_stuff.c b/src/sock_stuff.c
new file mode 100644
index 0000000..609c8ad
--- /dev/null
+++ b/src/sock_stuff.c
@@ -0,0 +1,375 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
+Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+static int wsa_init = 0;
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+#include "sock_stuff.h"
+
+#define RECV_TIMEOUT 20000
+
+static int verbose = 0;
+
+void sock_stuff_set_verbose(int level)
+{
+ verbose = level;
+}
+
+#ifndef WIN32
+int create_unix_socket(const char *filename)
+{
+ struct sockaddr_un name;
+ int sock;
+ size_t size;
+
+ // remove if still present
+ unlink(filename);
+
+ /* Create the socket. */
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ /* Bind a name to the socket. */
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof(name.sun_path));
+ name.sun_path[sizeof(name.sun_path) - 1] = '\0';
+
+ /* The size of the address is
+ the offset of the start of the filename,
+ plus its length,
+ plus one for the terminating null byte.
+ Alternatively you can just do:
+ size = SUN_LEN (&name);
+ */
+ size = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(name.sun_path) + 1);
+
+ if (bind(sock, (struct sockaddr *) &name, size) < 0) {
+ perror("bind");
+ close_socket(sock);
+ return -1;
+ }
+
+ if (listen(sock, 10) < 0) {
+ perror("listen");
+ close_socket(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+int connect_unix_socket(const char *filename)
+{
+ struct sockaddr_un name;
+ int sfd = -1;
+ size_t size;
+ struct stat fst;
+
+ // check if socket file exists...
+ if (stat(filename, &fst) != 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: stat '%s': %s\n", __func__, filename,
+ strerror(errno));
+ return -1;
+ }
+ // ... and if it is a unix domain socket
+ if (!S_ISSOCK(fst.st_mode)) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: File '%s' is not a socket!\n", __func__,
+ filename);
+ return -1;
+ }
+ // make a new socket
+ if ((sfd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: socket: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ // and connect to 'filename'
+ name.sun_family = AF_LOCAL;
+ strncpy(name.sun_path, filename, sizeof(name.sun_path));
+ name.sun_path[sizeof(name.sun_path) - 1] = 0;
+
+ size = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(name.sun_path) + 1);
+
+ if (connect(sfd, (struct sockaddr *) &name, size) < 0) {
+ close_socket(sfd);
+ if (verbose >= 2)
+ fprintf(stderr, "%s: connect: %s\n", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return sfd;
+}
+#endif
+
+int create_socket(uint16_t port)
+{
+ int sfd = -1;
+ int yes = 1;
+#ifdef WIN32
+ WSADATA wsa_data;
+ if (!wsa_init) {
+ if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
+ fprintf(stderr, "WSAStartup failed!\n");
+ ExitProcess(-1);
+ }
+ wsa_init = 1;
+ }
+#endif
+ struct sockaddr_in saddr;
+
+ if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
+ perror("socket()");
+ return -1;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ memset((void *) &saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons(port);
+
+ if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) {
+ perror("bind()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ if (listen(sfd, 1) == -1) {
+ perror("listen()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ return sfd;
+}
+
+#if defined(WIN32) || defined(__CYGWIN__)
+int connect_socket(const char *addr, uint16_t port)
+{
+ int sfd = -1;
+ int yes = 1;
+ struct hostent *hp;
+ struct sockaddr_in saddr;
+#ifdef WIN32
+ WSADATA wsa_data;
+ if (!wsa_init) {
+ if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
+ fprintf(stderr, "WSAStartup failed!\n");
+ ExitProcess(-1);
+ }
+ wsa_init = 1;
+ }
+#endif
+
+ if (!addr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((hp = gethostbyname(addr)) == NULL) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr);
+ return -1;
+ }
+
+ if (!hp->h_addr) {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: gethostbyname returned NULL address!\n",
+ __func__);
+ return -1;
+ }
+
+ if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
+ perror("socket()");
+ return -1;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
+ perror("setsockopt()");
+ close_socket(sfd);
+ return -1;
+ }
+
+ memset((void *) &saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr;
+ saddr.sin_port = htons(port);
+
+ if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
+ perror("connect");
+ close_socket(sfd);
+ return -2;
+ }
+
+ return sfd;
+}
+#endif /* WIN32 || __CYGWIN__ */
+
+int check_fd(int fd, fd_mode fdm, unsigned int timeout)
+{
+ fd_set fds;
+ int sret;
+ int eagain;
+ struct timeval to;
+ struct timeval *pto;
+
+ if (fd <= 0) {
+ if (verbose >= 2)
+ fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd);
+ return -1;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if (timeout > 0) {
+ to.tv_sec = (time_t) (timeout / 1000);
+ to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000);
+ pto = &to;
+ } else {
+ pto = NULL;
+ }
+
+ sret = -1;
+
+ do {
+ eagain = 0;
+ switch (fdm) {
+ case FDM_READ:
+ sret = select(fd + 1, &fds, NULL, NULL, pto);
+ break;
+ case FDM_WRITE:
+ sret = select(fd + 1, NULL, &fds, NULL, pto);
+ break;
+ case FDM_EXCEPT:
+ sret = select(fd + 1, NULL, NULL, &fds, pto);
+ break;
+ default:
+ return -1;
+ }
+
+ if (sret < 0) {
+ switch (errno) {
+ case EINTR:
+ // interrupt signal in select
+ if (verbose >= 2)
+ fprintf(stderr, "%s: EINTR\n", __func__);
+ eagain = 1;
+ break;
+ case EAGAIN:
+ if (verbose >= 2)
+ fprintf(stderr, "%s: EAGAIN\n", __func__);
+ break;
+ default:
+ if (verbose >= 2)
+ fprintf(stderr, "%s: select failed: %s\n", __func__,
+ strerror(errno));
+ return -1;
+ }
+ }
+ } while (eagain);
+
+ return sret;
+}
+
+int shutdown_socket(int fd, int how)
+{
+ return shutdown(fd, how);
+}
+
+int close_socket(int fd) {
+#ifdef WIN32
+ return closesocket(fd);
+#else
+ return close(fd);
+#endif
+}
+
+int recv_buf(int fd, void *data, size_t length)
+{
+ return recv_buf_timeout(fd, data, length, 0, RECV_TIMEOUT);
+}
+
+int peek_buf(int fd, void *data, size_t length)
+{
+ return recv_buf_timeout(fd, data, length, MSG_PEEK, RECV_TIMEOUT);
+}
+
+int recv_buf_timeout(int fd, void *data, size_t length, int flags,
+ unsigned int timeout)
+{
+ int res;
+ int result;
+
+ // check if data is available
+ res = check_fd(fd, FDM_READ, timeout);
+ if (res <= 0) {
+ return res;
+ }
+ // if we get here, there _is_ data available
+ result = recv(fd, data, length, flags);
+ if (res > 0 && result == 0) {
+ // but this is an error condition
+ if (verbose >= 3)
+ fprintf(stderr, "%s: fd=%d recv returned 0\n", __func__, fd);
+ return -EAGAIN;
+ }
+ if (result < 0) {
+ return -errno;
+ }
+ return result;
+}
+
+int send_buf(int fd, void *data, size_t length)
+{
+ return send(fd, data, length, 0);
+}
diff --git a/src/sock_stuff.h b/src/sock_stuff.h
new file mode 100644
index 0000000..5efcd27
--- /dev/null
+++ b/src/sock_stuff.h
@@ -0,0 +1,65 @@
+/*
+ libusbmuxd - client library to talk to usbmuxd
+
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
+Copyright (C) 2009 Martin Szulecki <opensuse@sukimashita.com>
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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
+
+*/
+
+#ifndef __SOCK_STUFF_H
+#define __SOCK_STUFF_H
+
+#include <stdint.h>
+
+enum fd_mode {
+ FDM_READ,
+ FDM_WRITE,
+ FDM_EXCEPT
+};
+typedef enum fd_mode fd_mode;
+
+#ifdef WIN32
+#include <winsock2.h>
+#define SHUT_RD SD_READ
+#define SHUT_WR SD_WRITE
+#define SHUT_RDWR SD_BOTH
+#endif
+
+#ifndef WIN32
+int create_unix_socket(const char *filename);
+int connect_unix_socket(const char *filename);
+#endif
+int create_socket(uint16_t port);
+#if defined(WIN32) || defined(__CYGWIN__)
+int connect_socket(const char *addr, uint16_t port);
+#endif
+int check_fd(int fd, fd_mode fdm, unsigned int timeout);
+
+int shutdown_socket(int fd, int how);
+int close_socket(int fd);
+
+int recv_buf(int fd, void *data, size_t size);
+int peek_buf(int fd, void *data, size_t size);
+int recv_buf_timeout(int fd, void *data, size_t size, int flags,
+ unsigned int timeout);
+
+int send_buf(int fd, void *data, size_t size);
+
+void sock_stuff_set_verbose(int level);
+
+#endif /* __SOCK_STUFF_H */
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..f1fa2f6
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,10 @@
+AM_CFLAGS = $(GLOBAL_CFLAGS) -I$(top_srcdir)/src
+AM_LDFLAGS =
+
+bin_PROGRAMS = iproxy
+
+iproxy_SOURCES = iproxy.c
+iproxy_CFLAGS = $(AM_CFLAGS)
+iproxy_LDFLAGS = $(AM_LDFLAGS)
+iproxy_LDADD = ../src/libusbmuxd.la
+
diff --git a/tools/iproxy.c b/tools/iproxy.c
new file mode 100644
index 0000000..f7c0827
--- /dev/null
+++ b/tools/iproxy.c
@@ -0,0 +1,281 @@
+/*
+ iproxy -- proxy that enables tcp service access to iPhone/iPod
+
+Copyright (C) 2009 Nikias Bassen <nikias@gmx.li>
+Copyright (C) 2009 Paul Sladen <libiphone@paul.sladen.org>
+
+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
+
+TODO: improve code...
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+typedef unsigned int socklen_t;
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#endif
+#include "sock_stuff.h"
+#include "usbmuxd.h"
+
+static uint16_t listen_port = 0;
+static uint16_t device_port = 0;
+
+struct client_data {
+ int fd;
+ int sfd;
+ volatile int stop_ctos;
+ volatile int stop_stoc;
+};
+
+static void *run_stoc_loop(void *arg)
+{
+ struct client_data *cdata = (struct client_data*)arg;
+ int recv_len;
+ int sent;
+ char buffer[131072];
+
+ printf("%s: fd = %d\n", __func__, cdata->fd);
+
+ while (!cdata->stop_stoc && cdata->fd>0 && cdata->sfd>0) {
+ recv_len = recv_buf_timeout(cdata->sfd, buffer, sizeof(buffer), 0, 5000);
+ if (recv_len <= 0) {
+ if (recv_len == 0) {
+ // try again
+ continue;
+ } else {
+ fprintf(stderr, "recv failed: %s\n", strerror(errno));
+ break;
+ }
+ } else {
+// printf("received %d bytes from server\n", recv_len);
+ // send to socket
+ sent = send_buf(cdata->fd, buffer, recv_len);
+ if (sent < recv_len) {
+ if (sent <= 0) {
+ fprintf(stderr, "send failed: %s\n", strerror(errno));
+ break;
+ } else {
+ fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
+ }
+ } else {
+ // sending succeeded, receive from device
+// printf("pushed %d bytes to client\n", sent);
+ }
+ }
+ }
+ close(cdata->fd);
+ cdata->fd = -1;
+ cdata->stop_ctos = 1;
+
+ return NULL;
+}
+
+static void *run_ctos_loop(void *arg)
+{
+ struct client_data *cdata = (struct client_data*)arg;
+ int recv_len;
+ int sent;
+ char buffer[131072];
+#ifdef WIN32
+ HANDLE stoc = NULL;
+#else
+ pthread_t stoc;
+#endif
+
+ printf("%s: fd = %d\n", __func__, cdata->fd);
+
+ cdata->stop_stoc = 0;
+#ifdef WIN32
+ stoc = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)run_stoc_loop, cdata, 0, NULL);
+#else
+ pthread_create(&stoc, NULL, run_stoc_loop, cdata);
+#endif
+
+ while (!cdata->stop_ctos && cdata->fd>0 && cdata->sfd>0) {
+ recv_len = recv_buf_timeout(cdata->fd, buffer, sizeof(buffer), 0, 5000);
+ if (recv_len <= 0) {
+ if (recv_len == 0) {
+ // try again
+ continue;
+ } else {
+ fprintf(stderr, "recv failed: %s\n", strerror(errno));
+ break;
+ }
+ } else {
+// printf("pulled %d bytes from client\n", recv_len);
+ // send to local socket
+ sent = send_buf(cdata->sfd, buffer, recv_len);
+ if (sent < recv_len) {
+ if (sent <= 0) {
+ fprintf(stderr, "send failed: %s\n", strerror(errno));
+ break;
+ } else {
+ fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
+ }
+ } else {
+ // sending succeeded, receive from device
+// printf("sent %d bytes to server\n", sent);
+ }
+ }
+ }
+ close(cdata->fd);
+ cdata->fd = -1;
+ cdata->stop_stoc = 1;
+
+#ifdef WIN32
+ WaitForSingleObject(stoc, INFINITE);
+#else
+ pthread_join(stoc, NULL);
+#endif
+
+ return NULL;
+}
+
+static void *acceptor_thread(void *arg)
+{
+ struct client_data *cdata;
+ usbmuxd_device_info_t *dev_list = NULL;
+#ifdef WIN32
+ HANDLE ctos = NULL;
+#else
+ pthread_t ctos;
+#endif
+ int count;
+
+ if (!arg) {
+ fprintf(stderr, "invalid client_data provided!\n");
+ return NULL;
+ }
+
+ cdata = (struct client_data*)arg;
+
+ if ((count = usbmuxd_get_device_list(&dev_list)) < 0) {
+ printf("Connecting to usbmuxd failed, terminating.\n");
+ free(dev_list);
+ return NULL;
+ }
+
+ fprintf(stdout, "Number of available devices == %d\n", count);
+
+ if (dev_list == NULL || dev_list[0].handle == 0) {
+ printf("No connected device found, terminating.\n");
+ free(dev_list);
+ return NULL;
+ }
+
+ fprintf(stdout, "Requesting connecion to device handle == %d (serial: %s), port %d\n", dev_list[0].handle, dev_list[0].udid, device_port);
+
+ cdata->sfd = usbmuxd_connect(dev_list[0].handle, device_port);
+ free(dev_list);
+ if (cdata->sfd < 0) {
+ fprintf(stderr, "Error connecting to device!\n");
+ } else {
+ cdata->stop_ctos = 0;
+#ifdef WIN32
+ ctos = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)run_ctos_loop, cdata, 0, NULL);
+ WaitForSingleObject(ctos, INFINITE);
+#else
+ pthread_create(&ctos, NULL, run_ctos_loop, cdata);
+ pthread_join(ctos, NULL);
+#endif
+ }
+
+ if (cdata->fd > 0) {
+ close(cdata->fd);
+ }
+ if (cdata->sfd > 0) {
+ close(cdata->sfd);
+ }
+
+ return NULL;
+}
+
+int main(int argc, char **argv)
+{
+ int mysock = -1;
+
+ if (argc != 3) {
+ printf("usage: %s LOCAL_TCP_PORT DEVICE_TCP_PORT\n", argv[0]);
+ return 0;
+ }
+
+ listen_port = atoi(argv[1]);
+ device_port = atoi(argv[2]);
+
+ if (!listen_port) {
+ fprintf(stderr, "Invalid listen_port specified!\n");
+ return -EINVAL;
+ }
+
+ if (!device_port) {
+ fprintf(stderr, "Invalid device_port specified!\n");
+ return -EINVAL;
+ }
+
+ // first create the listening socket endpoint waiting for connections.
+ mysock = create_socket(listen_port);
+ if (mysock < 0) {
+ fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
+ return -errno;
+ } else {
+#ifdef WIN32
+ HANDLE acceptor = NULL;
+#else
+ pthread_t acceptor;
+#endif
+ struct sockaddr_in c_addr;
+ socklen_t len = sizeof(struct sockaddr_in);
+ struct client_data cdata;
+ int c_sock;
+ while (1) {
+ printf("waiting for connection\n");
+ c_sock = accept(mysock, (struct sockaddr*)&c_addr, &len);
+ if (c_sock) {
+ printf("accepted connection, fd = %d\n", c_sock);
+ cdata.fd = c_sock;
+#ifdef WIN32
+ acceptor = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)acceptor_thread, &cdata, 0, NULL);
+ WaitForSingleObject(acceptor, INFINITE);
+#else
+ pthread_create(&acceptor, NULL, acceptor_thread, &cdata);
+ pthread_join(acceptor, NULL);
+#endif
+ } else {
+ break;
+ }
+ }
+ close(c_sock);
+ close(mysock);
+ }
+
+ return 0;
+}