summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--AUTHORS1
-rw-r--r--CMakeLists.txt10
-rw-r--r--COPYING.GPLv2 (renamed from COPYING)0
-rw-r--r--COPYING.GPLv3674
-rw-r--r--COPYING.LGPLv2.1 (renamed from COPYING.LESSER)0
-rw-r--r--Makefile.am9
-rw-r--r--Modules/FindUSB.cmake31
-rw-r--r--Modules/LibFindMacros.cmake99
-rw-r--r--README185
-rwxr-xr-xautogen.sh11
-rw-r--r--configure.ac30
-rw-r--r--libusbmuxd.pc.in12
-rw-r--r--libusbmuxd/CMakeLists.txt12
-rw-r--r--libusbmuxd/libusbmuxd.c (renamed from src/libusbmuxd.c)4
-rw-r--r--libusbmuxd/sock_stuff.c (renamed from src/sock_stuff.c)0
-rw-r--r--libusbmuxd/sock_stuff.h (renamed from src/sock_stuff.h)0
-rw-r--r--libusbmuxd/usbmuxd-proto.h (renamed from src/usbmuxd-proto.h)0
-rw-r--r--libusbmuxd/usbmuxd.h (renamed from src/usbmuxd.h)0
-rw-r--r--python-client/.gitignore3
-rw-r--r--python-client/tcprelay.py147
-rw-r--r--python-client/usbmux.py245
-rw-r--r--src/Makefile.am31
-rw-r--r--src/main.c1447
-rw-r--r--src/usbmux.c1264
-rw-r--r--src/usbmux.h53
-rw-r--r--stuff/README19
-rw-r--r--stuff/com.openssh.sftp.plist41
-rw-r--r--tools/CMakeLists.txt8
-rw-r--r--tools/Makefile.am5
-rw-r--r--udev/85-usbmuxd.rules.in4
-rw-r--r--udev/CMakeLists.txt2
-rw-r--r--udev/Makefile.am4
-rw-r--r--usbmuxd/CMakeLists.txt9
-rw-r--r--usbmuxd/client.c430
-rw-r--r--usbmuxd/client.h92
-rw-r--r--usbmuxd/device.c751
-rw-r--r--usbmuxd/device.h52
-rw-r--r--usbmuxd/log.c94
-rw-r--r--usbmuxd/log.h43
-rw-r--r--usbmuxd/main.c548
-rw-r--r--usbmuxd/usb-linux.c503
-rw-r--r--usbmuxd/usb.h67
-rw-r--r--usbmuxd/utils.c110
-rw-r--r--usbmuxd/utils.h65
45 files changed, 4217 insertions, 2905 deletions
diff --git a/.gitignore b/.gitignore
index e6a7834..0784410 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,3 @@
1*~ 1*~
2*.o 2build
3*.so 3
4.*.swp
5iproxy
6usbmuxd
diff --git a/AUTHORS b/AUTHORS
index f84e02b..c196afd 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,4 +1,5 @@
1Nikias Bassen 1Nikias Bassen
2Hector Martin
2Bastien Nocera 3Bastien Nocera
3Paul Sladen 4Paul Sladen
4Martin Szulecki 5Martin Szulecki
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..ba1653f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,10 @@
1PROJECT(usbmuxd)
2
3cmake_minimum_required(VERSION 2.6)
4
5set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/Modules/")
6
7add_subdirectory (libusbmuxd)
8add_subdirectory (usbmuxd)
9add_subdirectory (tools)
10add_subdirectory (udev)
diff --git a/COPYING b/COPYING.GPLv2
index 08ddefd..08ddefd 100644
--- a/COPYING
+++ b/COPYING.GPLv2
diff --git a/COPYING.GPLv3 b/COPYING.GPLv3
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING.GPLv3
@@ -0,0 +1,674 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 3, 29 June 2007
3
4 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The GNU General Public License is a free, copyleft license for
11software and other kinds of works.
12
13 The licenses for most software and other practical works are designed
14to take away your freedom to share and change the works. By contrast,
15the GNU General Public License is intended to guarantee your freedom to
16share and change all versions of a program--to make sure it remains free
17software for all its users. We, the Free Software Foundation, use the
18GNU General Public License for most of our software; it applies also to
19any other work released this way by its authors. You can apply it to
20your programs, too.
21
22 When we speak of free software, we are referring to freedom, not
23price. Our General Public Licenses are designed to make sure that you
24have the freedom to distribute copies of free software (and charge for
25them if you wish), that you receive source code or can get it if you
26want it, that you can change the software or use pieces of it in new
27free programs, and that you know you can do these things.
28
29 To protect your rights, we need to prevent others from denying you
30these rights or asking you to surrender the rights. Therefore, you have
31certain responsibilities if you distribute copies of the software, or if
32you modify it: responsibilities to respect the freedom of others.
33
34 For example, if you distribute copies of such a program, whether
35gratis or for a fee, you must pass on to the recipients the same
36freedoms that you received. You must make sure that they, too, receive
37or can get the source code. And you must show them these terms so they
38know their rights.
39
40 Developers that use the GNU GPL protect your rights with two steps:
41(1) assert copyright on the software, and (2) offer you this License
42giving you legal permission to copy, distribute and/or modify it.
43
44 For the developers' and authors' protection, the GPL clearly explains
45that there is no warranty for this free software. For both users' and
46authors' sake, the GPL requires that modified versions be marked as
47changed, so that their problems will not be attributed erroneously to
48authors of previous versions.
49
50 Some devices are designed to deny users access to install or run
51modified versions of the software inside them, although the manufacturer
52can do so. This is fundamentally incompatible with the aim of
53protecting users' freedom to change the software. The systematic
54pattern of such abuse occurs in the area of products for individuals to
55use, which is precisely where it is most unacceptable. Therefore, we
56have designed this version of the GPL to prohibit the practice for those
57products. If such problems arise substantially in other domains, we
58stand ready to extend this provision to those domains in future versions
59of the GPL, as needed to protect the freedom of users.
60
61 Finally, every program is threatened constantly by software patents.
62States should not allow patents to restrict development and use of
63software on general-purpose computers, but in those that do, we wish to
64avoid the special danger that patents applied to a free program could
65make it effectively proprietary. To prevent this, the GPL assures that
66patents cannot be used to render the program non-free.
67
68 The precise terms and conditions for copying, distribution and
69modification follow.
70
71 TERMS AND CONDITIONS
72
73 0. Definitions.
74
75 "This License" refers to version 3 of the GNU General Public License.
76
77 "Copyright" also means copyright-like laws that apply to other kinds of
78works, such as semiconductor masks.
79
80 "The Program" refers to any copyrightable work licensed under this
81License. Each licensee is addressed as "you". "Licensees" and
82"recipients" may be individuals or organizations.
83
84 To "modify" a work means to copy from or adapt all or part of the work
85in a fashion requiring copyright permission, other than the making of an
86exact copy. The resulting work is called a "modified version" of the
87earlier work or a work "based on" the earlier work.
88
89 A "covered work" means either the unmodified Program or a work based
90on the Program.
91
92 To "propagate" a work means to do anything with it that, without
93permission, would make you directly or secondarily liable for
94infringement under applicable copyright law, except executing it on a
95computer or modifying a private copy. Propagation includes copying,
96distribution (with or without modification), making available to the
97public, and in some countries other activities as well.
98
99 To "convey" a work means any kind of propagation that enables other
100parties to make or receive copies. Mere interaction with a user through
101a computer network, with no transfer of a copy, is not conveying.
102
103 An interactive user interface displays "Appropriate Legal Notices"
104to the extent that it includes a convenient and prominently visible
105feature that (1) displays an appropriate copyright notice, and (2)
106tells the user that there is no warranty for the work (except to the
107extent that warranties are provided), that licensees may convey the
108work under this License, and how to view a copy of this License. If
109the interface presents a list of user commands or options, such as a
110menu, a prominent item in the list meets this criterion.
111
112 1. Source Code.
113
114 The "source code" for a work means the preferred form of the work
115for making modifications to it. "Object code" means any non-source
116form of a work.
117
118 A "Standard Interface" means an interface that either is an official
119standard defined by a recognized standards body, or, in the case of
120interfaces specified for a particular programming language, one that
121is widely used among developers working in that language.
122
123 The "System Libraries" of an executable work include anything, other
124than the work as a whole, that (a) is included in the normal form of
125packaging a Major Component, but which is not part of that Major
126Component, and (b) serves only to enable use of the work with that
127Major Component, or to implement a Standard Interface for which an
128implementation is available to the public in source code form. A
129"Major Component", in this context, means a major essential component
130(kernel, window system, and so on) of the specific operating system
131(if any) on which the executable work runs, or a compiler used to
132produce the work, or an object code interpreter used to run it.
133
134 The "Corresponding Source" for a work in object code form means all
135the source code needed to generate, install, and (for an executable
136work) run the object code and to modify the work, including scripts to
137control those activities. However, it does not include the work's
138System Libraries, or general-purpose tools or generally available free
139programs which are used unmodified in performing those activities but
140which are not part of the work. For example, Corresponding Source
141includes interface definition files associated with source files for
142the work, and the source code for shared libraries and dynamically
143linked subprograms that the work is specifically designed to require,
144such as by intimate data communication or control flow between those
145subprograms and other parts of the work.
146
147 The Corresponding Source need not include anything that users
148can regenerate automatically from other parts of the Corresponding
149Source.
150
151 The Corresponding Source for a work in source code form is that
152same work.
153
154 2. Basic Permissions.
155
156 All rights granted under this License are granted for the term of
157copyright on the Program, and are irrevocable provided the stated
158conditions are met. This License explicitly affirms your unlimited
159permission to run the unmodified Program. The output from running a
160covered work is covered by this License only if the output, given its
161content, constitutes a covered work. This License acknowledges your
162rights of fair use or other equivalent, as provided by copyright law.
163
164 You may make, run and propagate covered works that you do not
165convey, without conditions so long as your license otherwise remains
166in force. You may convey covered works to others for the sole purpose
167of having them make modifications exclusively for you, or provide you
168with facilities for running those works, provided that you comply with
169the terms of this License in conveying all material for which you do
170not control copyright. Those thus making or running the covered works
171for you must do so exclusively on your behalf, under your direction
172and control, on terms that prohibit them from making any copies of
173your copyrighted material outside their relationship with you.
174
175 Conveying under any other circumstances is permitted solely under
176the conditions stated below. Sublicensing is not allowed; section 10
177makes it unnecessary.
178
179 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
181 No covered work shall be deemed part of an effective technological
182measure under any applicable law fulfilling obligations under article
18311 of the WIPO copyright treaty adopted on 20 December 1996, or
184similar laws prohibiting or restricting circumvention of such
185measures.
186
187 When you convey a covered work, you waive any legal power to forbid
188circumvention of technological measures to the extent such circumvention
189is effected by exercising rights under this License with respect to
190the covered work, and you disclaim any intention to limit operation or
191modification of the work as a means of enforcing, against the work's
192users, your or third parties' legal rights to forbid circumvention of
193technological measures.
194
195 4. Conveying Verbatim Copies.
196
197 You may convey verbatim copies of the Program's source code as you
198receive it, in any medium, provided that you conspicuously and
199appropriately publish on each copy an appropriate copyright notice;
200keep intact all notices stating that this License and any
201non-permissive terms added in accord with section 7 apply to the code;
202keep intact all notices of the absence of any warranty; and give all
203recipients a copy of this License along with the Program.
204
205 You may charge any price or no price for each copy that you convey,
206and you may offer support or warranty protection for a fee.
207
208 5. Conveying Modified Source Versions.
209
210 You may convey a work based on the Program, or the modifications to
211produce it from the Program, in the form of source code under the
212terms of section 4, provided that you also meet all of these conditions:
213
214 a) The work must carry prominent notices stating that you modified
215 it, and giving a relevant date.
216
217 b) The work must carry prominent notices stating that it is
218 released under this License and any conditions added under section
219 7. This requirement modifies the requirement in section 4 to
220 "keep intact all notices".
221
222 c) You must license the entire work, as a whole, under this
223 License to anyone who comes into possession of a copy. This
224 License will therefore apply, along with any applicable section 7
225 additional terms, to the whole of the work, and all its parts,
226 regardless of how they are packaged. This License gives no
227 permission to license the work in any other way, but it does not
228 invalidate such permission if you have separately received it.
229
230 d) If the work has interactive user interfaces, each must display
231 Appropriate Legal Notices; however, if the Program has interactive
232 interfaces that do not display Appropriate Legal Notices, your
233 work need not make them do so.
234
235 A compilation of a covered work with other separate and independent
236works, which are not by their nature extensions of the covered work,
237and which are not combined with it such as to form a larger program,
238in or on a volume of a storage or distribution medium, is called an
239"aggregate" if the compilation and its resulting copyright are not
240used to limit the access or legal rights of the compilation's users
241beyond what the individual works permit. Inclusion of a covered work
242in an aggregate does not cause this License to apply to the other
243parts of the aggregate.
244
245 6. Conveying Non-Source Forms.
246
247 You may convey a covered work in object code form under the terms
248of sections 4 and 5, provided that you also convey the
249machine-readable Corresponding Source under the terms of this License,
250in one of these ways:
251
252 a) Convey the object code in, or embodied in, a physical product
253 (including a physical distribution medium), accompanied by the
254 Corresponding Source fixed on a durable physical medium
255 customarily used for software interchange.
256
257 b) Convey the object code in, or embodied in, a physical product
258 (including a physical distribution medium), accompanied by a
259 written offer, valid for at least three years and valid for as
260 long as you offer spare parts or customer support for that product
261 model, to give anyone who possesses the object code either (1) a
262 copy of the Corresponding Source for all the software in the
263 product that is covered by this License, on a durable physical
264 medium customarily used for software interchange, for a price no
265 more than your reasonable cost of physically performing this
266 conveying of source, or (2) access to copy the
267 Corresponding Source from a network server at no charge.
268
269 c) Convey individual copies of the object code with a copy of the
270 written offer to provide the Corresponding Source. This
271 alternative is allowed only occasionally and noncommercially, and
272 only if you received the object code with such an offer, in accord
273 with subsection 6b.
274
275 d) Convey the object code by offering access from a designated
276 place (gratis or for a charge), and offer equivalent access to the
277 Corresponding Source in the same way through the same place at no
278 further charge. You need not require recipients to copy the
279 Corresponding Source along with the object code. If the place to
280 copy the object code is a network server, the Corresponding Source
281 may be on a different server (operated by you or a third party)
282 that supports equivalent copying facilities, provided you maintain
283 clear directions next to the object code saying where to find the
284 Corresponding Source. Regardless of what server hosts the
285 Corresponding Source, you remain obligated to ensure that it is
286 available for as long as needed to satisfy these requirements.
287
288 e) Convey the object code using peer-to-peer transmission, provided
289 you inform other peers where the object code and Corresponding
290 Source of the work are being offered to the general public at no
291 charge under subsection 6d.
292
293 A separable portion of the object code, whose source code is excluded
294from the Corresponding Source as a System Library, need not be
295included in conveying the object code work.
296
297 A "User Product" is either (1) a "consumer product", which means any
298tangible personal property which is normally used for personal, family,
299or household purposes, or (2) anything designed or sold for incorporation
300into a dwelling. In determining whether a product is a consumer product,
301doubtful cases shall be resolved in favor of coverage. For a particular
302product received by a particular user, "normally used" refers to a
303typical or common use of that class of product, regardless of the status
304of the particular user or of the way in which the particular user
305actually uses, or expects or is expected to use, the product. A product
306is a consumer product regardless of whether the product has substantial
307commercial, industrial or non-consumer uses, unless such uses represent
308the only significant mode of use of the product.
309
310 "Installation Information" for a User Product means any methods,
311procedures, authorization keys, or other information required to install
312and execute modified versions of a covered work in that User Product from
313a modified version of its Corresponding Source. The information must
314suffice to ensure that the continued functioning of the modified object
315code is in no case prevented or interfered with solely because
316modification has been made.
317
318 If you convey an object code work under this section in, or with, or
319specifically for use in, a User Product, and the conveying occurs as
320part of a transaction in which the right of possession and use of the
321User Product is transferred to the recipient in perpetuity or for a
322fixed term (regardless of how the transaction is characterized), the
323Corresponding Source conveyed under this section must be accompanied
324by the Installation Information. But this requirement does not apply
325if neither you nor any third party retains the ability to install
326modified object code on the User Product (for example, the work has
327been installed in ROM).
328
329 The requirement to provide Installation Information does not include a
330requirement to continue to provide support service, warranty, or updates
331for a work that has been modified or installed by the recipient, or for
332the User Product in which it has been modified or installed. Access to a
333network may be denied when the modification itself materially and
334adversely affects the operation of the network or violates the rules and
335protocols for communication across the network.
336
337 Corresponding Source conveyed, and Installation Information provided,
338in accord with this section must be in a format that is publicly
339documented (and with an implementation available to the public in
340source code form), and must require no special password or key for
341unpacking, reading or copying.
342
343 7. Additional Terms.
344
345 "Additional permissions" are terms that supplement the terms of this
346License by making exceptions from one or more of its conditions.
347Additional permissions that are applicable to the entire Program shall
348be treated as though they were included in this License, to the extent
349that they are valid under applicable law. If additional permissions
350apply only to part of the Program, that part may be used separately
351under those permissions, but the entire Program remains governed by
352this License without regard to the additional permissions.
353
354 When you convey a copy of a covered work, you may at your option
355remove any additional permissions from that copy, or from any part of
356it. (Additional permissions may be written to require their own
357removal in certain cases when you modify the work.) You may place
358additional permissions on material, added by you to a covered work,
359for which you have or can give appropriate copyright permission.
360
361 Notwithstanding any other provision of this License, for material you
362add to a covered work, you may (if authorized by the copyright holders of
363that material) supplement the terms of this License with terms:
364
365 a) Disclaiming warranty or limiting liability differently from the
366 terms of sections 15 and 16 of this License; or
367
368 b) Requiring preservation of specified reasonable legal notices or
369 author attributions in that material or in the Appropriate Legal
370 Notices displayed by works containing it; or
371
372 c) Prohibiting misrepresentation of the origin of that material, or
373 requiring that modified versions of such material be marked in
374 reasonable ways as different from the original version; or
375
376 d) Limiting the use for publicity purposes of names of licensors or
377 authors of the material; or
378
379 e) Declining to grant rights under trademark law for use of some
380 trade names, trademarks, or service marks; or
381
382 f) Requiring indemnification of licensors and authors of that
383 material by anyone who conveys the material (or modified versions of
384 it) with contractual assumptions of liability to the recipient, for
385 any liability that these contractual assumptions directly impose on
386 those licensors and authors.
387
388 All other non-permissive additional terms are considered "further
389restrictions" within the meaning of section 10. If the Program as you
390received it, or any part of it, contains a notice stating that it is
391governed by this License along with a term that is a further
392restriction, you may remove that term. If a license document contains
393a further restriction but permits relicensing or conveying under this
394License, you may add to a covered work material governed by the terms
395of that license document, provided that the further restriction does
396not survive such relicensing or conveying.
397
398 If you add terms to a covered work in accord with this section, you
399must place, in the relevant source files, a statement of the
400additional terms that apply to those files, or a notice indicating
401where to find the applicable terms.
402
403 Additional terms, permissive or non-permissive, may be stated in the
404form of a separately written license, or stated as exceptions;
405the above requirements apply either way.
406
407 8. Termination.
408
409 You may not propagate or modify a covered work except as expressly
410provided under this License. Any attempt otherwise to propagate or
411modify it is void, and will automatically terminate your rights under
412this License (including any patent licenses granted under the third
413paragraph of section 11).
414
415 However, if you cease all violation of this License, then your
416license from a particular copyright holder is reinstated (a)
417provisionally, unless and until the copyright holder explicitly and
418finally terminates your license, and (b) permanently, if the copyright
419holder fails to notify you of the violation by some reasonable means
420prior to 60 days after the cessation.
421
422 Moreover, your license from a particular copyright holder is
423reinstated permanently if the copyright holder notifies you of the
424violation by some reasonable means, this is the first time you have
425received notice of violation of this License (for any work) from that
426copyright holder, and you cure the violation prior to 30 days after
427your receipt of the notice.
428
429 Termination of your rights under this section does not terminate the
430licenses of parties who have received copies or rights from you under
431this License. If your rights have been terminated and not permanently
432reinstated, you do not qualify to receive new licenses for the same
433material under section 10.
434
435 9. Acceptance Not Required for Having Copies.
436
437 You are not required to accept this License in order to receive or
438run a copy of the Program. Ancillary propagation of a covered work
439occurring solely as a consequence of using peer-to-peer transmission
440to receive a copy likewise does not require acceptance. However,
441nothing other than this License grants you permission to propagate or
442modify any covered work. These actions infringe copyright if you do
443not accept this License. Therefore, by modifying or propagating a
444covered work, you indicate your acceptance of this License to do so.
445
446 10. Automatic Licensing of Downstream Recipients.
447
448 Each time you convey a covered work, the recipient automatically
449receives a license from the original licensors, to run, modify and
450propagate that work, subject to this License. You are not responsible
451for enforcing compliance by third parties with this License.
452
453 An "entity transaction" is a transaction transferring control of an
454organization, or substantially all assets of one, or subdividing an
455organization, or merging organizations. If propagation of a covered
456work results from an entity transaction, each party to that
457transaction who receives a copy of the work also receives whatever
458licenses to the work the party's predecessor in interest had or could
459give under the previous paragraph, plus a right to possession of the
460Corresponding Source of the work from the predecessor in interest, if
461the predecessor has it or can get it with reasonable efforts.
462
463 You may not impose any further restrictions on the exercise of the
464rights granted or affirmed under this License. For example, you may
465not impose a license fee, royalty, or other charge for exercise of
466rights granted under this License, and you may not initiate litigation
467(including a cross-claim or counterclaim in a lawsuit) alleging that
468any patent claim is infringed by making, using, selling, offering for
469sale, or importing the Program or any portion of it.
470
471 11. Patents.
472
473 A "contributor" is a copyright holder who authorizes use under this
474License of the Program or a work on which the Program is based. The
475work thus licensed is called the contributor's "contributor version".
476
477 A contributor's "essential patent claims" are all patent claims
478owned or controlled by the contributor, whether already acquired or
479hereafter acquired, that would be infringed by some manner, permitted
480by this License, of making, using, or selling its contributor version,
481but do not include claims that would be infringed only as a
482consequence of further modification of the contributor version. For
483purposes of this definition, "control" includes the right to grant
484patent sublicenses in a manner consistent with the requirements of
485this License.
486
487 Each contributor grants you a non-exclusive, worldwide, royalty-free
488patent license under the contributor's essential patent claims, to
489make, use, sell, offer for sale, import and otherwise run, modify and
490propagate the contents of its contributor version.
491
492 In the following three paragraphs, a "patent license" is any express
493agreement or commitment, however denominated, not to enforce a patent
494(such as an express permission to practice a patent or covenant not to
495sue for patent infringement). To "grant" such a patent license to a
496party means to make such an agreement or commitment not to enforce a
497patent against the party.
498
499 If you convey a covered work, knowingly relying on a patent license,
500and the Corresponding Source of the work is not available for anyone
501to copy, free of charge and under the terms of this License, through a
502publicly available network server or other readily accessible means,
503then you must either (1) cause the Corresponding Source to be so
504available, or (2) arrange to deprive yourself of the benefit of the
505patent license for this particular work, or (3) arrange, in a manner
506consistent with the requirements of this License, to extend the patent
507license to downstream recipients. "Knowingly relying" means you have
508actual knowledge that, but for the patent license, your conveying the
509covered work in a country, or your recipient's use of the covered work
510in a country, would infringe one or more identifiable patents in that
511country that you have reason to believe are valid.
512
513 If, pursuant to or in connection with a single transaction or
514arrangement, you convey, or propagate by procuring conveyance of, a
515covered work, and grant a patent license to some of the parties
516receiving the covered work authorizing them to use, propagate, modify
517or convey a specific copy of the covered work, then the patent license
518you grant is automatically extended to all recipients of the covered
519work and works based on it.
520
521 A patent license is "discriminatory" if it does not include within
522the scope of its coverage, prohibits the exercise of, or is
523conditioned on the non-exercise of one or more of the rights that are
524specifically granted under this License. You may not convey a covered
525work if you are a party to an arrangement with a third party that is
526in the business of distributing software, under which you make payment
527to the third party based on the extent of your activity of conveying
528the work, and under which the third party grants, to any of the
529parties who would receive the covered work from you, a discriminatory
530patent license (a) in connection with copies of the covered work
531conveyed by you (or copies made from those copies), or (b) primarily
532for and in connection with specific products or compilations that
533contain the covered work, unless you entered into that arrangement,
534or that patent license was granted, prior to 28 March 2007.
535
536 Nothing in this License shall be construed as excluding or limiting
537any implied license or other defenses to infringement that may
538otherwise be available to you under applicable patent law.
539
540 12. No Surrender of Others' Freedom.
541
542 If conditions are imposed on you (whether by court order, agreement or
543otherwise) that contradict the conditions of this License, they do not
544excuse you from the conditions of this License. If you cannot convey a
545covered work so as to satisfy simultaneously your obligations under this
546License and any other pertinent obligations, then as a consequence you may
547not convey it at all. For example, if you agree to terms that obligate you
548to collect a royalty for further conveying from those to whom you convey
549the Program, the only way you could satisfy both those terms and this
550License would be to refrain entirely from conveying the Program.
551
552 13. Use with the GNU Affero General Public License.
553
554 Notwithstanding any other provision of this License, you have
555permission to link or combine any covered work with a work licensed
556under version 3 of the GNU Affero General Public License into a single
557combined work, and to convey the resulting work. The terms of this
558License will continue to apply to the part which is the covered work,
559but the special requirements of the GNU Affero General Public License,
560section 13, concerning interaction through a network will apply to the
561combination as such.
562
563 14. Revised Versions of this License.
564
565 The Free Software Foundation may publish revised and/or new versions of
566the GNU General Public License from time to time. Such new versions will
567be similar in spirit to the present version, but may differ in detail to
568address new problems or concerns.
569
570 Each version is given a distinguishing version number. If the
571Program specifies that a certain numbered version of the GNU General
572Public License "or any later version" applies to it, you have the
573option of following the terms and conditions either of that numbered
574version or of any later version published by the Free Software
575Foundation. If the Program does not specify a version number of the
576GNU General Public License, you may choose any version ever published
577by the Free Software Foundation.
578
579 If the Program specifies that a proxy can decide which future
580versions of the GNU General Public License can be used, that proxy's
581public statement of acceptance of a version permanently authorizes you
582to choose that version for the Program.
583
584 Later license versions may give you additional or different
585permissions. However, no additional obligations are imposed on any
586author or copyright holder as a result of your choosing to follow a
587later version.
588
589 15. Disclaimer of Warranty.
590
591 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
600 16. Limitation of Liability.
601
602 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610SUCH DAMAGES.
611
612 17. Interpretation of Sections 15 and 16.
613
614 If the disclaimer of warranty and limitation of liability provided
615above cannot be given local legal effect according to their terms,
616reviewing courts shall apply local law that most closely approximates
617an absolute waiver of all civil liability in connection with the
618Program, unless a warranty or assumption of liability accompanies a
619copy of the Program in return for a fee.
620
621 END OF TERMS AND CONDITIONS
622
623 How to Apply These Terms to Your New Programs
624
625 If you develop a new program, and you want it to be of the greatest
626possible use to the public, the best way to achieve this is to make it
627free software which everyone can redistribute and change under these terms.
628
629 To do so, attach the following notices to the program. It is safest
630to attach them to the start of each source file to most effectively
631state the exclusion of warranty; and each file should have at least
632the "copyright" line and a pointer to where the full notice is found.
633
634 <one line to give the program's name and a brief idea of what it does.>
635 Copyright (C) <year> <name of author>
636
637 This program is free software: you can redistribute it and/or modify
638 it under the terms of the GNU General Public License as published by
639 the Free Software Foundation, either version 3 of the License, or
640 (at your option) any later version.
641
642 This program is distributed in the hope that it will be useful,
643 but WITHOUT ANY WARRANTY; without even the implied warranty of
644 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 GNU General Public License for more details.
646
647 You should have received a copy of the GNU General Public License
648 along with this program. If not, see <http://www.gnu.org/licenses/>.
649
650Also add information on how to contact you by electronic and paper mail.
651
652 If the program does terminal interaction, make it output a short
653notice like this when it starts in an interactive mode:
654
655 <program> Copyright (C) <year> <name of author>
656 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 This is free software, and you are welcome to redistribute it
658 under certain conditions; type `show c' for details.
659
660The hypothetical commands `show w' and `show c' should show the appropriate
661parts of the General Public License. Of course, your program's commands
662might be different; for a GUI interface, you would use an "about box".
663
664 You should also get your employer (if you work as a programmer) or school,
665if any, to sign a "copyright disclaimer" for the program, if necessary.
666For more information on this, and how to apply and follow the GNU GPL, see
667<http://www.gnu.org/licenses/>.
668
669 The GNU General Public License does not permit incorporating your program
670into proprietary programs. If your program is a subroutine library, you
671may consider it more useful to permit linking proprietary applications with
672the library. If this is what you want to do, use the GNU Lesser General
673Public License instead of this License. But first, please read
674<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/COPYING.LESSER b/COPYING.LGPLv2.1
index 732811e..732811e 100644
--- a/COPYING.LESSER
+++ b/COPYING.LGPLv2.1
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 403981d..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
1SUBDIRS = src udev tools
2
3pkgconfigdir = $(libdir)/pkgconfig
4pkgconfig_DATA = libusbmuxd.pc
5
6indent:
7 indent -kr -ut -ts4 -l120 src/*.c src/*.h tools/*.c
8
9EXTRA_DIST = README.devel
diff --git a/Modules/FindUSB.cmake b/Modules/FindUSB.cmake
new file mode 100644
index 0000000..d562b98
--- /dev/null
+++ b/Modules/FindUSB.cmake
@@ -0,0 +1,31 @@
1# - Try to find libusb-1.0
2# Once done, this will define
3#
4# USB_FOUND - system has libusb-1.0
5# USB_INCLUDE_DIRS - the libusb-1.0 include directories
6# USB_LIBRARIES - link these to use libusb-1.0
7
8include(LibFindMacros)
9
10# Dependencies
11
12# Use pkg-config to get hints about paths
13libfind_pkg_check_modules(USB_PKGCONF libusb-1.0)
14
15# Include dir
16find_path(USB_INCLUDE_DIR
17 NAMES libusb.h
18 PATHS ${USB_PKGCONF_INCLUDE_DIRS}
19)
20
21# Finally the library itself
22find_library(USB_LIBRARY
23 NAMES usb-1.0
24 PATHS ${USB_PKGCONF_LIBRARY_DIRS}
25)
26
27# Set the include dir variables and the libraries and let libfind_process do the rest.
28# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
29set(USB_PROCESS_INCLUDES USB_INCLUDE_DIR)
30set(USB_PROCESS_LIBS USB_LIBRARY)
31libfind_process(USB)
diff --git a/Modules/LibFindMacros.cmake b/Modules/LibFindMacros.cmake
new file mode 100644
index 0000000..795d6b7
--- /dev/null
+++ b/Modules/LibFindMacros.cmake
@@ -0,0 +1,99 @@
1# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments
2# used for the current package. For this to work, the first parameter must be the
3# prefix of the current package, then the prefix of the new package etc, which are
4# passed to find_package.
5macro (libfind_package PREFIX)
6 set (LIBFIND_PACKAGE_ARGS ${ARGN})
7 if (${PREFIX}_FIND_QUIETLY)
8 set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET)
9 endif (${PREFIX}_FIND_QUIETLY)
10 if (${PREFIX}_FIND_REQUIRED)
11 set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED)
12 endif (${PREFIX}_FIND_REQUIRED)
13 find_package(${LIBFIND_PACKAGE_ARGS})
14endmacro (libfind_package)
15
16# Damn CMake developers made the UsePkgConfig system deprecated in the same release (2.6)
17# where they added pkg_check_modules. Consequently I need to support both in my scripts
18# to avoid those deprecated warnings. Here's a helper that does just that.
19# Works identically to pkg_check_modules, except that no checks are needed prior to use.
20macro (libfind_pkg_check_modules PREFIX PKGNAME)
21 if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
22 include(UsePkgConfig)
23 pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS)
24 else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
25 find_package(PkgConfig)
26 if (PKG_CONFIG_FOUND)
27 pkg_check_modules(${PREFIX} ${PKGNAME})
28 endif (PKG_CONFIG_FOUND)
29 endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
30endmacro (libfind_pkg_check_modules)
31
32# Do the final processing once the paths have been detected.
33# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
34# all the variables, each of which contain one include directory.
35# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
36# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
37# Also handles errors in case library detection was required, etc.
38macro (libfind_process PREFIX)
39 # Skip processing if already processed during this run
40 if (NOT ${PREFIX}_FOUND)
41 # Start with the assumption that the library was found
42 set (${PREFIX}_FOUND TRUE)
43
44 # Process all includes and set _FOUND to false if any are missing
45 foreach (i ${${PREFIX}_PROCESS_INCLUDES})
46 if (${i})
47 set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}})
48 mark_as_advanced(${i})
49 else (${i})
50 set (${PREFIX}_FOUND FALSE)
51 endif (${i})
52 endforeach (i)
53
54 # Process all libraries and set _FOUND to false if any are missing
55 foreach (i ${${PREFIX}_PROCESS_LIBS})
56 if (${i})
57 set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}})
58 mark_as_advanced(${i})
59 else (${i})
60 set (${PREFIX}_FOUND FALSE)
61 endif (${i})
62 endforeach (i)
63
64 # Print message and/or exit on fatal error
65 if (${PREFIX}_FOUND)
66 if (NOT ${PREFIX}_FIND_QUIETLY)
67 message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
68 endif (NOT ${PREFIX}_FIND_QUIETLY)
69 else (${PREFIX}_FOUND)
70 if (${PREFIX}_FIND_REQUIRED)
71 foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS})
72 message("${i}=${${i}}")
73 endforeach (i)
74 message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.")
75 endif (${PREFIX}_FIND_REQUIRED)
76 endif (${PREFIX}_FOUND)
77 endif (NOT ${PREFIX}_FOUND)
78endmacro (libfind_process)
79
80macro(libfind_library PREFIX basename)
81 set(TMP "")
82 if(MSVC80)
83 set(TMP -vc80)
84 endif(MSVC80)
85 if(MSVC90)
86 set(TMP -vc90)
87 endif(MSVC90)
88 set(${PREFIX}_LIBNAMES ${basename}${TMP})
89 if(${ARGC} GREATER 2)
90 set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2})
91 string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES})
92 set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP})
93 endif(${ARGC} GREATER 2)
94 find_library(${PREFIX}_LIBRARY
95 NAMES ${${PREFIX}_LIBNAMES}
96 PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}
97 )
98endmacro(libfind_library)
99
diff --git a/README b/README
index ff28e19..0490e1f 100644
--- a/README
+++ b/README
@@ -1,31 +1,37 @@
1Background 1Background
2========== 2==========
3 3
4'usbmuxd' stands for "USB multiplexing daemon". To the user/developer 4'usbmuxd' stands for "USB multiplexing daemon". To the user/developer what it
5what it actually does is to proxy requests over a USB cable on directly 5actually does is to proxy requests over a USB cable on directly to a listening
6to a listening TCP port on the iPhone. 6TCP port on the iPhone.
7 7
8Multiple connections to different TCP ports can happen in parallel. 8Multiple connections to different TCP ports can happen in parallel. An example
9An example (and useful) tool called 'iproxy' is included that allows 9(and useful) tool called 'iproxy' is included that allows you to forward
10you to forward localhost ports to the device---allows SSH over USB on 10localhost ports to the device---allows SSH over USB on jailbroken devices, or
11jailbroken devices, or allowing access the lockdown daemon (and then 11allowing access the lockdown daemon (and then to all of the file access, sync,
12to all of the file access, sync, notification and backup services 12notification and backup services running on the device).
13running on the device). 13
14 14This higher-level layers are handled by libiphone. The version of libiphone
15This higher-level layers are handled by libiphone. The version of 15compatible with the 'usbmuxd' infrastructure is called 'libiphone-usbmuxd'.
16libiphone compatible with the 'usbmuxd' infrastructure is called 16'ifuse' is then able to sit on top of this.
17'libiphone-usbmuxd'. 'ifuse' is then able to sit on top of this. 17
18 18There is also a Python implementation of the client library in the python-client
19library, and an example tcprelay.py which performs a similar function to iproxy.
20This implementation supports OSX and Windows and the new iTunes plist-based
21usbmuxd protocol, so it is portable and will run on those operating systems with
22no modification, using Apple's native usbmuxd. This is useful if you need to
23tunnel to your phone from another OS in a pinch. Run python tcpclient.py --help
24for usage information.
19 25
20Building 26Building
21======== 27========
22 28
23 ./autogen.sh 29 mkdir build
24 ./configure --sysconfdir=/etc 30 cd build
31 cmake ..
25 make 32 make
26 sudo make install 33 sudo make install
27 34
28
29Running (with magic) 35Running (with magic)
30==================== 36====================
31 37
@@ -33,21 +39,21 @@ Running (with magic)
33 ./iproxy 2222 22 & 39 ./iproxy 2222 22 &
34 ssh -p 2222 root@localhost 40 ssh -p 2222 root@localhost
35 41
36Hopefully you get the normal SSH login prompt. You may still lots of 42Hopefully you get the normal SSH login prompt. You may still lots of debugging
37debugging output for the moment. If this is getting in the way of 43output for the moment. If this is getting in the way of your ssh login, then
38your ssh login, then run the 'ssh' command from a different xterminal 44run the 'ssh' command from a different xterminal
39or virtual console. 45or virtual console.
40 46
41 47
42Running (without magic) 48Running (without magic)
43======================= 49=======================
44 50
45If 'udev' is _not_ automatically running on your machine and picking 51If 'udev' is _not_ automatically running on your machine and picking up the new
46up the new .rules file, you will need to start usbmuxd by hand first. 52.rules file, you will need to start usbmuxd by hand first. Check it's running
47Check it's running and that there is only one copy with 'ps aux | grep 53and that there is only one copy with 'ps aux | grep
48usbmuxd'. 54usbmuxd'.
49 55
50 sudo usbmuxd & 56 sudo usbmuxd -U -v -v &
51 ./iproxy 2222 22 & 57 ./iproxy 2222 22 &
52 ssh -p 2222 root@localhost 58 ssh -p 2222 root@localhost
53 59
@@ -55,13 +61,13 @@ usbmuxd'.
55Tip: Starting SSH if disabled 61Tip: Starting SSH if disabled
56============================= 62=============================
57 63
58If your iphone is rooted, but SSH isn't started and you _cannot_ (for 64If your iphone is rooted, but SSH isn't started and you _cannot_ (for instance,
59instance, cracked/broken screen) get to the Services control panel on 65cracked/broken screen) get to the Services control panel on the device, then you
60the device, then you can start the SSH service over the USB by 66can start the SSH service over the USB by
61mounting the (jailbroken) filesystem. 67mounting the (jailbroken) filesystem.
62 68
63You will need to mount it usbing 'ifuse --afc2' (to access the root 69You will need to mount it usbing 'ifuse --afc2' (to access the root directory of
64directory of the device), and then edit: 70the device), and then edit:
65 71
66 /Library/LaunchDaemons/com.openssh.sshd.plist 72 /Library/LaunchDaemons/com.openssh.sshd.plist
67 73
@@ -71,3 +77,122 @@ to _remove_ the lines:
71 <true/> 77 <true/>
72 78
73Reboot the device and then sshd should be running. 79Reboot the device and then sshd should be running.
80
81TODO
82====
83
84The server currently assumes that the phone is well-behaved and does not do a
85bunch of checks like looking for the expected SEQ and ACK numbers from it. This
86is normally not an issue, but it's annoying for debugging because lost packets
87(which shouldn't happen, but can happen if the code is buggy) mean that stuff
88gets out of sync and then might crash and burn dozens of packets later.
89
90The server needs more testing, and some optimizing.
91
92Someone should probably do some edge-case testing on the TCP stuff.
93
94The outgoing ACK handling on the server probably needs some thought. Currently,
95when there's an outstanding ACK, we send it after a timeout (to avoid sending
96a no-payload ACK packet for everything the phone sends us). However, there's
97probably a better way of doing this.
98
99Architecture information
100========================
101
102The iPhone / iPod Touch basically implements a rather strange USB networking
103system that operates at a higher level. It is of course completely proprietary.
104Generally speaking, this is what goes on in a typical usage scenario:
105
1060. iTunes opens a connection to usbmuxd and asks it for device notifications
1071. User inserts phone into computer
1082. usbmuxd notices the phone and pings it with a version packet
1093. phone replies
1104. usbmuxd now considers the phone to be connected and tells iTunes
1115. iTunes opens another separate connection to usbmuxd and asks it to connect
112 to, say, the afc port on the device
1136. usbmuxd sends a pseudo-TCP SYN packet to the phone
1147. the phone's kernel driver receives the SYN packet and itself opens a
115 TCP connection to localhost on the afc port
1168. the phone replies with a pseudo-TCP SYN/ACK indicating that the port is open
117 and the connection can proceed
1187. usbmuxd sends a final ACK to the phone
1198. usbmuxd replies to iTunes with a "connection successful" message
1209. any data that iTunes writes to the usbmuxd socket from now on is forwarded,
121 through pseudo-TCP, through USB, back into a more regular TCP connection to
122 localhost, to the afc daemon on the phone, and vice versa
123
124The usbmuxd protocol is a relatively simple binary message protocol documented
125here:
126
127http://wikee.iphwn.org/usb:usbmux
128
129Note that once a connection is established the UNIX socket essentially becomes
130a dedicated pipe to the TCP connction and no more high-level control is
131possible (closing the socket closes the TCP connection). Ditto for the "listen
132for devices" mode - usbmuxd will reject any commands in such mode, and the
133socket essentially becomes a dedicated device notification pipe. This means
134that you need, at minimum, TWO connections to usbmuxd to do anything useful.
135
136On Windows, usbmuxd works the same way but a TCP connection to localhost port
13727015 replaces the UNIX socket. On OSX, the UNIX socket is /var/run/usbmuxd. The
138server and client implemented here default to /tmp/usbmuxd at the moment.
139
140The phone protocol operates over a pair of USB bulk endpoints. There is an outer
141layer used for packet size info and a "protocol" (version and TCP are the only
142two options), and that header is followed by TCP headers for actual data comms.
143However, the protocol isn't actual TCP, just a custom protocol which for some
144reason uses a standard TCP header and leaves most fields unused.
145
146There is no reordering or retransmission. There are no checksums, no URG, no
147PSH, no non-ACK, no FIN. What there *is* is the SEQ/ACK/window mechanism used
148for flow control, and RST is used as the only connection teardown mechanism (and
149also for "connection refused"), and the connection startup is SYN/SYNACK/ACK.
150
151Windows are constant-scaled by 8 bits. This is legal TCP as long as the
152corresponding option is negotiated. Of course, no such negotiation happens on
153this protocol.
154
155Note that, since there are no retransmissions, there is some overlap between ACK
156and window for flow control. For example, the server doesn't ever touch its
157window size, and just refuses to ACK stuff if its buffers are full and the
158client isn't reading data. The phone happily seems to stop sending stuff.
159
160Also, if the phone RSTs you out of nowhere, look at the packet payload for a
161textual error message. Note: if it claims to suffer from amnesia, that probably
162means you overflowed its input buffer by ignoring its flow control / window
163size. Go figure. Probably a logic bug in the kernel code.
164
165Note that all normal packets need to have flags set to ACK (and only ACK). There
166is no support for, erm, not-acking. Keep the ack number field valid at all
167times.
168
169The usbmuxd CONNECT request port field is byte-swapped (network-endian). This is
170even more annoying for the plist based protocol, since it's even true there
171(where the field is plain text). So even for the plain text int, you need to
172swap the bytes (port 22 becomes <integer>5632</integer>). I have no clue if this
173is the case on the new plist protocol on PPC macs (is the newer iTunes available
174for those?)
175
176There are a bunch of gotchas due to the USB framing, and this is even worse
177because implementations tend to get it wrong (i.e. libusb, and this is the
178reason for the patch). Basically, USB Bulk offers, at the low level, the ability
179to transfer packets from 0 to wMaxPacketSize (512 here) bytes, period. There is
180no other support for higher level framing of transfers. The way you do those is
181by breaking them up into packets, and the final shorter packet marks the end of
182the transfer. The critical bit is that, if the transfer happens to be divisible
183by 512, you send a zero-length packet (ZLP) to indicate the end of the transfer.
184Libusb doesn't set this option by default and the iPhone gets packets stuck to
185each other, which it doesn't like. Actually, this framing is sort of redundant
186because the usbmux packet header includes a length field, but the phone still
187wants the ZLPs or else it breaks. To make matters worse, usbdevfs imposes a max
188transfer size of 16k, so libusb breaks transfers into that size. This is okay
189for sending as long as the ZLP is only added to the last transfer (the patch
190does that), but it can easily cause nasty race conditions on RX due to libusb
191doing multiple outstanding reads at the same time and then cancelling the rest
192when shorter data arrives (but what if some data got into the other requests
193already?), so we only do 16k reads and stick them together ourselves by looking
194at the packet size header. We still depend on ZLPs being sent to end transfers
195at non-16k boundaries that are multiples of 512, but that seems to work fine. I
196guess the ZLPs might cause spurious 0-byte transfers to show up on RX if things
197line up right, but we ignore those. By the way, the maximum packet/transfer size
198is 65535 bytes due to the 16-bit length header of the usbmux protocol.
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755
index 82cc273..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,11 +0,0 @@
1#!/bin/sh
2if [ ! -d m4 ]; then mkdir m4; fi
3aclocal -I m4 || exit 1
4libtoolize || exit 1
5autoheader || exit 1
6automake --add-missing || exit 1
7autoconf || exit 1
8echo
9echo Executing ./configure $@
10echo
11./configure "$@"
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index 029eb1f..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,30 +0,0 @@
1# Inital configuration
2
3AC_PREREQ(2.61)
4
5AC_INIT([usbmuxd], [0.1.5], [nikias@gmx.li])
6AM_INIT_AUTOMAKE([-Wall -Werror foreign dist-bzip2])
7
8AC_CONFIG_HEADERS([config.h])
9
10# Check for programs
11
12AC_PROG_CC
13AM_PROG_CC_C_O
14AC_PROG_LIBTOOL
15AC_PROG_INSTALL
16
17# Check for libraries
18
19PKG_CHECK_MODULES(libusb, libusb >= 0.1.12)
20
21# Output files
22
23AC_OUTPUT([
24Makefile
25src/Makefile
26tools/Makefile
27udev/85-usbmuxd.rules
28udev/Makefile
29libusbmuxd.pc
30])
diff --git a/libusbmuxd.pc.in b/libusbmuxd.pc.in
deleted file mode 100644
index 84c5027..0000000
--- a/libusbmuxd.pc.in
+++ /dev/null
@@ -1,12 +0,0 @@
1prefix=@prefix@
2exec_prefix=@exec_prefix@
3libdir=@libdir@
4includedir=@includedir@
5
6Name: libusbmuxd
7Description: A library to communicate with the usbmux daemon
8Version: @VERSION@
9Requires: libusb >= 0.1.12
10Libs: -L${libdir} -lusbmuxd
11Cflags: -I${includedir}
12
diff --git a/libusbmuxd/CMakeLists.txt b/libusbmuxd/CMakeLists.txt
new file mode 100644
index 0000000..61de1a8
--- /dev/null
+++ b/libusbmuxd/CMakeLists.txt
@@ -0,0 +1,12 @@
1add_library (libusbmuxd libusbmuxd.c sock_stuff.c)
2
3# 'lib' is a UNIXism, the proper CMake target is usbmuxd
4# But we can't use that due to the conflict with the usbmuxd daemon,
5# so instead change the library output base name to usbmuxd here
6set_target_properties(libusbmuxd PROPERTIES OUTPUT_NAME usbmuxd)
7
8install(TARGETS libusbmuxd
9 ARCHIVE DESTINATION lib
10 LIBRARY DESTINATION lib
11)
12install(FILES usbmuxd.h usbmuxd-proto.h DESTINATION include)
diff --git a/src/libusbmuxd.c b/libusbmuxd/libusbmuxd.c
index 4cd0a6d..090695f 100644
--- a/src/libusbmuxd.c
+++ b/libusbmuxd/libusbmuxd.c
@@ -8,9 +8,9 @@
8#include <unistd.h> 8#include <unistd.h>
9 9
10// usbmuxd public interface 10// usbmuxd public interface
11#include <usbmuxd.h> 11#include "usbmuxd.h"
12// usbmuxd protocol 12// usbmuxd protocol
13#include <usbmuxd-proto.h> 13#include "usbmuxd-proto.h"
14// socket utility functions 14// socket utility functions
15#include "sock_stuff.h" 15#include "sock_stuff.h"
16 16
diff --git a/src/sock_stuff.c b/libusbmuxd/sock_stuff.c
index 137375d..137375d 100644
--- a/src/sock_stuff.c
+++ b/libusbmuxd/sock_stuff.c
diff --git a/src/sock_stuff.h b/libusbmuxd/sock_stuff.h
index 190f7e1..190f7e1 100644
--- a/src/sock_stuff.h
+++ b/libusbmuxd/sock_stuff.h
diff --git a/src/usbmuxd-proto.h b/libusbmuxd/usbmuxd-proto.h
index 7f8c2d6..7f8c2d6 100644
--- a/src/usbmuxd-proto.h
+++ b/libusbmuxd/usbmuxd-proto.h
diff --git a/src/usbmuxd.h b/libusbmuxd/usbmuxd.h
index ba45ec3..ba45ec3 100644
--- a/src/usbmuxd.h
+++ b/libusbmuxd/usbmuxd.h
diff --git a/python-client/.gitignore b/python-client/.gitignore
new file mode 100644
index 0000000..5da7ef5
--- /dev/null
+++ b/python-client/.gitignore
@@ -0,0 +1,3 @@
1*.pyc
2*.pyo
3
diff --git a/python-client/tcprelay.py b/python-client/tcprelay.py
new file mode 100644
index 0000000..2874ae1
--- /dev/null
+++ b/python-client/tcprelay.py
@@ -0,0 +1,147 @@
1#!/usr/bin/python
2#
3# tcprelay.py - TCP connection relay for usbmuxd
4#
5# Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 2 or version 3.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20import usbmux
21import SocketServer
22import select
23from optparse import OptionParser
24import sys
25import threading
26
27class SocketRelay(object):
28 def __init__(self, a, b, maxbuf=65535):
29 self.a = a
30 self.b = b
31 self.atob = ""
32 self.btoa = ""
33 self.maxbuf = maxbuf
34 def handle(self):
35 while True:
36 rlist = []
37 wlist = []
38 xlist = [self.a, self.b]
39 if self.atob:
40 wlist.append(self.b)
41 if self.btoa:
42 wlist.append(self.a)
43 if len(self.atob) < self.maxbuf:
44 rlist.append(self.a)
45 if len(self.btoa) < self.maxbuf:
46 rlist.append(self.b)
47 rlo, wlo, xlo = select.select(rlist, wlist, xlist)
48 if xlo:
49 return
50 if self.a in wlo:
51 n = self.a.send(self.btoa)
52 self.btoa = self.btoa[n:]
53 if self.b in wlo:
54 n = self.b.send(self.atob)
55 self.atob = self.atob[n:]
56 if self.a in rlo:
57 s = self.a.recv(self.maxbuf - len(self.atob))
58 if not s:
59 return
60 self.atob += s
61 if self.b in rlo:
62 s = self.b.recv(self.maxbuf - len(self.btoa))
63 if not s:
64 return
65 self.btoa += s
66 #print "Relay iter: %8d atob, %8d btoa, lists: %r %r %r"%(len(self.atob), len(self.btoa), rlo, wlo, xlo)
67
68class TCPRelay(SocketServer.BaseRequestHandler):
69 def handle(self):
70 print "Incoming connection to %d"%self.server.server_address[1]
71 mux = usbmux.USBMux(options.sockpath)
72 print "Waiting for devices..."
73 if not mux.devices:
74 mux.process(1.0)
75 if not mux.devices:
76 print "No device found"
77 self.request.close()
78 return
79 dev = mux.devices[0]
80 print "Connecting to device %s"%str(dev)
81 dsock = mux.connect(dev, self.server.rport)
82 lsock = self.request
83 print "Connection established, relaying data"
84 try:
85 fwd = SocketRelay(dsock, lsock, self.server.bufsize * 1024)
86 fwd.handle()
87 finally:
88 dsock.close()
89 lsock.close()
90 print "Connection closed"
91
92class TCPServer(SocketServer.TCPServer):
93 allow_reuse_address = True
94
95class ThreadedTCPServer(SocketServer.ThreadingMixIn, TCPServer):
96 pass
97
98HOST = "localhost"
99
100parser = OptionParser(usage="usage: %prog [OPTIONS] RemotePort[:LocalPort] [RemotePort[:LocalPort]]...")
101parser.add_option("-t", "--threaded", dest='threaded', action='store_true', default=False, help="use threading to handle multiple connections at once")
102parser.add_option("-b", "--bufsize", dest='bufsize', action='store', metavar='KILOBYTES', type='int', default=128, help="specify buffer size for socket forwarding")
103parser.add_option("-s", "--socket", dest='sockpath', action='store', metavar='PATH', type='str', default=None, help="specify the path of the usbmuxd socket")
104
105options, args = parser.parse_args()
106
107serverclass = TCPServer
108if options.threaded:
109 serverclass = ThreadedTCPServer
110
111if len(args) == 0:
112 parser.print_help()
113 sys.exit(1)
114
115ports = []
116
117for arg in args:
118 try:
119 if ':' in arg:
120 rport, lport = arg.split(":")
121 rport = int(rport)
122 lport = int(lport)
123 ports.append((rport, lport))
124 else:
125 ports.append((int(arg), int(arg)))
126 except:
127 parser.print_help()
128 sys.exit(1)
129
130servers=[]
131
132for rport, lport in ports:
133 print "Forwarding local port %d to remote port %d"%(lport, rport)
134 server = serverclass((HOST, lport), TCPRelay)
135 server.rport = rport
136 server.bufsize = options.bufsize
137 servers.append(server)
138
139alive = True
140
141while alive:
142 try:
143 rl, wl, xl = select.select(servers, [], [])
144 for server in rl:
145 server.handle_request()
146 except:
147 alive = False
diff --git a/python-client/usbmux.py b/python-client/usbmux.py
new file mode 100644
index 0000000..47112d3
--- /dev/null
+++ b/python-client/usbmux.py
@@ -0,0 +1,245 @@
1#!/usr/bin/python
2#
3# usbmux.py - usbmux client library for Python
4#
5# Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 2 or version 3.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20import socket, struct, select, sys
21
22try:
23 import plistlib
24 haveplist = True
25except:
26 haveplist = False
27
28class MuxError(Exception):
29 pass
30
31class MuxVersionError(MuxError):
32 pass
33
34class SafeStreamSocket:
35 def __init__(self, address, family):
36 self.sock = socket.socket(family, socket.SOCK_STREAM)
37 self.sock.connect(address)
38 def send(self, msg):
39 totalsent = 0
40 while totalsent < len(msg):
41 sent = self.sock.send(msg[totalsent:])
42 if sent == 0:
43 raise MuxError("socket connection broken")
44 totalsent = totalsent + sent
45 def recv(self, size):
46 msg = ''
47 while len(msg) < size:
48 chunk = self.sock.recv(size-len(msg))
49 if chunk == '':
50 raise MuxError("socket connection broken")
51 msg = msg + chunk
52 return msg
53
54class MuxDevice(object):
55 def __init__(self, devid, usbprod, serial, location):
56 self.devid = devid
57 self.usbprod = usbprod
58 self.serial = serial
59 self.location = location
60 def __str__(self):
61 return "<MuxDevice: ID %d ProdID 0x%04x Serial '%s' Location 0x%x>"%(self.devid, self.usbprod, self.serial, self.location)
62
63class BinaryProtocol(object):
64 TYPE_RESULT = 1
65 TYPE_CONNECT = 2
66 TYPE_LISTEN = 3
67 TYPE_DEVICE_ADD = 4
68 TYPE_DEVICE_REMOVE = 5
69 VERSION = 0
70 def __init__(self, socket):
71 self.socket = socket
72 self.connected = False
73
74 def _pack(self, req, payload):
75 if req == self.TYPE_CONNECT:
76 return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + "\x00\x00"
77 elif req == self.TYPE_LISTEN:
78 return ""
79 else:
80 raise ValueError("Invalid outgoing request type %d"%req)
81
82 def _unpack(self, resp, payload):
83 if resp == self.TYPE_RESULT:
84 return {'Number':struct.unpack("I", payload)[0]}
85 elif resp == self.TYPE_DEVICE_ADD:
86 devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload)
87 serial = serial.split("\0")[0]
88 return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}}
89 elif resp == self.TYPE_DEVICE_REMOVE:
90 devid = struct.unpack("I", payload)[0]
91 return {'DeviceID': devid}
92 else:
93 raise MuxError("Invalid incoming request type %d"%req)
94
95 def sendpacket(self, req, tag, payload={}):
96 payload = self._pack(req, payload)
97 if self.connected:
98 raise MuxError("Mux is connected, cannot issue control packets")
99 length = 16 + len(payload)
100 data = struct.pack("IIII", length, self.VERSION, req, tag) + payload
101 self.socket.send(data)
102 def getpacket(self):
103 if self.connected:
104 raise MuxError("Mux is connected, cannot issue control packets")
105 dlen = self.socket.recv(4)
106 dlen = struct.unpack("I", dlen)[0]
107 body = self.socket.recv(dlen - 4)
108 version, resp, tag = struct.unpack("III",body[:0xc])
109 if version != self.VERSION:
110 raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version))
111 payload = self._unpack(resp, body[0xc:])
112 return (resp, tag, payload)
113
114class PlistProtocol(BinaryProtocol):
115 TYPE_RESULT = "Result"
116 TYPE_CONNECT = "Connect"
117 TYPE_LISTEN = "Listen"
118 TYPE_DEVICE_ADD = "Attached"
119 TYPE_DEVICE_REMOVE = "Detached" #???
120 TYPE_PLIST = 8
121 VERSION = 1
122 def __init__(self, socket):
123 if not haveplist:
124 raise Exception("You need the plistlib module")
125 BinaryProtocol.__init__(self, socket)
126
127 def _pack(self, req, payload):
128 return payload
129
130 def _unpack(self, resp, payload):
131 return payload
132
133 def sendpacket(self, req, tag, payload={}):
134 payload['ClientVersionString'] = 'usbmux.py by marcan'
135 if isinstance(req, int):
136 req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2]
137 payload['MessageType'] = req
138 payload['ProgName'] = 'tcprelay'
139 BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.writePlistToString(payload))
140 def getpacket(self):
141 resp, tag, payload = BinaryProtocol.getpacket(self)
142 if resp != self.TYPE_PLIST:
143 raise MuxError("Received non-plist type %d"%resp)
144 payload = plistlib.readPlistFromString(payload)
145 return payload['MessageType'], tag, payload
146
147class MuxConnection(object):
148 def __init__(self, socketpath, protoclass):
149 self.socketpath = socketpath
150 if sys.platform in ['win32', 'cygwin']:
151 family = socket.AF_INET
152 address = ('127.0.0.1', 27015)
153 else:
154 family = socket.AF_UNIX
155 address = self.socketpath
156 self.socket = SafeStreamSocket(address, family)
157 self.proto = protoclass(self.socket)
158 self.pkttag = 1
159 self.devices = []
160
161 def _getreply(self):
162 while True:
163 resp, tag, data = self.proto.getpacket()
164 if resp == self.proto.TYPE_RESULT:
165 return tag, data
166 else:
167 raise MuxError("Invalid packet type received: %d"%resp)
168 def _processpacket(self):
169 resp, tag, data = self.proto.getpacket()
170 if resp == self.proto.TYPE_DEVICE_ADD:
171 self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID']))
172 elif resp == self.proto.TYPE_DEVICE_REMOVE:
173 for dev in self.devices:
174 if dev.devid == data['DeviceID']:
175 self.devices.remove(dev)
176 elif resp == self.proto.TYPE_RESULT:
177 raise MuxError("Unexpected result: %d"%resp)
178 else:
179 raise MuxError("Invalid packet type received: %d"%resp)
180 def _exchange(self, req, payload={}):
181 mytag = self.pkttag
182 self.pkttag += 1
183 self.proto.sendpacket(req, mytag, payload)
184 recvtag, data = self._getreply()
185 if recvtag != mytag:
186 raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag))
187 return data['Number']
188
189 def listen(self):
190 ret = self._exchange(self.proto.TYPE_LISTEN)
191 if ret != 0:
192 raise MuxError("Listen failed: error %d"%ret)
193 def process(self, timeout=None):
194 if self.proto.connected:
195 raise MuxError("Socket is connected, cannot process listener events")
196 rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout)
197 if xlo:
198 self.socket.sock.close()
199 raise MuxError("Exception in listener socket")
200 if rlo:
201 self._processpacket()
202 def connect(self, device, port):
203 ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)})
204 if ret != 0:
205 raise MuxError("Connect failed: error %d"%ret)
206 self.proto.connected = True
207 return self.socket.sock
208 def close(self):
209 self.socket.sock.close()
210
211class USBMux(object):
212 def __init__(self, socketpath=None):
213 if socketpath is None:
214 if sys.platform == 'darwin':
215 socketpath = "/var/run/usbmuxd"
216 else:
217 socketpath = "/var/run/usbmuxd"
218 self.socketpath = socketpath
219 self.listener = MuxConnection(socketpath, BinaryProtocol)
220 try:
221 self.listener.listen()
222 self.version = 0
223 self.protoclass = BinaryProtocol
224 except MuxVersionError:
225 self.listener = MuxConnection(socketpath, PlistProtocol)
226 self.listener.listen()
227 self.protoclass = PlistProtocol
228 self.version = 1
229 self.devices = self.listener.devices
230 def process(self, timeout=None):
231 self.listener.process(timeout)
232 def connect(self, device, port):
233 connector = MuxConnection(self.socketpath, self.protoclass)
234 return connector.connect(device, port)
235
236if __name__ == "__main__":
237 mux = USBMux()
238 print "Waiting for devices..."
239 if not mux.devices:
240 mux.process(0.1)
241 while True:
242 print "Devices:"
243 for dev in mux.devices:
244 print dev
245 mux.process()
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 547870e..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,31 +0,0 @@
1AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS)
2AM_LDFLAGS = $(libusb_LIBS) -lpthread -lrt
3
4# Libraries
5
6noinst_LTLIBRARIES = libusbmux.la libsock_stuff.la
7libsock_stuff_la_SOURCES = sock_stuff.c \
8 sock_stuff.h
9
10libusbmux_la_SOURCES = usbmux.c \
11 usbmux.h
12libusbmux_la_CFLAGS = $(AM_CFLAGS)
13libusbmux_la_LDFLAGS = $(AM_LDFLAGS)
14
15lib_LTLIBRARIES = libusbmuxd.la
16libusbmuxd_la_SOURCES = libusbmuxd.c \
17 usbmuxd.h \
18 usbmuxd-proto.h
19libusbmuxd_la_LIBADD = libsock_stuff.la
20
21include_HEADERS = usbmuxd.h \
22 usbmuxd-proto.h
23
24# Programs
25
26sbin_PROGRAMS = usbmuxd
27
28usbmuxd_SOURCES = main.c
29usbmuxd_LDADD = libusbmux.la \
30 libsock_stuff.la
31
diff --git a/src/main.c b/src/main.c
deleted file mode 100644
index 19649b1..0000000
--- a/src/main.c
+++ /dev/null
@@ -1,1447 +0,0 @@
1/*
2 * usbmuxd -- daemon for communication with iPhone/iPod via USB
3 *
4 * Copyright (c) 2009 Nikias Bassen. All Rights Reserved.
5 * Based upon iTunnel source code, Copyright (c) 2008 Jing Su.
6 * http://www.cs.toronto.edu/~jingsu/itunnel/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22#include <stddef.h>
23#include <stdio.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <string.h>
27#include <getopt.h>
28#include <stdarg.h>
29#include <syslog.h>
30#include <fcntl.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <sys/stat.h>
34#include <arpa/inet.h>
35#include <unistd.h>
36#include <signal.h>
37#include <pthread.h>
38#include <stdint.h>
39#include <usb.h>
40#include <pwd.h>
41
42#include "usbmuxd-proto.h"
43#include "sock_stuff.h"
44
45#include "usbmux.h"
46
47#define DEFAULT_TIMEOUT 4000
48#define DEFAULT_CHILDREN_CAPACITY 10
49#define DEBUG_LEVEL 0
50
51#define LOCKFILE "/var/run/usbmuxd.lock"
52
53#define THREAD (unsigned int)pthread_self()
54
55static int quit_flag = 0;
56static int fsock = -1;
57static int verbose = DEBUG_LEVEL;
58static int foreground = 0;
59static int exit_on_no_devices = 0;
60static int drop_privileges = 0;
61static int opt_udev = 0;
62static int opt_exit = 0;
63static int exit_signal = 0;
64
65struct device_info {
66 uint32_t device_id;
67 usbmux_device_t phone;
68 int use_count;
69 pthread_t bulk_reader;
70 pthread_mutex_t mutex;
71 /* mutex for mutual exclusion of calling the usbmux_send function
72 * TODO: I don't know if we need really need this? */
73 pthread_mutex_t writer_mutex;
74};
75
76struct client_data {
77 volatile int dead;
78 int socket;
79 int tag;
80 pthread_t thread;
81 pthread_t handler;
82 pthread_t reader;
83 int reader_quit;
84 int reader_dead;
85 int handler_dead;
86 int connected;
87 usbmux_client_t muxclient;
88 struct device_info *dev;
89};
90
91static struct device_info **devices = NULL;
92static int device_count = 0;
93static pthread_mutex_t usbmux_mutex = PTHREAD_MUTEX_INITIALIZER;
94static pthread_mutex_t usb_mutex = PTHREAD_MUTEX_INITIALIZER;
95
96/**
97 * logs a message to syslog when running as daemon or to stdout/stderr when
98 * running in foreground.
99 * @param prio The logging priority.
100 * @param format The message to be printed.
101 */
102static void logmsg(int prio, const char *format, ...)
103{
104 va_list args;
105 va_start(args, format);
106
107 if (!foreground) {
108 // daemon. log using syslog.
109 vsyslog(prio, format, args);
110 } else {
111 // running in foreground. log to stdout/stderr.
112 char msgbuf[256];
113 FILE *lfp = stdout;
114 switch (prio) {
115 case LOG_EMERG:
116 case LOG_ALERT:
117 case LOG_CRIT:
118 case LOG_ERR:
119 case LOG_WARNING:
120 lfp = stderr;
121 break;
122 default:
123 lfp = stdout;
124 }
125 strcpy(msgbuf, "usbmuxd: ");
126 vsnprintf(msgbuf + 9, 244, format, args);
127 strcat(msgbuf, "\n");
128 fputs(msgbuf, lfp);
129 }
130
131 va_end(args);
132}
133
134#ifdef DEBUG
135/**
136 * for debugging purposes.
137 */
138static void print_buffer(FILE * fp, const char *data, const int length)
139{
140 int i;
141 int j;
142 unsigned char c;
143
144 for (i = 0; i < length; i += 16) {
145 if (verbose >= 4)
146 fprintf(fp, "%04x: ", i);
147 for (j = 0; j < 16; j++) {
148 if (i + j >= length) {
149 if (verbose >= 4)
150 fprintf(fp, " ");
151 continue;
152 }
153 if (verbose >= 4)
154 fprintf(fp, "%02hhx ", *(data + i + j));
155 }
156 if (verbose >= 4)
157 fprintf(fp, " | ");
158 for (j = 0; j < 16; j++) {
159 if (i + j >= length)
160 break;
161 c = *(data + i + j);
162 if ((c < 32) || (c > 127)) {
163 if (verbose >= 4)
164 fprintf(fp, ".");
165 continue;
166 }
167 if (verbose >= 4)
168 fprintf(fp, "%c", c);
169 }
170 if (verbose >= 4)
171 fprintf(fp, "\n");
172 }
173 if (verbose >= 4)
174 fprintf(fp, "\n");
175}
176#endif
177
178/**
179 * Read incoming usbmuxd packet. If the packet is larger than
180 * the size specified by len, the data will be truncated.
181 *
182 * @param fd the file descriptor to read from.
183 * @param data pointer to a buffer to store the read data to.
184 * @param len the length of the data to be read. The buffer
185 * pointed to by data should be at least len bytes in size.
186 *
187 * @return
188 */
189static int usbmuxd_get_request(int fd, void **data, size_t len)
190{
191 uint32_t pktlen;
192 int recv_len;
193
194 if (peek_buf(fd, &pktlen, sizeof(pktlen)) < (int) sizeof(pktlen)) {
195 return -errno;
196 }
197
198 if (len == 0) {
199 // allocate buffer space
200 *data = malloc(pktlen);
201 } else if (len < pktlen) {
202 // target buffer is to small to hold this packet! fix it!
203 if (verbose >= 2)
204 logmsg(LOG_WARNING,
205 "%s: WARNING -- packet (%d) is larger than target buffer (%d)! Truncating.",
206 __func__, pktlen, len);
207 pktlen = len;
208 }
209
210 recv_len = recv_buf(fd, *data, pktlen);
211 if ((recv_len > 0) && ((uint32_t) recv_len < pktlen)) {
212 if (verbose >= 2)
213 logmsg(LOG_WARNING,
214 "%s: Uh-oh, we got less than the packet's size, %d instead of %d...",
215 __func__, recv_len, pktlen);
216 }
217#ifdef DEBUG
218 if (*data && (recv_len > 0) && verbose >= 4) {
219 fprintf(stderr, "%s: received:\n", __func__);
220 print_buffer(stderr, *data, recv_len);
221 }
222#endif
223
224 return recv_len;
225}
226
227/**
228 * Send a usbmuxd result packet with given tag and result_code.
229 *
230 * @param fd the file descriptor to write to.
231 * @param tag the tag value that identifies where this message belongs to.
232 * @param result_code the error value (0 = Success, most likely errno values otherwise)
233 *
234 * @return the return value returned by send_buf (normally the number of bytes sent)
235 */
236static int usbmuxd_send_result(int fd, uint32_t tag, uint32_t result_code)
237{
238 struct usbmuxd_result res;
239 int ret;
240
241 res.header.length = sizeof(res);
242 res.header.reserved = 0;
243 res.header.type = USBMUXD_RESULT;
244 res.header.tag = tag;
245 res.result = result_code;
246
247 if (verbose >= 4)
248 logmsg(LOG_NOTICE, "%s: tag=%d result=%d", __func__,
249 res.header.tag, res.result);
250
251 ret = send_buf(fd, &res, sizeof(res));
252 fsync(fd); // let's get it sent
253 return ret;
254}
255
256/**
257 * this thread reads from the usb connection and writes the
258 * data to the connected client.
259 *
260 * @param arg pointer to a client_data structure.
261 *
262 * @return NULL in any case
263 */
264static void *usbmuxd_client_reader_thread(void *arg)
265{
266 struct client_data *cdata;
267
268 char rbuffer[512];
269 uint32_t rbuffersize = 512;
270 uint32_t rlen;
271 int err;
272 char *cursor;
273 ssize_t len;
274 int result;
275
276 if (!arg) {
277 if (verbose >= 2)
278 logmsg(LOG_ERR, "%s: invalid client_data supplied!", __func__);
279 cdata->reader_dead = 1;
280 return NULL;
281 }
282
283 cdata = (struct client_data *) arg;
284
285 cdata->reader_dead = 0;
286
287 if (verbose >= 3)
288 logmsg(LOG_NOTICE, "%s[%x]: started (device %d:%d, use_count=%d)", __func__, THREAD,
289 cdata->dev->device_id >> 16, cdata->dev->device_id & 0xFFFF, cdata->dev->use_count);
290
291 while (!quit_flag && !cdata->reader_quit) {
292 result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT);
293 if (result <= 0) {
294 if (result < 0) {
295 if (verbose >= 2)
296 logmsg(LOG_ERR, "%s: select error: %s", __func__,
297 strerror(errno));
298 }
299 continue;
300 }
301
302 rlen = 0;
303 err =
304 usbmux_recv_timeout(cdata->muxclient, rbuffer, rbuffersize,
305 &rlen, DEFAULT_TIMEOUT);
306 if (err != 0) {
307 if (verbose >= 2)
308 logmsg(LOG_ERR,
309 "%s[%x]: encountered USB read error: %d",
310 __func__, THREAD, err);
311 break;
312 }
313
314 cursor = rbuffer;
315 while (rlen > 0) {
316 len = send_buf(cdata->socket, cursor, rlen);
317 if (len <= 0) {
318 logmsg(LOG_ERR, "%s: Error: send returned %d", __func__,
319 len);
320 err = 1;
321 break;
322 }
323 // calculate remainder
324 rlen -= len;
325 // advance cursor
326 cursor += len;
327 }
328 if (err != 0) {
329 logmsg(LOG_ERR, "%s: Error when writing to client...",
330 __func__);
331 break;
332 }
333 fsync(cdata->socket);
334 }
335
336 if (verbose >= 3)
337 logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD);
338
339 cdata->reader_dead = 1;
340
341 return NULL;
342}
343
344/**
345 * This function handles the connecting procedure to a previously
346 * set up usbmux client.
347 * Sends a usbmuxd result packet denoting success or failure.
348 * A successful result is mandatory for later communication.
349 *
350 * @param cdata pointer to a previously initialized client_data structure
351 *
352 * @return
353 */
354static int usbmuxd_handleConnectResult(struct client_data *cdata)
355{
356 int result;
357 char buffer[512];
358 char err_type[64];
359 int err_code;
360 ssize_t maxlen = 512;
361 uint32_t rlen;
362 int err;
363
364 if (!cdata) {
365 if (verbose >= 2)
366 logmsg(LOG_ERR, "%s: Invalid client_data provided!", __func__);
367 return -EINVAL;
368 }
369
370 result = check_fd(cdata->socket, FD_WRITE, DEFAULT_TIMEOUT);
371 if (result <= 0) {
372 if (result < 0) {
373 if (verbose >= 2)
374 logmsg(LOG_ERR, "%s: select error: %s", __func__,
375 strerror(errno));
376 return result;
377 }
378 } else {
379 result = 0;
380 err =
381 usbmux_recv_timeout(cdata->muxclient, buffer, maxlen, &rlen,
382 100);
383 if (err < 0) {
384 if (verbose >= 2)
385 logmsg(LOG_ERR, "%s: encountered USB read error: %d",
386 __func__, err);
387 usbmuxd_send_result(cdata->socket, cdata->tag, -err);
388 return err;
389 } else {
390 if (rlen > 0) {
391 if ((buffer[0] == 1) && (rlen > 20)
392 && !memcmp(buffer + 1, "handleConnectResult:", 20)) {
393 // hm... we got an error message!
394 buffer[rlen] = 0;
395 if (verbose >= 1)
396 logmsg(LOG_ERR, "%s: %s\n", __func__, buffer + 22);
397
398 if (sscanf
399 (buffer + 22, "%s - %d\n", err_type, &err_code)
400 == 2) {
401 usbmuxd_send_result(cdata->socket, cdata->tag,
402 err_code);
403 return -err_code;
404 } else {
405 usbmuxd_send_result(cdata->socket, cdata->tag,
406 ENODATA);
407 return -ENODATA;
408 }
409 } else {
410 // send success result
411 usbmuxd_send_result(cdata->socket, cdata->tag, 0);
412 // and the server greeting message
413 send_buf(cdata->socket, buffer, rlen);
414 }
415 } else {
416 // no server greeting? this seems to be ok. send success.
417 usbmuxd_send_result(cdata->socket, cdata->tag, 0);
418 }
419 }
420 //fsync(cdata->socket);
421 }
422 return result;
423}
424
425/**
426 * This thread handles the communication between the connected iPhone/iPod
427 * and the client that created the connection.
428 */
429static void *usbmuxd_client_handler_thread(void *arg)
430{
431 struct client_data *cdata;
432 int result;
433 char *cursor;
434 char buffer[32740];
435 ssize_t len;
436 ssize_t maxlen = sizeof(buffer);
437 uint32_t wlen;
438 int err;
439
440 if (!arg) {
441 if (verbose >= 2)
442 logmsg(LOG_ERR, "%s: invalid client_data provided!", __func__);
443 return NULL;
444 }
445
446 cdata = (struct client_data *) arg;
447
448 if (verbose >= 3)
449 logmsg(LOG_NOTICE, "%s[%x]: started (device %d:%d, use_count=%d)", __func__, THREAD,
450 cdata->dev->device_id >> 16, cdata->dev->device_id & 0xFFFF, cdata->dev->use_count);
451
452 if (usbmuxd_handleConnectResult(cdata)) {
453 if (verbose >= 3)
454 logmsg(LOG_ERR, "handleConnectResult: Error");
455 goto leave;
456 } else {
457 if (verbose >= 3)
458 logmsg(LOG_NOTICE, "handleConnectResult: Success");
459 }
460
461 // starting mux reader thread
462 cdata->reader_quit = 0;
463 cdata->reader_dead = 0;
464 if (pthread_create
465 (&cdata->reader, NULL, usbmuxd_client_reader_thread, cdata) != 0) {
466 if (verbose >= 2)
467 logmsg(LOG_ERR, "%s: could not start client_reader thread",
468 __func__);
469 cdata->reader = 0;
470 }
471
472 while (!quit_flag && !cdata->reader_dead) {
473 result = check_fd(cdata->socket, FD_READ, DEFAULT_TIMEOUT);
474 if (result <= 0) {
475 if (result < 0) {
476 if (verbose >= 3)
477 logmsg(LOG_ERR, "%s[%x]: Error: checkfd: %s", __func__, THREAD, strerror(errno));
478 }
479 continue;
480 }
481 // check_fd told us there's data available, so read from client
482 // and push to USB device.
483 len = recv(cdata->socket, buffer, maxlen, 0);
484 if (len == 0) {
485 break;
486 }
487 if (len < 0) {
488 if (verbose >= 2)
489 logmsg(LOG_ERR, "%s[%x]: Error: recv: %s", __func__, THREAD, strerror(errno));
490 break;
491 }
492
493 cursor = buffer;
494
495 pthread_mutex_lock(&cdata->dev->writer_mutex);
496 do {
497 wlen = 0;
498 err = usbmux_send(cdata->muxclient, cursor, len, &wlen);
499 if (err == -ETIMEDOUT) {
500 // some kind of timeout... just be patient and retry.
501 } else if (err < 0) {
502 if (verbose >= 2)
503 logmsg(LOG_ERR, "%s[%x]: USB write error: %d", __func__, THREAD, err);
504 len = -1;
505 break;
506 }
507 // calculate remainder.
508 len -= wlen;
509 // advance cursor appropiately.
510 cursor += wlen;
511 }
512 while ((len > 0) && !quit_flag);
513 pthread_mutex_unlock(&cdata->dev->writer_mutex);
514 if (len < 0) {
515 break;
516 }
517 }
518
519 leave:
520 // cleanup
521 if (verbose >= 3)
522 logmsg(LOG_NOTICE, "%s[%x]: terminating", __func__, THREAD);
523 if (cdata->reader != 0) {
524 cdata->reader_quit = 1;
525 pthread_join(cdata->reader, NULL);
526 }
527
528 cdata->handler_dead = 1;
529
530 if (verbose >= 3)
531 logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD);
532 return NULL;
533}
534
535/**
536 * Thread performing usb_bulk_read from the connected device.
537 * One thread per device. Lives as long as the device is in use.
538 */
539static void *usbmuxd_bulk_reader_thread(void *arg)
540{
541 struct device_info *cur_dev;
542 int err;
543
544 if (!arg) {
545 if (verbose >= 2)
546 logmsg(LOG_ERR, "%s: Invalid client_data provided", __func__);
547 return NULL;
548 }
549
550 cur_dev = (struct device_info *) arg;
551
552 if (verbose >= 3)
553 logmsg(LOG_NOTICE, "%s: started", __func__);
554
555 while (!quit_flag && cur_dev) {
556
557 pthread_mutex_lock(&cur_dev->mutex);
558 if (cur_dev->use_count <= 0) {
559 pthread_mutex_unlock(&cur_dev->mutex);
560 break;
561 }
562 pthread_mutex_unlock(&cur_dev->mutex);
563
564 if ((err = usbmux_pullbulk(cur_dev->phone)) < 0) {
565 if (verbose >= 1)
566 logmsg(LOG_ERR, "%s: error %d when reading from device",
567 __func__, err);
568 break;
569 }
570 }
571
572 if (verbose >= 3)
573 logmsg(LOG_NOTICE, "%s: terminated", __func__);
574
575 return NULL;
576}
577
578/**
579 * This thread is started when a new connection is accepted.
580 * It performs the handshake, then waits for the connect packet and
581 * on success it starts the usbmuxd_client_handler thread.
582 */
583static void *usbmuxd_client_init_thread(void *arg)
584{
585 struct client_data *cdata;
586 struct usbmuxd_scan_request *s_req;
587 struct usbmuxd_device_info_record dev_info_rec;
588 struct usbmuxd_connect_request *c_req = NULL;
589
590 struct usb_bus *bus;
591 struct usb_device *dev;
592
593 int recv_len;
594 int found = 0;
595 int res;
596 int i;
597
598 usbmux_device_t phone = NULL;
599 struct device_info *cur_dev = NULL;
600
601 if (!arg) {
602 if (verbose >= 1)
603 logmsg(LOG_ERR, "%s[%x]: invalid client_data provided!",
604 __func__, THREAD);
605 return NULL;
606 }
607
608 cdata = (struct client_data *) arg;
609 cdata->dead = 0;
610
611 if (verbose >= 3)
612 logmsg(LOG_NOTICE, "%s[%x]: started (fd=%d)", __func__, THREAD,
613 cdata->socket);
614
615 if ((recv_len =
616 usbmuxd_get_request(cdata->socket, (void **) &s_req, 0)) <= 0) {
617 if (verbose >= 2)
618 logmsg(LOG_ERR, "%s[%x]: No scan packet received, error %s",
619 __func__, THREAD, strerror(errno));
620 goto leave;
621 }
622
623 if ((recv_len == sizeof(struct usbmuxd_scan_request))
624 && (s_req->header.length == sizeof(struct usbmuxd_scan_request))
625 && (s_req->header.reserved == 0)
626 && (s_req->header.type == USBMUXD_SCAN)) {
627 // send success response
628 if (verbose >= 3)
629 logmsg(LOG_NOTICE, "%s[%x]: Got scan packet!", __func__,
630 THREAD);
631 usbmuxd_send_result(cdata->socket, s_req->header.tag, 0);
632 } else if ((recv_len == sizeof(struct usbmuxd_connect_request))
633 && (s_req->header.type == USBMUXD_CONNECT)) {
634 c_req = (struct usbmuxd_connect_request *) s_req;
635 s_req = NULL;
636 goto connect;
637 } else {
638 // send error response and exit
639 if (verbose >= 2)
640 logmsg(LOG_ERR, "%s[%x]: Invalid scan packet received.",
641 __func__, THREAD);
642 // TODO is this required?!
643 usbmuxd_send_result(cdata->socket, s_req->header.tag, EINVAL);
644 goto leave;
645 }
646
647 pthread_mutex_lock(&usb_mutex);
648 // gather data about all iPhones/iPods attached
649
650 if (verbose >= 5)
651 logmsg(LOG_DEBUG, "%s[%x]: usb init", __func__, THREAD);
652 usb_init();
653 if (verbose >= 5)
654 logmsg(LOG_DEBUG, "%s[%x]: usb find busses", __func__, THREAD);
655 usb_find_busses();
656 if (verbose >= 5)
657 logmsg(LOG_DEBUG, "%s[%x]: usb find devices", __func__, THREAD);
658 usb_find_devices();
659
660 if (verbose >= 2)
661 logmsg(LOG_NOTICE, "%s[%x]: Looking for attached devices...",
662 __func__, THREAD);
663
664 for (bus = usb_get_busses(); bus; bus = bus->next) {
665 for (dev = bus->devices; dev; dev = dev->next) {
666 if (dev->descriptor.idVendor == 0x05ac
667 && dev->descriptor.idProduct >= 0x1290
668 && dev->descriptor.idProduct <= 0x1294) {
669 if (verbose >= 1)
670 logmsg(LOG_NOTICE,
671 "%s[%x]: Found device on bus %s, id %s",
672 __func__, THREAD, bus->dirname, dev->filename);
673 found++;
674
675 // construct packet
676 memset(&dev_info_rec, 0, sizeof(dev_info_rec));
677 dev_info_rec.header.length = sizeof(dev_info_rec);
678 dev_info_rec.header.type = USBMUXD_DEVICE_INFO;
679 uint32_t dev_id =
680 strtol(dev->filename, NULL, 10)
681 + (strtoul(bus->dirname, NULL, 10) << 16);
682 dev_info_rec.device.device_id = dev_id;
683 dev_info_rec.device.product_id = dev->descriptor.idProduct;
684 if (dev->descriptor.iSerialNumber) {
685 usb_dev_handle *udev;
686 //pthread_mutex_lock(&usbmux_mutex);
687 udev = usb_open(dev);
688 if (udev) {
689 usb_get_string_simple(udev,
690 dev->descriptor.iSerialNumber,
691 dev_info_rec.device.serial_number,
692 sizeof(dev_info_rec.device.serial_number) + 1);
693 usb_close(udev);
694 } else {
695 logmsg(LOG_ERR, "%s[%x]: Error: usb_open(): %s\n", __func__, THREAD, usb_strerror());
696 }
697 //pthread_mutex_unlock(&usbmux_mutex);
698 }
699#ifdef DEBUG
700 if (verbose >= 4)
701 print_buffer(stderr, (char *) &dev_info_rec,
702 sizeof(dev_info_rec));
703#endif
704
705 // send it
706 if (send_buf
707 (cdata->socket, &dev_info_rec,
708 sizeof(dev_info_rec)) <= 0) {
709 if (verbose >= 3)
710 logmsg(LOG_ERR,
711 "%s[%x]: Error: Could not send device info: %s",
712 __func__, THREAD, strerror(errno));
713 found--;
714 }
715 }
716 }
717 }
718 pthread_mutex_unlock(&usb_mutex);
719
720 if (found <= 0) {
721 if (verbose >= 1)
722 logmsg(LOG_NOTICE,
723 "%s[%x]: No attached iPhone/iPod devices found.",
724 __func__, THREAD);
725 usbmuxd_send_result(cdata->socket, s_req->header.tag, -ENODEV);
726 goto leave;
727 }
728
729 goto leave;
730
731/* if (verbose >= 3)
732 logmsg(LOG_NOTICE, "%s[%x]: Waiting for connect request", __func__,
733 THREAD);
734
735 // now wait for connect request
736 //memset(&c_req, 0, sizeof(c_req));
737 if ((recv_len =
738 usbmuxd_get_request(cdata->socket, (void **) &c_req, 0)) <= 0) {
739 if (verbose >= 3)
740 logmsg(LOG_NOTICE,
741 "%s[%x]: Did not receive any connect request.",
742 __func__, THREAD);
743 goto leave;
744 }*/
745
746 connect:
747
748 if (c_req->header.type != USBMUXD_CONNECT) {
749 if (verbose >= 2)
750 logmsg(LOG_ERR,
751 "%s[%x]: Unexpected packet of type %d received.",
752 __func__, THREAD, c_req->header.type);
753 goto leave;
754 }
755
756 if (verbose >= 3)
757 logmsg(LOG_NOTICE,
758 "%s[%x]: Setting up connection to usb device %d:%d on port %d",
759 __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF, ntohs(c_req->tcp_dport));
760
761 // find the device, and open usb connection
762 pthread_mutex_lock(&usbmux_mutex);
763 phone = NULL;
764 cur_dev = NULL;
765 // first check if we already have an open connection
766 if (devices) {
767 for (i = 0; i < device_count; i++) {
768 if (devices[i]) {
769 if (devices[i]->device_id == c_req->device_id) {
770 devices[i]->use_count++;
771 cur_dev = devices[i];
772 phone = cur_dev->phone;
773 break;
774 }
775 }
776 }
777 }
778 if (!phone) {
779 // if not found, make a new connection
780 if (verbose >= 2)
781 logmsg(LOG_NOTICE,
782 "%s[%x]: creating new usb connection, device %d:%d",
783 __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF);
784
785 pthread_mutex_lock(&usb_mutex);
786 if (usbmux_get_specific_device(c_req->device_id >> 16, c_req->device_id & 0xFFFF, &phone) < 0) {
787 pthread_mutex_unlock(&usb_mutex);
788 pthread_mutex_unlock(&usbmux_mutex);
789 if (verbose >= 1)
790 logmsg(LOG_ERR, "%s[%x]: device %d:%d could not be opened",
791 __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF);
792 usbmuxd_send_result(cdata->socket, c_req->header.tag, ENODEV);
793 goto leave;
794 }
795 pthread_mutex_unlock(&usb_mutex);
796
797 // create device object
798 if (verbose >= 3)
799 logmsg(LOG_DEBUG, "%s[%x]: add to device list", __func__,
800 THREAD);
801 cur_dev =
802 (struct device_info *) malloc(sizeof(struct device_info));
803 memset(cur_dev, 0, sizeof(struct device_info));
804 cur_dev->use_count = 1;
805 cur_dev->device_id = c_req->device_id;
806 cur_dev->phone = phone;
807 cur_dev->bulk_reader = 0;
808 pthread_mutex_init(&cur_dev->mutex, NULL);
809 pthread_mutex_init(&cur_dev->writer_mutex, NULL);
810
811 if (verbose >= 3)
812 logmsg(LOG_DEBUG, "%s[%x]: device_count = %d", __func__,
813 THREAD, device_count);
814
815 // add to list of devices
816 devices =
817 (struct device_info **) realloc(devices,
818 sizeof(struct device_info *) *
819 (device_count + 1));
820 if (devices) {
821 devices[device_count] = cur_dev;
822 device_count++;
823 }
824 } else {
825 if (verbose >= 2)
826 logmsg(LOG_NOTICE,
827 "%s[%x]: reusing usb connection, device %d:%d",
828 __func__, THREAD, c_req->device_id >> 16, c_req->device_id & 0xFFFF);
829 }
830 pthread_mutex_unlock(&usbmux_mutex);
831
832 // setup connection to iPhone/iPod
833 pthread_mutex_lock(&cur_dev->writer_mutex);
834 res =
835 usbmux_new_client(cur_dev->phone, 0, ntohs(c_req->tcp_dport),
836 &(cdata->muxclient));
837 pthread_mutex_unlock(&cur_dev->writer_mutex);
838
839 if (res != 0) {
840 usbmuxd_send_result(cdata->socket, c_req->header.tag, res);
841 if (verbose >= 1)
842 logmsg(LOG_ERR,
843 "%s[%x]: mux_new_client returned %d, aborting.",
844 __func__, THREAD, res);
845 goto leave;
846 }
847 // start bulk reader thread (once per device)
848 pthread_mutex_lock(&cur_dev->mutex);
849 if (cur_dev->bulk_reader == 0) {
850 pthread_create(&cur_dev->bulk_reader, NULL,
851 usbmuxd_bulk_reader_thread, cur_dev);
852 }
853 pthread_mutex_unlock(&cur_dev->mutex);
854
855 // wait for the initial handshake (SYN->SYN+ACK->ACK) to complete)
856 struct timespec ts;
857 ts.tv_sec = 0;
858 ts.tv_nsec = 100000000;
859
860 i = 0;
861 while (i < 10000) {
862 if (usbmux_is_connected(cdata->muxclient)) {
863 break;
864 }
865 nanosleep(&ts, NULL);
866 i+=100;
867 }
868 if (!usbmux_is_connected(cdata->muxclient)) {
869 usbmuxd_send_result(cdata->socket, c_req->header.tag, -ENOTCONN);
870 goto leave;
871 }
872
873 // start connection handler thread
874 cdata->handler_dead = 0;
875 cdata->tag = c_req->header.tag;
876 cdata->dev = cur_dev;
877 if (pthread_create
878 (&cdata->handler, NULL, usbmuxd_client_handler_thread, cdata) != 0)
879 {
880 if (verbose >= 1)
881 logmsg(LOG_ERR,
882 "%s[%x]: could not create usbmuxd_client_handler_thread!",
883 __func__, THREAD);
884 cdata->handler = 0;
885 goto leave;
886 }
887 // wait for handler thread to finish its work
888 if (cdata->handler != 0) {
889 pthread_join(cdata->handler, NULL);
890 }
891
892 if (verbose >= 2)
893 logmsg(LOG_NOTICE, "%s[%x]: closing connection", __func__, THREAD);
894
895 // time to clean up
896 if (cdata && cdata->muxclient) { // should be non-NULL
897 pthread_mutex_lock(&cdata->dev->writer_mutex);
898 usbmux_free_client(cdata->muxclient);
899 pthread_mutex_unlock(&cdata->dev->writer_mutex);
900 }
901
902 leave:
903 if (verbose >= 3)
904 logmsg(LOG_NOTICE, "%s[%x]: terminating", __func__, THREAD);
905
906 if (s_req) {
907 free(s_req);
908 }
909 if (c_req) {
910 free(c_req);
911 }
912 // this has to be freed only if it's not in use anymore as it closes
913 // the USB connection
914 pthread_mutex_lock(&usbmux_mutex);
915 if (cur_dev) {
916 pthread_mutex_lock(&cur_dev->mutex);
917 if (cur_dev->use_count > 1) {
918 if (verbose >= 2)
919 logmsg(LOG_NOTICE,
920 "%s[%x]: decreasing device use count (from %d to %d)",
921 __func__, THREAD, cur_dev->use_count,
922 cur_dev->use_count - 1);
923 cur_dev->use_count--;
924 pthread_mutex_unlock(&cur_dev->mutex);
925 } else {
926 if (verbose >= 2)
927 logmsg(LOG_NOTICE,
928 "%s[%x]: last client disconnected, cleaning up",
929 __func__, THREAD);
930 cur_dev->use_count = 0;
931 pthread_mutex_unlock(&cur_dev->mutex);
932 if (cur_dev->bulk_reader != 0) {
933 if (verbose >= 3)
934 logmsg(LOG_NOTICE, "%s[%x]: joining bulk_reader...",
935 __func__, THREAD);
936 pthread_join(cur_dev->bulk_reader, NULL);
937 }
938 pthread_mutex_lock(&usb_mutex);
939 usbmux_free_device(cur_dev->phone);
940 pthread_mutex_unlock(&usb_mutex);
941 pthread_mutex_destroy(&cur_dev->writer_mutex);
942 pthread_mutex_destroy(&cur_dev->mutex);
943 free(cur_dev);
944 cur_dev = NULL;
945 if (device_count > 1) {
946 struct device_info **newlist;
947 int j = 0;
948
949 newlist =
950 (struct device_info **)
951 malloc(sizeof(struct device_info *)
952 * device_count - 1);
953 for (i = 0; i < device_count; i++) {
954 if (devices[i] != NULL) {
955 newlist[j++] = devices[i];
956 }
957 }
958 free(devices);
959 devices = newlist;
960 device_count--;
961 } else {
962 free(devices);
963 devices = NULL;
964 device_count = 0;
965 }
966 }
967 }
968 pthread_mutex_unlock(&usbmux_mutex);
969
970 cdata->dead = 1;
971 close(cdata->socket);
972
973 if (verbose >= 3)
974 logmsg(LOG_NOTICE, "%s[%x]: terminated", __func__, THREAD);
975
976 return NULL;
977}
978
979/**
980 * make this program run detached from the current console
981 */
982static int daemonize()
983{
984 pid_t pid;
985 pid_t sid;
986
987 // already a daemon
988 if (getppid() == 1)
989 return 0;
990
991 pid = fork();
992 if (pid < 0) {
993 exit(EXIT_FAILURE);
994 }
995
996 if (pid > 0) {
997 // exit parent process
998 exit(EXIT_SUCCESS);
999 }
1000 // At this point we are executing as the child process
1001
1002 // Change the file mode mask
1003 umask(0);
1004
1005 // Create a new SID for the child process
1006 sid = setsid();
1007 if (sid < 0) {
1008 return -1;
1009 }
1010 // Change the current working directory.
1011 if ((chdir("/")) < 0) {
1012 return -2;
1013 }
1014 // Redirect standard files to /dev/null
1015 if (!freopen("/dev/null", "r", stdin)) {
1016 logmsg(LOG_ERR, "ERROR: redirection of stdin failed.\n");
1017 }
1018 if (!freopen("/dev/null", "w", stdout)) {
1019 logmsg(LOG_ERR, "ERROR: redirection of stdout failed.\n");
1020 }
1021 if (!freopen("/dev/null", "w", stderr)) {
1022 logmsg(LOG_ERR, "ERROR: redirection of stderr failed.\n");
1023 }
1024
1025 return 0;
1026}
1027
1028static void usage()
1029{
1030 printf("usage: usbmuxd [options]\n");
1031 printf("\t-h|--help print this message\n");
1032 printf("\t-v|--verbose be verbose (use twice or more to increase)\n");
1033 printf("\t-f|--foreground do not daemonize\n");
1034 printf("\t-e|--exit-on-no-devices exit if no device is attached\n");
1035 printf("\t-d|--drop-privileges drop privileges after startup\n");
1036 printf("\t-u|--udev udev operation mode\n");
1037 printf("\t-x|--exit tell a running instance to exit\n");
1038 printf("\t-X|--force-exit tell a running instance to exit, even if\n");
1039 printf("\t there are still devices connected\n");
1040 printf("\n");
1041}
1042
1043static void parse_opts(int argc, char **argv)
1044{
1045 static struct option longopts[] = {
1046 {"help", 0, NULL, 'h'},
1047 {"foreground", 0, NULL, 'f'},
1048 {"verbose", 0, NULL, 'v'},
1049 {"exit-on-no-devices", 0, NULL, 'e'},
1050 {"drop-privileges", 0, NULL, 'd'},
1051 {"udev", 0, NULL, 'u'},
1052 {"exit", 0, NULL, 'x'},
1053 {"force-exit", 0, NULL, 'X'},
1054 {NULL, 0, NULL, 0}
1055 };
1056 int c;
1057
1058 while (1) {
1059 c = getopt_long(argc, argv, "hfveduxX", longopts, (int *) 0);
1060 if (c == -1) {
1061 break;
1062 }
1063
1064 switch (c) {
1065 case 'h':
1066 usage();
1067 exit(0);
1068 case 'f':
1069 foreground = 1;
1070 break;
1071 case 'v':
1072 sock_stuff_set_verbose(++verbose);
1073 break;
1074 case 'e':
1075 exit_on_no_devices = 1;
1076 break;
1077 case 'd':
1078 drop_privileges = 1;
1079 break;
1080 case 'u':
1081 opt_udev = 1;
1082 break;
1083 case 'x':
1084 opt_exit = 1;
1085 exit_signal = SIGQUIT;
1086 break;
1087 case 'X':
1088 opt_exit = 1;
1089 exit_signal = SIGTERM;
1090 break;
1091 default:
1092 usage();
1093 exit(2);
1094 }
1095 }
1096 if (opt_udev)
1097 foreground = 0;
1098}
1099
1100/**
1101 * checks for attached devices
1102 *
1103 * @return number of devices found
1104 */
1105static int devices_attached()
1106{
1107 struct usb_bus *bus;
1108 struct usb_device *dev;
1109 int res = 0;
1110
1111 usb_init();
1112 usb_find_busses();
1113 usb_find_devices();
1114
1115 for (bus = usb_get_busses(); bus; bus = bus->next) {
1116 for (dev = bus->devices; dev; dev = dev->next) {
1117 if (dev->descriptor.idVendor == 0x05ac
1118 && dev->descriptor.idProduct >= 0x1290
1119 && dev->descriptor.idProduct <= 0x1294) {
1120 res++;
1121 }
1122 }
1123 }
1124
1125 return res;
1126}
1127
1128/**
1129 * signal handler function for cleaning up properly
1130 */
1131static void handle_signal(int sig)
1132{
1133 if (sig == SIGTERM) {
1134 quit_flag = 1;
1135 } else {
1136 if (sig == SIGINT) {
1137 if (verbose >= 1)
1138 fprintf(stderr, "CTRL+C pressed\n");
1139 }
1140
1141 if (verbose >= 1)
1142 fprintf(stderr, "Checking if we can terminate (no more devices attached)...\n");
1143
1144 if (devices_attached() > 0) {
1145 // we can't quit, there are still devices attached.
1146 if (verbose >= 1)
1147 fprintf(stderr, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.\n");
1148 } else {
1149 // it's safe to quit
1150 quit_flag = 1;
1151 }
1152 }
1153}
1154
1155/**
1156 * main function. Initializes all stuff and then loops waiting in accept.
1157 */
1158int main(int argc, char **argv)
1159{
1160 struct sockaddr_un c_addr;
1161 socklen_t len = sizeof(struct sockaddr_un);
1162 struct client_data *cdata = NULL;
1163 struct client_data **children = NULL;
1164 int children_capacity = DEFAULT_CHILDREN_CAPACITY;
1165 int i;
1166 int result = 0;
1167 int exit_val = 0;
1168 int cnt = 0;
1169 FILE *lfd = NULL;
1170 struct flock lock;
1171
1172 parse_opts(argc, argv);
1173
1174 argc -= optind;
1175 argv += optind;
1176
1177 if (!foreground) {
1178 openlog("usbmuxd", LOG_PID, 0);
1179 }
1180
1181 if (verbose >= 1)
1182 logmsg(LOG_NOTICE, "starting");
1183
1184 // signal(SIGHUP, reload_conf); // none yet
1185 signal(SIGINT, handle_signal);
1186 signal(SIGQUIT, handle_signal);
1187 signal(SIGTERM, handle_signal);
1188 signal(SIGPIPE, SIG_IGN);
1189
1190 // check for other running instance
1191 lfd = fopen(LOCKFILE, "r");
1192 if (lfd) {
1193 lock.l_type = 0;
1194 lock.l_whence = SEEK_SET;
1195 lock.l_start = 0;
1196 lock.l_len = 0;
1197 fcntl(fileno(lfd), F_GETLK, &lock);
1198 fclose(lfd);
1199 if (lock.l_type != F_UNLCK) {
1200 if (opt_exit) {
1201 if (lock.l_pid && !kill(lock.l_pid, 0)) {
1202 logmsg(LOG_NOTICE, "sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
1203 if (kill(lock.l_pid, exit_signal) < 0) {
1204 logmsg(LOG_ERR, "Error: could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
1205 }
1206 exit_val = 0;
1207 goto terminate;
1208 } else {
1209 logmsg(LOG_ERR, "Error: could not determine pid of the other running instance!");
1210 exit_val = -1;
1211 goto terminate;
1212 }
1213 } else {
1214 logmsg(LOG_NOTICE,
1215 "another instance is already running (pid %d). exiting.", lock.l_pid);
1216 if (!opt_udev) {
1217 exit_val = -1;
1218 }
1219 goto terminate;
1220 }
1221 }
1222 }
1223
1224 if (opt_exit) {
1225 logmsg(LOG_NOTICE, "no running instance found, none killed. exiting.");
1226 goto terminate;
1227 }
1228
1229 if (exit_on_no_devices) {
1230 if (devices_attached() <= 0) {
1231 logmsg(LOG_NOTICE, "no devices attached. exiting.");
1232 return 0;
1233 }
1234 }
1235
1236 fsock = create_unix_socket(USBMUXD_SOCKET_FILE);
1237 if (fsock < 0) {
1238 logmsg(LOG_ERR, "Could not create socket, exiting");
1239 if (!foreground) {
1240 closelog();
1241 }
1242 return -1;
1243 }
1244
1245 chmod(USBMUXD_SOCKET_FILE, 0666);
1246
1247 if (verbose >= 2)
1248 usb_set_debug(verbose);
1249
1250 if (verbose >= 4)
1251 usbmux_set_debug(1);
1252
1253 if (!foreground) {
1254 if (daemonize() < 0) {
1255 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
1256 syslog(LOG_ERR, "FATAL: Could not daemonize!");
1257 closelog();
1258 exit(EXIT_FAILURE);
1259 }
1260 }
1261 // now open the lockfile and place the lock
1262 lfd = fopen(LOCKFILE, "w");
1263 if (lfd) {
1264 lock.l_type = F_WRLCK;
1265 lock.l_whence = SEEK_SET;
1266 lock.l_start = 0;
1267 lock.l_len = 0;
1268 if (fcntl(fileno(lfd), F_SETLK, &lock) == -1) {
1269 logmsg(LOG_ERR, "ERROR: lockfile locking failed!");
1270 exit(EXIT_FAILURE);
1271 }
1272 }
1273 // drop elevated privileges
1274 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
1275 struct passwd *pw = getpwnam("nobody");
1276 if (pw) {
1277 setuid(pw->pw_uid);
1278 } else {
1279 logmsg(LOG_ERR,
1280 "ERROR: Dropping privileges failed, check if user 'nobody' exists! Will now terminate.");
1281 exit(EXIT_FAILURE);
1282 }
1283
1284 // security check
1285 if (setuid(0) != -1) {
1286 logmsg(LOG_ERR, "ERROR: Failed to drop privileges properly!");
1287 exit(EXIT_FAILURE);
1288 }
1289 if (verbose >= 2)
1290 logmsg(LOG_NOTICE, "Successfully dropped privileges");
1291 }
1292 // Reserve space for 10 clients which should be enough. If not, the
1293 // buffer gets enlarged later.
1294 children =
1295 (struct client_data **) malloc(sizeof(struct client_data *) *
1296 children_capacity);
1297 if (!children) {
1298 logmsg(LOG_ERR,
1299 "Out of memory when allocating memory for child threads. Terminating.");
1300 if (!foreground) {
1301 closelog();
1302 }
1303 exit(EXIT_FAILURE);
1304 }
1305 memset(children, 0, sizeof(struct client_data *) * children_capacity);
1306
1307 if (verbose >= 2)
1308 logmsg(LOG_NOTICE, "waiting for connection");
1309 while (!quit_flag) {
1310 // Check the file descriptor before accepting a connection.
1311 // If no connection attempt is made, just repeat...
1312 result = check_fd(fsock, FD_READ, 1000);
1313 if (result <= 0) {
1314 if (result == 0) {
1315 // cleanup
1316 for (i = 0; i < children_capacity; i++) {
1317 if (children[i]) {
1318 if (children[i]->dead != 0) {
1319 pthread_join(children[i]->thread, NULL);
1320 if (verbose >= 3)
1321 logmsg(LOG_NOTICE,
1322 "reclaimed client thread (fd=%d)",
1323 children[i]->socket);
1324 free(children[i]);
1325 children[i] = NULL;
1326 cnt++;
1327 } else {
1328 cnt = 0;
1329 }
1330 } else {
1331 cnt++;
1332 }
1333 }
1334
1335 if ((children_capacity > DEFAULT_CHILDREN_CAPACITY)
1336 && ((children_capacity - cnt) <=
1337 DEFAULT_CHILDREN_CAPACITY)) {
1338 children_capacity = DEFAULT_CHILDREN_CAPACITY;
1339 children =
1340 realloc(children,
1341 sizeof(struct client_data *) *
1342 children_capacity);
1343 }
1344 continue;
1345 } else {
1346 if (verbose >= 3)
1347 logmsg(LOG_ERR, "usbmuxd: select error: %s",
1348 strerror(errno));
1349 continue;
1350 }
1351 }
1352
1353 cdata = (struct client_data *) malloc(sizeof(struct client_data));
1354 memset(cdata, 0, sizeof(struct client_data));
1355 if (!cdata) {
1356 quit_flag = 1;
1357 logmsg(LOG_ERR, "Error: Out of memory! Terminating.");
1358 break;
1359 }
1360
1361 cdata->socket = accept(fsock, (struct sockaddr *) &c_addr, &len);
1362 if (cdata->socket < 0) {
1363 free(cdata);
1364 if (errno == EINTR) {
1365 continue;
1366 } else {
1367 if (verbose >= 3)
1368 logmsg(LOG_ERR, "Error in accept: %s",
1369 strerror(errno));
1370 continue;
1371 }
1372 }
1373
1374 if (verbose >= 1)
1375 logmsg(LOG_NOTICE, "new client connected (fd=%d)",
1376 cdata->socket);
1377
1378 // create client thread:
1379 if (pthread_create
1380 (&cdata->thread, NULL, usbmuxd_client_init_thread, cdata) == 0)
1381 {
1382 for (i = 0; i < children_capacity; i++) {
1383 if (children[i] == NULL)
1384 break;
1385 }
1386 if (i == children_capacity) {
1387 // enlarge buffer
1388 children_capacity++;
1389 children =
1390 realloc(children,
1391 sizeof(struct client_data *) *
1392 children_capacity);
1393 if (!children) {
1394 logmsg(LOG_ERR,
1395 "Out of memory when enlarging child thread buffer");
1396 }
1397 }
1398 children[i] = cdata;
1399 } else {
1400 logmsg(LOG_ERR, "Failed to create client_init_thread.");
1401 close(cdata->socket);
1402 free(cdata);
1403 cdata = NULL;
1404 }
1405 }
1406
1407 if (verbose >= 3)
1408 logmsg(LOG_NOTICE, "terminating");
1409
1410 // preparing for shutdown: wait for child threads to terminate (if any)
1411 if (verbose >= 2)
1412 logmsg(LOG_NOTICE, "waiting for child threads to terminate...");
1413 for (i = 0; i < children_capacity; i++) {
1414 if (children[i] != NULL) {
1415 pthread_join(children[i]->thread, NULL);
1416 free(children[i]);
1417 children[i] = NULL;
1418 }
1419 }
1420
1421 // delete the children set.
1422 free(children);
1423 children = NULL;
1424
1425
1426 if (fsock >= 0) {
1427 close(fsock);
1428 }
1429
1430 unlink(USBMUXD_SOCKET_FILE);
1431
1432 // unlock lock file and close it.
1433 if (lfd) {
1434 lock.l_type = F_UNLCK;
1435 fcntl(fileno(lfd), F_SETLK, &lock);
1436 fclose(lfd);
1437 }
1438
1439terminate:
1440 if (verbose >= 1)
1441 logmsg(LOG_NOTICE, "terminated");
1442 if (!foreground) {
1443 closelog();
1444 }
1445
1446 return 0;
1447}
diff --git a/src/usbmux.c b/src/usbmux.c
deleted file mode 100644
index c5e38dd..0000000
--- a/src/usbmux.c
+++ /dev/null
@@ -1,1264 +0,0 @@
1/*
2 * Copyright (c) 2008 Jing Su. All Rights Reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18#include <stdint.h>
19#include <stdarg.h>
20#include <stdlib.h>
21#include <string.h>
22#include <usb.h>
23#include <stdio.h>
24#include <arpa/inet.h>
25#include <errno.h>
26#include <pthread.h>
27#include "usbmux.h"
28
29#define BULKIN 0x85
30#define BULKOUT 0x04
31
32static const uint8_t TCP_FIN = 1;
33static const uint8_t TCP_SYN = 1 << 1;
34static const uint8_t TCP_RST = 1 << 2;
35static const uint8_t TCP_PSH = 1 << 3;
36static const uint8_t TCP_ACK = 1 << 4;
37static const uint8_t TCP_URG = 1 << 5;
38
39// I have trouble figuring out how to properly manage the windowing to
40// the device. It keeps sending back 512 and seems to drop off a cliff
41// when the device gets overwhelmed. In addition, the device likes to
42// panic and send out RESETS before the window hits zero. Also, waiting
43// for responses seems to not be a winning strategy.
44//
45// Since I'm not sure how in the hell to interpret the window sizes that
46// the device is sending back to us, I've figured out some magic number
47// constants which seem to work okay.
48static const uint32_t WINDOW_MAX = 5 * 1024;
49static const uint32_t WINDOW_INCREMENT = 512;
50
51typedef struct {
52 char *buffer;
53 int leftover;
54 int capacity;
55} receivebuf_t;
56
57struct usbmux_device_int {
58 struct usb_dev_handle *usbdev;
59 struct usb_device *__device;
60 receivebuf_t usbReceive;
61 int wMaxPacketSize;
62};
63
64typedef struct {
65 uint32_t type, length, major, minor, allnull;
66} usbmux_version_header;
67
68typedef struct {
69 uint32_t type, length;
70 uint16_t sport, dport;
71 uint32_t scnt, ocnt;
72 uint8_t offset, tcp_flags;
73 uint16_t window, nullnull, length16;
74} usbmux_tcp_header;
75
76struct usbmux_client_int {
77 usbmux_tcp_header *header;
78 usbmux_device_t device;
79
80 char *recv_buffer;
81 int r_len;
82 pthread_cond_t wait;
83
84 // this contains a conditional variable which usb-writers can wait
85 // on while waiting for window updates from the device.
86 pthread_cond_t wr_wait;
87 // I'm going to do something really cheesy here. We are going to
88 // just record the most recent scnt that we are expecting to hear
89 // back on. We will actually halt progress by limiting the number
90 // of outstanding un-acked bulk sends that we have beamed out.
91 uint32_t wr_pending_scnt;
92 long wr_window;
93
94 pthread_mutex_t mutex;
95
96 // this variable is not protected by the mutex. This will always
97 // be E_SUCCESS, unless an error of some kind breaks this stream.
98 // this will then be set to the error that caused the broken stream.
99 // no further operations other than free_client will be allowed.
100 int error;
101
102 int cleanup;
103
104 int connected;
105};
106
107
108static pthread_mutex_t usbmuxmutex = PTHREAD_MUTEX_INITIALIZER;
109static pthread_mutex_t printmutex = PTHREAD_MUTEX_INITIALIZER;
110static usbmux_client_t *connlist = NULL;
111static int clients = 0;
112
113
114/**
115 */
116int toto_debug = 0;
117
118void usbmux_set_debug(int e)
119{
120 toto_debug = e;
121}
122
123static void log_debug_msg(const char *format, ...)
124{
125#ifndef STRIP_DEBUG_CODE
126 va_list args;
127 /* run the real fprintf */
128 va_start(args, format);
129
130 if (toto_debug) {
131 pthread_mutex_lock(&printmutex);
132 vfprintf(stderr, format, args);
133 pthread_mutex_unlock(&printmutex);
134 }
135
136 va_end(args);
137#endif
138}
139
140/**
141 * for debugging purposes.
142 */
143static void print_buffer(const char *data, const int length)
144{
145 if (toto_debug <= 0) {
146 return;
147 }
148 int i;
149 int j;
150 unsigned char c;
151
152 for (i = 0; i < length; i += 16) {
153 printf("%04x: ", i);
154 for (j = 0; j < 16; j++) {
155 if (i + j >= length) {
156 printf(" ");
157 continue;
158 }
159 printf("%02hhx ", *(data + i + j));
160 }
161 printf(" | ");
162 for (j = 0; j < 16; j++) {
163 if (i + j >= length)
164 break;
165 c = *(data + i + j);
166 if ((c < 32) || (c > 127)) {
167 printf(".");
168 continue;
169 }
170 printf("%c", c);
171 }
172 printf("\n");
173 }
174 printf("\n");
175}
176
177static void hton_header(usbmux_tcp_header * hdr)
178{
179 if (hdr) {
180 hdr->length = htonl(hdr->length);
181 hdr->scnt = htonl(hdr->scnt);
182 hdr->ocnt = htonl(hdr->ocnt);
183 hdr->length16 = htons(hdr->length16);
184 }
185}
186
187static void ntoh_header(usbmux_tcp_header * hdr)
188{
189 if (hdr) {
190 hdr->length = ntohl(hdr->length);
191 hdr->scnt = ntohl(hdr->scnt);
192 hdr->ocnt = ntohl(hdr->ocnt);
193 hdr->length16 = ntohs(hdr->length16);
194 }
195}
196
197/** Creates a USBMux header containing version information
198 *
199 * @return A USBMux header
200 */
201static usbmux_version_header *version_header()
202{
203 usbmux_version_header *version =
204 (usbmux_version_header *) malloc(sizeof(usbmux_version_header));
205 version->type = 0;
206 version->length = htonl(20);
207 version->major = htonl(1);
208 version->minor = 0;
209 version->allnull = 0;
210 return version;
211}
212
213/**
214 * This function sets the configuration of the given device to 3
215 * and claims the interface 1. If usb_set_configuration fails, it detaches
216 * the kernel driver that blocks the device, and retries configuration.
217 *
218 * @param device which device to configure
219 */
220static int usbmux_config_usb_device(usbmux_device_t device)
221{
222 int ret;
223 int bytes;
224 char buf[512];
225
226 log_debug_msg("claiming interface... ");
227 ret = usb_claim_interface(device->usbdev, 1);
228 if (ret != 0) {
229 log_debug_msg("Error: usb_claim_interface returned %d: %s\n", ret,
230 strerror(-ret));
231 return -ENODEV;
232 } else {
233 log_debug_msg("done.\n");
234 }
235
236 // get the last configuration
237 struct usb_config_descriptor *cfg = &device->__device->config[device->__device->descriptor.bNumConfigurations-1];
238 if (cfg && cfg->bNumInterfaces >= 2) {
239 struct usb_interface *ifp = &cfg->interface[1];
240 if (ifp && ifp->num_altsetting >= 1) {
241 struct usb_interface_descriptor *as = &ifp->altsetting[0];
242 int i;
243 for (i = 0; i < as->bNumEndpoints; i++) {
244 struct usb_endpoint_descriptor *ep=&as->endpoint[i];
245 if (ep->bEndpointAddress == BULKOUT) {
246 device->wMaxPacketSize = ep->wMaxPacketSize;
247 }
248 }
249 }
250 }
251
252 log_debug_msg("Setting wMaxPacketSize to %d\n", device->wMaxPacketSize);
253
254 do {
255 bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800);
256 } while (bytes > 0);
257
258 return 0;
259}
260
261/**
262 * Given a USB bus and device number, returns a device handle to the device on
263 * that bus. To aid compatibility with future devices, this function does not
264 * check the vendor and device IDs! To do that, you should use
265 * usbmux_get_device() or a system-specific API (e.g. HAL).
266 *
267 * @param bus_n The USB bus number.
268 * @param dev_n The USB device number.
269 * @param device A pointer to a usbmux_device_t, which must be set to NULL upon
270 * calling usbmux_get_specific_device, which will be filled with a device
271 * descriptor on return.
272 * @return 0 if ok, otherwise a negative errno value.
273 */
274int usbmux_get_specific_device(int bus_n, int dev_n,
275 usbmux_device_t * device)
276{
277 struct usb_bus *bus;
278 struct usb_device *dev;
279 usbmux_version_header *version;
280 int bytes = 0;
281
282 //check we can actually write in device
283 if (!device || (device && *device))
284 return -EINVAL;
285
286 usbmux_device_t newdevice =
287 (usbmux_device_t) malloc(sizeof(struct usbmux_device_int));
288
289 // Initialize the struct
290 newdevice->usbdev = NULL;
291 newdevice->__device = NULL;
292
293 // don't forget these:
294 newdevice->usbReceive.buffer = NULL;
295 newdevice->usbReceive.leftover = 0;
296 newdevice->usbReceive.capacity = 0;
297
298 // wMaxPacketSize
299 newdevice->wMaxPacketSize = 64;
300
301 // Initialize libusb
302 usb_init();
303 usb_find_busses();
304 usb_find_devices();
305
306 // Set the device configuration
307 for (bus = usb_get_busses(); bus; bus = bus->next)
308 if (strtoul(bus->dirname, NULL, 10) == bus_n)
309 for (dev = bus->devices; dev != NULL; dev = dev->next)
310 if (strtol(dev->filename, NULL, 10) == dev_n) {
311 newdevice->__device = dev;
312 newdevice->usbdev = usb_open(newdevice->__device);
313 if (!newdevice->usbdev) {
314 fprintf(stderr, "%s: Error: usb_open(): %s\n", __func__, usb_strerror());
315 }
316 if (usbmux_config_usb_device(newdevice) == 0) {
317 goto found;
318 }
319 }
320
321 usbmux_free_device(newdevice);
322
323 log_debug_msg("usbmux_get_specific_device: device not found\n");
324 return -ENODEV;
325
326 found:
327 // Send the version command to the device
328 version = version_header();
329 bytes =
330 usb_bulk_write(newdevice->usbdev, BULKOUT, (char *) version,
331 sizeof(*version), 800);
332 if (bytes < 20) {
333 log_debug_msg("%s: libusb did NOT send enough!\n", __func__);
334 if (bytes < 0) {
335 log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n",
336 __func__, bytes, usb_strerror(),
337 strerror(-bytes));
338 }
339 }
340 // Read the device's response
341 bytes =
342 usb_bulk_read(newdevice->usbdev, BULKIN, (char *) version,
343 sizeof(*version), 800);
344
345 // Check for bad response
346 if (bytes < 20) {
347 free(version);
348 usbmux_free_device(newdevice);
349 log_debug_msg("%s: Invalid version message -- header too short.\n",
350 __func__);
351 if (bytes < 0) {
352 log_debug_msg("%s: libusb error message %d: %s (%s)\n",
353 __func__, bytes, usb_strerror(),
354 strerror(-bytes));
355 return bytes;
356 }
357 return -EBADMSG;
358 }
359 // Check for correct version
360 if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) {
361 // We're all ready to roll.
362 log_debug_msg("%s: success\n", __func__);
363 free(version);
364 *device = newdevice;
365 return 0;
366 } else {
367 // Bad header
368 usbmux_free_device(newdevice);
369 free(version);
370 log_debug_msg("%s: Received a bad header/invalid version number.",
371 __func__);
372 return -EBADMSG;
373 }
374
375 // If it got to this point it's gotta be bad
376 log_debug_msg("%s: Unknown error.\n", __func__);
377 usbmux_free_device(newdevice);
378 free(version);
379 return -EBADMSG; // if it got to this point it's gotta be bad
380}
381
382/** Cleans up an usbmux_device_t structure, then frees the structure itself.
383 * This is a library-level function; deals directly with the device to tear
384 * down relations, but otherwise is mostly internal.
385 *
386 * @param device A pointer to an usbmux_device_t structure.
387 */
388int usbmux_free_device(usbmux_device_t device)
389{
390 char buf[512];
391 int bytes = -1;
392
393 if (!device)
394 return -EINVAL;
395 int ret = 0;
396
397 if (device->usbdev) {
398 do {
399 bytes = usb_bulk_read(device->usbdev, BULKIN, buf, 512, 800);
400 } while (bytes > 0);
401 }
402
403 if (bytes < 0) {
404 ret = bytes;
405 }
406
407 if (device->usbReceive.buffer) {
408 free(device->usbReceive.buffer);
409 }
410 if (device->usbdev) {
411 usb_release_interface(device->usbdev, 1);
412 usb_close(device->usbdev);
413 ret = 0;
414 }
415 free(device);
416
417 return ret;
418}
419
420
421
422/** Sends data to the device
423 * This is a low-level (i.e. directly to device) function.
424 *
425 * @param device The device to send data to
426 * @param data The data to send
427 * @param datalen The length of the data
428 * @return The number of bytes sent, or -ERRNO on error
429 */
430static int send_to_device(usbmux_device_t device, char *data, int datalen)
431{
432 if (!device)
433 return -EINVAL;
434
435 int timeout = 1000;
436 int retrycount = 0;
437 int bytes = 0;
438
439if (toto_debug > 0) {
440 pthread_mutex_lock(&printmutex);
441 printf("===============================\n%s: trying to send\n",
442 __func__);
443 print_buffer(data, datalen);
444 printf("===============================\n");
445 pthread_mutex_unlock(&printmutex);
446}
447
448 do {
449 if (retrycount > 3) {
450 log_debug_msg
451 ("EPIC FAIL! aborting on retry count overload.\n");
452 return -ECOMM;
453 }
454
455 bytes =
456 usb_bulk_write(device->usbdev, BULKOUT, data, datalen,
457 timeout);
458 if (bytes == -ETIMEDOUT) {
459 // timed out waiting for write.
460 log_debug_msg("usb_bulk_write timeout error.\n");
461 return bytes;
462 } else if (bytes < 0) {
463 log_debug_msg
464 ("usb_bulk_write failed with error. err:%d (%s)(%s)\n",
465 bytes, usb_strerror(), strerror(-bytes));
466 return bytes;
467 } else if (bytes == 0) {
468 log_debug_msg("usb_bulk_write sent nothing. retrying.\n");
469 timeout = timeout * 4;
470 retrycount++;
471 continue;
472 } else if (bytes < datalen) {
473 log_debug_msg
474 ("usb_bulk_write failed to send full dataload. %d of %d\n",
475 bytes, datalen);
476 timeout = timeout * 4;
477 retrycount++;
478 data += bytes;
479 datalen -= bytes;
480 continue;
481 }
482 if ((bytes % device->wMaxPacketSize) == 0) {
483 log_debug_msg("NOTE: sending NULL packet\n");
484 char nullp = 0;
485 int res = usb_bulk_write(device->usbdev, BULKOUT,
486 &nullp, 0, timeout);
487 if (res < 0) {
488 log_debug_msg("ERROR: NULL packet write returned %d\n", res);
489 }
490 }
491 } while (0); // fall out
492
493 if (bytes > 0) {
494 if (toto_debug > 0) {
495 pthread_mutex_lock(&printmutex);
496 printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
497 printf("%s: sent to device\n", __func__);
498 print_buffer(data, bytes);
499 printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
500 pthread_mutex_unlock(&printmutex);
501 }
502 }
503 return bytes;
504}
505
506/** Receives data from the device
507 * This function is a low-level (i.e. direct from device) function.
508 *
509 * @param device The device to receive data from
510 * @param data Where to put data read
511 * @param datalen How much data to read in
512 * @param timeout How many milliseconds to wait for data
513 *
514 * @return How many bytes were read in, or -1 on error.
515 */
516static int recv_from_device_timeout(usbmux_device_t device, char *data,
517 int datalen, int timeoutmillis)
518{
519 if (!device)
520 return -EINVAL;
521 //log_debug_msg("%s: attempting to receive %i bytes\n", __func__, datalen);
522
523 int bytes =
524 usb_bulk_read(device->usbdev, BULKIN, data, datalen,
525 timeoutmillis);
526 // There are some things which are errors, others which are no problem.
527 // It's not documented in libUSB, but it seems that the error values
528 // returned are just negated ERRNO values.
529 if (bytes < 0) {
530 if (bytes == -ETIMEDOUT) {
531 // ignore this. it just means timeout reached before we
532 // picked up any data. no problem.
533 return 0;
534 } else {
535 fprintf(stderr, "%s: libusb gave me the error %d: %s (%s)\n",
536 __func__, bytes, usb_strerror(), strerror(-bytes));
537 log_debug_msg("%s: libusb gave me the error %d: %s (%s)\n",
538 __func__, bytes, usb_strerror(),
539 strerror(-bytes));
540 }
541 return bytes;
542 }
543 if (bytes > 0) {
544 if (toto_debug > 0) {
545 pthread_mutex_lock(&printmutex);
546 printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
547 printf("%s: received from device:\n", __func__);
548 print_buffer(data, bytes);
549 printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
550 pthread_mutex_unlock(&printmutex);
551 }
552 }
553
554 return bytes;
555}
556
557/** Creates a USBMux packet for the given set of ports.
558 *
559 * @param s_port The source port for the connection.
560 * @param d_port The destination port for the connection.
561 *
562 * @return A USBMux packet
563 */
564static usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port)
565{
566 usbmux_tcp_header *conn =
567 (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header));
568 conn->type = htonl(6);
569 conn->length = sizeof(usbmux_tcp_header);
570 conn->sport = htons(s_port);
571 conn->dport = htons(d_port);
572 conn->scnt = 0;
573 conn->ocnt = 0;
574 conn->offset = 0x50;
575 conn->window = htons(0x0200);
576 conn->nullnull = 0x0000;
577 conn->length16 = sizeof(usbmux_tcp_header);
578 return conn;
579}
580
581
582/** Removes a connection from the list of connections made.
583 * The list of connections is necessary for buffering.
584 *
585 * @param connection The connection to delete from the tracking list.
586 */
587static void delete_connection(usbmux_client_t connection)
588{
589 usbmux_client_t *newlist = NULL;
590
591 pthread_mutex_lock(&usbmuxmutex);
592
593 // update the global list of connections
594 if (clients > 1) {
595 newlist =
596 (usbmux_client_t *) malloc(sizeof(usbmux_client_t) *
597 (clients - 1));
598 int i = 0, j = 0;
599 for (i = 0; i < clients; i++) {
600 if (connlist[i] == connection)
601 continue;
602 else {
603 newlist[j] = connlist[i];
604 j++;
605 }
606 }
607 }
608 if (connlist) {
609 free(connlist);
610 }
611 connlist = newlist;
612 clients--;
613
614 // free up this connection
615 pthread_mutex_lock(&connection->mutex);
616 if (connection->recv_buffer) {
617 free(connection->recv_buffer);
618 connection->recv_buffer = NULL;
619 }
620 if (connection->header) {
621 free(connection->header);
622 connection->header = NULL;
623 }
624 connection->r_len = 0;
625 pthread_mutex_unlock(&connection->mutex);
626 pthread_mutex_destroy(&connection->mutex);
627 free(connection);
628
629 pthread_mutex_unlock(&usbmuxmutex);
630}
631
632/** Adds a connection to the list of connections made.
633 * The connection list is necessary for buffering.
634 *
635 * @param connection The connection to add to the global list of connections.
636 */
637
638static void add_connection(usbmux_client_t connection)
639{
640 pthread_mutex_lock(&usbmuxmutex);
641 usbmux_client_t *newlist =
642 (usbmux_client_t *) realloc(connlist,
643 sizeof(usbmux_client_t) * (clients +
644 1));
645 newlist[clients] = connection;
646 connlist = newlist;
647 clients++;
648 pthread_mutex_unlock(&usbmuxmutex);
649}
650
651/**
652 * Get a source port number that is not used by one of our connections
653 * This is needed for us to make sure we are not sending on another
654 * connection.
655 */
656static uint16_t get_free_port()
657{
658 int i;
659 uint16_t newport = 30000;
660 int cnt = 0;
661
662 pthread_mutex_lock(&usbmuxmutex);
663 while (1) {
664 cnt = 0;
665 for (i = 0; i < clients; i++) {
666 if (ntohs(connlist[i]->header->sport) == newport) {
667 cnt++;
668 }
669 }
670 if (cnt == 0) {
671 // newport is not used in our list of connections!
672 break;
673 } else {
674 newport++;
675 if (newport < 30000) {
676 // if all ports from 30000 to 65535 are in use,
677 // the value wraps (16-bit overflow)
678 // return 0, no port is available.
679 // This should not happen, but just in case ;)
680 newport = 0;
681 break;
682 }
683 }
684 }
685 pthread_mutex_unlock(&usbmuxmutex);
686
687 return newport;
688}
689
690/** Initializes a connection to 'device' with source port s_port and destination port d_port
691 *
692 * @param device The device to initialize a connection on.
693 * @param src_port The source port
694 * @param dst_port The destination port -- 0xf27e for lockdownd.
695 * @param client A mux TCP header for the connection which is used for tracking and data transfer.
696 * @return 0 on success, a negative errno value otherwise.
697 */
698int usbmux_new_client(usbmux_device_t device, uint16_t src_port,
699 uint16_t dst_port, usbmux_client_t * client)
700{
701 if (!device || !dst_port)
702 return -EINVAL;
703
704 src_port = get_free_port();
705
706 if (!src_port) {
707 // this is a special case, if we get 0, this is not good, so
708 return -EISCONN; // TODO: error code suitable?
709 }
710 // Initialize connection stuff
711 usbmux_client_t new_connection =
712 (usbmux_client_t) malloc(sizeof(struct usbmux_client_int));
713 new_connection->header = new_mux_packet(src_port, dst_port);
714
715 // send TCP syn
716 if (new_connection && new_connection->header) {
717 int err = 0;
718 new_connection->header->tcp_flags = TCP_SYN;
719 new_connection->header->length = new_connection->header->length;
720 new_connection->header->length16 =
721 new_connection->header->length16;
722 new_connection->header->scnt = 0;
723 new_connection->header->ocnt = 0;
724 new_connection->device = device;
725 new_connection->recv_buffer = NULL;
726 new_connection->r_len = 0;
727 pthread_cond_init(&new_connection->wait, NULL);
728 pthread_mutex_init(&new_connection->mutex, NULL);
729 pthread_cond_init(&new_connection->wr_wait, NULL);
730 new_connection->wr_pending_scnt = 0;
731 new_connection->wr_window = 0;
732 add_connection(new_connection);
733 new_connection->error = 0;
734 new_connection->cleanup = 0;
735 new_connection->connected = 0;
736 hton_header(new_connection->header);
737 log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__,
738 ntohs(new_connection->header->sport),
739 ntohs(new_connection->header->dport));
740 err =
741 send_to_device(device, (char *) new_connection->header,
742 sizeof(usbmux_tcp_header));
743 if (err >= 0) {
744 *client = new_connection;
745 return 0;
746 } else {
747 delete_connection(new_connection);
748 return err;
749 }
750 }
751 // if we get to this point it's probably bad
752 return -ENOMEM;
753}
754
755/** Cleans up the given USBMux connection.
756 * @note Once a connection is closed it may not be used again.
757 *
758 * @param connection The connection to close.
759 *
760 * @return 0 on success or a negative errno value on error.
761 */
762int usbmux_free_client(usbmux_client_t client)
763{
764 if (!client || !client->device)
765 return -EINVAL;
766
767 int err = 0;
768 int result = 0;
769 pthread_mutex_lock(&client->mutex);
770 client->header->tcp_flags = TCP_RST;
771 client->header->length = 0x1C;
772 client->header->window = 0;
773 client->header->length16 = 0x1C;
774 hton_header(client->header);
775
776 err =
777 send_to_device(client->device, (char *) client->header,
778 sizeof(usbmux_tcp_header));
779 if (err < 0) {
780 log_debug_msg("%s: error sending TCP_FIN\n", __func__);
781 result = err;
782 }
783
784 client->cleanup = 1;
785
786 // make sure we don't have any last-minute laggards waiting on this.
787 // I put it after the mutex unlock because we have cases where the
788 // conditional wait is dependent on re-grabbing that mutex.
789 pthread_cond_broadcast(&client->wait);
790 pthread_cond_destroy(&client->wait);
791 pthread_cond_broadcast(&client->wr_wait);
792 pthread_cond_destroy(&client->wr_wait);
793
794 pthread_mutex_unlock(&client->mutex);
795
796 return result;
797}
798
799/** Sends the given data over the selected connection.
800 *
801 * @param client The client we're sending data on.
802 * @param data A pointer to the data to send.
803 * @param datalen How much data we're sending.
804 * @param sent_bytes The number of bytes sent, minus the header (28)
805 *
806 * @return 0 on success or a negative errno value on error.
807 */
808int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen,
809 uint32_t * sent_bytes)
810{
811 if (!client->device || !client || !sent_bytes)
812 return -EINVAL;
813
814 if (client->error < 0) {
815 return client->error;
816 }
817
818 *sent_bytes = 0;
819 pthread_mutex_lock(&client->mutex);
820
821 int sendresult = 0;
822 uint32_t blocksize = 0;
823 if (client->wr_window <= 0) {
824 struct timespec ts;
825 clock_gettime(CLOCK_REALTIME, &ts);
826 //ts.tv_sec += 1;
827 ts.tv_nsec += 750 * 1000;
828 if (pthread_cond_timedwait(&client->wait, &client->mutex, &ts) ==
829 ETIMEDOUT) {
830 // timed out. optimistically grow the window and try to make progress
831 client->wr_window += WINDOW_INCREMENT;
832 }
833 }
834
835 blocksize = sizeof(usbmux_tcp_header) + datalen;
836
837 // client->scnt and client->ocnt should already be in host notation...
838 // we don't need to change them juuuust yet.
839 char *buffer = (char *) malloc(blocksize + 2); // allow 2 bytes of safety padding
840 // Set the length
841 client->header->length = blocksize;
842 client->header->length16 = blocksize;
843
844 // Put header into big-endian notation
845 hton_header(client->header);
846 // Concatenation of stuff in the buffer.
847 memcpy(buffer, client->header, sizeof(usbmux_tcp_header));
848 memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen);
849
850 log_debug_msg("%s: send_to_device(%d --> %d)\n", __func__,
851 ntohs(client->header->sport),
852 ntohs(client->header->dport));
853 sendresult = send_to_device(client->device, buffer, blocksize);
854 // Now that we've sent it off, we can clean up after our sloppy selves.
855 if (buffer)
856 free(buffer);
857
858 // revert header fields that have been swapped before trying to send
859 ntoh_header(client->header);
860
861 // update counts ONLY if the send succeeded.
862 if ((uint32_t) sendresult == blocksize) {
863 // Re-calculate scnt
864 client->header->scnt += datalen;
865 client->wr_window -= blocksize;
866 }
867
868 pthread_mutex_unlock(&client->mutex);
869
870 if (sendresult == -ETIMEDOUT || sendresult == 0) {
871 // no problem for now...
872 *sent_bytes = 0;
873 return -ETIMEDOUT;
874 } else if (sendresult < 0) {
875 return sendresult;
876 } else if ((uint32_t) sendresult == blocksize) {
877 // actual number of data bytes sent.
878 *sent_bytes = sendresult - sizeof(usbmux_tcp_header);
879 return 0;
880 } else {
881 fprintf(stderr,
882 "usbsend managed to dump a packet that is not full size. %d of %d\n",
883 sendresult, blocksize);
884 return -EBADMSG;
885 }
886}
887
888/** append the packet's DATA to the receive buffer for the client.
889 *
890 * this has a few other corner-case functions:
891 * 1. this will properly handle the handshake syn+ack.
892 * 2. for all receives, this will appropriately update the ocnt.
893 *
894 * @return number of bytes consumed (header + data)
895 */
896static uint32_t append_receive_buffer(usbmux_client_t client, char *packet)
897{
898 if (client == NULL || packet == NULL)
899 return 0;
900
901 usbmux_tcp_header *header = (usbmux_tcp_header *) packet;
902 char *data = &packet[sizeof(usbmux_tcp_header)];
903 uint32_t packetlen = ntohl(header->length);
904 uint32_t datalen = packetlen - sizeof(usbmux_tcp_header);
905
906 int dobroadcast = 0;
907
908 pthread_mutex_lock(&client->mutex);
909
910 // we need to handle a few corner case tasks and book-keeping which
911 // falls on our responsibility because we are the ones reading in
912 // feedback.
913 if (client->header->scnt == 0 && client->header->ocnt == 0) {
914 log_debug_msg("client is still waiting for handshake.\n");
915 if (header->tcp_flags == (TCP_SYN | TCP_ACK)) {
916 log_debug_msg("yes, got syn+ack ; replying with ack.\n");
917 client->header->tcp_flags = TCP_ACK;
918 client->header->length = sizeof(usbmux_tcp_header);
919 client->header->length16 = sizeof(usbmux_tcp_header);
920 client->header->scnt += 1;
921 client->header->ocnt = header->ocnt;
922 hton_header(client->header);
923 // push it to USB
924 // TODO: need to check for error in the send here.... :(
925 log_debug_msg("%s: send_to_device (%d --> %d)\n", __func__,
926 ntohs(client->header->sport),
927 ntohs(client->header->dport));
928 if (send_to_device
929 (client->device, (char *) client->header,
930 sizeof(usbmux_tcp_header)) <= 0) {
931 log_debug_msg("%s: error when pushing to usb...\n",
932 __func__);
933 } else {
934 client->connected = 1;
935 }
936 // need to revert some of the fields back to host notation.
937 ntoh_header(client->header);
938 } else {
939 client->error = -ECONNABORTED;
940 // woah... this connection failed us.
941 // TODO: somehow signal that this stream is a no-go.
942 log_debug_msg("WOAH! client failed to get proper syn+ack.\n");
943 }
944 }
945 // update TCP counters and windows.
946 //
947 // save the window that we're getting from the USB device.
948 // apparently the window is bigger than just the 512 that's typically
949 // advertised. iTunes apparently shifts this value by 8 to get a much
950 // larger number.
951 if (header->tcp_flags & TCP_RST) {
952 client->error = -ECONNRESET;
953
954 if (datalen > 0) {
955 char e_msg[128];
956 e_msg[0] = 0;
957 if (datalen > 1) {
958 memcpy(e_msg, data + 1, datalen - 1);
959 e_msg[datalen - 1] = 0;
960 }
961 // fetch the message
962 switch (data[0]) {
963 case 0:
964 // this is not an error, it's just a status message.
965 log_debug_msg("received status message: %s\n", e_msg);
966 datalen = 0;
967 break;
968 case 1:
969 log_debug_msg("received error message: %s\n", e_msg);
970 datalen = 0;
971 break;
972 default:
973 log_debug_msg
974 ("received unknown message (type 0x%02x): %s\n",
975 data[0], e_msg);
976 //datalen = 0; // <-- we let this commented out for testing
977 break;
978 }
979 } else {
980 log_debug_msg
981 ("peer sent connection reset. setting error: %d\n",
982 client->error);
983 }
984 }
985 // the packet's ocnt tells us how much of our data the device has received.
986 if (header->tcp_flags & TCP_ACK) {
987 // this is a hacky magic number condition. it seems that once
988 // the window reported by the device starts to drop below this
989 // number, we quickly fall into connection reset problems.
990 // Once we see the reported window size start falling off,
991 // ut off and wait for solid acks to come back.
992 if (ntohs(header->window) < 256)
993 client->wr_window = 0;
994
995 // check what just got acked.
996 if (ntohl(header->ocnt) < client->header->scnt) {
997 // we got some kind of ack, but it hasn't caught up
998 // with the pending that have been sent.
999 pthread_cond_broadcast(&client->wr_wait);
1000 } else if (ntohl(header->ocnt) >
1001 /*client->wr_pending_scnt */ client->header->scnt) {
1002 fprintf(stderr,
1003 "WTF?! acks overtook pending outstanding. %u,%u\n",
1004 ntohl(header->ocnt), client->wr_pending_scnt);
1005 } else {
1006 // reset the window
1007 client->wr_window = WINDOW_MAX;
1008 pthread_cond_broadcast(&client->wr_wait);
1009 }
1010 }
1011 // the packet's scnt will be our new ocnt.
1012 client->header->ocnt = ntohl(header->scnt);
1013
1014 // ensure there is enough space, either by first malloc or realloc
1015 if (datalen > 0) {
1016 log_debug_msg("%s: putting %d bytes into client's recv_buffer\n",
1017 __func__, datalen);
1018 if (client->r_len == 0)
1019 dobroadcast = 1;
1020
1021 if (client->recv_buffer == NULL) {
1022 client->recv_buffer = malloc(datalen);
1023 client->r_len = 0;
1024 } else {
1025 client->recv_buffer =
1026 realloc(client->recv_buffer, client->r_len + datalen);
1027 }
1028
1029 memcpy(&client->recv_buffer[client->r_len], data, datalen);
1030 client->r_len += datalen;
1031 }
1032
1033 pthread_mutex_unlock(&client->mutex);
1034
1035 // I put this outside the mutex unlock just so that when the threads
1036 // wake, we don't have to do another round of unlock+try to grab.
1037 if (dobroadcast)
1038 pthread_cond_broadcast(&client->wait);
1039
1040 return packetlen;
1041}
1042
1043/**
1044 * @note THERE IS NO MUTEX LOCK IN THIS FUNCTION!
1045 * because we're only called from one location, pullbulk, where the lock
1046 * is already held.
1047 */
1048static usbmux_client_t find_client(usbmux_tcp_header * recv_header)
1049{
1050 // remember, as we're looking for the client, the receive header is
1051 // coming from the USB into our client. This means that when we check
1052 // the src/dst ports, we need to reverse them.
1053 usbmux_client_t retval = NULL;
1054
1055 // just for debugging check, I'm going to convert the numbers to host-endian.
1056 uint16_t hsport = ntohs(recv_header->sport);
1057 uint16_t hdport = ntohs(recv_header->dport);
1058
1059 pthread_mutex_lock(&usbmuxmutex);
1060 int i;
1061 for (i = 0; i < clients; i++) {
1062 uint16_t csport = ntohs(connlist[i]->header->sport);
1063 uint16_t cdport = ntohs(connlist[i]->header->dport);
1064
1065 if (hsport == cdport && hdport == csport) {
1066 retval = connlist[i];
1067 break;
1068 }
1069 }
1070 pthread_mutex_unlock(&usbmuxmutex);
1071
1072 return retval;
1073}
1074
1075/** pull in a big USB bulk packet and distribute it to queues appropriately.
1076 */
1077int usbmux_pullbulk(usbmux_device_t device)
1078{
1079 if (!device)
1080 return -EINVAL;
1081
1082 int res = 0;
1083 static const int DEFAULT_CAPACITY = 128 * 1024;
1084 if (device->usbReceive.buffer == NULL) {
1085 device->usbReceive.capacity = DEFAULT_CAPACITY;
1086 device->usbReceive.buffer = malloc(device->usbReceive.capacity);
1087 device->usbReceive.leftover = 0;
1088 }
1089 // start the cursor off just ahead of the leftover.
1090 char *cursor = &device->usbReceive.buffer[device->usbReceive.leftover];
1091 // pull in content, note that the amount we can pull is capacity minus leftover
1092 int readlen =
1093 recv_from_device_timeout(device, cursor,
1094 device->usbReceive.capacity -
1095 device->usbReceive.leftover, 3000);
1096 if (readlen < 0) {
1097 res = readlen;
1098 //fprintf(stderr, "recv_from_device_timeout gave us an error.\n");
1099 readlen = 0;
1100 }
1101 if (readlen > 0) {
1102 //fprintf(stdout, "recv_from_device_timeout pulled an extra %d bytes\n", readlen);
1103 }
1104 // the amount of content we have to work with is the remainder plus
1105 // what we managed to read
1106 device->usbReceive.leftover += readlen;
1107
1108 // reset the cursor to the front of that buffer and work through
1109 // trying to decode packets out of them.
1110 cursor = device->usbReceive.buffer;
1111 while (1) {
1112 // check if there's even sufficient data to decode a header
1113 if (device->usbReceive.leftover < sizeof(usbmux_tcp_header))
1114 break;
1115 usbmux_tcp_header *header = (usbmux_tcp_header *) cursor;
1116
1117 log_debug_msg("%s: recv_from_device_timeout (%d --> %d)\n",
1118 __func__, ntohs(header->sport),
1119 ntohs(header->dport));
1120
1121 // now that we have a header, check if there is sufficient data
1122 // to construct a full packet, including its data
1123 uint32_t packetlen = ntohl(header->length);
1124 if ((uint32_t) device->usbReceive.leftover < packetlen) {
1125 fprintf(stderr,
1126 "%s: not enough data to construct a full packet\n",
1127 __func__);
1128 break;
1129 }
1130 // ok... find the client this packet will get stuffed to.
1131 usbmux_client_t client = find_client(header);
1132 if (client == NULL) {
1133 log_debug_msg
1134 ("WARNING: client for packet cannot be found. dropping packet.\n");
1135 } else {
1136 // stuff the data
1137 log_debug_msg
1138 ("%s: found client, calling append_receive_buffer\n",
1139 __func__);
1140 append_receive_buffer(client, cursor);
1141
1142 // perhaps this is too general, == -ECONNRESET
1143 // might be a better check here
1144 if (client->error < 0) {
1145 pthread_mutex_lock(&client->mutex);
1146 if (client->cleanup) {
1147 pthread_mutex_unlock(&client->mutex);
1148 log_debug_msg("freeing up connection (%d->%d)\n",
1149 ntohs(client->header->sport),
1150 ntohs(client->header->dport));
1151 delete_connection(client);
1152 } else {
1153 pthread_mutex_unlock(&client->mutex);
1154 }
1155 }
1156 }
1157
1158 // move the cursor and account for the consumption
1159 cursor += packetlen;
1160 device->usbReceive.leftover -= packetlen;
1161 }
1162
1163 // now, we need to manage any leftovers.
1164 // I'm going to manage the leftovers by alloc'ing a new block and
1165 // copyingthe leftovers to it. This is just to prevent problems with
1166 // memory moves where there may be overlap. Besides, the leftovers
1167 // should be small enough that this copy is minimal in overhead.
1168 //
1169 // if there are no leftovers, we just leave the datastructure as is,
1170 // and re-use the block next time.
1171 if (device->usbReceive.leftover > 0
1172 && cursor != device->usbReceive.buffer) {
1173 log_debug_msg("%s: we got a leftover, so handle it\n", __func__);
1174 char *newbuff = malloc(DEFAULT_CAPACITY);
1175 memcpy(newbuff, cursor, device->usbReceive.leftover);
1176 free(device->usbReceive.buffer);
1177 device->usbReceive.buffer = newbuff;
1178 device->usbReceive.capacity = DEFAULT_CAPACITY;
1179 }
1180
1181 return res;
1182}
1183
1184/**
1185 * return the error code stored in usbmux_client_t structure,
1186 * e.g. non-zero when an usb read error occurs.
1187 *
1188 * @param client the usbmux client
1189 *
1190 * @return 0 or a negative errno value.
1191 */
1192int usbmux_get_error(usbmux_client_t client)
1193{
1194 if (!client) {
1195 return 0;
1196 }
1197 return client->error;
1198}
1199
1200/** This function reads from the client's recv_buffer.
1201 *
1202 * @param client The client to receive data from.
1203 * @param data Where to put the data we receive.
1204 * @param datalen How much data to read.
1205 * @param timeout How many milliseconds to wait for data
1206 *
1207 * @return 0 on success or a negative errno value on failure.
1208 */
1209int usbmux_recv_timeout(usbmux_client_t client, char *data,
1210 uint32_t datalen, uint32_t * recv_bytes,
1211 int timeout)
1212{
1213
1214 if (!client || !data || datalen == 0 || !recv_bytes)
1215 return -EINVAL;
1216
1217 if (client->error < 0)
1218 return client->error;
1219
1220 pthread_mutex_lock(&client->mutex);
1221
1222 if (timeout > 0 && (client->recv_buffer == NULL || client->r_len == 0)) {
1223 struct timespec ts;
1224 clock_gettime(CLOCK_REALTIME, &ts);
1225 ts.tv_sec += timeout / 1000;
1226 ts.tv_nsec += (timeout - ((int) (timeout / 1000)) * 1000) * 1000;
1227 pthread_cond_timedwait(&client->wait, &client->mutex, &ts);
1228 }
1229
1230 *recv_bytes = 0;
1231 if (client->recv_buffer != NULL && client->r_len > 0) {
1232 uint32_t foolen = datalen;
1233 if ((int) foolen > client->r_len)
1234 foolen = client->r_len;
1235 memcpy(data, client->recv_buffer, foolen);
1236 *recv_bytes = foolen;
1237
1238 // preserve any left-over unread amounts.
1239 int remainder = client->r_len - foolen;
1240 if (remainder > 0) {
1241 char *newbuf = malloc(remainder);
1242 memcpy(newbuf, client->recv_buffer + foolen, remainder);
1243 client->r_len = remainder;
1244 free(client->recv_buffer);
1245 client->recv_buffer = newbuf;
1246 } else {
1247 free(client->recv_buffer);
1248 client->recv_buffer = NULL;
1249 client->r_len = 0;
1250 }
1251 }
1252
1253 pthread_mutex_unlock(&client->mutex);
1254
1255 return 0;
1256}
1257
1258int usbmux_is_connected(usbmux_client_t client)
1259{
1260 if (!client) {
1261 return 0;
1262 }
1263 return client->connected;
1264}
diff --git a/src/usbmux.h b/src/usbmux.h
deleted file mode 100644
index 155316a..0000000
--- a/src/usbmux.h
+++ /dev/null
@@ -1,53 +0,0 @@
1/*
2 * Copyright (c) 2008 Jing Su. All Rights Reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#ifndef __USBMUX_H__
20#define __USBMUX_H__
21
22#include <stdint.h>
23#include <sys/types.h>
24//#include <sys/stat.h>
25
26
27void usbmux_set_debug(int e);
28
29struct usbmux_device_int;
30typedef struct usbmux_device_int *usbmux_device_t;
31
32struct usbmux_client_int;
33typedef struct usbmux_client_int *usbmux_client_t;
34
35int usbmux_get_device ( usbmux_device_t *device );
36int usbmux_get_specific_device(int bus_n, int dev_n, usbmux_device_t * device);
37int usbmux_free_device ( usbmux_device_t device );
38
39
40int usbmux_new_client ( usbmux_device_t device, uint16_t src_port, uint16_t dst_port, usbmux_client_t *client );
41int usbmux_free_client ( usbmux_client_t client );
42
43int usbmux_send(usbmux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes);
44
45int usbmux_recv_timeout(usbmux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout);
46
47int usbmux_pullbulk(usbmux_device_t device);
48
49int usbmux_get_error(usbmux_client_t client);
50
51int usbmux_is_connected(usbmux_client_t client);
52
53#endif
diff --git a/stuff/README b/stuff/README
new file mode 100644
index 0000000..8c5c423
--- /dev/null
+++ b/stuff/README
@@ -0,0 +1,19 @@
1com.openssh.sft.plist is a launchd configuration to set up a bare SFTP server
2on TCP port 2299 localhost-only for USB use. It's nice for relatively fast music
3syncing and file transfer under Linux (and it avoids encryption). Con: it gives
4anyone with usb access root FS access on the phone, as well as anything running
5on the phone itself.
6
7Use it with a command like this:
8
9IPATH=/var/mobile/Media
10MOUNTPOINT=$HOME/media/iphone
11$ sshfs localhost:$IPATH $MOUNTPOINT -o workaround=rename -o directport=2299 \
12 -o kernel_cache -o entry_timeout=30 -o attr_timeout=30
13
14Make sure you run tcprelay.py:
15$ python tcprelay.py -t 2299
16
17Remember that to bypass the stupid new iTunesDB hash you need to edit
18/System/Library/Lockdown/Checkpoint.xml and change DBVersion to 2.
19
diff --git a/stuff/com.openssh.sftp.plist b/stuff/com.openssh.sftp.plist
new file mode 100644
index 0000000..569fabc
--- /dev/null
+++ b/stuff/com.openssh.sftp.plist
@@ -0,0 +1,41 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3<plist version="1.0">
4
5<dict>
6 <key>Label</key>
7 <string>com.openssh.sftpd</string>
8
9 <key>Program</key>
10 <string>/usr/libexec/sftp-server</string>
11
12 <key>ProgramArguments</key>
13 <array>
14 <string>/usr/libexec/sftp-server</string>
15 </array>
16
17 <key>SessionCreate</key>
18 <true/>
19
20 <key>Sockets</key>
21 <dict>
22 <key>Listeners</key>
23 <dict>
24 <key>SockServiceName</key>
25 <string>2299</string>
26 <key>SockNodeName</key>
27 <string>127.0.0.1</string>
28 </dict>
29 </dict>
30
31 <key>StandardErrorPath</key>
32 <string>/dev/null</string>
33
34 <key>inetdCompatibility</key>
35 <dict>
36 <key>Wait</key>
37 <false/>
38 </dict>
39</dict>
40
41</plist>
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..70ec3b3
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,8 @@
1include_directories (${CMAKE_SOURCE_DIR}/libusbmuxd)
2link_directories (${CMAKE_BINARY_DIR}/libusbmuxd)
3
4add_definitions(-Wall -O2 -g)
5add_executable(iproxy iproxy.c)
6target_link_libraries(iproxy libusbmuxd pthread)
7
8install(TARGETS iproxy RUNTIME DESTINATION bin) \ No newline at end of file
diff --git a/tools/Makefile.am b/tools/Makefile.am
deleted file mode 100644
index 5d20bbf..0000000
--- a/tools/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
1bin_PROGRAMS = iproxy
2
3iproxy_SOURCES = iproxy.c
4iproxy_CFLAGS = -I$(top_srcdir)/src
5iproxy_LDADD = ../src/libusbmuxd.la
diff --git a/udev/85-usbmuxd.rules.in b/udev/85-usbmuxd.rules.in
index c0a3900..3521ccb 100644
--- a/udev/85-usbmuxd.rules.in
+++ b/udev/85-usbmuxd.rules.in
@@ -1,7 +1,7 @@
1# usbmuxd (iPhone "Apple Mobile Device" MUXer listening on /var/run/usbmuxd) 1# usbmuxd (iPhone "Apple Mobile Device" MUXer listening on /var/run/usbmuxd)
2 2
3# Forces iPhone 1.0, 3G, 3GS and iPodTouch 1 and 2 to USB configuration 3 and run usbmuxd 3# Forces iPhone 1.0, 3G, 3GS and iPodTouch 1 and 2 to USB configuration 3 and run usbmuxd
4ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="129[0-4]", ATTR{bConfigurationValue}!="$attr{bNumConfigurations}", ATTR{bConfigurationValue}="$attr{bNumConfigurations}", RUN+="@prefix@/sbin/usbmuxd -u" 4ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="129[0-4]", ATTR{bConfigurationValue}!="$attr{bNumConfigurations}", ATTR{bConfigurationValue}="$attr{bNumConfigurations}", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -u -U"
5 5
6# Exit usbmuxd when the last phone is removed 6# Exit usbmuxd when the last phone is removed
7ACTION=="remove", SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="05ac", ENV{ID_MODEL_ID}=="129[0-4]", RUN+="@prefix@/sbin/usbmuxd -x" 7ACTION=="remove", SUBSYSTEM=="usb", ENV{PRODUCT}=="5ac/129[0-4]/*", ENV{INTERFACE}=="255/*", RUN+="@CMAKE_INSTALL_PREFIX@/sbin/usbmuxd -x"
diff --git a/udev/CMakeLists.txt b/udev/CMakeLists.txt
new file mode 100644
index 0000000..0f7042d
--- /dev/null
+++ b/udev/CMakeLists.txt
@@ -0,0 +1,2 @@
1CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/85-usbmuxd.rules.in ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules @ONLY)
2install(FILES ${CMAKE_CURRENT_BINARY_DIR}/85-usbmuxd.rules DESTINATION /lib/udev/rules.d/)
diff --git a/udev/Makefile.am b/udev/Makefile.am
deleted file mode 100644
index e44b077..0000000
--- a/udev/Makefile.am
+++ /dev/null
@@ -1,4 +0,0 @@
1udevdir=/lib/udev/rules.d/
2udev_DATA=85-usbmuxd.rules
3
4EXTRA_DIST = 85-usbmuxd.rules
diff --git a/usbmuxd/CMakeLists.txt b/usbmuxd/CMakeLists.txt
new file mode 100644
index 0000000..b982dc0
--- /dev/null
+++ b/usbmuxd/CMakeLists.txt
@@ -0,0 +1,9 @@
1find_package(USB REQUIRED)
2include_directories(${USB_INCLUDE_DIRS})
3set(LIBS ${LIBS} ${USB_LIBRARIES})
4
5add_definitions(-Wall -O2 -g)
6add_executable(usbmuxd main.c usb-linux.c log.c utils.c device.c client.c)
7target_link_libraries(usbmuxd ${LIBS})
8
9install(TARGETS usbmuxd RUNTIME DESTINATION sbin) \ No newline at end of file
diff --git a/usbmuxd/client.c b/usbmuxd/client.c
new file mode 100644
index 0000000..7a3160f
--- /dev/null
+++ b/usbmuxd/client.c
@@ -0,0 +1,430 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <errno.h>
27#include <unistd.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <arpa/inet.h>
32
33#include "log.h"
34#include "usb.h"
35#include "client.h"
36#include "device.h"
37
38#define CMD_BUF_SIZE 256
39#define REPLY_BUF_SIZE 1024
40
41enum client_state {
42 CLIENT_COMMAND, // waiting for command
43 CLIENT_LISTEN, // listening for devices
44 CLIENT_CONNECTING1, // issued connection request
45 CLIENT_CONNECTING2, // connection established, but waiting for response message to get sent
46 CLIENT_CONNECTED, // connected
47 CLIENT_DEAD
48};
49
50struct mux_client {
51 int fd;
52 unsigned char *ob_buf;
53 int ob_size;
54 int ob_capacity;
55 unsigned char *ib_buf;
56 int ib_size;
57 int ib_capacity;
58 short events, devents;
59 uint32_t connect_tag;
60 int connect_device;
61 enum client_state state;
62};
63
64static struct collection client_list;
65
66int client_read(struct mux_client *client, void *buffer, int len)
67{
68 usbmuxd_log(LL_SPEW, "client_read fd %d buf %p len %d", client->fd, buffer, len);
69 if(client->state != CLIENT_CONNECTED) {
70 usbmuxd_log(LL_ERROR, "Attempted to read from client %d not in CONNECTED state", client->fd);
71 return -1;
72 }
73 return recv(client->fd, buffer, len, 0);
74}
75
76int client_write(struct mux_client *client, void *buffer, int len)
77{
78 usbmuxd_log(LL_SPEW, "client_write fd %d buf %p len %d", client->fd, buffer, len);
79 if(client->state != CLIENT_CONNECTED) {
80 usbmuxd_log(LL_ERROR, "Attempted to write to client %d not in CONNECTED state", client->fd);
81 return -1;
82 }
83 return send(client->fd, buffer, len, 0);
84}
85
86int client_set_events(struct mux_client *client, short events)
87{
88 if((client->state != CLIENT_CONNECTED) && (client->state != CLIENT_CONNECTING2)) {
89 usbmuxd_log(LL_ERROR, "client_set_events to client %d not in CONNECTED state", client->fd);
90 return -1;
91 }
92 client->devents = events;
93 if(client->state == CLIENT_CONNECTED)
94 client->events = events;
95 return 0;
96}
97
98int client_accept(int listenfd)
99{
100 struct sockaddr_un addr;
101 int cfd;
102 socklen_t len = sizeof(struct sockaddr_un);
103 cfd = accept(listenfd, (struct sockaddr *)&addr, &len);
104 if (cfd < 0) {
105 usbmuxd_log(LL_ERROR, "accept() failed (%s)", strerror(errno));
106 return cfd;
107 }
108
109 struct mux_client *client;
110 client = malloc(sizeof(struct mux_client));
111 memset(client, 0, sizeof(struct mux_client));
112
113 client->fd = cfd;
114 client->ob_buf = malloc(REPLY_BUF_SIZE);
115 client->ob_size = 0;
116 client->ob_capacity = REPLY_BUF_SIZE;
117 client->ib_buf = malloc(CMD_BUF_SIZE);
118 client->ib_size = 0;
119 client->ib_capacity = CMD_BUF_SIZE;
120 client->state = CLIENT_COMMAND;
121 client->events = POLLIN;
122
123 collection_add(&client_list, client);
124
125 usbmuxd_log(LL_INFO, "New client on fd %d", client->fd);
126 return client->fd;
127}
128
129void client_close(struct mux_client *client)
130{
131 usbmuxd_log(LL_INFO, "Disconnecting client fd %d", client->fd);
132 if(client->state == CLIENT_CONNECTING1 || client->state == CLIENT_CONNECTING2) {
133 usbmuxd_log(LL_INFO, "Client died mid-connect, aborting device %d connection", client->connect_device);
134 client->state = CLIENT_DEAD;
135 device_abort_connect(client->connect_device, client);
136 }
137 close(client->fd);
138 if(client->ob_buf)
139 free(client->ob_buf);
140 if(client->ib_buf)
141 free(client->ib_buf);
142 collection_remove(&client_list, client);
143 free(client);
144}
145
146void client_get_fds(struct fdlist *list)
147{
148 FOREACH(struct mux_client *client, &client_list) {
149 fdlist_add(list, FD_CLIENT, client->fd, client->events);
150 } ENDFOREACH
151}
152
153static int send_pkt(struct mux_client *client, uint32_t tag, enum client_msgtype msg, void *payload, int payload_length)
154{
155 struct client_header hdr;
156 hdr.version = CLIENT_PROTOCOL_VERSION;
157 hdr.length = sizeof(hdr) + payload_length;
158 hdr.message = msg;
159 hdr.tag = tag;
160 usbmuxd_log(LL_DEBUG, "send_pkt fd %d tag %d msg %d payload_length %d", client->fd, tag, msg, payload_length);
161 if((client->ob_capacity - client->ob_size) < hdr.length) {
162 usbmuxd_log(LL_ERROR, "Client %d output buffer full (%d bytes) while sending message %d (%d bytes)", client->fd, client->ob_capacity, hdr.message, hdr.length);
163 client_close(client);
164 return -1;
165 }
166 memcpy(client->ob_buf + client->ob_size, &hdr, sizeof(hdr));
167 if(payload && payload_length)
168 memcpy(client->ob_buf + client->ob_size + sizeof(hdr), payload, payload_length);
169 client->ob_size += hdr.length;
170 client->events |= POLLOUT;
171 return hdr.length;
172}
173
174static int send_result(struct mux_client *client, uint32_t tag, uint32_t result)
175{
176 return send_pkt(client, tag, MESSAGE_RESULT, &result, sizeof(uint32_t));
177}
178
179int client_notify_connect(struct mux_client *client, enum client_result result)
180{
181 usbmuxd_log(LL_SPEW, "client_notify_connect fd %d result %d", client->fd, result);
182 if(client->state == CLIENT_DEAD)
183 return -1;
184 if(client->state != CLIENT_CONNECTING1) {
185 usbmuxd_log(LL_ERROR, "client_notify_connect when client %d is not in CONNECTING1 state", client->fd);
186 return -1;
187 }
188 if(send_result(client, client->connect_tag, result) < 0)
189 return -1;
190 if(result == RESULT_OK) {
191 client->state = CLIENT_CONNECTING2;
192 client->events = POLLOUT; // wait for the result packet to go through
193 // no longer need this
194 free(client->ib_buf);
195 client->ib_buf = NULL;
196 } else {
197 client->state = CLIENT_COMMAND;
198 }
199 return 0;
200}
201
202static int notify_device(struct mux_client *client, struct device_info *dev)
203{
204 struct client_msg_dev dmsg;
205 memset(&dmsg, 0, sizeof(dmsg));
206 dmsg.device_id = dev->id;
207 strncpy(dmsg.device_serial, dev->serial, 256);
208 dmsg.device_serial[255] = 0;
209 dmsg.location = dev->location;
210 dmsg.device_pid = dev->pid;
211 return send_pkt(client, 0, MESSAGE_DEVICE_ADD, &dmsg, sizeof(dmsg));
212}
213
214static int start_listen(struct mux_client *client)
215{
216 struct device_info *devs;
217 struct device_info *dev;
218 int count, i;
219
220 client->state = CLIENT_LISTEN;
221 count = device_get_count();
222 if(!count)
223 return 0;
224 devs = malloc(sizeof(struct device_info) * count);
225 count = device_get_list(devs);
226
227 // going to need a larger buffer for many devices
228 int needed_buffer = count * (sizeof(struct client_msg_dev) + sizeof(struct client_header)) + REPLY_BUF_SIZE;
229 if(client->ob_capacity < needed_buffer) {
230 usbmuxd_log(LL_DEBUG, "Enlarging client %d reply buffer %d -> %d to make space for device notifications", client->fd, client->ob_capacity, needed_buffer);
231 client->ob_buf = realloc(client->ob_buf, needed_buffer);
232 client->ob_capacity = needed_buffer;
233 }
234 dev = devs;
235 for(i=0; i<count; i++) {
236 if(notify_device(client, dev++) < 0) {
237 free(devs);
238 return -1;
239 }
240 }
241 free(devs);
242 return count;
243}
244
245static int client_command(struct mux_client *client, struct client_header *hdr, const char *payload)
246{
247 int res;
248 usbmuxd_log(LL_DEBUG, "Client command in fd %d len %d ver %d msg %d tag %d", client->fd, hdr->length, hdr->version, hdr->message, hdr->tag);
249
250 if(client->state != CLIENT_COMMAND) {
251 usbmuxd_log(LL_ERROR, "Client %d command received in the wrong state", client->fd);
252 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
253 return -1;
254 client_close(client);
255 return -1;
256 }
257
258 struct client_msg_connect *ch;
259 switch(hdr->message) {
260 case MESSAGE_LISTEN:
261 if(send_result(client, hdr->tag, 0) < 0)
262 return -1;
263 usbmuxd_log(LL_DEBUG, "Client %d now LISTENING", client->fd);
264 return start_listen(client);
265 case MESSAGE_CONNECT:
266 ch = (void*)payload;
267 usbmuxd_log(LL_DEBUG, "Client %d connection request to device %d port %d", client->fd, ch->device_id, ntohs(ch->port));
268 res = device_start_connect(ch->device_id, ntohs(ch->port), client);
269 if(res < 0) {
270 if(send_result(client, hdr->tag, -res) < 0)
271 return -1;
272 } else {
273 client->connect_tag = hdr->tag;
274 client->connect_device = ch->device_id;
275 client->state = CLIENT_CONNECTING1;
276 }
277 return 0;
278 default:
279 usbmuxd_log(LL_ERROR, "Client %d invalid command %d", client->fd, hdr->message);
280 if(send_result(client, hdr->tag, RESULT_BADCOMMAND) < 0)
281 return -1;
282 return 0;
283 }
284 return -1;
285}
286
287static void process_send(struct mux_client *client)
288{
289 int res;
290 if(!client->ob_size) {
291 usbmuxd_log(LL_WARNING, "Client %d OUT process but nothing to send?", client->fd);
292 client->events &= ~POLLOUT;
293 return;
294 }
295 res = send(client->fd, client->ob_buf, client->ob_size, 0);
296 if(res <= 0) {
297 usbmuxd_log(LL_ERROR, "Send to client fd %d failed: %d %s", client->fd, res, strerror(errno));
298 client_close(client);
299 return;
300 }
301 if(res == client->ob_size) {
302 client->ob_size = 0;
303 client->events &= ~POLLOUT;
304 if(client->state == CLIENT_CONNECTING2) {
305 usbmuxd_log(LL_DEBUG, "Client %d switching to CONNECTED state", client->fd);
306 client->state = CLIENT_CONNECTED;
307 client->events = client->devents;
308 // no longer need this
309 free(client->ob_buf);
310 client->ob_buf = NULL;
311 }
312 } else {
313 client->ob_size -= res;
314 memmove(client->ob_buf, client->ob_buf + res, client->ob_size);
315 }
316}
317static void process_recv(struct mux_client *client)
318{
319 int res;
320 int did_read = 0;
321 if(client->ib_size < sizeof(struct client_header)) {
322 res = recv(client->fd, client->ib_buf + client->ib_size, sizeof(struct client_header) - client->ib_size, 0);
323 if(res <= 0) {
324 if(res < 0)
325 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
326 else
327 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
328 client_close(client);
329 return;
330 }
331 client->ib_size += res;
332 if(client->ib_size < sizeof(struct client_header))
333 return;
334 did_read = 1;
335 }
336 struct client_header *hdr = (void*)client->ib_buf;
337 if(hdr->version != CLIENT_PROTOCOL_VERSION) {
338 usbmuxd_log(LL_INFO, "Client %d version mismatch: expected %d, got %d", client->fd, CLIENT_PROTOCOL_VERSION, hdr->version);
339 client_close(client);
340 }
341 if(hdr->length > client->ib_capacity) {
342 usbmuxd_log(LL_INFO, "Client %d message is too long (%d bytes)", client->fd, hdr->length);
343 client_close(client);
344 }
345 if(hdr->length < sizeof(struct client_header)) {
346 usbmuxd_log(LL_ERROR, "Client %d message is too short (%d bytes)", client->fd, hdr->length);
347 client_close(client);
348 }
349 if(client->ib_size < hdr->length) {
350 if(did_read)
351 return; //maybe we would block, so defer to next loop
352 res = recv(client->fd, client->ib_buf + client->ib_size, hdr->length - client->ib_size, 0);
353 if(res < 0) {
354 usbmuxd_log(LL_ERROR, "Receive from client fd %d failed: %s", client->fd, strerror(errno));
355 client_close(client);
356 return;
357 } else if(res == 0) {
358 usbmuxd_log(LL_INFO, "Client %d connection closed", client->fd);
359 client_close(client);
360 return;
361 }
362 client->ib_size += res;
363 if(client->ib_size < hdr->length)
364 return;
365 }
366 client_command(client, hdr, (char *)(hdr+1));
367 client->ib_size = 0;
368}
369
370void client_process(int fd, short events)
371{
372 struct mux_client *client = NULL;
373 FOREACH(struct mux_client *lc, &client_list) {
374 if(lc->fd == fd) {
375 client = lc;
376 break;
377 }
378 } ENDFOREACH
379
380 if(!client) {
381 usbmuxd_log(LL_ERROR, "client_process: fd %d not found in client list", fd);
382 return;
383 }
384
385 if(client->state == CLIENT_CONNECTED) {
386 usbmuxd_log(LL_SPEW, "client_process in CONNECTED state");
387 device_client_process(client->connect_device, client, events);
388 } else {
389 if(events & POLLIN) {
390 process_recv(client);
391 } else if(events & POLLOUT) { //not both in case client died as part of process_recv
392 process_send(client);
393 }
394 }
395
396}
397
398void client_device_add(struct device_info *dev)
399{
400 usbmuxd_log(LL_DEBUG, "client_device_add: id %d, location 0x%x, serial %s", dev->id, dev->location, dev->serial);
401 FOREACH(struct mux_client *client, &client_list) {
402 if(client->state == CLIENT_LISTEN)
403 notify_device(client, dev);
404 } ENDFOREACH
405}
406void client_device_remove(int device_id)
407{
408 uint32_t id = device_id;
409 usbmuxd_log(LL_DEBUG, "client_device_remove: id %d", device_id);
410 FOREACH(struct mux_client *client, &client_list) {
411 if(client->state == CLIENT_LISTEN)
412 send_pkt(client, 0, MESSAGE_DEVICE_REMOVE, &id, sizeof(uint32_t));
413 } ENDFOREACH
414}
415
416
417void client_init(void)
418{
419 usbmuxd_log(LL_DEBUG, "client_init");
420 collection_init(&client_list);
421}
422
423void client_shutdown(void)
424{
425 usbmuxd_log(LL_DEBUG, "client_shutdown");
426 FOREACH(struct mux_client *client, &client_list) {
427 client_close(client);
428 } ENDFOREACH
429 collection_free(&client_list);
430}
diff --git a/usbmuxd/client.h b/usbmuxd/client.h
new file mode 100644
index 0000000..0cda676
--- /dev/null
+++ b/usbmuxd/client.h
@@ -0,0 +1,92 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __CLIENT_H__
22#define __CLIENT_H__
23
24#include <stdint.h>
25
26struct device_info;
27struct mux_client;
28
29enum client_result {
30 RESULT_OK = 0,
31 RESULT_BADCOMMAND = 1,
32 RESULT_BADDEV = 2,
33 RESULT_CONNREFUSED = 3,
34 // ???
35 // ???
36 RESULT_BADVERSION = 6,
37};
38
39enum client_msgtype {
40 MESSAGE_RESULT = 1,
41 MESSAGE_CONNECT = 2,
42 MESSAGE_LISTEN = 3,
43 MESSAGE_DEVICE_ADD = 4,
44 MESSAGE_DEVICE_REMOVE = 5,
45 //???
46 //???
47 //MESSAGE_PLIST = 8,
48};
49
50#define CLIENT_PROTOCOL_VERSION 0
51
52struct client_header {
53 uint32_t length;
54 uint32_t version;
55 uint32_t message;
56 uint32_t tag;
57};
58
59struct client_msg_result {
60 uint32_t result;
61};
62
63struct client_msg_connect {
64 uint32_t device_id;
65 uint16_t port;
66};
67
68struct client_msg_dev {
69 uint32_t device_id;
70 uint16_t device_pid;
71 char device_serial[256];
72 uint16_t padding;
73 uint32_t location;
74};
75
76int client_read(struct mux_client *client, void *buffer, int len);
77int client_write(struct mux_client *client, void *buffer, int len);
78int client_set_events(struct mux_client *client, short events);
79void client_close(struct mux_client *client);
80int client_notify_connect(struct mux_client *client, enum client_result result);
81
82void client_device_add(struct device_info *dev);
83void client_device_remove(int device_id);
84
85int client_accept(int fd);
86void client_get_fds(struct fdlist *list);
87void client_process(int fd, short events);
88
89void client_init(void);
90void client_shutdown(void);
91
92#endif
diff --git a/usbmuxd/device.c b/usbmuxd/device.c
new file mode 100644
index 0000000..3a5883c
--- /dev/null
+++ b/usbmuxd/device.c
@@ -0,0 +1,751 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#define _BSD_SOURCE
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <sys/time.h>
28#include <netinet/in.h>
29#include <netinet/tcp.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdint.h>
33#include <inttypes.h>
34#include "device.h"
35#include "client.h"
36#include "usb.h"
37#include "log.h"
38
39int next_device_id;
40
41#define DEV_PKTBUF_SIZE 65536
42
43#define CONN_INBUF_SIZE 262144
44#define CONN_OUTBUF_SIZE 65536
45
46#define ACK_TIMEOUT 30
47
48enum mux_protocol {
49 MUX_PROTO_VERSION = 0,
50 MUX_PROTO_TCP = IPPROTO_TCP,
51};
52
53enum mux_dev_state {
54 MUXDEV_INIT, // sent version packet
55 MUXDEV_ACTIVE, // received version packet, active
56 MUXDEV_DEAD // dead
57};
58
59enum mux_conn_state {
60 CONN_CONNECTING, // SYN
61 CONN_CONNECTED, // SYN/SYNACK/ACK -> active
62 CONN_REFUSED, // RST received during SYN
63 CONN_DYING, // RST received
64 CONN_DEAD // being freed; used to prevent infinite recursion between client<->device freeing
65};
66
67struct mux_header
68{
69 uint32_t protocol;
70 uint32_t length;
71};
72
73struct version_header
74{
75 uint32_t major;
76 uint32_t minor;
77 uint32_t padding;
78};
79
80struct mux_device;
81
82#define CONN_ACK_PENDING 1
83
84struct mux_connection
85{
86 struct mux_device *dev;
87 struct mux_client *client;
88 enum mux_conn_state state;
89 uint16_t sport, dport;
90 uint32_t tx_seq, tx_ack, tx_acked, tx_win;
91 uint32_t rx_seq, rx_recvd, rx_ack, rx_win;
92 int max_payload;
93 int sendable;
94 int flags;
95 unsigned char *ib_buf;
96 int ib_size;
97 int ib_capacity;
98 unsigned char *ob_buf;
99 int ob_capacity;
100 short events;
101 uint64_t last_ack_time;
102};
103
104struct mux_device
105{
106 struct usb_device *usbdev;
107 int id;
108 enum mux_dev_state state;
109 struct collection connections;
110 uint16_t next_sport;
111 unsigned char *pktbuf;
112 int pktlen;
113};
114
115static struct collection device_list;
116
117uint64_t mstime64(void)
118{
119 struct timeval tv;
120 gettimeofday(&tv, NULL);
121 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
122}
123
124static int get_next_device_id(void)
125{
126 while(1) {
127 int ok = 1;
128 FOREACH(struct mux_device *dev, &device_list) {
129 if(dev->id == next_device_id) {
130 next_device_id++;
131 ok = 0;
132 break;
133 }
134 } ENDFOREACH
135 if(ok)
136 return next_device_id++;
137 }
138}
139
140static int send_packet(struct mux_device *dev, enum mux_protocol proto, void *header, const void *data, int length)
141{
142 unsigned char *buffer;
143 int hdrlen;
144 int res;
145
146 switch(proto) {
147 case MUX_PROTO_VERSION:
148 hdrlen = sizeof(struct version_header);
149 break;
150 case MUX_PROTO_TCP:
151 hdrlen = sizeof(struct tcphdr);
152 break;
153 default:
154 usbmuxd_log(LL_ERROR, "Invalid protocol %d for outgoing packet (dev %d hdr %p data %p len %d)", proto, dev->id, header, data, length);
155 return -1;
156 }
157 usbmuxd_log(LL_SPEW, "send_packet(%d, 0x%x, %p, %p, %d)", dev->id, proto, header, data, length);
158
159 int total = sizeof(struct mux_header) + hdrlen + length;
160
161 if(total > USB_MTU) {
162 usbmuxd_log(LL_ERROR, "Tried to send packet larger than USB MTU (hdr %d data %d total %d) to device %d", hdrlen, length, total, dev->id);
163 return -1;
164 }
165
166 buffer = malloc(total);
167 struct mux_header *mhdr = (struct mux_header *)buffer;
168 mhdr->protocol = htonl(proto);
169 mhdr->length = htonl(total);;
170 memcpy(buffer + sizeof(struct mux_header), header, hdrlen);
171 if(data && length)
172 memcpy(buffer + sizeof(struct mux_header) + hdrlen, data, length);
173
174 if((res = usb_send(dev->usbdev, buffer, total)) < 0) {
175 usbmuxd_log(LL_ERROR, "usb_send failed while sending packet (len %d) to device %d: %d", total, dev->id, res);
176 free(buffer);
177 return res;
178 }
179 return total;
180}
181
182static uint16_t find_sport(struct mux_device *dev)
183{
184 if(collection_count(&dev->connections) >= 65535)
185 return 0; //insanity
186
187 while(1) {
188 int ok = 1;
189 FOREACH(struct mux_connection *conn, &dev->connections) {
190 if(dev->next_sport == conn->sport) {
191 dev->next_sport++;
192 ok = 0;
193 break;
194 }
195 } ENDFOREACH
196 if(ok)
197 return dev->next_sport++;
198 }
199}
200
201static int send_anon_rst(struct mux_device *dev, uint16_t sport, uint16_t dport, uint32_t ack)
202{
203 struct tcphdr th;
204 memset(&th, 0, sizeof(th));
205 th.th_sport = htons(sport);
206 th.th_dport = htons(dport);
207 th.th_ack = htonl(ack);
208 th.th_flags = TH_RST;
209 th.th_off = sizeof(th) / 4;
210
211 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d flags=0x%x", dev->id, sport, dport, th.th_flags);
212
213 int res = send_packet(dev, MUX_PROTO_TCP, &th, NULL, 0);
214 return res;
215}
216
217static int send_tcp(struct mux_connection *conn, uint8_t flags, const unsigned char *data, int length)
218{
219 struct tcphdr th;
220 memset(&th, 0, sizeof(th));
221 th.th_sport = htons(conn->sport);
222 th.th_dport = htons(conn->dport);
223 th.th_seq = htonl(conn->tx_seq);
224 th.th_ack = htonl(conn->tx_ack);
225 th.th_flags = flags;
226 th.th_off = sizeof(th) / 4;
227 th.th_win = htons(conn->tx_win >> 8);
228
229 usbmuxd_log(LL_DEBUG, "[OUT] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
230 conn->dev->id, conn->sport, conn->dport, conn->tx_seq, conn->tx_ack, flags, conn->tx_win, conn->tx_win >> 8, length);
231
232 int res = send_packet(conn->dev, MUX_PROTO_TCP, &th, data, length);
233 if(res >= 0) {
234 conn->tx_acked = conn->tx_ack;
235 conn->last_ack_time = mstime64();
236 conn->flags &= ~CONN_ACK_PENDING;
237 }
238 return res;
239}
240
241static void connection_teardown(struct mux_connection *conn)
242{
243 int res;
244 if(conn->state == CONN_DEAD)
245 return;
246 usbmuxd_log(LL_DEBUG, "connection_teardown dev %d sport %d dport %d", conn->dev->id, conn->sport, conn->dport);
247 if(conn->dev->state != MUXDEV_DEAD && conn->state != CONN_DYING && conn->state != CONN_REFUSED) {
248 res = send_tcp(conn, TH_RST, NULL, 0);
249 if(res < 0)
250 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, conn->sport, conn->dport);
251 }
252 if(conn->client) {
253 if(conn->state == CONN_REFUSED || conn->state == CONN_CONNECTING) {
254 client_notify_connect(conn->client, RESULT_CONNREFUSED);
255 } else {
256 conn->state = CONN_DEAD;
257 client_close(conn->client);
258 }
259 }
260 if(conn->ib_buf)
261 free(conn->ib_buf);
262 if(conn->ob_buf)
263 free(conn->ob_buf);
264 collection_remove(&conn->dev->connections, conn);
265 free(conn);
266}
267
268int device_start_connect(int device_id, uint16_t dport, struct mux_client *client)
269{
270 struct mux_device *dev = NULL;
271 FOREACH(struct mux_device *cdev, &device_list) {
272 if(cdev->id == device_id) {
273 dev = cdev;
274 break;
275 }
276 } ENDFOREACH
277 if(!dev) {
278 usbmuxd_log(LL_WARNING, "Attempted to connect to nonexistent device %d", device_id);
279 return -RESULT_BADDEV;
280 }
281
282 uint16_t sport = find_sport(dev);
283 if(!sport) {
284 usbmuxd_log(LL_WARNING, "Unable to allocate port for device %d", device_id);
285 return -RESULT_BADDEV;
286 }
287
288 struct mux_connection *conn;
289 conn = malloc(sizeof(struct mux_connection));
290 memset(conn, 0, sizeof(struct mux_connection));
291
292 conn->dev = dev;
293 conn->client = client;
294 conn->state = CONN_CONNECTING;
295 conn->sport = sport;
296 conn->dport = dport;
297 conn->tx_seq = 0;
298 conn->tx_ack = 0;
299 conn->tx_acked = 0;
300 conn->tx_win = 131072;
301 conn->rx_recvd = 0;
302 conn->flags = 0;
303 conn->max_payload = USB_MTU - sizeof(struct mux_header) - sizeof(struct tcphdr);
304
305 conn->ob_buf = malloc(CONN_OUTBUF_SIZE);
306 conn->ob_capacity = CONN_OUTBUF_SIZE;
307 conn->ib_buf = malloc(CONN_INBUF_SIZE);
308 conn->ib_capacity = CONN_INBUF_SIZE;
309 conn->ib_size = 0;
310
311 int res;
312
313 res = send_tcp(conn, TH_SYN, NULL, 0);
314 if(res < 0) {
315 usbmuxd_log(LL_ERROR, "Error sending TCP SYN to device %d (%d->%d)", dev->id, sport, dport);
316 free(conn);
317 return -RESULT_CONNREFUSED; //bleh
318 }
319 collection_add(&dev->connections, conn);
320 return 0;
321}
322
323static void update_connection(struct mux_connection *conn)
324{
325 conn->sendable = conn->rx_win - (conn->tx_seq - conn->rx_ack);
326
327 if(conn->sendable > conn->ob_capacity)
328 conn->sendable = conn->ob_capacity;
329 if(conn->sendable > conn->max_payload)
330 conn->sendable = conn->max_payload;
331
332 if(conn->sendable > 0)
333 conn->events |= POLLIN;
334 else
335 conn->events &= ~POLLIN;
336
337 if(conn->ib_size)
338 conn->events |= POLLOUT;
339 else
340 conn->events &= ~POLLOUT;
341
342 if(conn->tx_acked != conn->tx_ack)
343 conn->flags |= CONN_ACK_PENDING;
344 else
345 conn->flags &= ~CONN_ACK_PENDING;
346
347 usbmuxd_log(LL_SPEW, "update_connection: sendable %d, events %d, flags %d", conn->sendable, conn->events, conn->flags);
348 client_set_events(conn->client, conn->events);
349}
350
351void device_client_process(int device_id, struct mux_client *client, short events)
352{
353 struct mux_connection *conn = NULL;
354 FOREACH(struct mux_device *dev, &device_list) {
355 if(dev->id == device_id) {
356 FOREACH(struct mux_connection *lconn, &dev->connections) {
357 if(lconn->client == client) {
358 conn = lconn;
359 break;
360 }
361 } ENDFOREACH
362 break;
363 }
364 } ENDFOREACH
365
366 if(!conn) {
367 usbmuxd_log(LL_WARNING, "Could not find connection for device %d client %p", device_id, client);
368 return;
369 }
370 usbmuxd_log(LL_SPEW, "device_client_process (%d)", events);
371
372 int res;
373 int size;
374 if(events & POLLOUT) {
375 size = client_write(conn->client, conn->ib_buf, conn->ib_size);
376 if(size <= 0) {
377 usbmuxd_log(LL_DEBUG, "error writing to client (%d)", size);
378 connection_teardown(conn);
379 return;
380 }
381 conn->tx_ack += size;
382 if(size == conn->ib_size) {
383 conn->ib_size = 0;
384 } else {
385 conn->ib_size -= size;
386 memmove(conn->ib_buf, conn->ib_buf + size, conn->ib_size);
387 }
388 }
389 if(events & POLLIN) {
390 size = client_read(conn->client, conn->ob_buf, conn->sendable);
391 if(size <= 0) {
392 usbmuxd_log(LL_DEBUG, "error reading from client (%d)", size);
393 connection_teardown(conn);
394 return;
395 }
396 res = send_tcp(conn, TH_ACK, conn->ob_buf, size);
397 if(res < 0) {
398 connection_teardown(conn);
399 return;
400 }
401 conn->tx_seq += size;
402 }
403
404 update_connection(conn);
405}
406
407static void connection_device_input(struct mux_connection *conn, unsigned char *payload, int payload_length)
408{
409 if((conn->ib_size + payload_length) > conn->ib_capacity) {
410 usbmuxd_log(LL_ERROR, "Input buffer overflow on device %d connection %d->%d (space=%d, payload=%d)", conn->dev->id, conn->sport, conn->dport, conn->ib_capacity-conn->ib_size, payload_length);
411 connection_teardown(conn);
412 return;
413 }
414 memcpy(conn->ib_buf + conn->ib_size, payload, payload_length);
415 conn->ib_size += payload_length;
416 conn->rx_recvd += payload_length;
417 update_connection(conn);
418}
419
420void device_abort_connect(int device_id, struct mux_client *client)
421{
422 FOREACH(struct mux_device *dev, &device_list) {
423 if(dev->id == device_id) {
424 FOREACH(struct mux_connection *conn, &dev->connections) {
425 if(conn->client == client) {
426 connection_teardown(conn);
427 return;
428 }
429 } ENDFOREACH
430 usbmuxd_log(LL_WARNING, "Attempted to abort for nonexistent connection for device %d", device_id);
431 return;
432 }
433 } ENDFOREACH
434 usbmuxd_log(LL_WARNING, "Attempted to abort connection for nonexistent device %d", device_id);
435}
436
437static void device_version_input(struct mux_device *dev, struct version_header *vh)
438{
439 if(dev->state != MUXDEV_INIT) {
440 usbmuxd_log(LL_WARNING, "Version packet from already initialized device %d", dev->id);
441 return;
442 }
443 vh->major = ntohl(vh->major);
444 vh->minor = ntohl(vh->minor);
445 if(vh->major != 1 || vh->minor != 0) {
446 usbmuxd_log(LL_ERROR, "Device %d has unknown version %d.%d\n", dev->id, vh->major, vh->minor);
447 collection_remove(&device_list, dev);
448 free(dev);
449 return;
450 }
451 usbmuxd_log(LL_NOTICE, "Connected to v%d.%d device %d on location 0x%x with serial number %s", vh->major, vh->minor, dev->id, usb_get_location(dev->usbdev), usb_get_serial(dev->usbdev));
452 dev->state = MUXDEV_ACTIVE;
453 collection_init(&dev->connections);
454 struct device_info info;
455 info.id = dev->id;
456 info.location = usb_get_location(dev->usbdev);
457 info.serial = usb_get_serial(dev->usbdev);
458 info.pid = usb_get_pid(dev->usbdev);
459 client_device_add(&info);
460}
461
462static void device_tcp_input(struct mux_device *dev, struct tcphdr *th, unsigned char *payload, int payload_length)
463{
464 usbmuxd_log(LL_DEBUG, "[IN] dev=%d sport=%d dport=%d seq=%d ack=%d flags=0x%x window=%d[%d] len=%d",
465 dev->id, ntohs(th->th_sport), ntohs(th->th_dport), ntohl(th->th_seq), ntohl(th->th_ack), th->th_flags, ntohs(th->th_win) << 8, ntohs(th->th_win), payload_length);
466
467 uint16_t sport = ntohs(th->th_dport);
468 uint16_t dport = ntohs(th->th_sport);
469 struct mux_connection *conn = NULL;
470 FOREACH(struct mux_connection *lconn, &dev->connections) {
471 if(lconn->sport == sport && lconn->dport == dport) {
472 conn = lconn;
473 break;
474 }
475 } ENDFOREACH
476
477 if(!conn) {
478 usbmuxd_log(LL_WARNING, "No connection for device %d incoming packet %d->%d", dev->id, dport, sport);
479 if(!(th->th_flags & TH_RST)) {
480 if(send_anon_rst(dev, sport, dport, ntohl(th->th_seq)) < 0)
481 usbmuxd_log(LL_ERROR, "Error sending TCP RST to device %d (%d->%d)", conn->dev->id, sport, dport);
482 }
483 return;
484 }
485
486 conn->rx_seq = ntohl(th->th_seq);
487 conn->rx_ack = ntohl(th->th_ack);
488 conn->rx_win = ntohs(th->th_win) << 8;
489
490 if(th->th_flags & TH_RST) {
491 char *buf = malloc(payload_length+1);
492 memcpy(buf, payload, payload_length);
493 if(payload_length && (buf[payload_length-1] == '\n'))
494 buf[payload_length-1] = 0;
495 buf[payload_length] = 0;
496 usbmuxd_log(LL_DEBUG, "RST reason: %s", buf);
497 free(buf);
498 }
499
500 if(conn->state == CONN_CONNECTING) {
501 if(th->th_flags != (TH_SYN|TH_ACK)) {
502 if(th->th_flags & TH_RST)
503 conn->state = CONN_REFUSED;
504 usbmuxd_log(LL_INFO, "Connection refused by device %d (%d->%d)", dev->id, sport, dport);
505 connection_teardown(conn); //this also sends the notification to the client
506 } else {
507 conn->tx_seq++;
508 conn->tx_ack++;
509 conn->rx_recvd = conn->rx_seq;
510 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
511 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, sport, dport);
512 connection_teardown(conn);
513 return;
514 }
515 conn->state = CONN_CONNECTED;
516 if(client_notify_connect(conn->client, RESULT_OK) < 0) {
517 conn->client = NULL;
518 connection_teardown(conn);
519 }
520 update_connection(conn);
521 }
522 } else if(conn->state == CONN_CONNECTED) {
523 if(th->th_flags != TH_ACK) {
524 usbmuxd_log(LL_INFO, "Connection reset by device %d (%d->%d)", dev->id, sport, dport);
525 if(th->th_flags & TH_RST)
526 conn->state = CONN_DYING;
527 connection_teardown(conn);
528 } else {
529 connection_device_input(conn, payload, payload_length);
530 }
531 }
532}
533
534void device_data_input(struct usb_device *usbdev, unsigned char *buffer, int length)
535{
536 struct mux_device *dev = NULL;
537 FOREACH(struct mux_device *tdev, &device_list) {
538 if(tdev->usbdev == usbdev) {
539 dev = tdev;
540 break;
541 }
542 } ENDFOREACH
543 if(!dev) {
544 usbmuxd_log(LL_WARNING, "Cannot find device entry for RX input from USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
545 return;
546 }
547
548 if(!length)
549 return;
550
551 usbmuxd_log(LL_SPEW, "Mux data input for device %p: %p len %d", dev, buffer, length);
552
553 // handle broken up transfers
554 if(dev->pktlen) {
555 memcpy(dev->pktbuf + dev->pktlen, buffer, length);
556 struct mux_header *mhdr = (struct mux_header *)dev->pktbuf;
557 if((length < USB_MRU) || (ntohl(mhdr->length) == length)) {
558 buffer = dev->pktbuf;
559 length += dev->pktlen;
560 dev->pktlen = 0;
561 usbmuxd_log(LL_SPEW, "Gathered mux data from buffer (total size: %d)", length);
562 } else {
563 dev->pktlen += length;
564 usbmuxd_log(LL_SPEW, "Appended mux data to buffer (total size: %d)", dev->pktlen);
565 return;
566 }
567 } else {
568 struct mux_header *mhdr = (struct mux_header *)buffer;
569 if((length == USB_MRU) && (length < ntohl(mhdr->length))) {
570 memcpy(dev->pktbuf, buffer, length);
571 dev->pktlen = length;
572 usbmuxd_log(LL_SPEW, "Copied mux data to buffer (size: %d)", dev->pktlen);
573 return;
574 }
575 }
576
577 struct mux_header *mhdr = (struct mux_header *)buffer;
578
579 if(ntohl(mhdr->length) != length) {
580 usbmuxd_log(LL_ERROR, "Incoming packet size mismatch (dev %d, expected %d, got %d)", dev->id, ntohl(mhdr->length), length);
581 return;
582 }
583
584 struct tcphdr *th;
585 unsigned char *payload;
586 int payload_length;
587
588 switch(ntohl(mhdr->protocol)) {
589 case MUX_PROTO_VERSION:
590 device_version_input(dev, (struct version_header *)(mhdr+1));
591 break;
592 case MUX_PROTO_TCP:
593 th = (struct tcphdr *)(mhdr+1);
594 payload = (unsigned char *)(th+1);
595 payload_length = length - sizeof(struct tcphdr) - sizeof(struct mux_header);
596 device_tcp_input(dev, (struct tcphdr *)(mhdr+1), payload, payload_length);
597 break;
598 default:
599 usbmuxd_log(LL_ERROR, "Incoming packet for device %d has unknown protocol 0x%x)", dev->id, ntohl(mhdr->protocol));
600 break;
601 }
602
603}
604
605int device_add(struct usb_device *usbdev)
606{
607 int res;
608 int id = get_next_device_id();
609 struct mux_device *dev;
610 usbmuxd_log(LL_NOTICE, "Connecting to new device on location 0x%x as ID %d", usb_get_location(usbdev), id);
611 dev = malloc(sizeof(struct mux_device));
612 dev->id = id;
613 dev->usbdev = usbdev;
614 dev->state = MUXDEV_INIT;
615 dev->next_sport = 1;
616 dev->pktbuf = malloc(DEV_PKTBUF_SIZE);
617 dev->pktlen = 0;
618 struct version_header vh;
619 vh.major = htonl(1);
620 vh.minor = htonl(0);
621 vh.padding = 0;
622 if((res = send_packet(dev, MUX_PROTO_VERSION, &vh, NULL, 0)) < 0) {
623 usbmuxd_log(LL_ERROR, "Error sending version request packet to device %d\n", id);
624 free(dev);
625 return res;
626 }
627 collection_add(&device_list, dev);
628 return 0;
629}
630
631void device_remove(struct usb_device *usbdev)
632{
633 FOREACH(struct mux_device *dev, &device_list) {
634 if(dev->usbdev == usbdev) {
635 usbmuxd_log(LL_NOTICE, "Removed device %d on location 0x%x", dev->id, usb_get_location(usbdev));
636 if(dev->state == MUXDEV_ACTIVE) {
637 dev->state = MUXDEV_DEAD;
638 FOREACH(struct mux_connection *conn, &dev->connections) {
639 connection_teardown(conn);
640 } ENDFOREACH
641 client_device_remove(dev->id);
642 collection_free(&dev->connections);
643 }
644 collection_remove(&device_list, dev);
645 free(dev->pktbuf);
646 free(dev);
647 return;
648 }
649 } ENDFOREACH
650 usbmuxd_log(LL_WARNING, "Cannot find device entry while removing USB device %p on location 0x%x", usbdev, usb_get_location(usbdev));
651}
652
653int device_get_count(void)
654{
655 int count = 0;
656 FOREACH(struct mux_device *dev, &device_list) {
657 if(dev->state == MUXDEV_ACTIVE)
658 count++;
659 } ENDFOREACH
660 return count;
661}
662
663int device_get_list(struct device_info *p)
664{
665 int count = 0;
666 FOREACH(struct mux_device *dev, &device_list) {
667 if(dev->state == MUXDEV_ACTIVE) {
668 p->id = dev->id;
669 p->serial = usb_get_serial(dev->usbdev);
670 p->location = usb_get_location(dev->usbdev);
671 p->pid = usb_get_pid(dev->usbdev);
672 count++;
673 p++;
674 }
675 } ENDFOREACH
676 return count;
677}
678
679int device_get_timeout(void)
680{
681 uint64_t oldest = (uint64_t)-1;
682 FOREACH(struct mux_device *dev, &device_list) {
683 if(dev->state == MUXDEV_ACTIVE) {
684 FOREACH(struct mux_connection *conn, &dev->connections) {
685 if((conn->state == CONN_CONNECTED) && (conn->flags & CONN_ACK_PENDING) && conn->last_ack_time < oldest)
686 oldest = conn->last_ack_time;
687 } ENDFOREACH
688 }
689 } ENDFOREACH
690 uint64_t ct = mstime64();
691 if(oldest == -1)
692 return 100000; //meh
693 if((ct - oldest) > ACK_TIMEOUT)
694 return 0;
695 return ACK_TIMEOUT - (ct - oldest);
696}
697
698void device_check_timeouts(void)
699{
700 uint64_t ct = mstime64();
701 FOREACH(struct mux_device *dev, &device_list) {
702 if(dev->state == MUXDEV_ACTIVE) {
703 FOREACH(struct mux_connection *conn, &dev->connections) {
704 if((conn->state == CONN_CONNECTED) &&
705 (conn->flags & CONN_ACK_PENDING) &&
706 (ct - conn->last_ack_time) > ACK_TIMEOUT) {
707 usbmuxd_log(LL_DEBUG, "Sending ACK due to expired timeout (%" PRIu64 " -> %" PRIu64 ")", conn->last_ack_time, ct);
708 if(send_tcp(conn, TH_ACK, NULL, 0) < 0) {
709 usbmuxd_log(LL_ERROR, "Error sending TCP ACK to device %d (%d->%d)", dev->id, conn->sport, conn->dport);
710 connection_teardown(conn);
711 }
712 }
713 } ENDFOREACH
714 }
715 } ENDFOREACH
716}
717
718void device_init(void)
719{
720 usbmuxd_log(LL_DEBUG, "device_init");
721 collection_init(&device_list);
722 next_device_id = 1;
723}
724
725void device_kill_connections(void)
726{
727 usbmuxd_log(LL_DEBUG, "device_kill_connections");
728 FOREACH(struct mux_device *dev, &device_list) {
729 if(dev->state != MUXDEV_INIT) {
730 FOREACH(struct mux_connection *conn, &dev->connections) {
731 connection_teardown(conn);
732 } ENDFOREACH
733 }
734 } ENDFOREACH
735 // give USB a while to send the final connection RSTs and the like
736 usb_process_timeout(100);
737}
738
739void device_shutdown(void)
740{
741 usbmuxd_log(LL_DEBUG, "device_shutdown");
742 FOREACH(struct mux_device *dev, &device_list) {
743 FOREACH(struct mux_connection *conn, &dev->connections) {
744 connection_teardown(conn);
745 } ENDFOREACH
746 collection_free(&dev->connections);
747 collection_remove(&device_list, dev);
748 free(dev);
749 } ENDFOREACH
750 collection_free(&device_list);
751}
diff --git a/usbmuxd/device.h b/usbmuxd/device.h
new file mode 100644
index 0000000..ce6c50b
--- /dev/null
+++ b/usbmuxd/device.h
@@ -0,0 +1,52 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __DEVICE_H__
22#define __DEVICE_H__
23
24#include "usb.h"
25#include "client.h"
26
27struct device_info {
28 int id;
29 const char *serial;
30 uint32_t location;
31 uint16_t pid;
32};
33
34void device_data_input(struct usb_device *dev, unsigned char *buf, int length);
35
36int device_add(struct usb_device *dev);
37void device_remove(struct usb_device *dev);
38
39int device_start_connect(int device_id, uint16_t port, struct mux_client *client);
40void device_client_process(int device_id, struct mux_client *client, short events);
41void device_abort_connect(int device_id, struct mux_client *client);
42
43int device_get_count(void);
44int device_get_list(struct device_info *p);
45
46int device_get_timeout(void);
47void device_check_timeouts(void);
48
49void device_init(void);
50void device_kill_connections(void);
51void device_shutdown(void);
52#endif
diff --git a/usbmuxd/log.c b/usbmuxd/log.c
new file mode 100644
index 0000000..2ccb3cc
--- /dev/null
+++ b/usbmuxd/log.c
@@ -0,0 +1,94 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdarg.h>
29#include <time.h>
30#include <sys/time.h>
31#include <syslog.h>
32
33#include "log.h"
34
35int log_level = LL_WARNING;
36
37int log_syslog = 0;
38
39void log_enable_syslog()
40{
41 if (!log_syslog) {
42 openlog("usbmuxd", LOG_PID, 0);
43 log_syslog = 1;
44 }
45}
46
47void log_disable_syslog()
48{
49 if (log_syslog) {
50 closelog();
51 }
52}
53
54static int level_to_syslog_level(int level)
55{
56 int result = level + LOG_CRIT;
57 if (result > LOG_DEBUG) {
58 result = LOG_DEBUG;
59 }
60 return result;
61}
62
63void usbmuxd_log(enum loglevel level, const char *fmt, ...)
64{
65 va_list ap;
66 char *fs;
67 struct timeval ts;
68 struct tm *tp;
69
70 gettimeofday(&ts, NULL);
71 tp = localtime(&ts.tv_sec);
72
73 if(level > log_level)
74 return;
75
76 fs = malloc(20 + strlen(fmt));
77
78 if(log_syslog) {
79 sprintf(fs, "[%d] %s\n", level, fmt);
80 } else {
81 strftime(fs, 10, "[%H:%M:%S", tp);
82 sprintf(fs+9, ".%03d][%d] %s\n", (int)(ts.tv_usec / 1000), level, fmt);
83 }
84
85 va_start(ap, fmt);
86 if (log_syslog) {
87 vsyslog(level_to_syslog_level(level), fs, ap);
88 } else {
89 vfprintf(stderr, fs, ap);
90 }
91 va_end(ap);
92
93 free(fs);
94}
diff --git a/usbmuxd/log.h b/usbmuxd/log.h
new file mode 100644
index 0000000..4a2ac2e
--- /dev/null
+++ b/usbmuxd/log.h
@@ -0,0 +1,43 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __LOG_H__
22#define __LOG_H__
23
24enum loglevel {
25 LL_FATAL = 0,
26 LL_ERROR,
27 LL_WARNING,
28 LL_NOTICE,
29 LL_INFO,
30 LL_DEBUG,
31 LL_SPEW,
32 LL_FLOOD,
33};
34
35extern int log_level;
36
37void log_enable_syslog();
38void log_disable_syslog();
39
40void usbmuxd_log(enum loglevel level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
41
42
43#endif
diff --git a/usbmuxd/main.c b/usbmuxd/main.c
new file mode 100644
index 0000000..dde99c2
--- /dev/null
+++ b/usbmuxd/main.c
@@ -0,0 +1,548 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#define _BSD_SOURCE
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <errno.h>
29#include <string.h>
30#include <stdlib.h>
31#include <signal.h>
32#include <unistd.h>
33#include <sys/socket.h>
34#include <sys/un.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <fcntl.h>
38#include <getopt.h>
39#include <pwd.h>
40#include <grp.h>
41
42#include "log.h"
43#include "usb.h"
44#include "device.h"
45#include "client.h"
46
47static const char *socket_path = "/var/run/usbmuxd";
48static const char *lockfile = "/var/run/usbmuxd.pid";
49
50int should_exit;
51
52static int verbose = 0;
53static int foreground = 0;
54static int drop_privileges = 0;
55static const char *drop_user = "usbmux";
56static int opt_udev = 0;
57static int opt_exit = 0;
58static int exit_signal = 0;
59static int daemon_pipe;
60
61static int report_to_parent = 0;
62
63int create_socket(void) {
64 struct sockaddr_un bind_addr;
65 int listenfd;
66
67 if(unlink(socket_path) == -1 && errno != ENOENT) {
68 usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
69 return -1;
70 }
71
72 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
73 if (listenfd == -1) {
74 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
75 return -1;
76 }
77
78 bzero(&bind_addr, sizeof(bind_addr));
79 bind_addr.sun_family = AF_UNIX;
80 strcpy(bind_addr.sun_path, socket_path);
81 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
82 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
83 return -1;
84 }
85
86 // Start listening
87 if (listen(listenfd, 5) != 0) {
88 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
89 return -1;
90 }
91
92 chmod(socket_path, 0666);
93
94 return listenfd;
95}
96
97void handle_signal(int sig)
98{
99 if (sig != SIGUSR1) {
100 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
101 should_exit = 1;
102 } else {
103 if(opt_udev) {
104 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
105 if (device_get_count() > 0) {
106 // we can't quit, there are still devices attached.
107 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
108 } else {
109 // it's safe to quit
110 should_exit = 1;
111 }
112 } else {
113 usbmuxd_log(LL_INFO, "Caught SIGUSR1 but we weren't started in --udev mode, ignoring");
114 }
115 }
116}
117
118void set_signal_handlers(void)
119{
120 struct sigaction sa;
121 memset(&sa, 0, sizeof(struct sigaction));
122 sa.sa_handler = handle_signal;
123 sigaction(SIGINT, &sa, NULL);
124 sigaction(SIGQUIT, &sa, NULL);
125 sigaction(SIGTERM, &sa, NULL);
126 sigaction(SIGUSR1, &sa, NULL);
127}
128
129int main_loop(int listenfd)
130{
131 int to, cnt, i, dto;
132 struct fdlist pollfds;
133
134 while(!should_exit) {
135 usbmuxd_log(LL_FLOOD, "main_loop iteration");
136 to = usb_get_timeout();
137 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
138 dto = device_get_timeout();
139 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to);
140 if(dto < to)
141 to = dto;
142
143 fdlist_create(&pollfds);
144 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
145 usb_get_fds(&pollfds);
146 client_get_fds(&pollfds);
147 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
148
149 cnt = poll(pollfds.fds, pollfds.count, to);
150 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
151
152 if(cnt == -1) {
153 if(errno == EINTR && should_exit) {
154 usbmuxd_log(LL_INFO, "event processing interrupted");
155 fdlist_free(&pollfds);
156 return 0;
157 }
158 } else if(cnt == 0) {
159 if(usb_process() < 0) {
160 usbmuxd_log(LL_FATAL, "usb_process() failed");
161 fdlist_free(&pollfds);
162 return -1;
163 }
164 device_check_timeouts();
165 } else {
166 int done_usb = 0;
167 for(i=0; i<pollfds.count; i++) {
168 if(pollfds.fds[i].revents) {
169 if(!done_usb && pollfds.owners[i] == FD_USB) {
170 if(usb_process() < 0) {
171 usbmuxd_log(LL_FATAL, "usb_process() failed");
172 fdlist_free(&pollfds);
173 return -1;
174 }
175 done_usb = 1;
176 }
177 if(pollfds.owners[i] == FD_LISTEN) {
178 if(client_accept(listenfd) < 0) {
179 usbmuxd_log(LL_FATAL, "client_accept() failed");
180 fdlist_free(&pollfds);
181 return -1;
182 }
183 }
184 if(pollfds.owners[i] == FD_CLIENT) {
185 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
186 }
187 }
188 }
189 }
190 fdlist_free(&pollfds);
191 }
192 return 0;
193}
194
195/**
196 * make this program run detached from the current console
197 */
198static int daemonize(void)
199{
200 pid_t pid;
201 pid_t sid;
202 int pfd[2];
203 int res;
204
205 // already a daemon
206 if (getppid() == 1)
207 return 0;
208
209 if((res = pipe(pfd)) < 0) {
210 usbmuxd_log(LL_FATAL, "pipe() failed.");
211 return res;
212 }
213
214 pid = fork();
215 if (pid < 0) {
216 usbmuxd_log(LL_FATAL, "fork() failed.");
217 return pid;
218 }
219
220 if (pid > 0) {
221 // exit parent process
222 int status;
223 close(pfd[1]);
224
225 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
226 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
227 exit(1);
228 }
229 if(status != 0)
230 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
231 exit(status);
232 }
233 // At this point we are executing as the child process
234 // but we need to do one more fork
235
236 daemon_pipe = pfd[1];
237 close(pfd[0]);
238 report_to_parent = 1;
239
240 // Change the file mode mask
241 umask(0);
242
243 // Create a new SID for the child process
244 sid = setsid();
245 if (sid < 0) {
246 usbmuxd_log(LL_FATAL, "setsid() failed.");
247 return -1;
248 }
249
250 pid = fork();
251 if (pid < 0) {
252 usbmuxd_log(LL_FATAL, "fork() failed (second).");
253 return pid;
254 }
255
256 if (pid > 0) {
257 // exit parent process
258 close(daemon_pipe);
259 exit(0);
260 }
261
262 // Change the current working directory.
263 if ((chdir("/")) < 0) {
264 usbmuxd_log(LL_FATAL, "chdir() failed");
265 return -2;
266 }
267 // Redirect standard files to /dev/null
268 if (!freopen("/dev/null", "r", stdin)) {
269 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
270 return -3;
271 }
272 if (!freopen("/dev/null", "w", stdout)) {
273 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
274 return -3;
275 }
276
277 return 0;
278}
279
280static int notify_parent(int status)
281{
282 int res;
283
284 report_to_parent = 0;
285 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
286 usbmuxd_log(LL_FATAL, "Could not notify parent!");
287 if(res >= 0)
288 return -2;
289 else
290 return res;
291 }
292 close(daemon_pipe);
293 if (!freopen("/dev/null", "w", stderr)) {
294 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
295 return -1;
296 }
297 return 0;
298}
299
300static void usage()
301{
302 printf("usage: usbmuxd [options]\n");
303 printf("\t-h|--help Print this message.\n");
304 printf("\t-v|--verbose Be verbose (use twice or more to increase).\n");
305 printf("\t-f|--foreground Do not daemonize (implies one -v).\n");
306 printf("\t-U|--user[=USER] Change to this user after startup (needs usb privileges).\n");
307 printf("\t If USER is not specified, defaults to usbmux.\n");
308 printf("\t-u|--udev Run in udev operation mode.\n");
309 printf("\t-x|--exit Tell a running instance to exit if there are no devices\n");
310 printf("\t connected (must be in udev mode).\n");
311 printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n");
312 printf("\t devices connected (always works).\n");
313 printf("\n");
314}
315
316static void parse_opts(int argc, char **argv)
317{
318 static struct option longopts[] = {
319 {"help", 0, NULL, 'h'},
320 {"foreground", 0, NULL, 'f'},
321 {"verbose", 0, NULL, 'v'},
322 {"user", 2, NULL, 'U'},
323 {"udev", 0, NULL, 'u'},
324 {"exit", 0, NULL, 'x'},
325 {"force-exit", 0, NULL, 'X'},
326 {NULL, 0, NULL, 0}
327 };
328 int c;
329
330 while (1) {
331 c = getopt_long(argc, argv, "hfvuU::xX", longopts, (int *) 0);
332 if (c == -1) {
333 break;
334 }
335
336 switch (c) {
337 case 'h':
338 usage();
339 exit(0);
340 case 'f':
341 foreground = 1;
342 break;
343 case 'v':
344 ++verbose;
345 break;
346 case 'U':
347 drop_privileges = 1;
348 if(optarg)
349 drop_user = optarg;
350 break;
351 case 'u':
352 opt_udev = 1;
353 break;
354 case 'x':
355 opt_exit = 1;
356 exit_signal = SIGUSR1;
357 break;
358 case 'X':
359 opt_exit = 1;
360 exit_signal = SIGTERM;
361 break;
362 default:
363 usage();
364 exit(2);
365 }
366 }
367}
368
369int main(int argc, char *argv[])
370{
371 int listenfd;
372 int res = 0;
373 int lfd;
374 struct flock lock;
375 char pids[10];
376
377 parse_opts(argc, argv);
378
379 argc -= optind;
380 argv += optind;
381
382 if (!foreground) {
383 verbose += LL_WARNING;
384 log_enable_syslog();
385 } else {
386 verbose += LL_NOTICE;
387 }
388
389 /* set log level to specified verbosity */
390 log_level = verbose;
391
392 usbmuxd_log(LL_NOTICE, "usbmux v0.1 starting up");
393 should_exit = 0;
394
395 set_signal_handlers();
396
397 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
398 if(res == -1) {
399 usbmuxd_log(LL_FATAL, "Could not open lockfile");
400 goto terminate;
401 }
402 lock.l_type = F_WRLCK;
403 lock.l_whence = SEEK_SET;
404 lock.l_start = 0;
405 lock.l_len = 0;
406 fcntl(lfd, F_GETLK, &lock);
407 close(lfd);
408 if (lock.l_type != F_UNLCK) {
409 if (opt_exit) {
410 if (lock.l_pid && !kill(lock.l_pid, 0)) {
411 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
412 res = 0;
413 if (kill(lock.l_pid, exit_signal) < 0) {
414 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
415 res = -1;
416 }
417 goto terminate;
418 } else {
419 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
420 res = -1;
421 goto terminate;
422 }
423 } else {
424 if (!opt_udev) {
425 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
426 res = -1;
427 } else {
428 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). exiting.", lock.l_pid);
429 res = 0;
430 }
431 goto terminate;
432 }
433 }
434 unlink(lockfile);
435
436 if (opt_exit) {
437 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
438 goto terminate;
439 }
440
441 if (!foreground) {
442 if ((res = daemonize()) < 0) {
443 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
444 usbmuxd_log(LL_FATAL, "Could not daemonize!");
445 goto terminate;
446 }
447 }
448
449 // now open the lockfile and place the lock
450 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
451 if(res < 0) {
452 usbmuxd_log(LL_FATAL, "Could not open lockfile");
453 goto terminate;
454 }
455 lock.l_type = F_WRLCK;
456 lock.l_whence = SEEK_SET;
457 lock.l_start = 0;
458 lock.l_len = 0;
459 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
460 usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
461 goto terminate;
462 }
463 sprintf(pids, "%d", getpid());
464 if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
465 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
466 if(res >= 0)
467 res = -2;
468 goto terminate;
469 }
470
471 usbmuxd_log(LL_INFO, "Creating socket");
472 res = listenfd = create_socket();
473 if(listenfd < 0)
474 goto terminate;
475
476 // drop elevated privileges
477 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
478 struct passwd *pw = getpwnam(drop_user);
479 if (!pw) {
480 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
481 res = -1;
482 goto terminate;
483 }
484
485 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
486 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
487 goto terminate;
488 }
489 if ((res = setgid(pw->pw_gid)) < 0) {
490 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
491 goto terminate;
492 }
493 if ((res = setuid(pw->pw_uid)) < 0) {
494 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
495 goto terminate;
496 }
497
498 // security check
499 if (setuid(0) != -1) {
500 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
501 res = -1;
502 goto terminate;
503 }
504 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
505 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
506 res = -1;
507 goto terminate;
508 }
509 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
510 }
511
512 client_init();
513 device_init();
514 usbmuxd_log(LL_INFO, "Initializing USB");
515 if((res = usb_init()) < 0)
516 goto terminate;
517
518 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
519
520 usbmuxd_log(LL_NOTICE, "Initialization complete");
521
522 if (report_to_parent)
523 if((res = notify_parent(0)) < 0)
524 goto terminate;
525
526 res = main_loop(listenfd);
527 if(res < 0)
528 usbmuxd_log(LL_FATAL, "main_loop failed");
529
530 usbmuxd_log(LL_NOTICE, "usbmux shutting down");
531 device_kill_connections();
532 usb_shutdown();
533 device_shutdown();
534 client_shutdown();
535 usbmuxd_log(LL_NOTICE, "Shutdown complete");
536
537terminate:
538 log_disable_syslog();
539
540 if (res < 0)
541 res = -res;
542 else
543 res = 0;
544 if (report_to_parent)
545 notify_parent(res);
546
547 return res;
548}
diff --git a/usbmuxd/usb-linux.c b/usbmuxd/usb-linux.c
new file mode 100644
index 0000000..6e99a95
--- /dev/null
+++ b/usbmuxd/usb-linux.c
@@ -0,0 +1,503 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdint.h>
28#include <string.h>
29
30#include <libusb.h>
31
32#include "usb.h"
33#include "log.h"
34#include "device.h"
35
36// interval for device connection/disconnection polling, in milliseconds
37// we need this because there is currently no asynchronous device discovery mechanism in libusb
38#define DEVICE_POLL_TIME 1000
39
40struct usb_device {
41 libusb_device_handle *dev;
42 uint8_t bus, address;
43 uint16_t vid, pid;
44 char serial[256];
45 int alive;
46 struct libusb_transfer *rx_xfer;
47 struct collection tx_xfers;
48};
49
50static struct collection device_list;
51
52static struct timeval next_dev_poll_time;
53
54static int devlist_failures;
55
56static void usb_disconnect(struct usb_device *dev)
57{
58 if(!dev->dev) {
59 return;
60 }
61
62 // kill the rx xfer and tx xfers and try to make sure the callbacks get called before we free the device
63 if(dev->rx_xfer) {
64 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling RX xfer");
65 libusb_cancel_transfer(dev->rx_xfer);
66 }
67 FOREACH(struct libusb_transfer *xfer, &dev->tx_xfers) {
68 usbmuxd_log(LL_DEBUG, "usb_disconnect: cancelling TX xfer %p", xfer);
69 libusb_cancel_transfer(xfer);
70 } ENDFOREACH
71
72 while(dev->rx_xfer || collection_count(&dev->tx_xfers)) {
73 struct timeval tv;
74 int res;
75
76 tv.tv_sec = 0;
77 tv.tv_usec = 1000;
78 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
79 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for usb_disconnect failed: %d", res);
80 break;
81 }
82 }
83 collection_free(&dev->tx_xfers);
84 libusb_release_interface(dev->dev, USB_INTERFACE);
85 libusb_close(dev->dev);
86 dev->dev = NULL;
87 collection_remove(&device_list, dev);
88 free(dev);
89}
90
91static void tx_callback(struct libusb_transfer *xfer)
92{
93 struct usb_device *dev = xfer->user_data;
94 usbmuxd_log(LL_SPEW, "TX callback dev %d-%d len %d -> %d status %d", dev->bus, dev->address, xfer->length, xfer->actual_length, xfer->status);
95 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
96 switch(xfer->status) {
97 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
98 case LIBUSB_TRANSFER_ERROR:
99 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
100 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
101 break;
102 case LIBUSB_TRANSFER_TIMED_OUT:
103 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
104 break;
105 case LIBUSB_TRANSFER_CANCELLED:
106 usbmuxd_log(LL_DEBUG, "Device %d-%d TX transfer cancelled", dev->bus, dev->address);
107 break;
108 case LIBUSB_TRANSFER_STALL:
109 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
110 break;
111 case LIBUSB_TRANSFER_NO_DEVICE:
112 // other times, this happens, and also even when we abort the transfer after device removal
113 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
114 break;
115 case LIBUSB_TRANSFER_OVERFLOW:
116 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
117 break;
118 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
119 }
120 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
121 // we'll do device_remove there too
122 dev->alive = 0;
123 }
124 if(xfer->buffer)
125 free(xfer->buffer);
126 collection_remove(&dev->tx_xfers, xfer);
127 libusb_free_transfer(xfer);
128}
129
130int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
131{
132 int res;
133 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
134 libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, (void*)buf, length, tx_callback, dev, 0);
135 xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
136 if((res = libusb_submit_transfer(xfer)) < 0) {
137 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res);
138 libusb_free_transfer(xfer);
139 return res;
140 }
141 collection_add(&dev->tx_xfers, xfer);
142 if((length % 512) == 0) {
143 usbmuxd_log(LL_DEBUG, "Send ZLP");
144 // Send Zero Length Packet
145 xfer = libusb_alloc_transfer(0);
146 void *buffer = malloc(1);
147 libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, buffer, 0, tx_callback, dev, 0);
148 xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
149 if((res = libusb_submit_transfer(xfer)) < 0) {
150 usbmuxd_log(LL_ERROR, "Failed to submit TX ZLP transfer to device %d-%d: %d", dev->bus, dev->address, res);
151 libusb_free_transfer(xfer);
152 return res;
153 }
154 collection_add(&dev->tx_xfers, xfer);
155 }
156 return 0;
157}
158
159static void rx_callback(struct libusb_transfer *xfer)
160{
161 struct usb_device *dev = xfer->user_data;
162 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
163 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
164 device_data_input(dev, xfer->buffer, xfer->actual_length);
165 libusb_submit_transfer(xfer);
166 } else {
167 switch(xfer->status) {
168 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
169 case LIBUSB_TRANSFER_ERROR:
170 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
171 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
172 break;
173 case LIBUSB_TRANSFER_TIMED_OUT:
174 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
175 break;
176 case LIBUSB_TRANSFER_CANCELLED:
177 usbmuxd_log(LL_DEBUG, "Device %d-%d RX transfer cancelled", dev->bus, dev->address);
178 break;
179 case LIBUSB_TRANSFER_STALL:
180 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
181 break;
182 case LIBUSB_TRANSFER_NO_DEVICE:
183 // other times, this happens, and also even when we abort the transfer after device removal
184 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
185 break;
186 case LIBUSB_TRANSFER_OVERFLOW:
187 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
188 break;
189 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
190 }
191 free(xfer->buffer);
192 dev->rx_xfer = NULL;
193 libusb_free_transfer(xfer);
194 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
195 // we'll do device_remove there too
196 dev->alive = 0;
197 }
198}
199
200static int start_rx(struct usb_device *dev)
201{
202 int res;
203 void *buf;
204 dev->rx_xfer = libusb_alloc_transfer(0);
205 buf = malloc(USB_MRU);
206 libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, BULK_IN, buf, USB_MRU, rx_callback, dev, 0);
207 if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) {
208 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
209 libusb_free_transfer(dev->rx_xfer);
210 dev->rx_xfer = NULL;
211 return res;
212 }
213 return 0;
214}
215
216static int usb_discover(void)
217{
218 int cnt, i, res;
219 int valid_count = 0;
220 libusb_device **devs;
221
222 cnt = libusb_get_device_list(NULL, &devs);
223 if(cnt < 0) {
224 usbmuxd_log(LL_WARNING, "Could not get device list: %d", cnt);
225 devlist_failures++;
226 // sometimes libusb fails getting the device list if you've just removed something
227 if(devlist_failures > 5) {
228 usbmuxd_log(LL_FATAL, "Too many errors getting device list\n");
229 return cnt;
230 } else {
231 gettimeofday(&next_dev_poll_time, NULL);
232 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
233 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
234 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
235 return 0;
236 }
237 }
238 devlist_failures = 0;
239
240 usbmuxd_log(LL_SPEW, "usb_discover: scanning %d devices", cnt);
241
242 FOREACH(struct usb_device *usbdev, &device_list) {
243 usbdev->alive = 0;
244 } ENDFOREACH
245
246 for(i=0; i<cnt; i++) {
247 // the following are non-blocking operations on the device list
248 libusb_device *dev = devs[i];
249 uint8_t bus = libusb_get_bus_number(dev);
250 uint8_t address = libusb_get_device_address(dev);
251 struct libusb_device_descriptor devdesc;
252 int found = 0;
253 FOREACH(struct usb_device *usbdev, &device_list) {
254 if(usbdev->bus == bus && usbdev->address == address) {
255 valid_count++;
256 usbdev->alive = 1;
257 found = 1;
258 break;
259 }
260 } ENDFOREACH
261 if(found)
262 continue; //device already found
263 if((res = libusb_get_device_descriptor(dev, &devdesc)) != 0) {
264 usbmuxd_log(LL_WARNING, "Could not get device descriptor for device %d-%d: %d", bus, address, res);
265 continue;
266 }
267 if(devdesc.idVendor != VID_APPLE)
268 continue;
269 if( (devdesc.idProduct != PID_IPHONE2G) &&
270 (devdesc.idProduct != PID_ITOUCH1G) &&
271 (devdesc.idProduct != PID_IPHONE3G) &&
272 (devdesc.idProduct != PID_ITOUCH2G) &&
273 (devdesc.idProduct != PID_IPHONE3GS))
274 continue;
275 libusb_device_handle *handle;
276 usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address);
277 // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable
278 if((res = libusb_open(dev, &handle)) != 0) {
279 usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res);
280 continue;
281 }
282 if((res = libusb_set_configuration(handle, USB_CONFIGURATION)) != 0) {
283 usbmuxd_log(LL_WARNING, "Could not set configuration %d for device %d-%d: %d", USB_CONFIGURATION, bus, address, res);
284 libusb_close(handle);
285 continue;
286 }
287 if((res = libusb_claim_interface(handle, USB_INTERFACE)) != 0) {
288 usbmuxd_log(LL_WARNING, "Could not claim interface %d for device %d-%d: %d", USB_INTERFACE, bus, address, res);
289 libusb_close(handle);
290 continue;
291 }
292 struct usb_device *usbdev;
293 usbdev = malloc(sizeof(struct usb_device));
294
295 if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) {
296 usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res);
297 libusb_release_interface(handle, USB_INTERFACE);
298 libusb_close(handle);
299 free(usbdev);
300 continue;
301 }
302 usbdev->serial[res] = 0;
303 usbdev->bus = bus;
304 usbdev->address = address;
305 usbdev->vid = devdesc.idVendor;
306 usbdev->pid = devdesc.idProduct;
307 usbdev->dev = handle;
308 usbdev->alive = 1;
309 collection_init(&usbdev->tx_xfers);
310
311 collection_add(&device_list, usbdev);
312
313 if(device_add(usbdev) < 0) {
314 usb_disconnect(usbdev);
315 continue;
316 }
317 if(start_rx(usbdev) < 0) {
318 device_remove(usbdev);
319 usb_disconnect(usbdev);
320 continue;
321 }
322 valid_count++;
323 }
324 FOREACH(struct usb_device *usbdev, &device_list) {
325 if(!usbdev->alive) {
326 device_remove(usbdev);
327 usb_disconnect(usbdev);
328 }
329 } ENDFOREACH
330
331 libusb_free_device_list(devs, 1);
332
333 gettimeofday(&next_dev_poll_time, NULL);
334 next_dev_poll_time.tv_usec += DEVICE_POLL_TIME * 1000;
335 next_dev_poll_time.tv_sec += next_dev_poll_time.tv_usec / 1000000;
336 next_dev_poll_time.tv_usec = next_dev_poll_time.tv_usec % 1000000;
337
338 return valid_count;
339}
340
341const char *usb_get_serial(struct usb_device *dev)
342{
343 if(!dev->dev) {
344 return NULL;
345 }
346 return dev->serial;
347}
348
349uint32_t usb_get_location(struct usb_device *dev)
350{
351 if(!dev->dev) {
352 return 0;
353 }
354 return (dev->bus << 16) | dev->address;
355}
356
357uint16_t usb_get_pid(struct usb_device *dev)
358{
359 if(!dev->dev) {
360 return 0;
361 }
362 return dev->pid;
363}
364
365void usb_get_fds(struct fdlist *list)
366{
367 const struct libusb_pollfd **usbfds;
368 const struct libusb_pollfd **p;
369 usbfds = libusb_get_pollfds(NULL);
370 if(!usbfds) {
371 usbmuxd_log(LL_ERROR, "libusb_get_pollfds failed");
372 return;
373 }
374 p = usbfds;
375 while(*p) {
376 fdlist_add(list, FD_USB, (*p)->fd, (*p)->events);
377 p++;
378 }
379 free(usbfds);
380}
381
382static int dev_poll_remain_ms(void)
383{
384 int msecs;
385 struct timeval tv;
386 gettimeofday(&tv, NULL);
387 msecs = (next_dev_poll_time.tv_sec - tv.tv_sec) * 1000;
388 msecs += (next_dev_poll_time.tv_usec - tv.tv_usec) / 1000;
389 if(msecs < 0)
390 return 0;
391 return msecs;
392}
393
394int usb_get_timeout(void)
395{
396 struct timeval tv;
397 int msec;
398 int res;
399 int pollrem;
400 pollrem = dev_poll_remain_ms();
401 res = libusb_get_next_timeout(NULL, &tv);
402 if(res == 0)
403 return pollrem;
404 if(res < 0) {
405 usbmuxd_log(LL_ERROR, "libusb_get_next_timeout failed: %d", res);
406 return pollrem;
407 }
408 msec = tv.tv_sec * 1000;
409 msec += tv.tv_usec / 1000;
410 if(msec > pollrem)
411 return pollrem;
412 return msec;
413}
414
415int usb_process(void)
416{
417 int res;
418 struct timeval tv;
419 tv.tv_sec = tv.tv_usec = 0;
420 res = libusb_handle_events_timeout(NULL, &tv);
421 if(res < 0) {
422 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
423 return res;
424 }
425 // reap devices marked dead due to an RX error
426 FOREACH(struct usb_device *usbdev, &device_list) {
427 if(!usbdev->alive) {
428 device_remove(usbdev);
429 usb_disconnect(usbdev);
430 }
431 } ENDFOREACH
432
433 if(dev_poll_remain_ms() <= 0) {
434 res = usb_discover();
435 if(res < 0) {
436 usbmuxd_log(LL_ERROR, "usb_discover failed: %d", res);
437 return res;
438 }
439 }
440 return 0;
441}
442
443int usb_process_timeout(int msec)
444{
445 int res;
446 struct timeval tleft, tcur, tfin;
447 gettimeofday(&tcur, NULL);
448 tfin.tv_sec = tcur.tv_sec + (msec / 1000);
449 tfin.tv_usec = tcur.tv_usec + (msec % 1000) * 1000;
450 tfin.tv_sec += tfin.tv_usec / 1000000;
451 tfin.tv_usec %= 1000000;
452 while((tfin.tv_sec > tcur.tv_sec) || ((tfin.tv_sec == tcur.tv_sec) && (tfin.tv_usec > tcur.tv_usec))) {
453 tleft.tv_sec = tfin.tv_sec - tcur.tv_sec;
454 tleft.tv_usec = tfin.tv_usec - tcur.tv_usec;
455 if(tleft.tv_usec < 0) {
456 tleft.tv_usec += 1000000;
457 tleft.tv_sec -= 1;
458 }
459 res = libusb_handle_events_timeout(NULL, &tleft);
460 if(res < 0) {
461 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
462 return res;
463 }
464 // reap devices marked dead due to an RX error
465 FOREACH(struct usb_device *usbdev, &device_list) {
466 if(!usbdev->alive) {
467 device_remove(usbdev);
468 usb_disconnect(usbdev);
469 }
470 } ENDFOREACH
471 gettimeofday(&tcur, NULL);
472 }
473 return 0;
474}
475
476int usb_init(void)
477{
478 int res;
479 usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
480
481 devlist_failures = 0;
482 res = libusb_init(NULL);
483 //libusb_set_debug(NULL, 3);
484 if(res != 0) {
485 usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
486 return -1;
487 }
488
489 collection_init(&device_list);
490
491 return usb_discover();
492}
493
494void usb_shutdown(void)
495{
496 usbmuxd_log(LL_DEBUG, "usb_shutdown");
497 FOREACH(struct usb_device *usbdev, &device_list) {
498 device_remove(usbdev);
499 usb_disconnect(usbdev);
500 } ENDFOREACH
501 collection_free(&device_list);
502 libusb_exit(NULL);
503}
diff --git a/usbmuxd/usb.h b/usbmuxd/usb.h
new file mode 100644
index 0000000..d13d9ba
--- /dev/null
+++ b/usbmuxd/usb.h
@@ -0,0 +1,67 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __USB_H__
22#define __USB_H__
23
24#include <stdint.h>
25#include "utils.h"
26
27#define BULK_IN 0x85
28#define BULK_OUT 0x04
29
30// libusb fragments packets larger than this (usbfs limitation)
31// on input, this creates race conditions and other issues
32#define USB_MRU 16384
33
34// max transmission packet size
35// libusb fragments these too, but doesn't send ZLPs so we're safe
36// but we need to send a ZLP ourselves at the end (see usb-linux.c)
37// we're using 3 * 16384 to optimize for the fragmentation
38// this results in three URBs per full transfer, 32 USB packets each
39// if there are ZLP issues this should make them show up easily too
40#define USB_MTU (3 * 16384)
41
42#define USB_PACKET_SIZE 512
43
44#define VID_APPLE 0x5ac
45#define PID_IPHONE2G 0x1290
46#define PID_ITOUCH1G 0x1291
47#define PID_IPHONE3G 0x1292
48#define PID_ITOUCH2G 0x1293
49#define PID_IPHONE3GS 0x1294
50
51#define USB_CONFIGURATION 3
52#define USB_INTERFACE 1
53
54struct usb_device;
55
56int usb_init(void);
57void usb_shutdown(void);
58const char *usb_get_serial(struct usb_device *dev);
59uint32_t usb_get_location(struct usb_device *dev);
60uint16_t usb_get_pid(struct usb_device *dev);
61void usb_get_fds(struct fdlist *list);
62int usb_get_timeout(void);
63int usb_send(struct usb_device *dev, const unsigned char *buf, int length);
64int usb_process(void);
65int usb_process_timeout(int msec);
66
67#endif
diff --git a/usbmuxd/utils.c b/usbmuxd/utils.c
new file mode 100644
index 0000000..1ffa04a
--- /dev/null
+++ b/usbmuxd/utils.c
@@ -0,0 +1,110 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <string.h>
27#include "utils.h"
28#include "log.h"
29
30void fdlist_create(struct fdlist *list)
31{
32 list->count = 0;
33 list->capacity = 4;
34 list->owners = malloc(sizeof(*list->owners) * list->capacity);
35 list->fds = malloc(sizeof(*list->fds) * list->capacity);
36}
37void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events)
38{
39 if(list->count == list->capacity) {
40 list->capacity *= 2;
41 list->owners = realloc(list->owners, sizeof(*list->owners) * list->capacity);
42 list->fds = realloc(list->fds, sizeof(*list->fds) * list->capacity);
43 }
44 list->owners[list->count] = owner;
45 list->fds[list->count].fd = fd;
46 list->fds[list->count].events = events;
47 list->fds[list->count].revents = 0;
48 list->count++;
49}
50
51void fdlist_free(struct fdlist *list)
52{
53 list->count = 0;
54 list->capacity = 0;
55 free(list->owners);
56 list->owners = NULL;
57 free(list->fds);
58 list->fds = NULL;
59}
60
61void collection_init(struct collection *col)
62{
63 col->list = malloc(sizeof(void *));
64 memset(col->list, 0, sizeof(void *));
65 col->capacity = 1;
66}
67
68void collection_free(struct collection *col)
69{
70 free(col->list);
71 col->list = NULL;
72 col->capacity = 0;
73}
74
75void collection_add(struct collection *col, void *element)
76{
77 int i;
78 for(i=0; i<col->capacity; i++) {
79 if(!col->list[i]) {
80 col->list[i] = element;
81 return;
82 }
83 }
84 col->list = realloc(col->list, sizeof(void*) * col->capacity * 2);
85 memset(&col->list[col->capacity], 0, sizeof(void *) * col->capacity);
86 col->list[col->capacity] = element;
87 col->capacity *= 2;
88}
89
90void collection_remove(struct collection *col, void *element)
91{
92 int i;
93 for(i=0; i<col->capacity; i++) {
94 if(col->list[i] == element) {
95 col->list[i] = NULL;
96 return;
97 }
98 }
99 usbmuxd_log(LL_ERROR, "collection_remove: element %p not present in collection %p (cap %d)", element, col, col->capacity);
100}
101
102int collection_count(struct collection *col)
103{
104 int i, cnt = 0;
105 for(i=0; i<col->capacity; i++) {
106 if(col->list[i])
107 cnt++;
108 }
109 return cnt;
110}
diff --git a/usbmuxd/utils.h b/usbmuxd/utils.h
new file mode 100644
index 0000000..ad4ac9d
--- /dev/null
+++ b/usbmuxd/utils.h
@@ -0,0 +1,65 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#ifndef __UTILS_H__
22#define __UTILS_H__
23
24#include <poll.h>
25
26enum fdowner {
27 FD_LISTEN,
28 FD_CLIENT,
29 FD_USB
30};
31
32struct fdlist {
33 int count;
34 int capacity;
35 enum fdowner *owners;
36 struct pollfd *fds;
37};
38
39void fdlist_create(struct fdlist *list);
40void fdlist_add(struct fdlist *list, enum fdowner owner, int fd, short events);
41void fdlist_free(struct fdlist *list);
42
43struct collection {
44 void **list;
45 int capacity;
46};
47
48void collection_init(struct collection *col);
49void collection_add(struct collection *col, void *element);
50void collection_remove(struct collection *col, void *element);
51int collection_count(struct collection *col);
52void collection_free(struct collection *col);
53
54#define FOREACH(var, col) \
55 do { \
56 int _iter; \
57 for(_iter=0; _iter<(col)->capacity; _iter++) { \
58 if(!(col)->list[_iter]) continue; \
59 var = (col)->list[_iter];
60
61#define ENDFOREACH \
62 } \
63 } while(0);
64
65#endif