initial commit of adapted source tree.
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;
+}