summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml3
-rw-r--r--.github/workflows/build.yml187
-rw-r--r--.gitignore40
-rw-r--r--COPYING502
-rw-r--r--LICENSE674
-rw-r--r--Makefile37
-rw-r--r--Makefile.am14
-rw-r--r--NEWS171
-rw-r--r--README22
-rw-r--r--README.md261
-rw-r--r--TODO15
-rwxr-xr-xautogen.sh26
-rw-r--r--configure.ac217
-rwxr-xr-xgit-version-gen20
-rw-r--r--include/Makefile.am1
-rw-r--r--include/libirecovery.h248
-rw-r--r--m4/as-compiler-flag.m462
-rw-r--r--scripts/boot.irs5
-rw-r--r--scripts/test.irs21
-rw-r--r--src/Makefile.am24
-rw-r--r--src/irecovery.c355
-rw-r--r--src/libirecovery-1.0.pc.in11
-rw-r--r--src/libirecovery.c4318
-rw-r--r--tools/Makefile.am16
-rw-r--r--tools/irecovery.c760
-rw-r--r--udev/39-libirecovery.rules.in8
-rw-r--r--udev/Makefile.am21
27 files changed, 6569 insertions, 1470 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..e995b30
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
1github: nikias
2patreon: nikias
3custom: ["https://www.paypal.me/NikiasBassen"]
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..600e9c6
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,187 @@
1name: build
2
3on:
4 push:
5 schedule:
6 - cron: '0 0 1 * *'
7
8jobs:
9 build-linux-ubuntu:
10 runs-on: ubuntu-latest
11 steps:
12 - name: install dependencies
13 run: |
14 sudo apt-get update
15 sudo apt-get install libusb-1.0-0-dev
16 - name: prepare environment
17 run: |
18 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
19 - name: fetch libplist
20 uses: dawidd6/action-download-artifact@v6
21 with:
22 github_token: ${{secrets.GITHUB_TOKEN}}
23 workflow: build.yml
24 name: libplist-latest_${{env.target_triplet}}
25 repo: libimobiledevice/libplist
26 - name: fetch libimobiledevice-glue
27 uses: dawidd6/action-download-artifact@v6
28 with:
29 github_token: ${{secrets.GITHUB_TOKEN}}
30 workflow: build.yml
31 name: libimobiledevice-glue-latest_${{env.target_triplet}}
32 repo: libimobiledevice/libimobiledevice-glue
33 - name: install external dependencies
34 run: |
35 mkdir extract
36 for I in *.tar; do
37 tar -C extract -xvf $I
38 done
39 sudo cp -r extract/* /
40 sudo ldconfig
41 - uses: actions/checkout@v4
42 - name: autogen
43 run: ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig LDFLAGS="-Wl,-rpath=/usr/local/lib"
44 - name: make
45 run: make
46 - name: make install
47 run: sudo make install
48 - name: prepare artifact
49 run: |
50 mkdir -p dest
51 DESTDIR=`pwd`/dest make install
52 tar -C dest -cf libirecovery.tar --strip-components 1 .
53 - name: publish artifact
54 uses: actions/upload-artifact@v4
55 with:
56 name: libirecovery-latest_${{env.target_triplet}}
57 path: libirecovery.tar
58 build-macOS:
59 runs-on: macOS-latest
60 steps:
61 - name: install dependencies
62 run: |
63 if test -x "`which port`"; then
64 sudo port install libtool autoconf automake pkgconfig
65 else
66 brew install libtool autoconf automake pkgconfig
67 fi
68 shell: bash
69 - name: fetch libplist
70 uses: dawidd6/action-download-artifact@v6
71 with:
72 github_token: ${{secrets.GITHUB_TOKEN}}
73 workflow: build.yml
74 name: libplist-latest_macOS
75 repo: libimobiledevice/libplist
76 - name: fetch libimobiledevice-glue
77 uses: dawidd6/action-download-artifact@v6
78 with:
79 github_token: ${{secrets.GITHUB_TOKEN}}
80 workflow: build.yml
81 name: libimobiledevice-glue-latest_macOS
82 repo: libimobiledevice/libimobiledevice-glue
83 - name: install external dependencies
84 run: |
85 mkdir extract
86 for I in *.tar; do
87 tar -C extract -xvf $I
88 done
89 sudo cp -r extract/* /
90 - uses: actions/checkout@v4
91 - name: autogen
92 run: |
93 SDKDIR=`xcrun --sdk macosx --show-sdk-path`
94 TESTARCHS="arm64 x86_64"
95 USEARCHS=
96 for ARCH in $TESTARCHS; do
97 if echo "int main(int argc, char **argv) { return 0; }" |clang -arch $ARCH -o /dev/null -isysroot $SDKDIR -x c - 2>/dev/null; then
98 USEARCHS="$USEARCHS -arch $ARCH"
99 fi
100 done
101 export CFLAGS="$USEARCHS -isysroot $SDKDIR"
102 echo "Using CFLAGS: $CFLAGS"
103 ./autogen.sh PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
104 - name: make
105 run: make
106 - name: make install
107 run: sudo make install
108 - name: prepare artifact
109 run: |
110 mkdir -p dest
111 DESTDIR=`pwd`/dest make install
112 tar -C dest -cf libirecovery.tar --strip-components 1 .
113 - name: publish artifact
114 uses: actions/upload-artifact@v4
115 with:
116 name: libirecovery-latest_macOS
117 path: libirecovery.tar
118 build-windows:
119 runs-on: windows-latest
120 defaults:
121 run:
122 shell: msys2 {0}
123 strategy:
124 fail-fast: false
125 matrix:
126 include: [
127 { msystem: MINGW64, arch: x86_64 },
128 { msystem: MINGW32, arch: i686 }
129 ]
130 steps:
131 - uses: msys2/setup-msys2@v2
132 with:
133 msystem: ${{ matrix.msystem }}
134 release: false
135 update: false
136 install: >-
137 base-devel
138 git
139 mingw-w64-${{ matrix.arch }}-gcc
140 mingw-w64-${{ matrix.arch }}-pkg-config
141 make
142 libtool
143 autoconf
144 automake-wrapper
145 - name: prepare environment
146 run: |
147 dest=`echo ${{ matrix.msystem }} |tr [:upper:] [:lower:]`
148 echo "dest=$dest" >> $GITHUB_ENV
149 echo "target_triplet=`gcc -dumpmachine`" >> $GITHUB_ENV
150 - name: fetch libplist
151 uses: dawidd6/action-download-artifact@v6
152 with:
153 github_token: ${{secrets.GITHUB_TOKEN}}
154 workflow: build.yml
155 name: libplist-latest_${{ matrix.arch }}-${{ env.dest }}
156 repo: libimobiledevice/libplist
157 - name: fetch libimobiledevice-glue
158 uses: dawidd6/action-download-artifact@v6
159 with:
160 github_token: ${{secrets.GITHUB_TOKEN}}
161 workflow: build.yml
162 name: libimobiledevice-glue-latest_${{ matrix.arch }}-${{ env.dest }}
163 repo: libimobiledevice/libimobiledevice-glue
164 - name: install external dependencies
165 run: |
166 mkdir extract
167 for I in *.tar; do
168 tar -C extract -xvf $I
169 done
170 cp -r extract/* /
171 - uses: actions/checkout@v4
172 - name: autogen
173 run: ./autogen.sh CC=gcc CXX=g++
174 - name: make
175 run: make
176 - name: make install
177 run: make install
178 - name: prepare artifact
179 run: |
180 mkdir -p dest
181 DESTDIR=`pwd`/dest make install
182 tar -C dest -cf libirecovery.tar ${{ env.dest }}
183 - name: publish artifact
184 uses: actions/upload-artifact@v4
185 with:
186 name: libirecovery-latest_${{ matrix.arch }}-${{ env.dest }}
187 path: libirecovery.tar
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ccad36f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,40 @@
1*.[oa]
2*~
3*.po
4*.lo
5*.la
6autom4te.cache/*
7*.in
8*/.deps/*
9m4/*
10*.dll
11*.so
12*.dylib
13*.exe
14*.patch
15aclocal.m4
16config.h
17config.log
18config.sub
19config.guess
20config.status
21configure
22depcomp
23install-sh
24compile
25main
26ltmain.sh
27missing
28mkinstalldirs
29libtool
30*Makefile
31stamp-h1
32src/.libs
33*.pc
34tools/.libs/*
35tools/irecovery
36.irecovery
37udev/39-libirecovery.rules
38.idea
39.vscode
40.DS_Store
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f166cc5
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,502 @@
1 GNU LESSER GENERAL PUBLIC LICENSE
2 Version 2.1, February 1999
3
4 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9[This is the first released version of the Lesser GPL. It also counts
10 as the successor of the GNU Library Public License, version 2, hence
11 the version number 2.1.]
12
13 Preamble
14
15 The licenses for most software are designed to take away your
16freedom to share and change it. By contrast, the GNU General Public
17Licenses are intended to guarantee your freedom to share and change
18free software--to make sure the software is free for all its users.
19
20 This license, the Lesser General Public License, applies to some
21specially designated software packages--typically libraries--of the
22Free Software Foundation and other authors who decide to use it. You
23can use it too, but we suggest you first think carefully about whether
24this license or the ordinary General Public License is the better
25strategy to use in any particular case, based on the explanations below.
26
27 When we speak of free software, we are referring to freedom of use,
28not price. Our General Public Licenses are designed to make sure that
29you have the freedom to distribute copies of free software (and charge
30for this service if you wish); that you receive source code or can get
31it if you want it; that you can change the software and use pieces of
32it in new free programs; and that you are informed that you can do
33these things.
34
35 To protect your rights, we need to make restrictions that forbid
36distributors to deny you these rights or to ask you to surrender these
37rights. These restrictions translate to certain responsibilities for
38you if you distribute copies of the library or if you modify it.
39
40 For example, if you distribute copies of the library, whether gratis
41or for a fee, you must give the recipients all the rights that we gave
42you. You must make sure that they, too, receive or can get the source
43code. If you link other code with the library, you must provide
44complete object files to the recipients, so that they can relink them
45with the library after making changes to the library and recompiling
46it. And you must show them these terms so they know their rights.
47
48 We protect your rights with a two-step method: (1) we copyright the
49library, and (2) we offer you this license, which gives you legal
50permission to copy, distribute and/or modify the library.
51
52 To protect each distributor, we want to make it very clear that
53there is no warranty for the free library. Also, if the library is
54modified by someone else and passed on, the recipients should know
55that what they have is not the original version, so that the original
56author's reputation will not be affected by problems that might be
57introduced by others.
58
59 Finally, software patents pose a constant threat to the existence of
60any free program. We wish to make sure that a company cannot
61effectively restrict the users of a free program by obtaining a
62restrictive license from a patent holder. Therefore, we insist that
63any patent license obtained for a version of the library must be
64consistent with the full freedom of use specified in this license.
65
66 Most GNU software, including some libraries, is covered by the
67ordinary GNU General Public License. This license, the GNU Lesser
68General Public License, applies to certain designated libraries, and
69is quite different from the ordinary General Public License. We use
70this license for certain libraries in order to permit linking those
71libraries into non-free programs.
72
73 When a program is linked with a library, whether statically or using
74a shared library, the combination of the two is legally speaking a
75combined work, a derivative of the original library. The ordinary
76General Public License therefore permits such linking only if the
77entire combination fits its criteria of freedom. The Lesser General
78Public License permits more lax criteria for linking other code with
79the library.
80
81 We call this license the "Lesser" General Public License because it
82does Less to protect the user's freedom than the ordinary General
83Public License. It also provides other free software developers Less
84of an advantage over competing non-free programs. These disadvantages
85are the reason we use the ordinary General Public License for many
86libraries. However, the Lesser license provides advantages in certain
87special circumstances.
88
89 For example, on rare occasions, there may be a special need to
90encourage the widest possible use of a certain library, so that it becomes
91a de-facto standard. To achieve this, non-free programs must be
92allowed to use the library. A more frequent case is that a free
93library does the same job as widely used non-free libraries. In this
94case, there is little to gain by limiting the free library to free
95software only, so we use the Lesser General Public License.
96
97 In other cases, permission to use a particular library in non-free
98programs enables a greater number of people to use a large body of
99free software. For example, permission to use the GNU C Library in
100non-free programs enables many more people to use the whole GNU
101operating system, as well as its variant, the GNU/Linux operating
102system.
103
104 Although the Lesser General Public License is Less protective of the
105users' freedom, it does ensure that the user of a program that is
106linked with the Library has the freedom and the wherewithal to run
107that program using a modified version of the Library.
108
109 The precise terms and conditions for copying, distribution and
110modification follow. Pay close attention to the difference between a
111"work based on the library" and a "work that uses the library". The
112former contains code derived from the library, whereas the latter must
113be combined with the library in order to run.
114
115 GNU LESSER GENERAL PUBLIC LICENSE
116 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117
118 0. This License Agreement applies to any software library or other
119program which contains a notice placed by the copyright holder or
120other authorized party saying it may be distributed under the terms of
121this Lesser General Public License (also called "this License").
122Each licensee is addressed as "you".
123
124 A "library" means a collection of software functions and/or data
125prepared so as to be conveniently linked with application programs
126(which use some of those functions and data) to form executables.
127
128 The "Library", below, refers to any such software library or work
129which has been distributed under these terms. A "work based on the
130Library" means either the Library or any derivative work under
131copyright law: that is to say, a work containing the Library or a
132portion of it, either verbatim or with modifications and/or translated
133straightforwardly into another language. (Hereinafter, translation is
134included without limitation in the term "modification".)
135
136 "Source code" for a work means the preferred form of the work for
137making modifications to it. For a library, complete source code means
138all the source code for all modules it contains, plus any associated
139interface definition files, plus the scripts used to control compilation
140and installation of the library.
141
142 Activities other than copying, distribution and modification are not
143covered by this License; they are outside its scope. The act of
144running a program using the Library is not restricted, and output from
145such a program is covered only if its contents constitute a work based
146on the Library (independent of the use of the Library in a tool for
147writing it). Whether that is true depends on what the Library does
148and what the program that uses the Library does.
149
150 1. You may copy and distribute verbatim copies of the Library's
151complete source code as you receive it, in any medium, provided that
152you conspicuously and appropriately publish on each copy an
153appropriate copyright notice and disclaimer of warranty; keep intact
154all the notices that refer to this License and to the absence of any
155warranty; and distribute a copy of this License along with the
156Library.
157
158 You may charge a fee for the physical act of transferring a copy,
159and you may at your option offer warranty protection in exchange for a
160fee.
161
162 2. You may modify your copy or copies of the Library or any portion
163of it, thus forming a work based on the Library, and copy and
164distribute such modifications or work under the terms of Section 1
165above, provided that you also meet all of these conditions:
166
167 a) The modified work must itself be a software library.
168
169 b) You must cause the files modified to carry prominent notices
170 stating that you changed the files and the date of any change.
171
172 c) You must cause the whole of the work to be licensed at no
173 charge to all third parties under the terms of this License.
174
175 d) If a facility in the modified Library refers to a function or a
176 table of data to be supplied by an application program that uses
177 the facility, other than as an argument passed when the facility
178 is invoked, then you must make a good faith effort to ensure that,
179 in the event an application does not supply such function or
180 table, the facility still operates, and performs whatever part of
181 its purpose remains meaningful.
182
183 (For example, a function in a library to compute square roots has
184 a purpose that is entirely well-defined independent of the
185 application. Therefore, Subsection 2d requires that any
186 application-supplied function or table used by this function must
187 be optional: if the application does not supply it, the square
188 root function must still compute square roots.)
189
190These requirements apply to the modified work as a whole. If
191identifiable sections of that work are not derived from the Library,
192and can be reasonably considered independent and separate works in
193themselves, then this License, and its terms, do not apply to those
194sections when you distribute them as separate works. But when you
195distribute the same sections as part of a whole which is a work based
196on the Library, the distribution of the whole must be on the terms of
197this License, whose permissions for other licensees extend to the
198entire whole, and thus to each and every part regardless of who wrote
199it.
200
201Thus, it is not the intent of this section to claim rights or contest
202your rights to work written entirely by you; rather, the intent is to
203exercise the right to control the distribution of derivative or
204collective works based on the Library.
205
206In addition, mere aggregation of another work not based on the Library
207with the Library (or with a work based on the Library) on a volume of
208a storage or distribution medium does not bring the other work under
209the scope of this License.
210
211 3. You may opt to apply the terms of the ordinary GNU General Public
212License instead of this License to a given copy of the Library. To do
213this, you must alter all the notices that refer to this License, so
214that they refer to the ordinary GNU General Public License, version 2,
215instead of to this License. (If a newer version than version 2 of the
216ordinary GNU General Public License has appeared, then you can specify
217that version instead if you wish.) Do not make any other change in
218these notices.
219
220 Once this change is made in a given copy, it is irreversible for
221that copy, so the ordinary GNU General Public License applies to all
222subsequent copies and derivative works made from that copy.
223
224 This option is useful when you wish to copy part of the code of
225the Library into a program that is not a library.
226
227 4. You may copy and distribute the Library (or a portion or
228derivative of it, under Section 2) in object code or executable form
229under the terms of Sections 1 and 2 above provided that you accompany
230it with the complete corresponding machine-readable source code, which
231must be distributed under the terms of Sections 1 and 2 above on a
232medium customarily used for software interchange.
233
234 If distribution of object code is made by offering access to copy
235from a designated place, then offering equivalent access to copy the
236source code from the same place satisfies the requirement to
237distribute the source code, even though third parties are not
238compelled to copy the source along with the object code.
239
240 5. A program that contains no derivative of any portion of the
241Library, but is designed to work with the Library by being compiled or
242linked with it, is called a "work that uses the Library". Such a
243work, in isolation, is not a derivative work of the Library, and
244therefore falls outside the scope of this License.
245
246 However, linking a "work that uses the Library" with the Library
247creates an executable that is a derivative of the Library (because it
248contains portions of the Library), rather than a "work that uses the
249library". The executable is therefore covered by this License.
250Section 6 states terms for distribution of such executables.
251
252 When a "work that uses the Library" uses material from a header file
253that is part of the Library, the object code for the work may be a
254derivative work of the Library even though the source code is not.
255Whether this is true is especially significant if the work can be
256linked without the Library, or if the work is itself a library. The
257threshold for this to be true is not precisely defined by law.
258
259 If such an object file uses only numerical parameters, data
260structure layouts and accessors, and small macros and small inline
261functions (ten lines or less in length), then the use of the object
262file is unrestricted, regardless of whether it is legally a derivative
263work. (Executables containing this object code plus portions of the
264Library will still fall under Section 6.)
265
266 Otherwise, if the work is a derivative of the Library, you may
267distribute the object code for the work under the terms of Section 6.
268Any executables containing that work also fall under Section 6,
269whether or not they are linked directly with the Library itself.
270
271 6. As an exception to the Sections above, you may also combine or
272link a "work that uses the Library" with the Library to produce a
273work containing portions of the Library, and distribute that work
274under terms of your choice, provided that the terms permit
275modification of the work for the customer's own use and reverse
276engineering for debugging such modifications.
277
278 You must give prominent notice with each copy of the work that the
279Library is used in it and that the Library and its use are covered by
280this License. You must supply a copy of this License. If the work
281during execution displays copyright notices, you must include the
282copyright notice for the Library among them, as well as a reference
283directing the user to the copy of this License. Also, you must do one
284of these things:
285
286 a) Accompany the work with the complete corresponding
287 machine-readable source code for the Library including whatever
288 changes were used in the work (which must be distributed under
289 Sections 1 and 2 above); and, if the work is an executable linked
290 with the Library, with the complete machine-readable "work that
291 uses the Library", as object code and/or source code, so that the
292 user can modify the Library and then relink to produce a modified
293 executable containing the modified Library. (It is understood
294 that the user who changes the contents of definitions files in the
295 Library will not necessarily be able to recompile the application
296 to use the modified definitions.)
297
298 b) Use a suitable shared library mechanism for linking with the
299 Library. A suitable mechanism is one that (1) uses at run time a
300 copy of the library already present on the user's computer system,
301 rather than copying library functions into the executable, and (2)
302 will operate properly with a modified version of the library, if
303 the user installs one, as long as the modified version is
304 interface-compatible with the version that the work was made with.
305
306 c) Accompany the work with a written offer, valid for at
307 least three years, to give the same user the materials
308 specified in Subsection 6a, above, for a charge no more
309 than the cost of performing this distribution.
310
311 d) If distribution of the work is made by offering access to copy
312 from a designated place, offer equivalent access to copy the above
313 specified materials from the same place.
314
315 e) Verify that the user has already received a copy of these
316 materials or that you have already sent this user a copy.
317
318 For an executable, the required form of the "work that uses the
319Library" must include any data and utility programs needed for
320reproducing the executable from it. However, as a special exception,
321the materials to be distributed need not include anything that is
322normally distributed (in either source or binary form) with the major
323components (compiler, kernel, and so on) of the operating system on
324which the executable runs, unless that component itself accompanies
325the executable.
326
327 It may happen that this requirement contradicts the license
328restrictions of other proprietary libraries that do not normally
329accompany the operating system. Such a contradiction means you cannot
330use both them and the Library together in an executable that you
331distribute.
332
333 7. You may place library facilities that are a work based on the
334Library side-by-side in a single library together with other library
335facilities not covered by this License, and distribute such a combined
336library, provided that the separate distribution of the work based on
337the Library and of the other library facilities is otherwise
338permitted, and provided that you do these two things:
339
340 a) Accompany the combined library with a copy of the same work
341 based on the Library, uncombined with any other library
342 facilities. This must be distributed under the terms of the
343 Sections above.
344
345 b) Give prominent notice with the combined library of the fact
346 that part of it is a work based on the Library, and explaining
347 where to find the accompanying uncombined form of the same work.
348
349 8. You may not copy, modify, sublicense, link with, or distribute
350the Library except as expressly provided under this License. Any
351attempt otherwise to copy, modify, sublicense, link with, or
352distribute the Library is void, and will automatically terminate your
353rights under this License. However, parties who have received copies,
354or rights, from you under this License will not have their licenses
355terminated so long as such parties remain in full compliance.
356
357 9. You are not required to accept this License, since you have not
358signed it. However, nothing else grants you permission to modify or
359distribute the Library or its derivative works. These actions are
360prohibited by law if you do not accept this License. Therefore, by
361modifying or distributing the Library (or any work based on the
362Library), you indicate your acceptance of this License to do so, and
363all its terms and conditions for copying, distributing or modifying
364the Library or works based on it.
365
366 10. Each time you redistribute the Library (or any work based on the
367Library), the recipient automatically receives a license from the
368original licensor to copy, distribute, link with or modify the Library
369subject to these terms and conditions. You may not impose any further
370restrictions on the recipients' exercise of the rights granted herein.
371You are not responsible for enforcing compliance by third parties with
372this License.
373
374 11. If, as a consequence of a court judgment or allegation of patent
375infringement or for any other reason (not limited to patent issues),
376conditions are imposed on you (whether by court order, agreement or
377otherwise) that contradict the conditions of this License, they do not
378excuse you from the conditions of this License. If you cannot
379distribute so as to satisfy simultaneously your obligations under this
380License and any other pertinent obligations, then as a consequence you
381may not distribute the Library at all. For example, if a patent
382license would not permit royalty-free redistribution of the Library by
383all those who receive copies directly or indirectly through you, then
384the only way you could satisfy both it and this License would be to
385refrain entirely from distribution of the Library.
386
387If any portion of this section is held invalid or unenforceable under any
388particular circumstance, the balance of the section is intended to apply,
389and the section as a whole is intended to apply in other circumstances.
390
391It is not the purpose of this section to induce you to infringe any
392patents or other property right claims or to contest validity of any
393such claims; this section has the sole purpose of protecting the
394integrity of the free software distribution system which is
395implemented by public license practices. Many people have made
396generous contributions to the wide range of software distributed
397through that system in reliance on consistent application of that
398system; it is up to the author/donor to decide if he or she is willing
399to distribute software through any other system and a licensee cannot
400impose that choice.
401
402This section is intended to make thoroughly clear what is believed to
403be a consequence of the rest of this License.
404
405 12. If the distribution and/or use of the Library is restricted in
406certain countries either by patents or by copyrighted interfaces, the
407original copyright holder who places the Library under this License may add
408an explicit geographical distribution limitation excluding those countries,
409so that distribution is permitted only in or among countries not thus
410excluded. In such case, this License incorporates the limitation as if
411written in the body of this License.
412
413 13. The Free Software Foundation may publish revised and/or new
414versions of the Lesser General Public License from time to time.
415Such new versions will be similar in spirit to the present version,
416but may differ in detail to address new problems or concerns.
417
418Each version is given a distinguishing version number. If the Library
419specifies a version number of this License which applies to it and
420"any later version", you have the option of following the terms and
421conditions either of that version or of any later version published by
422the Free Software Foundation. If the Library does not specify a
423license version number, you may choose any version ever published by
424the Free Software Foundation.
425
426 14. If you wish to incorporate parts of the Library into other free
427programs whose distribution conditions are incompatible with these,
428write to the author to ask for permission. For software which is
429copyrighted by the Free Software Foundation, write to the Free
430Software Foundation; we sometimes make exceptions for this. Our
431decision will be guided by the two goals of preserving the free status
432of all derivatives of our free software and of promoting the sharing
433and reuse of software generally.
434
435 NO WARRANTY
436
437 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446
447 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456DAMAGES.
457
458 END OF TERMS AND CONDITIONS
459
460 How to Apply These Terms to Your New Libraries
461
462 If you develop a new library, and you want it to be of the greatest
463possible use to the public, we recommend making it free software that
464everyone can redistribute and change. You can do so by permitting
465redistribution under these terms (or, alternatively, under the terms of the
466ordinary General Public License).
467
468 To apply these terms, attach the following notices to the library. It is
469safest to attach them to the start of each source file to most effectively
470convey the exclusion of warranty; and each file should have at least the
471"copyright" line and a pointer to where the full notice is found.
472
473 <one line to give the library's name and a brief idea of what it does.>
474 Copyright (C) <year> <name of author>
475
476 This library is free software; you can redistribute it and/or
477 modify it under the terms of the GNU Lesser General Public
478 License as published by the Free Software Foundation; either
479 version 2.1 of the License, or (at your option) any later version.
480
481 This library is distributed in the hope that it will be useful,
482 but WITHOUT ANY WARRANTY; without even the implied warranty of
483 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
484 Lesser General Public License for more details.
485
486 You should have received a copy of the GNU Lesser General Public
487 License along with this library; if not, write to the Free Software
488 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
489
490Also add information on how to contact you by electronic and paper mail.
491
492You should also get your employer (if you work as a programmer) or your
493school, if any, to sign a "copyright disclaimer" for the library, if
494necessary. Here is a sample; alter the names:
495
496 Yoyodyne, Inc., hereby disclaims all copyright interest in the
497 library `Frob' (a library for tweaking knobs) written by James Random Hacker.
498
499 <signature of Ty Coon>, 1 April 1990
500 Ty Coon, President of Vice
501
502That's all there is to it! \ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 20d40b6..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
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>. \ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index b7f3522..0000000
--- a/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
1all: linux
2 @echo "Please choose either macosx, linux, or windows"
3
4static:
5 gcc -o libirecovery.o -c src/libirecovery.c -g -I./include
6 ar rs libirecovery.a libirecovery.o
7 gcc -o irecovery src/irecovery.c -g -I./include -L. -lirecovery -lreadline -lusb-1.0
8
9linux:
10 gcc -o libirecovery.o -c src/libirecovery.c -g -I./include -lreadline -fPIC
11 gcc -o libirecovery.so libirecovery.o -g -shared -Wl,-soname,libirecovery.so -lusb-1.0
12 gcc -o irecovery src/irecovery.c -g -I./include -L. -lirecovery -lreadline
13
14macosx:
15 gcc -o libirecovery.dylib -c src/libirecovery.c -dynamiclib
16 gcc -o irecovery src/irecovery.c -I./include -L. -lirecovery -lreadline -lusb-1.0
17
18windows:
19 gcc -o libirecovery.dll -c src/libirecovery.c -I. -lusb-1.0 -lreadline -shared -fPIC
20 gcc -o irecovery irecovery.c -I. -lirecovery -lreadline
21
22install:
23 cp libirecovery.so /usr/local/lib/libirecovery.so
24 cp include/libirecovery.h /usr/local/include/libirecovery.h
25 cp irecovery /usr/local/bin/irecovery
26 ldconfig
27
28uninstall:
29 rm -rf /usr/local/lib/libirecovery.so
30 rm -rf /usr/local/include/libirecovery.h
31 rm -rf /usr/local/bin/irecovery
32
33clean:
34 rm -rf irecovery libirecovery.o libirecovery.so libirecovery.a
35
36
37
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..80b0eae
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,14 @@
1AUTOMAKE_OPTIONS = foreign
2ACLOCAL_AMFLAGS = -I m4
3SUBDIRS = src include tools udev
4
5EXTRA_DIST = \
6 README.md \
7 git-version-gen
8
9dist-hook:
10 @if ! git diff --quiet; then echo "Uncommitted changes present; not releasing"; exit 1; fi
11 echo $(VERSION) > $(distdir)/.tarball-version
12
13DISTCHECK_CONFIGURE_FLAGS = \
14 --with-udevrulesdir=$$dc_install_base/$(udevrulesdir)
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..f7e2c2f
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,171 @@
1Version 1.3.1
2~~~~~~~~~~~~~
3
4* Device database changes:
5 - Support iPad Pro M5 family devices
6 - Support Apple Vision Pro M5
7 - Support MacBook Pro 14-inch M5
8
9* Bug Fixes:
10 Fix: array initialization compatibility with MSVC
11
12
13Version 1.3.0
14~~~~~~~~~~~~~
15
16* Changes:
17 - Switch to better initializer strategy
18 - Allow building without readline support for the irecovery tool
19 - Added support for iOS 1 and iOS 2 recovery mode (Linux, macOS)
20
21* Device database changes:
22 - Support iPhone 17 family devices
23 - Support Watch 11 / SE3 / Ultra3 models
24 - Support March 2025 iPad and Mac models
25 - Support iPhone 16e
26 - Support November 2024 Mac models
27 - Support iPad mini (A17 Pro)
28
29* Bug Fixes:
30 - IOKit: Fix race condition when trying to delete runloop before it even started
31
32
33Version 1.2.1
34~~~~~~~~~~~~~
35
36* Changes:
37 - Make sure IRECV_DEVICE_REMOVE event has the mode set the device was in
38 - KIS: Add some retry loops around open/set config/set interface operations
39
40* Device database changes:
41 - Support Apple Watch Series 10 and iPhone 16 models
42 - Add iPad Air (M2) and iPad Pro (M4) models
43
44* Bug Fixes:
45 - Windows: Fix crash due to access to uninitialized data
46
47
48Version 1.2.0
49~~~~~~~~~~~~~
50
51* Changes:
52 - Allow building --without-tools
53 - Add KIS (aka Debug USB) support for macOS, Linux, and Windows
54 (Windows note: requires up-to-date AppleMobileDeviceSupport64.msi package installed)
55 - Add Port DFU support (restore procedure is handled externally)
56 - irecovery: Print detailed mode for -q and -m commands
57 - Overall code cleanup and de-duplication
58 - Windows-specific code cleanup and improvements
59
60* Device database changes:
61 - Add Mac Pro, Mac Studio (M2) and MacBook Air (M2) models
62 - Add iPhone 15 family
63 - Add Apple Watch Series 9 and Ultra 2 models
64 - Add November 2023 iMac and MacBook Pro models
65 - Add support for Apple Vision Pro (RealityDevice14,1)
66
67* Bug Fixes:
68 - A few minor Windows-specific bug fixes
69
70Version 1.1.0
71~~~~~~~~~~~~~
72
73* Changes:
74 - Remove duplicated thread/collection code and use new libimobiledevice-glue instead
75 - Add new `irecv_send_command_breq` (for M1 restoring)
76 - Add new `setenvnp` command
77 - Add support for iPad 8 and iPad Air 4 models
78 - Add all current Apple Watch models (device lookup)
79 - Add support for HomePod and HomePod mini (device lookup)
80 - Add support for Apple Silicon/M1 Macs (device lookup) and remaining T2/iBridge devices
81 - Add iMac 24-inch M1 models
82 - Add iPad Pro 11-inch (3rd gen) and iPad Pro 12.9-inch (5th gen) devices
83 - Add Apple TV 4K (2nd gen)
84 - Add iPhone 13 family
85 - Add 9th gen iPad devices
86 - Add 6th gen iPad mini
87 - Add Apple Watch Series 7
88 - Add MacBook Pro 2021 models (device lookup)
89 - Add iPad Air (5th gen)
90 - Add iPhone SE (3rd gen)
91 - Add Mac Studio
92 - Add Studio Display (device lookup)
93 - Add device ID for macOS 12 Apple Silicon VMs
94 - Add M2 MacBook models
95 - Add iPhone 14 family
96 - Add Apple Watch SE 2, Series 8 and Ultra family
97 - Add iPad (10th gen)
98 - Add iPad Pro 11-inch (4th gen) and iPad Pro 12.9-inch (6th gen)
99 - Add Apple TV 4K 3rd gen
100 - Add January 2023 Macs and 2nd generation HomePod models
101 - [Windows] Add support for newer drivers
102 - irecovery: Add new "--devices" option to list internal device data
103 - irecovery: Output product, model and marketing name for device information
104
105* Bug Fixes:
106 - Send a ZLP in recovery mode if the buffer size is a multiple of 512
107 - Make sure DEVICE_ADD events are sent to additional event listeners
108 - [Windows] Use ANSI versions for SetupDI and CreateFile API to prevent errors when compiling with unicode support
109 - irecovery: Fix sending certain commands
110
111Version 1.0.0
112~~~~~~~~~~~~~
113
114* Changes:
115 - Output basic device information after connecting
116 - Remove obsolete "in-tree" copy of libusb-1.0
117 - Improve source code directory structure
118 - Clean up and update of build system files
119 - Major code refactoring
120 - Add getters to retrieve device model information
121 - Change exploit related wording to more accurate limera1n
122 - Various improvements/fixes for win32 build
123 - Add support for latest device models
124 - Fix some memory leaks
125 - Add requirement for autoconf 2.64
126 - Support IOKit on OSX (removes dependency on libusb)
127 - Add DFU mode error handling
128 - Add udev rules to allow non-root device access
129 - Support ECID in hex or decimal format
130 - Fix various compiler warnings
131 - Add device add/remove event subscription interface
132 - Convert README to markdown
133 - Print PWND string if present
134 - Add support for Apple T2 processors
135 - Allow compiling without USB functionality
136 - Support checkra1n DFU mode devices
137 - Allow toggling debug level using "LIBIRECOVERY_DEBUG_LEVEL" environment
138 variable
139 - Add long argument name variants to irecovery
140 - Add new "--version" argument to irecovery
141 - Add support for Apple Watch 1st gen devices
142 - Add support for missing iPad4,3 model and fix wrong device information
143 iPad7 variants
144 - Improve README.md with project description, installation, contributing and
145 usage sections
146 - Rename library and all related files by adding an API version resulting
147 in "libirecovery-1.0"
148
149Version 0.1.1
150~~~~~~~~~~~~~
151
152* Changes:
153 - Add serial number and imei getters
154 - Improve USB communication stability
155 - Add support for WTF mode
156 - Add option to target device by ECID
157 - Add nonce getter
158 - Improve win32 device detection and mingw compatibility
159 - Add support for new device models
160 - Switch to autotools build system instead of plain Makefile
161 - Expose control and bulk transfer methods in public interface
162 - Improve maintainability of device model information
163 - Change license to LGPL 2.1
164
165Version 0.1.0
166~~~~~~~~~~~~~
167
168* Changes:
169 - Implement initial interface and device communication
170 - Add basic irecovery tool
171 - Setup build system
diff --git a/README b/README
deleted file mode 100644
index 50baf0d..0000000
--- a/README
+++ /dev/null
@@ -1,22 +0,0 @@
1== What is iRecovery? ==
2
3iRecovery is a libusb-based commandline utility for Mac OS X, Windows, and Linux. It is able to talk to iBoot/iBSS in Apple's iPhone/iPod touch via USB.
4
5It's completely open-source, the source code is released under the terms of the GNU General Public License v3.
6The full license text can be found in the LICENSE file.
7
8Here's its usage:
9
10irecovery [args]
11 -v Start irecovery in verbose mode.
12 -c <cmd> Send command to client.
13 -f <file> Send file to client.
14 -k [payload] Send usb exploit to client.
15 -h Show this help.
16 -r Reset client.
17 -s Start interactive shell.
18 -e <script> Executes recovery shell script.
19
20You can get info on the shell commands here:
21http://code.google.com/p/chronicdev/wiki/iBootCommands
22
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..be5eb02
--- /dev/null
+++ b/README.md
@@ -0,0 +1,261 @@
1# libirecovery
2
3*The libirecovery library allows communication with iBoot/iBSS of iOS devices
4via USB.*
5
6![](https://github.com/libimobiledevice/libirecovery/workflows/build/badge.svg)
7
8## Table of Contents
9- [Features](#features)
10- [Building](#building)
11 - [Prerequisites](#prerequisites)
12 - [Linux (Debian/Ubuntu based)](#linux-debianubuntu-based)
13 - [macOS](#macos)
14 - [Windows](#windows)
15 - [Configuring the source tree](#configuring-the-source-tree)
16 - [Building and installation](#building-and-installation)
17- [Usage](#usage)
18- [Contributing](#contributing)
19- [Links](#links)
20- [License](#license)
21- [Credits](#credits)
22
23## Features
24
25libirecovery is a cross-platform library which implements communication to
26iBoot/iBSS found on Apple's iOS devices via USB. A command-line utility named
27`irecovery` is also provided.
28
29This is a fork of an older version from former openjailbreak.org and is meant to
30be used with [idevicerestore](https://github.com/libimobiledevice/idevicerestore.git/) from the [libimobiledevice](https://github.com/libimobiledevice/) project.
31
32## Building
33
34### Prerequisites
35
36You need to have a working compiler (gcc/clang) and development environent
37available. This project uses autotools for the build process, allowing to
38have common build steps across different platforms.
39Only the prerequisites differ and they are described in this section.
40
41libirecovery requires [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue).
42Some platforms already provide it as a package.
43Check the [Building](https://github.com/libimobiledevice/libimobiledevice-glue?tab=readme-ov-file#building)
44section of the README on how to build it.
45
46#### Linux (Debian/Ubuntu based)
47
48* Install all required dependencies and build tools:
49 ```shell
50 sudo apt-get install \
51 build-essential \
52 pkg-config \
53 checkinstall \
54 git \
55 autoconf \
56 automake \
57 libtool-bin \
58 libimobiledevice-glue-dev \
59 libreadline-dev \
60 libusb-1.0-0-dev
61 ```
62
63 In case libimobiledevice-glue-dev is not available, you can manually build and install it. See note above.
64
65#### macOS
66
67* Make sure the Xcode command line tools are installed. Then, use either [MacPorts](https://www.macports.org/)
68 or [Homebrew](https://brew.sh/) to install `automake`, `autoconf`, `libtool`, etc.
69
70 Using MacPorts:
71 ```shell
72 sudo port install libtool autoconf automake pkgconfig libimobiledevice-glue
73 ```
74
75 Using Homebrew:
76 ```shell
77 brew install libtool autoconf automake pkg-config libimobiledevice-glue
78 ```
79
80#### Windows
81
82* Using [MSYS2](https://www.msys2.org/) is the official way of compiling this project on Windows. Download the MSYS2 installer
83 and follow the installation steps.
84
85 It is recommended to use the _MSYS2 MinGW 64-bit_ shell. Run it and make sure the required dependencies are installed:
86
87 ```shell
88 pacman -S base-devel \
89 git \
90 mingw-w64-x86_64-gcc \
91 make \
92 libtool \
93 autoconf \
94 automake-wrapper \
95 pkg-config \
96 mingw-w64-x86_64-libimobiledevice-glue \
97 mingw-w64-x86_64-readline
98 ```
99 NOTE: You can use a different shell and different compiler according to your needs. Adapt the above command accordingly.
100
101### Configuring the source tree
102
103You can build the source code from a git checkout, or from a `.tar.bz2` release tarball from [Releases](https://github.com/libimobiledevice/libirecovery/releases).
104Before we can build it, the source tree has to be configured for building. The steps depend on where you got the source from.
105
106Since libirecovery depends on other packages, you should set the pkg-config environment variable `PKG_CONFIG_PATH`
107accordingly. Make sure to use a path with the same prefix as the dependencies. If they are installed in `/usr/local` you would do
108
109```shell
110export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
111```
112
113* **From git**
114
115 If you haven't done already, clone the actual project repository and change into the directory.
116 ```shell
117 git clone https://github.com/libimobiledevice/libirecovery
118 cd libirecovery
119 ```
120
121 Configure the source tree for building:
122 ```shell
123 ./autogen.sh
124 ```
125
126* **From release tarball (.tar.bz2)**
127
128 When using an official [release tarball](https://github.com/libimobiledevice/libirecovery/releases) (`libirecovery-x.y.z.tar.bz2`)
129 the procedure is slightly different.
130
131 Extract the tarball:
132 ```shell
133 tar xjf libirecovery-x.y.z.tar.bz2
134 cd libirecovery-x.y.z
135 ```
136
137 Configure the source tree for building:
138 ```shell
139 ./configure
140 ```
141
142Both `./configure` and `./autogen.sh` (which generates and calls `configure`) accept a few options, for example `--prefix` to allow
143building for a different target folder. You can simply pass them like this:
144
145```shell
146./autogen.sh --prefix=/usr/local
147```
148or
149```shell
150./configure --prefix=/usr/local
151```
152
153Once the command is successful, the last few lines of output will look like this:
154```
155[...]
156config.status: creating config.h
157config.status: executing depfiles commands
158config.status: executing libtool commands
159
160Configuration for libirecovery 1.2.0:
161-------------------------------------------
162
163 Install prefix: .........: /usr/local
164 USB backend: ............: IOKit
165
166 Now type 'make' to build libirecovery 1.2.0,
167 and then 'make install' for installation.
168```
169
170### Building and installation
171
172If you followed all the steps successfully, and `autogen.sh` or `configure` did not print any errors,
173you are ready to build the project. This is simply done with
174
175```shell
176make
177```
178
179If no errors are emitted you are ready for installation. Depending on whether
180the current user has permissions to write to the destination directory or not,
181you would either run
182```shell
183make install
184```
185_OR_
186```shell
187sudo make install
188```
189
190If you are on Linux, you want to run `sudo ldconfig` after installation to
191make sure the installed libraries are made available.
192
193## Usage
194
195First of all attach your device to your machine. Make sure your device is not
196in normal mode. You can use the `ideviceenterrecovery` application from
197[libimobiledevice](https://github.com/libimobiledevice/libimobiledevice.git/)
198to let your device boot into recovery mode if you need it.
199
200Then simply run:
201```shell
202irecovery --shell
203```
204
205This connects to your device and opens a simple shell to interact with the
206device.
207
208For instance to make your device boot into normal mode again use:
209```shell
210setenv auto-boot true
211saveenv
212reboot
213```
214
215Please consult the usage information or manual page for a full documentation of
216available command line options:
217```shell
218irecovery --help
219man irecovery
220```
221
222## Contributing
223
224We welcome contributions from anyone and are grateful for every pull request!
225
226If you'd like to contribute, please fork the `master` branch, change, commit and
227send a pull request for review. Once approved it can be merged into the main
228code base.
229
230If you plan to contribute larger changes or a major refactoring, please create a
231ticket first to discuss the idea upfront to ensure less effort for everyone.
232
233Please make sure your contribution adheres to:
234* Try to follow the code style of the project
235* Commit messages should describe the change well without being too short
236* Try to split larger changes into individual commits of a common domain
237* Use your real name and a valid email address for your commits
238
239## Links
240
241* Homepage: https://libimobiledevice.org/
242* Repository: https://github.com/libimobiledevice/libirecovery.git
243* Repository (Mirror): https://git.libimobiledevice.org/libirecovery.git
244* Issue Tracker: https://github.com/libimobiledevice/libirecovery/issues
245* Mailing List: https://lists.libimobiledevice.org/mailman/listinfo/libimobiledevice-devel
246* Twitter: https://twitter.com/libimobiledev
247
248## License
249
250This project is licensed under the [GNU Lesser General Public License v2.1](https://www.gnu.org/licenses/lgpl-2.1.en.html),
251also included in the repository in the `COPYING` file.
252
253## Credits
254
255Apple, iPhone, iPad, iPod, iPod Touch, Apple TV, Apple Watch, Mac, iOS,
256iPadOS, tvOS, watchOS, and macOS are trademarks of Apple Inc.
257
258This project is an independent software library and has not been authorized,
259sponsored, or otherwise approved by Apple Inc.
260
261README Updated on: 2025-09-10
diff --git a/TODO b/TODO
deleted file mode 100644
index 411f6e6..0000000
--- a/TODO
+++ /dev/null
@@ -1,15 +0,0 @@
1TODO List
2------------------------------------------------
3
4o) Need to implement irecv_saveenv()
5o) Need to implement irecv_bootx()
6o) Neex to implement irecv_go()
7o) Need to implement irecv_bgcolor()
8o) Need to implememt irecv_setpicture()
9o) Need to impelemnt irecv_reboot()
10o) Should figure out a better place to store callbacks so the CONNECTED callback can actually be used
11o) would be nice to change to use asyncronous connections
12o) could add a function to identify whether we're connected to iBoot/iBEC/iBSS or DFU
13o) could add a function to identify which version we're connected to
14o) could add a function to return the device serial number
15o) fix command parsing to strip quotes \ No newline at end of file
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..5a0ec43
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,26 @@
1#!/bin/sh
2
3olddir=`pwd`
4srcdir=`dirname $0`
5test -z "$srcdir" && srcdir=.
6
7(
8 cd "$srcdir"
9
10 gprefix=`which glibtoolize 2>&1 >/dev/null`
11 if [ $? -eq 0 ]; then
12 glibtoolize --force
13 else
14 libtoolize --force
15 fi
16 aclocal -I m4
17 autoheader
18 automake --add-missing
19 autoconf
20
21 cd "$olddir"
22)
23
24if [ -z "$NOCONFIGURE" ]; then
25 $srcdir/configure "$@"
26fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..52a28b5
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,217 @@
1# -*- Autoconf -*-
2# Process this file with autoconf to produce a configure script.
3
4AC_PREREQ(2.68)
5AC_INIT([libirecovery], [m4_esyscmd(./git-version-gen $RELEASE_VERSION)], [https://github.com/libimobiledevice/libirecovery/issues], [], [https://libimobiledevice.org])
6AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip check-news])
7m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
8AC_CONFIG_SRCDIR([src/])
9AC_CONFIG_HEADERS([config.h])
10AC_CONFIG_MACRO_DIR([m4])
11
12dnl libtool versioning
13# +1 : 0 : +1 == adds new functions to the interface
14# +1 : 0 : 0 == changes or removes functions (changes include both
15# changes to the signature and the semantic)
16# ? :+1 : ? == just internal changes
17# CURRENT : REVISION : AGE
18LIBIRECOVERY_SO_VERSION=6:2:1
19
20dnl Minimum package versions
21LIBUSB_VERSION=1.0.3
22LIMD_GLUE_VERSION=1.2.0
23
24AC_SUBST(LIBIRECOVERY_SO_VERSION)
25AC_SUBST(LIMD_GLUE_VERSION)
26
27# Checks for programs.
28AC_PROG_CC
29#AC_PROG_CXX
30AM_PROG_CC_C_O
31LT_INIT
32
33# Checks for libraries.
34PKG_CHECK_MODULES(limd_glue, libimobiledevice-glue-1.0 >= $LIMD_GLUE_VERSION)
35
36# Checks for header files.
37AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
38
39# Checks for typedefs, structures, and compiler characteristics.
40AC_C_CONST
41AC_TYPE_SIZE_T
42AC_TYPE_SSIZE_T
43AC_TYPE_UINT16_T
44AC_TYPE_UINT32_T
45AC_TYPE_UINT8_T
46
47# Checks for library functions.
48AC_CHECK_FUNCS([strdup strerror strcasecmp strndup malloc realloc calloc])
49
50# Check additional platform flags
51AC_MSG_CHECKING([for platform-specific build settings])
52case ${host_os} in
53 darwin*)
54 AC_MSG_RESULT([${host_os}])
55 AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [
56 AC_CHECK_HEADER(IOKit/usb/IOUSBLib.h, [
57 GLOBAL_LDFLAGS+=" -framework IOKit -framework CoreFoundation"
58 have_iokit=yes
59 ], [])
60 ], [])
61 ;;
62 mingw32*)
63 AC_MSG_RESULT([${host_os}])
64 GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lsetupapi"
65 win32=true
66 ;;
67 cygwin*)
68 AC_MSG_RESULT([${host_os}])
69 CC=gcc-3
70 CFLAGS+=" -mno-cygwin"
71 GLOBAL_LDFLAGS+=" -static-libgcc -lkernel32 -lsetupapi"
72 win32=true
73 ;;
74 *)
75 AC_MSG_RESULT([${host_os}])
76 ;;
77esac
78AM_CONDITIONAL(WIN32, test x$win32 = xtrue)
79
80AC_ARG_WITH([tools],
81 [AS_HELP_STRING([--with-tools], [Build irecovery tools. [default=yes]])],
82 [],
83 [with_tools=yes])
84
85AS_IF([test "x$with_tools" = "xyes"], [
86 have_readline=no
87 AC_DEFINE(BUILD_TOOLS, 1, [Define if we are building irecovery tools])
88 AC_CHECK_HEADERS([readline/readline.h],
89 [AC_DEFINE(HAVE_READLINE, 1, [Define if readline is available])
90 have_readline=yes],
91 [AC_MSG_NOTICE([NOTE: Building without readline support. If you want readline support, install its development package.])]
92 )
93 AM_CONDITIONAL(HAVE_READLINE, test "x$have_readline" = "xyes")
94])
95AM_CONDITIONAL(BUILD_TOOLS, test "x$with_tools" = "xyes")
96
97AC_ARG_WITH([dummy],
98 [AS_HELP_STRING([--with-dummy], [Use no USB driver at all [default=no]. This is only useful if you just want to query the device list by product type or hardware model. All other operations are no-ops or will return IRECV_E_UNSUPPORTED.])],
99 [],
100 [with_dummy=no])
101
102AS_IF([test "x$have_iokit" = "xyes"], [
103 AC_ARG_WITH([iokit],
104 [AS_HELP_STRING([--with-iokit], [Use IOKit instead of libusb on OS X [default=yes]])],
105 [],
106 [with_iokit=yes])
107 ]
108)
109
110AS_IF([test "x$with_dummy" = "xyes"], [
111 AC_DEFINE(USE_DUMMY, 1, [Define if we are using dummy USB driver])
112 USB_BACKEND="dummy"
113], [
114 AS_IF([test "x$with_iokit" = "xyes" && test "x$have_iokit" = "xyes"], [
115 AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
116 USB_BACKEND="IOKit"
117 ], [
118 AS_IF([test "x$win32" = "xtrue"], [
119 USB_BACKEND="win32 native (setupapi)"
120 ], [
121 PKG_CHECK_MODULES(libusb, libusb-1.0 >= $LIBUSB_VERSION)
122 USB_BACKEND="libusb `$PKG_CONFIG --modversion libusb-1.0`"
123 LIBUSB_REQUIRED="libusb-1.0 >= $LIBUSB_VERSION"
124 AC_SUBST(LIBUSB_REQUIRED)
125 ])
126 ])
127])
128
129AS_COMPILER_FLAGS(GLOBAL_CFLAGS, "-Wall -Wextra -Wmissing-declarations -Wredundant-decls -Wshadow -Wpointer-arith -Wwrite-strings -Wswitch-default -Wno-unused-parameter -fvisibility=hidden")
130
131if test "x$enable_static" = "xyes" -a "x$enable_shared" = "xno"; then
132 GLOBAL_CFLAGS+=" -DIRECV_STATIC"
133fi
134
135AC_SUBST(GLOBAL_CFLAGS)
136AC_SUBST(GLOBAL_LDFLAGS)
137
138# check for large file support
139AC_SYS_LARGEFILE
140
141AC_ARG_WITH([udev],
142 AS_HELP_STRING([--with-udev],
143 [Configure and install udev rules file for DFU/Recovery mode devices]),
144 [],
145 [if $($PKG_CONFIG --exists udev); then with_udev=yes; else with_udev=no; fi])
146
147AC_ARG_WITH([udevrulesdir],
148 AS_HELP_STRING([--with-udevrulesdir=DIR],
149 [Directory for udev rules (implies --with-udev)]),
150 [with_udev=yes],
151 [with_udevrulesdir=auto])
152
153AC_ARG_WITH([udevrule],
154 AS_HELP_STRING([--with-udevrule="RULE"],
155 [udev activation rule (implies --with-udev)]),
156 [with_udev=yes],
157 [with_udevrule=auto])
158
159if test "x$with_udev" = "xyes"; then
160 if test "x$with_udevrule" = "xauto"; then
161 for I in plugdev storage disk staff; do
162 if grep $I /etc/group >/dev/null; then
163 USEGROUP=$I
164 break
165 fi
166 done
167 if test "x$USEGROUP" != "x"; then
168 if ! groups |grep $USEGROUP >/dev/null; then
169 AC_MSG_WARN([The group '$USEGROUP' was determined to be used for the udev rule, but the current user is not member of this group.])
170 fi
171 else
172 AC_MSG_ERROR([Could not determine an appropriate user group for the udev activation rule.
173 Please manually specify a udev activation rule using --with-udevrule=<RULE>
174 Example: --with-udevrule="OWNER=\\"root\\", GROUP=\\"myusergroup\\", MODE=\\"0660\\""])
175 fi
176 with_udevrule="OWNER=\"root\", GROUP=\"$USEGROUP\", MODE=\"0660\""
177 fi
178
179 if test "x$with_udevrulesdir" = "xauto"; then
180 udevdir=$($PKG_CONFIG --silence-errors --variable=udevdir udev)
181 if test "x$udevdir" != "x"; then
182 with_udevrulesdir=$udevdir"/rules.d"
183 else
184 with_udevrulesdir="\${prefix}/lib/udev/rules.d"
185 AC_MSG_WARN([Could not determine default udev rules directory. Using $with_udevrulesdir.])
186 fi
187 fi
188
189 AC_SUBST([udev_activation_rule], [$with_udevrule])
190 AC_SUBST([udevrulesdir], [$with_udevrulesdir])
191fi
192AM_CONDITIONAL(WITH_UDEV, test "x$with_udev" = "xyes")
193
194m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
195
196AC_CONFIG_FILES([
197Makefile
198src/Makefile
199src/libirecovery-1.0.pc
200udev/39-libirecovery.rules
201include/Makefile
202tools/Makefile
203udev/Makefile
204])
205AC_OUTPUT
206
207echo "
208Configuration for $PACKAGE $VERSION:
209-------------------------------------------
210
211 Install prefix: .........: $prefix
212 USB backend: ............: $USB_BACKEND
213 Build tools: ............: $with_tools
214
215 Now type 'make' to build $PACKAGE $VERSION,
216 and then 'make install' for installation.
217"
diff --git a/git-version-gen b/git-version-gen
new file mode 100755
index 0000000..d868952
--- /dev/null
+++ b/git-version-gen
@@ -0,0 +1,20 @@
1#!/bin/sh
2SRCDIR=`dirname $0`
3if test -n "$1"; then
4 VER=$1
5else
6 if test -r "${SRCDIR}/.git" && test -x "`which git`" ; then
7 git update-index -q --refresh
8 if ! VER=`git describe --tags --dirty 2>/dev/null`; then
9 COMMIT=`git rev-parse --short HEAD`
10 DIRTY=`git diff --quiet HEAD || echo "-dirty"`
11 VER=`sed -n '1,/RE/s/Version \(.*\)/\1/p' ${SRCDIR}/NEWS`-git-${COMMIT}${DIRTY}
12 fi
13 else
14 if test -f "${SRCDIR}/.tarball-version"; then
15 VER=`cat "${SRCDIR}/.tarball-version"`
16 fi
17 fi
18fi
19VER=`printf %s "$VER" | head -n1`
20printf %s "$VER"
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..aa885aa
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1 @@
nobase_dist_include_HEADERS = libirecovery.h \ No newline at end of file
diff --git a/include/libirecovery.h b/include/libirecovery.h
index c459984..7c3323c 100644
--- a/include/libirecovery.h
+++ b/include/libirecovery.h
@@ -1,20 +1,22 @@
1/** 1/*
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode 2 * libirecovery.h
3 * Copyright (C) 2008 - 2009 westbaer 3 * Communication to iBoot/iBSS on Apple iOS devices via USB
4 * 4 *
5 * This program is free software: you can redistribute it and/or modify 5 * Copyright (c) 2012-2023 Nikias Bassen <nikias@gmx.li>
6 * it under the terms of the GNU General Public License as published by 6 * Copyright (c) 2012-2013 Martin Szulecki <m.szulecki@libimobiledevice.org>
7 * the Free Software Foundation, either version 3 of the License, or 7 * Copyright (c) 2010 Chronic-Dev Team
8 * (at your option) any later version. 8 * Copyright (c) 2010 Joshua Hill
9 * 9 *
10 * This program is distributed in the hope that it will be useful, 10 * All rights reserved. This program and the accompanying materials
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * are made available under the terms of the GNU Lesser General Public License
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * (LGPL) version 2.1 which accompanies this distribution, and is available at
13 * GNU General Public License for more details. 13 * http://www.gnu.org/licenses/lgpl-2.1.html
14 * 14 *
15 * You should have received a copy of the GNU General Public License 15 * This library is distributed in the hope that it will be useful,
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **/ 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 */
18 20
19#ifndef LIBIRECOVERY_H 21#ifndef LIBIRECOVERY_H
20#define LIBIRECOVERY_H 22#define LIBIRECOVERY_H
@@ -23,93 +25,179 @@
23extern "C" { 25extern "C" {
24#endif 26#endif
25 27
26#include <libusb-1.0/libusb.h> 28#include <stdint.h>
27 29
28#define APPLE_VENDOR_ID 0x05AC 30#ifndef IRECV_API
31 #ifdef IRECV_STATIC
32 #define IRECV_API
33 #elif defined(_WIN32)
34 #define IRECV_API __declspec(dllimport)
35 #else
36 #define IRECV_API
37 #endif
38#endif
29 39
30enum { 40enum irecv_mode {
31 kRecoveryMode1 = 0x1280, 41 IRECV_K_RECOVERY_MODE_1 = 0x1280,
32 kRecoveryMode2 = 0x1281, 42 IRECV_K_RECOVERY_MODE_2 = 0x1281,
33 kRecoveryMode3 = 0x1282, 43 IRECV_K_RECOVERY_MODE_3 = 0x1282,
34 kRecoveryMode4 = 0x1283, 44 IRECV_K_RECOVERY_MODE_4 = 0x1283,
35 kDfuMode = 0x1227 45 IRECV_K_WTF_MODE = 0x1222,
46 IRECV_K_DFU_MODE = 0x1227,
47 IRECV_K_PORT_DFU_MODE = 0xf014
36}; 48};
37 49
38typedef enum { 50typedef enum {
39 IRECV_E_SUCCESS = 0, 51 IRECV_E_SUCCESS = 0,
40 IRECV_E_NO_DEVICE = -1, 52 IRECV_E_NO_DEVICE = -1,
41 IRECV_E_OUT_OF_MEMORY = -2, 53 IRECV_E_OUT_OF_MEMORY = -2,
42 IRECV_E_UNABLE_TO_CONNECT = -3, 54 IRECV_E_UNABLE_TO_CONNECT = -3,
43 IRECV_E_INVALID_INPUT = -4, 55 IRECV_E_INVALID_INPUT = -4,
44 IRECV_E_FILE_NOT_FOUND = -5, 56 IRECV_E_FILE_NOT_FOUND = -5,
45 IRECV_E_USB_UPLOAD = -6, 57 IRECV_E_USB_UPLOAD = -6,
46 IRECV_E_USB_STATUS = -7, 58 IRECV_E_USB_STATUS = -7,
47 IRECV_E_USB_INTERFACE = -8, 59 IRECV_E_USB_INTERFACE = -8,
48 IRECV_E_USB_CONFIGURATION = -9, 60 IRECV_E_USB_CONFIGURATION = -9,
49 IRECV_E_PIPE = -10, 61 IRECV_E_PIPE = -10,
50 IRECV_E_TIMEOUT = -11, 62 IRECV_E_TIMEOUT = -11,
51 IRECV_E_UNKNOWN_ERROR = -255 63 IRECV_E_UNSUPPORTED = -254,
64 IRECV_E_UNKNOWN_ERROR = -255
52} irecv_error_t; 65} irecv_error_t;
53 66
54typedef enum { 67typedef enum {
55 IRECV_RECEIVED = 1, 68 IRECV_RECEIVED = 1,
56 IRECV_PRECOMMAND = 2, 69 IRECV_PRECOMMAND = 2,
57 IRECV_POSTCOMMAND = 3, 70 IRECV_POSTCOMMAND = 3,
58 IRECV_CONNECTED = 4, 71 IRECV_CONNECTED = 4,
59 IRECV_DISCONNECTED = 5, 72 IRECV_DISCONNECTED = 5,
60 IRECV_PROGRESS = 6 73 IRECV_PROGRESS = 6
61} irecv_event_type; 74} irecv_event_type;
62 75
63typedef struct { 76typedef struct {
64 int size; 77 int size;
65 char* data; 78 const char* data;
66 double progress; 79 double progress;
67 irecv_event_type type; 80 irecv_event_type type;
68} irecv_event_t; 81} irecv_event_t;
69 82
70struct irecv_client; 83struct irecv_device {
71typedef struct irecv_client* irecv_client_t; 84 const char* product_type;
72typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event); 85 const char* hardware_model;
86 unsigned int board_id;
87 unsigned int chip_id;
88 const char* display_name;
89};
90typedef struct irecv_device* irecv_device_t;
91
92struct irecv_device_info {
93 unsigned int cpid;
94 unsigned int cprv;
95 unsigned int cpfm;
96 unsigned int scep;
97 unsigned int bdid;
98 uint64_t ecid;
99 unsigned int ibfl;
100 char* srnm;
101 char* imei;
102 char* srtg;
103 char* serial_string;
104 unsigned char* ap_nonce;
105 unsigned int ap_nonce_size;
106 unsigned char* sep_nonce;
107 unsigned int sep_nonce_size;
108 uint16_t pid;
109 unsigned int have_cpid : 1;
110 unsigned int have_cprv : 1;
111 unsigned int have_cpfm : 1;
112 unsigned int have_scep : 1;
113 unsigned int have_bdid : 1;
114 unsigned int have_ecid : 1;
115 unsigned int have_ibfl : 1;
116};
73 117
74struct irecv_client { 118typedef enum {
75 int debug; 119 IRECV_DEVICE_ADD = 1,
76 int config; 120 IRECV_DEVICE_REMOVE = 2
77 int interface; 121} irecv_device_event_type;
78 int alt_interface; 122
79 unsigned short mode; 123typedef struct {
80 char serial[256]; 124 irecv_device_event_type type;
81 libusb_device_handle* handle; 125 enum irecv_mode mode;
82 irecv_event_cb_t progress_callback; 126 struct irecv_device_info *device_info;
83 irecv_event_cb_t received_callback; 127} irecv_device_event_t;
84 irecv_event_cb_t connected_callback; 128
85 irecv_event_cb_t precommand_callback; 129typedef struct irecv_client_private irecv_client_private;
86 irecv_event_cb_t postcommand_callback; 130typedef irecv_client_private* irecv_client_t;
87 irecv_event_cb_t disconnected_callback; 131
132enum {
133 IRECV_SEND_OPT_NONE = 0,
134 IRECV_SEND_OPT_DFU_NOTIFY_FINISH = (1 << 0),
135 IRECV_SEND_OPT_DFU_FORCE_ZLP = (1 << 1),
136 IRECV_SEND_OPT_DFU_SMALL_PKT = (1 << 2)
88}; 137};
89 138
90void irecv_set_debug_level(int level); 139/* library */
91const char* irecv_strerror(irecv_error_t error); 140IRECV_API void irecv_set_debug_level(int level);
92irecv_error_t irecv_open(irecv_client_t* client); 141IRECV_API const char* irecv_strerror(irecv_error_t error);
93irecv_error_t irecv_reset(irecv_client_t client); 142
94irecv_error_t irecv_close(irecv_client_t client); 143IRECV_API const char* irecv_version();
95irecv_error_t irecv_receive(irecv_client_t client); 144
96irecv_error_t irecv_send_exploit(irecv_client_t client); 145/* device connectivity */
97irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename); 146IRECV_API irecv_error_t irecv_open_with_ecid(irecv_client_t* client, uint64_t ecid);
98irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration); 147IRECV_API irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, uint64_t ecid, int attempts);
99 148IRECV_API irecv_error_t irecv_reset(irecv_client_t client);
100irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data); 149IRECV_API irecv_error_t irecv_close(irecv_client_t client);
101irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type); 150IRECV_API irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause);
102 151
103irecv_error_t irecv_send_file(irecv_client_t client, const char* filename); 152/* misc */
104irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command); 153IRECV_API irecv_error_t irecv_receive(irecv_client_t client);
105irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length); 154IRECV_API irecv_error_t irecv_execute_script(irecv_client_t client, const char* script);
106 155IRECV_API irecv_error_t irecv_reset_counters(irecv_client_t client);
107irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value); 156IRECV_API irecv_error_t irecv_finish_transfer(irecv_client_t client);
108irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value); 157IRECV_API irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client);
109irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface); 158
110irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid); 159/* usb helpers */
111irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid); 160IRECV_API irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration);
112irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid); 161IRECV_API irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface);
162IRECV_API int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout);
163IRECV_API int irecv_async_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length);
164IRECV_API int irecv_async_usb_control_transfer_with_cancel(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int u_time);
165IRECV_API int irecv_usb_bulk_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout);
166IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout);
167
168/* events */
169typedef void(*irecv_device_event_cb_t)(const irecv_device_event_t* event, void *user_data);
170typedef struct irecv_device_event_context* irecv_device_event_context_t;
171IRECV_API irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data);
172IRECV_API irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context);
173typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event);
174IRECV_API irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data);
175IRECV_API irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type);
176
177/* I/O */
178IRECV_API irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, unsigned int options);
179IRECV_API irecv_error_t irecv_send_command(irecv_client_t client, const char* command);
180IRECV_API irecv_error_t irecv_send_command_breq(irecv_client_t client, const char* command, uint8_t b_request);
181IRECV_API irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options);
182IRECV_API irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length);
183
184/* commands */
185IRECV_API irecv_error_t irecv_saveenv(irecv_client_t client);
186IRECV_API irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value);
187IRECV_API irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value);
188IRECV_API irecv_error_t irecv_setenv_np(irecv_client_t client, const char* variable, const char* value);
189IRECV_API irecv_error_t irecv_reboot(irecv_client_t client);
190IRECV_API irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value);
191
192/* device information */
193IRECV_API irecv_error_t irecv_get_mode(irecv_client_t client, int* mode);
194IRECV_API const struct irecv_device_info* irecv_get_device_info(irecv_client_t client);
195
196/* device database queries */
197IRECV_API irecv_device_t irecv_devices_get_all(void);
198IRECV_API irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device);
199IRECV_API irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device);
200IRECV_API irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device);
113 201
114#ifdef __cplusplus 202#ifdef __cplusplus
115} 203}
diff --git a/m4/as-compiler-flag.m4 b/m4/as-compiler-flag.m4
new file mode 100644
index 0000000..baab5d9
--- /dev/null
+++ b/m4/as-compiler-flag.m4
@@ -0,0 +1,62 @@
1dnl as-compiler-flag.m4 0.1.0
2
3dnl autostars m4 macro for detection of compiler flags
4
5dnl David Schleef <ds@schleef.org>
6
7dnl $Id: as-compiler-flag.m4,v 1.1 2005/12/15 23:35:19 ds Exp $
8
9dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
10dnl Tries to compile with the given CFLAGS.
11dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
12dnl and ACTION-IF-NOT-ACCEPTED otherwise.
13
14AC_DEFUN([AS_COMPILER_FLAG],
15[
16 AC_MSG_CHECKING([to see if compiler understands $1])
17
18 save_CFLAGS="$CFLAGS"
19 CFLAGS="$CFLAGS $1"
20
21 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no])
22 CFLAGS="$save_CFLAGS"
23
24 if test "X$flag_ok" = Xyes ; then
25 m4_ifvaln([$2],[$2])
26 true
27 else
28 m4_ifvaln([$3],[$3])
29 true
30 fi
31 AC_MSG_RESULT([$flag_ok])
32])
33
34dnl AS_COMPILER_FLAGS(VAR, FLAGS)
35dnl Tries to compile with the given CFLAGS.
36
37AC_DEFUN([AS_COMPILER_FLAGS],
38[
39 list=$2
40 flags_supported=""
41 flags_unsupported=""
42 AC_MSG_CHECKING([for supported compiler flags])
43 for each in $list
44 do
45 save_CFLAGS="$CFLAGS"
46 CFLAGS="$CFLAGS $each"
47 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [flag_ok=yes], [flag_ok=no])
48 CFLAGS="$save_CFLAGS"
49
50 if test "X$flag_ok" = Xyes ; then
51 flags_supported="$flags_supported $each"
52 else
53 flags_unsupported="$flags_unsupported $each"
54 fi
55 done
56 AC_MSG_RESULT([$flags_supported])
57 if test "X$flags_unsupported" != X ; then
58 AC_MSG_WARN([unsupported compiler flags: $flags_unsupported])
59 fi
60 $1="$$1 $flags_supported"
61])
62
diff --git a/scripts/boot.irs b/scripts/boot.irs
deleted file mode 100644
index f989328..0000000
--- a/scripts/boot.irs
+++ /dev/null
@@ -1,5 +0,0 @@
1# Simple script to kick you out of recovery mode
2setenv auto-boot true
3saveenv
4reboot
5
diff --git a/scripts/test.irs b/scripts/test.irs
deleted file mode 100644
index 77f959d..0000000
--- a/scripts/test.irs
+++ /dev/null
@@ -1,21 +0,0 @@
1# This small script should make the device flash
2# Red, Green, Blue a few times then reboot.
3bgcolor 255 0 0
4bgcolor 0 255 0
5bgcolor 0 0 255
6bgcolor 255 0 0
7bgcolor 0 255 0
8bgcolor 0 0 255
9bgcolor 255 0 0
10bgcolor 0 255 0
11bgcolor 0 0 255
12bgcolor 255 0 0
13bgcolor 0 255 0
14bgcolor 0 0 255
15bgcolor 255 0 0
16bgcolor 0 255 0
17bgcolor 0 0 255
18bgcolor 255 0 0
19bgcolor 0 255 0
20bgcolor 0 0 255
21reboot
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..f80ffa7
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,24 @@
1AM_CPPFLAGS = -I$(top_srcdir)/include
2
3AM_CFLAGS = \
4 $(GLOBAL_CFLAGS) \
5 $(LFS_CFLAGS) \
6 $(limd_glue_CFLAGS) \
7 $(libusb_CFLAGS)
8
9AM_LDFLAGS = \
10 $(GLOBAL_LDFLAGS) \
11 $(limd_glue_LIBS) \
12 $(libusb_LIBS)
13
14lib_LTLIBRARIES = libirecovery-1.0.la
15libirecovery_1_0_la_CFLAGS = $(AM_CFLAGS)
16libirecovery_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIRECOVERY_SO_VERSION) -no-undefined
17libirecovery_1_0_la_SOURCES = libirecovery.c
18
19if WIN32
20libirecovery_1_0_la_LDFLAGS += -avoid-version
21endif
22
23pkgconfigdir = $(libdir)/pkgconfig
24pkgconfig_DATA = libirecovery-1.0.pc
diff --git a/src/irecovery.c b/src/irecovery.c
deleted file mode 100644
index 5a59cd0..0000000
--- a/src/irecovery.c
+++ /dev/null
@@ -1,355 +0,0 @@
1/**
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode
3 * Copyright (C) 2008 - 2009 westbaer
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 **/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <libirecovery.h>
23#include <readline/readline.h>
24#include <readline/history.h>
25
26#define FILE_HISTORY_PATH ".irecovery"
27#define debug(...) if(verbose) fprintf(stderr, __VA_ARGS__)
28
29enum {
30 kResetDevice, kStartShell, kSendCommand, kSendFile, kSendExploit, kSendScript
31};
32
33static unsigned int quit = 0;
34static unsigned int verbose = 0;
35
36void print_progress_bar(double progress);
37int received_cb(irecv_client_t client, const irecv_event_t* event);
38int progress_cb(irecv_client_t client, const irecv_event_t* event);
39int precommand_cb(irecv_client_t client, const irecv_event_t* event);
40int postcommand_cb(irecv_client_t client, const irecv_event_t* event);
41
42void shell_usage() {
43 printf("Usage:\n");
44 printf("\t/upload <file>\tSend file to client.\n");
45 printf("\t/exploit [file]\tSend usb exploit with optional payload\n");
46 printf("\t/help\t\tShow this help.\n");
47 printf("\t/exit\t\tExit interactive shell.\n");
48}
49
50void parse_command(irecv_client_t client, unsigned char* command, unsigned int size) {
51 irecv_error_t error = 0;
52 char* cmd = strdup(command);
53 char* action = strtok(cmd, " ");
54 debug("Executing %s\n", action);
55 if (!strcmp(cmd, "/exit")) {
56 quit = 1;
57 } else
58
59 if (!strcmp(cmd, "/help")) {
60 shell_usage();
61 } else
62
63 if (!strcmp(cmd, "/upload")) {
64 char* filename = strtok(NULL, " ");
65 debug("Uploading files %s\n", filename);
66 if (filename != NULL) {
67 error = irecv_send_file(client, filename);
68 debug("%s\n", irecv_strerror(error));
69 }
70 } else
71
72 if (!strcmp(cmd, "/exploit")) {
73 char* filename = strtok(NULL, " ");
74 debug("Sending exploit %s\n", filename);
75 if (filename != NULL) {
76 error = irecv_send_file(client, filename);
77 debug("%s\n", irecv_strerror(error));
78 }
79 irecv_send_exploit(client);
80 } else
81
82 if (!strcmp(cmd, "/execute")) {
83 char* filename = strtok(NULL, " ");
84 debug("Executing script %s\n", filename);
85 if (filename != NULL) {
86 irecv_execute_script(client, filename);
87 }
88 }
89
90
91 free(action);
92}
93
94void load_command_history() {
95 read_history(FILE_HISTORY_PATH);
96}
97
98void append_command_to_history(char* cmd) {
99 add_history(cmd);
100 write_history(FILE_HISTORY_PATH);
101}
102
103void init_shell(irecv_client_t client) {
104 irecv_error_t error = 0;
105 load_command_history();
106 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
107 irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL);
108 irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL);
109 irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL);
110
111 error = irecv_receive(client);
112 if (error != IRECV_E_SUCCESS) {
113 debug("%s\n", irecv_strerror(error));
114 }
115
116 while (!quit) {
117 char* cmd = readline("> ");
118 if (cmd && *cmd) {
119 error = irecv_send_command(client, cmd);
120 if (error != IRECV_E_SUCCESS) {
121 quit = 1;
122 }
123
124 append_command_to_history(cmd);
125 free(cmd);
126 }
127 }
128}
129
130int received_cb(irecv_client_t client, const irecv_event_t* event) {
131 if (event->type == IRECV_RECEIVED) {
132 int i = 0;
133 int size = event->size;
134 char* data = event->data;
135 for (i = 0; i < size; i++) {
136 printf("%c", data[i]);
137 }
138 }
139 return 0;
140}
141
142int precommand_cb(irecv_client_t client, const irecv_event_t* event) {
143 char* value = NULL;
144 char* action = NULL;
145 char* command = NULL;
146 char* argument = NULL;
147 irecv_error_t error = IRECV_E_SUCCESS;
148
149 if (event->type == IRECV_PRECOMMAND) {
150 if (event->data[0] == '/') {
151 parse_command(client, event->data, event->size);
152 return -1;
153 }
154
155 command = strdup(event->data);
156 action = strtok(command, " ");
157
158 if (!strcmp(action, "getenv")) {
159 argument = strtok(NULL, " ");
160 error = irecv_getenv(client, argument, &value);
161 if (error != IRECV_E_SUCCESS) {
162 debug("%s\n", irecv_strerror(error));
163 free(command);
164 return error;
165 }
166 if (value) {
167 printf("%s\n", value);
168 free(value);
169 }
170 return 1;
171 }
172 }
173 return 0;
174}
175
176int postcommand_cb(irecv_client_t client, const irecv_event_t* event) {
177 char* action = NULL;
178 char* command = NULL;
179
180 if (event->type == IRECV_POSTCOMMAND) {
181 command = strdup(event->data);
182 action = strtok(command, " ");
183
184 if (!strcmp(action, "reboot")) {
185 quit = 1;
186 }
187 }
188
189 if (command) free(command);
190 return 0;
191}
192
193int progress_cb(irecv_client_t client, const irecv_event_t* event) {
194 if (event->type == IRECV_PROGRESS) {
195 print_progress_bar(event->progress);
196 }
197 return 0;
198}
199
200void print_progress_bar(double progress) {
201 int i = 0;
202 if(progress < 0) {
203 return;
204 }
205
206 if(progress > 100) {
207 progress = 100;
208 }
209
210 printf("\r[");
211 for(i = 0; i < 50; i++) {
212 if(i < progress / 2) {
213 printf("=");
214 } else {
215 printf(" ");
216 }
217 }
218
219 printf("] %3.1f%%", progress);
220 fflush(stdout);
221 if(progress == 100) {
222 printf("\n");
223 }
224}
225
226void print_usage() {
227 printf("iRecovery - iDevice Recovery Utility\n");
228 printf("Usage: ./irecovery [args]\n");
229 printf("\t-v\t\tStart irecovery in verbose mode.\n");
230 printf("\t-c <cmd>\tSend command to client.\n");
231 printf("\t-f <file>\tSend file to client.\n");
232 printf("\t-k [payload]\tSend usb exploit to client.\n");
233 printf("\t-h\t\tShow this help.\n");
234 printf("\t-r\t\tReset client.\n");
235 printf("\t-s\t\tStart interactive shell.\n");
236 printf("\t-e <script>\tExecutes recovery shell script.\n");
237 exit(1);
238}
239
240int main(int argc, char** argv) {
241 int i = 0;
242 int opt = 0;
243 int action = 0;
244 char* argument = NULL;
245 irecv_error_t error = 0;
246 if (argc == 1) print_usage();
247 while ((opt = getopt(argc, argv, "vhrsc:f:e:k::")) > 0) {
248 switch (opt) {
249 case 'v':
250 verbose += 1;
251 break;
252
253 case 'h':
254 print_usage();
255 break;
256
257 case 'r':
258 action = kResetDevice;
259 break;
260
261 case 's':
262 action = kStartShell;
263 break;
264
265 case 'f':
266 action = kSendFile;
267 argument = optarg;
268 break;
269
270 case 'c':
271 action = kSendCommand;
272 argument = optarg;
273 break;
274
275 case 'k':
276 action = kSendExploit;
277 argument = optarg;
278 break;
279
280 case 'e':
281 action = kSendScript;
282 argument = optarg;
283 break;
284
285 default:
286 fprintf(stderr, "Unknown argument\n");
287 return -1;
288 }
289 }
290
291 if (verbose) irecv_set_debug_level(verbose);
292
293 irecv_client_t client = NULL;
294 for (i = 0; i <= 5; i++) {
295 debug("Attempting to connect... \n");
296
297 if (irecv_open(&client) != IRECV_E_SUCCESS)
298 sleep(1);
299 else
300 break;
301
302 if (i == 5) {
303 return -1;
304 }
305 }
306
307 switch (action) {
308 case kResetDevice:
309 irecv_reset(client);
310 break;
311
312 case kSendFile:
313 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
314 error = irecv_send_file(client, argument);
315 debug("%s\n", irecv_strerror(error));
316 break;
317
318 case kSendCommand:
319 error = irecv_send_command(client, argument);
320 debug("%s\n", irecv_strerror(error));
321 break;
322
323 case kSendExploit:
324 if (argument != NULL) {
325 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
326 error = irecv_send_file(client, argument);
327 if (error != IRECV_E_SUCCESS) {
328 debug("%s\n", irecv_strerror(error));
329 break;
330 }
331 }
332 error = irecv_send_exploit(client);
333 debug("%s\n", irecv_strerror(error));
334 break;
335
336 case kStartShell:
337 init_shell(client);
338 break;
339
340 case kSendScript:
341 error = irecv_execute_script(client, argument);
342 if(error != IRECV_E_SUCCESS) {
343 debug("%s\n", irecv_strerror(error));
344 }
345 break;
346
347 default:
348 fprintf(stderr, "Unknown action\n");
349 break;
350 }
351
352 irecv_close(client);
353 return 0;
354}
355
diff --git a/src/libirecovery-1.0.pc.in b/src/libirecovery-1.0.pc.in
new file mode 100644
index 0000000..e6c94a3
--- /dev/null
+++ b/src/libirecovery-1.0.pc.in
@@ -0,0 +1,11 @@
1prefix=@prefix@
2exec_prefix=@exec_prefix@
3libdir=@libdir@
4includedir=@includedir@
5
6Name: @PACKAGE_NAME@
7Description: A library to communicate with iBoot/iBSS on iOS devices via USB
8Version: @PACKAGE_VERSION@
9Libs: -L${libdir} -lirecovery-1.0
10Cflags: -I${includedir}
11Requires.private: libimobiledevice-glue-1.0 >= @LIMD_GLUE_VERSION@ @LIBUSB_REQUIRED@
diff --git a/src/libirecovery.c b/src/libirecovery.c
index 38e0fc3..71563e1 100644
--- a/src/libirecovery.c
+++ b/src/libirecovery.c
@@ -1,117 +1,2238 @@
1/** 1/*
2 * iRecovery - Utility for DFU 2.0, WTF and Recovery Mode 2 * libirecovery.c
3 * Copyright (C) 2008 - 2009 westbaer 3 * Communication to iBoot/iBSS on Apple iOS devices via USB
4 * 4 *
5 * This program is free software: you can redistribute it and/or modify 5 * Copyright (c) 2011-2023 Nikias Bassen <nikias@gmx.li>
6 * it under the terms of the GNU General Public License as published by 6 * Copyright (c) 2012-2020 Martin Szulecki <martin.szulecki@libimobiledevice.org>
7 * the Free Software Foundation, either version 3 of the License, or 7 * Copyright (c) 2010 Chronic-Dev Team
8 * (at your option) any later version. 8 * Copyright (c) 2010 Joshua Hill
9 * Copyright (c) 2008-2011 Nicolas Haunold
9 * 10 *
10 * This program is distributed in the hope that it will be useful, 11 * All rights reserved. This program and the accompanying materials
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * are made available under the terms of the GNU Lesser General Public License
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * (LGPL) version 2.1 which accompanies this distribution, and is available at
13 * GNU General Public License for more details. 14 * http://www.gnu.org/licenses/lgpl-2.1.html
14 * 15 *
15 * You should have received a copy of the GNU General Public License 16 * This library is distributed in the hope that it will be useful,
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **/ 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
18 25
19#include <stdio.h> 26#include <stdio.h>
27#include <stdint.h>
20#include <stdlib.h> 28#include <stdlib.h>
21#include <string.h> 29#include <string.h>
30#include <inttypes.h>
31#include <ctype.h>
22#include <unistd.h> 32#include <unistd.h>
23#include <libusb-1.0/libusb.h> 33#include <sys/stat.h>
34
35#include <libimobiledevice-glue/collection.h>
36#include <libimobiledevice-glue/thread.h>
37
38#ifndef USE_DUMMY
39#ifndef _WIN32
40#ifndef HAVE_IOKIT
41#include <libusb.h>
42#if (defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)) || (defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102))
43#define HAVE_LIBUSB_HOTPLUG_API 1
44#endif
45#else
46#include <CoreFoundation/CoreFoundation.h>
47#include <IOKit/usb/IOUSBLib.h>
48#include <IOKit/IOCFPlugIn.h>
49#endif
50#else
51#define WIN32_LEAN_AND_MEAN
52#include <windows.h>
53#include <setupapi.h>
54#ifndef sleep
55#define sleep(n) Sleep(1000 * n)
56#endif
57#endif
58#endif
59
60#ifdef IRECV_STATIC
61 #define IRECV_API
62#elif defined(_WIN32)
63 #define IRECV_API __declspec( dllexport )
64#else
65 #if __GNUC__ >= 4
66 #define IRECV_API __attribute__((visibility("default")))
67 #else
68 #define IRECV_API
69 #endif
70#endif
24 71
25#include "libirecovery.h" 72#include "libirecovery.h"
26 73
74// Reference: https://stackoverflow.com/a/2390626/1806760
75// Initializer/finalizer sample for MSVC and GCC/Clang.
76// 2010-2016 Joe Lowe. Released into the public domain.
77
78#ifdef __cplusplus
79 #define INITIALIZER(f) \
80 static void f(void); \
81 struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
82 static void f(void)
83#elif defined(_MSC_VER)
84 #pragma section(".CRT$XCU",read)
85 #define INITIALIZER2_(f,p) \
86 static void f(void); \
87 __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
88 __pragma(comment(linker,"/include:" p #f "_")) \
89 static void f(void)
90 #ifdef _WIN64
91 #define INITIALIZER(f) INITIALIZER2_(f,"")
92 #else
93 #define INITIALIZER(f) INITIALIZER2_(f,"_")
94 #endif
95#else
96 #define INITIALIZER(f) \
97 static void f(void) __attribute__((__constructor__)); \
98 static void f(void)
99#endif
100
101#ifndef WIN32
102struct irecv_async_transfer {
103 uint32_t len;
104 #ifdef HAVE_IOKIT
105 kern_return_t ret;
106 #else
107 enum libusb_transfer_status ret;
108 #endif
109};
110
111#endif
112
113struct irecv_client_private {
114 int debug;
115 int usb_config;
116 int usb_interface;
117 int usb_alt_interface;
118 unsigned int mode;
119 int isKIS;
120 struct irecv_device_info device_info;
121#ifndef USE_DUMMY
122#ifndef _WIN32
123#ifndef HAVE_IOKIT
124 libusb_device_handle* handle;
125#else
126 IOUSBDeviceInterface320 **handle;
127 IOUSBInterfaceInterface300 **usbInterface;
128 CFRunLoopSourceRef async_event_source;
129#endif
130#else
131 HANDLE handle;
132#endif
133 irecv_event_cb_t progress_callback;
134 irecv_event_cb_t received_callback;
135 irecv_event_cb_t connected_callback;
136 irecv_event_cb_t precommand_callback;
137 irecv_event_cb_t postcommand_callback;
138 irecv_event_cb_t disconnected_callback;
139#endif
140};
141
142#define USB_TIMEOUT 10000
143#define APPLE_VENDOR_ID 0x05AC
144
145// KIS
146#define KIS_PRODUCT_ID 0x1881
147
148#define KIS_PORTAL_CONFIG 0x01
149#define KIS_PORTAL_RSM 0x10
150
151#define KIS_INDEX_UPLOAD 0x0D
152#define KIS_INDEX_ENABLE_A 0x0A // macOS writes to this
153#define KIS_INDEX_ENABLE_B 0x14 // macOS writes to this
154#define KIS_INDEX_GET_INFO 0x100
155#define KIS_INDEX_BOOT_IMG 0x103
156
157#define KIS_ENABLE_A_VAL 0x21 // Value to write to KIS_INDEX_ENABLE_A
158#define KIS_ENABLE_B_VAL 0x01 // Value to write to KIS_INDEX_ENABLE_B
159
27#define BUFFER_SIZE 0x1000 160#define BUFFER_SIZE 0x1000
28#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) 161#define debug(...) if (libirecovery_debug) fprintf(stderr, __VA_ARGS__)
29 162
30static int libirecovery_debug = 0; 163static int libirecovery_debug = 0;
164#ifndef USE_DUMMY
165#ifndef _WIN32
166#ifndef HAVE_IOKIT
31static libusb_context* libirecovery_context = NULL; 167static libusb_context* libirecovery_context = NULL;
168#endif
169#endif
170#endif
171
172static struct irecv_device irecv_devices[] = {
173 /* iPhone */
174 { "iPhone1,1", "m68ap", 0x00, 0x8900, "iPhone 2G" },
175 { "iPhone1,2", "n82ap", 0x04, 0x8900, "iPhone 3G" },
176 { "iPhone2,1", "n88ap", 0x00, 0x8920, "iPhone 3Gs" },
177 { "iPhone3,1", "n90ap", 0x00, 0x8930, "iPhone 4 (GSM)" },
178 { "iPhone3,2", "n90bap", 0x04, 0x8930, "iPhone 4 (GSM) R2 2012" },
179 { "iPhone3,3", "n92ap", 0x06, 0x8930, "iPhone 4 (CDMA)" },
180 { "iPhone4,1", "n94ap", 0x08, 0x8940, "iPhone 4s" },
181 { "iPhone5,1", "n41ap", 0x00, 0x8950, "iPhone 5 (GSM)" },
182 { "iPhone5,2", "n42ap", 0x02, 0x8950, "iPhone 5 (Global)" },
183 { "iPhone5,3", "n48ap", 0x0a, 0x8950, "iPhone 5c (GSM)" },
184 { "iPhone5,4", "n49ap", 0x0e, 0x8950, "iPhone 5c (Global)" },
185 { "iPhone6,1", "n51ap", 0x00, 0x8960, "iPhone 5s (GSM)" },
186 { "iPhone6,2", "n53ap", 0x02, 0x8960, "iPhone 5s (Global)" },
187 { "iPhone7,1", "n56ap", 0x04, 0x7000, "iPhone 6 Plus" },
188 { "iPhone7,2", "n61ap", 0x06, 0x7000, "iPhone 6" },
189 { "iPhone8,1", "n71ap", 0x04, 0x8000, "iPhone 6s" },
190 { "iPhone8,1", "n71map", 0x04, 0x8003, "iPhone 6s" },
191 { "iPhone8,2", "n66ap", 0x06, 0x8000, "iPhone 6s Plus" },
192 { "iPhone8,2", "n66map", 0x06, 0x8003, "iPhone 6s Plus" },
193 { "iPhone8,4", "n69ap", 0x02, 0x8003, "iPhone SE (1st gen)" },
194 { "iPhone8,4", "n69uap", 0x02, 0x8000, "iPhone SE (1st gen)" },
195 { "iPhone9,1", "d10ap", 0x08, 0x8010, "iPhone 7 (Global)" },
196 { "iPhone9,2", "d11ap", 0x0a, 0x8010, "iPhone 7 Plus (Global)" },
197 { "iPhone9,3", "d101ap", 0x0c, 0x8010, "iPhone 7 (GSM)" },
198 { "iPhone9,4", "d111ap", 0x0e, 0x8010, "iPhone 7 Plus (GSM)" },
199 { "iPhone10,1", "d20ap", 0x02, 0x8015, "iPhone 8 (Global)" },
200 { "iPhone10,2", "d21ap", 0x04, 0x8015, "iPhone 8 Plus (Global)" },
201 { "iPhone10,3", "d22ap", 0x06, 0x8015, "iPhone X (Global)" },
202 { "iPhone10,4", "d201ap", 0x0a, 0x8015, "iPhone 8 (GSM)" },
203 { "iPhone10,5", "d211ap", 0x0c, 0x8015, "iPhone 8 Plus (GSM)" },
204 { "iPhone10,6", "d221ap", 0x0e, 0x8015, "iPhone X (GSM)" },
205 { "iPhone11,2", "d321ap", 0x0e, 0x8020, "iPhone XS" },
206 { "iPhone11,4", "d331ap", 0x0a, 0x8020, "iPhone XS Max (China)" },
207 { "iPhone11,6", "d331pap", 0x1a, 0x8020, "iPhone XS Max" },
208 { "iPhone11,8", "n841ap", 0x0c, 0x8020, "iPhone XR" },
209 { "iPhone12,1", "n104ap", 0x04, 0x8030, "iPhone 11" },
210 { "iPhone12,3", "d421ap", 0x06, 0x8030, "iPhone 11 Pro" },
211 { "iPhone12,5", "d431ap", 0x02, 0x8030, "iPhone 11 Pro Max" },
212 { "iPhone12,8", "d79ap", 0x10, 0x8030, "iPhone SE (2nd gen)" },
213 { "iPhone13,1", "d52gap", 0x0A, 0x8101, "iPhone 12 mini" },
214 { "iPhone13,2", "d53gap", 0x0C, 0x8101, "iPhone 12" },
215 { "iPhone13,3", "d53pap", 0x0E, 0x8101, "iPhone 12 Pro" },
216 { "iPhone13,4", "d54pap", 0x08, 0x8101, "iPhone 12 Pro Max" },
217 { "iPhone14,2", "d63ap", 0x0C, 0x8110, "iPhone 13 Pro" },
218 { "iPhone14,3", "d64ap", 0x0E, 0x8110, "iPhone 13 Pro Max" },
219 { "iPhone14,4", "d16ap", 0x08, 0x8110, "iPhone 13 mini" },
220 { "iPhone14,5", "d17ap", 0x0A, 0x8110, "iPhone 13" },
221 { "iPhone14,6", "d49ap", 0x10, 0x8110, "iPhone SE (3rd gen)" },
222 { "iPhone14,7", "d27ap", 0x18, 0x8110, "iPhone 14" },
223 { "iPhone14,8", "d28ap", 0x1A, 0x8110, "iPhone 14 Plus" },
224 { "iPhone15,2", "d73ap", 0x0C, 0x8120, "iPhone 14 Pro" },
225 { "iPhone15,3", "d74ap", 0x0E, 0x8120, "iPhone 14 Pro Max" },
226 { "iPhone15,4", "d37ap", 0x08, 0x8120, "iPhone 15" },
227 { "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
228 { "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
229 { "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" },
230 { "iPhone17,1", "d93ap", 0x0C, 0x8140, "iPhone 16 Pro" },
231 { "iPhone17,2", "d94ap", 0x0E, 0x8140, "iPhone 16 Pro Max" },
232 { "iPhone17,3", "d47ap", 0x08, 0x8140, "iPhone 16" },
233 { "iPhone17,4", "d48ap", 0x0A, 0x8140, "iPhone 16 Plus" },
234 { "iPhone17,5", "v59ap", 0x04, 0x8140, "iPhone 16e" },
235 { "iPhone18,1", "v53ap", 0x0C, 0x8150, "iPhone 17 Pro" },
236 { "iPhone18,2", "v54ap", 0x0E, 0x8150, "iPhone 17 Pro Max" },
237 { "iPhone18,3", "v57ap", 0x08, 0x8150, "iPhone 17" },
238 { "iPhone18,4", "d23ap", 0x0A, 0x8150, "iPhone Air" },
239 { "iPhone18,5", "v159ap", 0x16, 0x8150, "iPhone 17e" },
240 /* iPod */
241 { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
242 { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
243 { "iPod3,1", "n18ap", 0x02, 0x8922, "iPod Touch (3rd gen)" },
244 { "iPod4,1", "n81ap", 0x08, 0x8930, "iPod Touch (4th gen)" },
245 { "iPod5,1", "n78ap", 0x00, 0x8942, "iPod Touch (5th gen)" },
246 { "iPod7,1", "n102ap", 0x10, 0x7000, "iPod Touch (6th gen)" },
247 { "iPod9,1", "n112ap", 0x16, 0x8010, "iPod Touch (7th gen)" },
248 /* iPad */
249 { "iPad1,1", "k48ap", 0x02, 0x8930, "iPad" },
250 { "iPad2,1", "k93ap", 0x04, 0x8940, "iPad 2 (WiFi)" },
251 { "iPad2,2", "k94ap", 0x06, 0x8940, "iPad 2 (GSM)" },
252 { "iPad2,3", "k95ap", 0x02, 0x8940, "iPad 2 (CDMA)" },
253 { "iPad2,4", "k93aap", 0x06, 0x8942, "iPad 2 (WiFi) R2 2012" },
254 { "iPad2,5", "p105ap", 0x0a, 0x8942, "iPad mini (WiFi)" },
255 { "iPad2,6", "p106ap", 0x0c, 0x8942, "iPad mini (GSM)" },
256 { "iPad2,7", "p107ap", 0x0e, 0x8942, "iPad mini (Global)" },
257 { "iPad3,1", "j1ap", 0x00, 0x8945, "iPad (3rd gen, WiFi)" },
258 { "iPad3,2", "j2ap", 0x02, 0x8945, "iPad (3rd gen, CDMA)" },
259 { "iPad3,3", "j2aap", 0x04, 0x8945, "iPad (3rd gen, GSM)" },
260 { "iPad3,4", "p101ap", 0x00, 0x8955, "iPad (4th gen, WiFi)" },
261 { "iPad3,5", "p102ap", 0x02, 0x8955, "iPad (4th gen, GSM)" },
262 { "iPad3,6", "p103ap", 0x04, 0x8955, "iPad (4th gen, Global)" },
263 { "iPad4,1", "j71ap", 0x10, 0x8960, "iPad Air (WiFi)" },
264 { "iPad4,2", "j72ap", 0x12, 0x8960, "iPad Air (Cellular)" },
265 { "iPad4,3", "j73ap", 0x14, 0x8960, "iPad Air (China)" },
266 { "iPad4,4", "j85ap", 0x0a, 0x8960, "iPad mini 2 (WiFi)" },
267 { "iPad4,5", "j86ap", 0x0c, 0x8960, "iPad mini 2 (Cellular)" },
268 { "iPad4,6", "j87ap", 0x0e, 0x8960, "iPad mini 2 (China)" },
269 { "iPad4,7", "j85map", 0x32, 0x8960, "iPad mini 3 (WiFi)" },
270 { "iPad4,8", "j86map", 0x34, 0x8960, "iPad mini 3 (Cellular)" },
271 { "iPad4,9", "j87map", 0x36, 0x8960, "iPad mini 3 (China)" },
272 { "iPad5,1", "j96ap", 0x08, 0x7000, "iPad mini 4 (WiFi)" },
273 { "iPad5,2", "j97ap", 0x0A, 0x7000, "iPad mini 4 (Cellular)" },
274 { "iPad5,3", "j81ap", 0x06, 0x7001, "iPad Air 2 (WiFi)" },
275 { "iPad5,4", "j82ap", 0x02, 0x7001, "iPad Air 2 (Cellular)" },
276 { "iPad6,3", "j127ap", 0x08, 0x8001, "iPad Pro 9.7-inch (WiFi)" },
277 { "iPad6,4", "j128ap", 0x0a, 0x8001, "iPad Pro 9.7-inch (Cellular)" },
278 { "iPad6,7", "j98aap", 0x10, 0x8001, "iPad Pro 12.9-inch (1st gen, WiFi)" },
279 { "iPad6,8", "j99aap", 0x12, 0x8001, "iPad Pro 12.9-inch (1st gen, Cellular)" },
280 { "iPad6,11", "j71sap", 0x10, 0x8000, "iPad (5th gen, WiFi)" },
281 { "iPad6,11", "j71tap", 0x10, 0x8003, "iPad (5th gen, WiFi)" },
282 { "iPad6,12", "j72sap", 0x12, 0x8000, "iPad (5th gen, Cellular)" },
283 { "iPad6,12", "j72tap", 0x12, 0x8003, "iPad (5th gen, Cellular)" },
284 { "iPad7,1", "j120ap", 0x0C, 0x8011, "iPad Pro 12.9-inch (2nd gen, WiFi)" },
285 { "iPad7,2", "j121ap", 0x0E, 0x8011, "iPad Pro 12.9-inch (2nd gen, Cellular)" },
286 { "iPad7,3", "j207ap", 0x04, 0x8011, "iPad Pro 10.5-inch (WiFi)" },
287 { "iPad7,4", "j208ap", 0x06, 0x8011, "iPad Pro 10.5-inch (Cellular)" },
288 { "iPad7,5", "j71bap", 0x18, 0x8010, "iPad (6th gen, WiFi)" },
289 { "iPad7,6", "j72bap", 0x1A, 0x8010, "iPad (6th gen, Cellular)" },
290 { "iPad7,11", "j171ap", 0x1C, 0x8010, "iPad (7th gen, WiFi)" },
291 { "iPad7,12", "j172ap", 0x1E, 0x8010, "iPad (7th gen, Cellular)" },
292 { "iPad8,1", "j317ap", 0x0C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi)" },
293 { "iPad8,2", "j317xap", 0x1C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi, 1TB)" },
294 { "iPad8,3", "j318ap", 0x0E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular)" },
295 { "iPad8,4", "j318xap", 0x1E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular, 1TB)" },
296 { "iPad8,5", "j320ap", 0x08, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi)" },
297 { "iPad8,6", "j320xap", 0x18, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi, 1TB)" },
298 { "iPad8,7", "j321ap", 0x0A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular)" },
299 { "iPad8,8", "j321xap", 0x1A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular, 1TB)" },
300 { "iPad8,9", "j417ap", 0x3C, 0x8027, "iPad Pro 11-inch (2nd gen, WiFi)" },
301 { "iPad8,10", "j418ap", 0x3E, 0x8027, "iPad Pro 11-inch (2nd gen, Cellular)" },
302 { "iPad8,11", "j420ap", 0x38, 0x8027, "iPad Pro 12.9-inch (4th gen, WiFi)" },
303 { "iPad8,12", "j421ap", 0x3A, 0x8027, "iPad Pro 12.9-inch (4th gen, Cellular)" },
304 { "iPad11,1", "j210ap", 0x14, 0x8020, "iPad mini (5th gen, WiFi)" },
305 { "iPad11,2", "j211ap", 0x16, 0x8020, "iPad mini (5th gen, Cellular)" },
306 { "iPad11,3", "j217ap", 0x1C, 0x8020, "iPad Air (3rd gen, WiFi)" },
307 { "iPad11,4", "j218ap", 0x1E, 0x8020, "iPad Air (3rd gen, Cellular)" },
308 { "iPad11,6", "j171aap", 0x24, 0x8020, "iPad (8th gen, WiFi)" },
309 { "iPad11,7", "j172aap", 0x26, 0x8020, "iPad (8th gen, Cellular)" },
310 { "iPad12,1", "j181ap", 0x18, 0x8030, "iPad (9th gen, WiFi)" },
311 { "iPad12,2", "j182ap", 0x1A, 0x8030, "iPad (9th gen, Cellular)" },
312 { "iPad13,1", "j307ap", 0x04, 0x8101, "iPad Air (4th gen, WiFi)" },
313 { "iPad13,2", "j308ap", 0x06, 0x8101, "iPad Air (4th gen, Cellular)" },
314 { "iPad13,4", "j517ap", 0x08, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi)" },
315 { "iPad13,5", "j517xap", 0x0A, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi, 2TB)" },
316 { "iPad13,6", "j518ap", 0x0C, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular)" },
317 { "iPad13,7", "j518xap", 0x0E, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular, 2TB)" },
318 { "iPad13,8", "j522ap", 0x18, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi)" },
319 { "iPad13,9", "j522xap", 0x1A, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi, 2TB)" },
320 { "iPad13,10", "j523ap", 0x1C, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular)" },
321 { "iPad13,11", "j523xap", 0x1E, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular, 2TB)" },
322 { "iPad13,16", "j407ap", 0x10, 0x8103, "iPad Air (5th gen, WiFi)" },
323 { "iPad13,17", "j408ap", 0x12, 0x8103, "iPad Air (5th gen, Cellular)" },
324 { "iPad13,18", "j271ap", 0x14, 0x8101, "iPad (10th gen, WiFi)" },
325 { "iPad13,19", "j272ap", 0x16, 0x8101, "iPad (10th gen, Cellular)" },
326 { "iPad14,1", "j310ap", 0x04, 0x8110, "iPad mini (6th gen, WiFi)" },
327 { "iPad14,2", "j311ap", 0x06, 0x8110, "iPad mini (6th gen, Cellular)" },
328 { "iPad14,3", "j617ap", 0x08, 0x8112, "iPad Pro 11-inch (4th gen, WiFi)" },
329 { "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
330 { "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
331 { "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" },
332 { "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" },
333 { "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" },
334 { "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" },
335 { "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" },
336 { "iPad15,3", "j607ap", 0x08, 0x8122, "iPad Air 11-inch (M3, WiFi)" },
337 { "iPad15,4", "j608ap", 0x0A, 0x8122, "iPad Air 11-inch (M3, Cellular)" },
338 { "iPad15,5", "j637ap", 0x0C, 0x8122, "iPad Air 13-inch (M3, WiFi)" },
339 { "iPad15,6", "j638ap", 0x0E, 0x8122, "iPad Air 13-inch (M3, Cellular)" },
340 { "iPad15,7", "j481ap", 0x10, 0x8120, "iPad (A16, WiFi)" },
341 { "iPad15,8", "j482ap", 0x12, 0x8120, "iPad (A16, Cellular)" },
342 { "iPad16,1", "j410ap", 0x08, 0x8130, "iPad mini (A17 Pro, WiFi)" },
343 { "iPad16,2", "j411ap", 0x0A, 0x8130, "iPad mini (A17 Pro, Cellular)" },
344 { "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" },
345 { "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" },
346 { "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" },
347 { "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" },
348 { "iPad16,8", "j707ap", 0x10, 0x8132, "iPad Air 11-inch (M4, Wi-Fi)" },
349 { "iPad16,9", "j708ap", 0x12, 0x8132, "iPad Air 11-inch (M4, Cellular)" },
350 { "iPad16,10", "j737ap", 0x14, 0x8132, "iPad Air 13-inch (M4, Wi-Fi)" },
351 { "iPad16,11", "j738ap", 0x16, 0x8132, "iPad Air 13-inch (M4, Cellular)" },
352 { "iPad17,1", "j817ap", 0x08, 0x8142, "iPad Pro 11-inch (M5, WiFi)" },
353 { "iPad17,2", "j818ap", 0x0A, 0x8142, "iPad Pro 11-inch (M5, Cellular)" },
354 { "iPad17,3", "j820ap", 0x0C, 0x8142, "iPad Pro 13-inch (M5, WiFi)" },
355 { "iPad17,4", "j821ap", 0x0E, 0x8142, "iPad Pro 13-inch (M5, Cellular)" },
356 /* Apple TV */
357 { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
358 { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
359 { "AppleTV3,2", "j33iap", 0x00, 0x8947, "Apple TV 3 (2013)" },
360 { "AppleTV5,3", "j42dap", 0x34, 0x7000, "Apple TV 4" },
361 { "AppleTV6,2", "j105aap", 0x02, 0x8011, "Apple TV 4K" },
362 { "AppleTV11,1", "j305ap", 0x08, 0x8020, "Apple TV 4K (2nd gen)" },
363 { "AppleTV14,1", "j255ap", 0x02, 0x8110, "Apple TV 4K (3rd gen)" },
364 /* HomePod */
365 { "AudioAccessory1,1", "b238aap", 0x38, 0x7000, "HomePod (1st gen)" },
366 { "AudioAccessory1,2", "b238ap", 0x1A, 0x7000, "HomePod (1st gen)" },
367 { "AudioAccessory5,1", "b520ap", 0x22, 0x8006, "HomePod mini" },
368 { "AudioAccessory6,1", "b620ap", 0x18, 0x8301, "HomePod (2nd gen)" },
369 /* Apple Watch */
370 { "Watch1,1", "n27aap", 0x02, 0x7002, "Apple Watch 38mm (1st gen)" },
371 { "Watch1,2", "n28aap", 0x04, 0x7002, "Apple Watch 42mm (1st gen)" },
372 { "Watch2,6", "n27dap", 0x02, 0x8002, "Apple Watch Series 1 (38mm)" },
373 { "Watch2,7", "n28dap", 0x04, 0x8002, "Apple Watch Series 1 (42mm)" },
374 { "Watch2,3", "n74ap", 0x0C, 0x8002, "Apple Watch Series 2 (38mm)" },
375 { "Watch2,4", "n75ap", 0x0E, 0x8002, "Apple Watch Series 2 (42mm)" },
376 { "Watch3,1", "n111sap", 0x1C, 0x8004, "Apple Watch Series 3 (38mm Cellular)" },
377 { "Watch3,2", "n111bap", 0x1E, 0x8004, "Apple Watch Series 3 (42mm Cellular)" },
378 { "Watch3,3", "n121sap", 0x18, 0x8004, "Apple Watch Series 3 (38mm)" },
379 { "Watch3,4", "n121bap", 0x1A, 0x8004, "Apple Watch Series 3 (42mm)" },
380 { "Watch4,1", "n131sap", 0x08, 0x8006, "Apple Watch Series 4 (40mm)" },
381 { "Watch4,2", "n131bap", 0x0A, 0x8006, "Apple Watch Series 4 (44mm)" },
382 { "Watch4,3", "n141sap", 0x0C, 0x8006, "Apple Watch Series 4 (40mm Cellular)" },
383 { "Watch4,4", "n141bap", 0x0E, 0x8006, "Apple Watch Series 4 (44mm Cellular)" },
384 { "Watch5,1", "n144sap", 0x10, 0x8006, "Apple Watch Series 5 (40mm)" },
385 { "Watch5,2", "n144bap", 0x12, 0x8006, "Apple Watch Series 5 (44mm)" },
386 { "Watch5,3", "n146sap", 0x14, 0x8006, "Apple Watch Series 5 (40mm Cellular)" },
387 { "Watch5,4", "n146bap", 0x16, 0x8006, "Apple Watch Series 5 (44mm Cellular)" },
388 { "Watch5,9", "n140sap", 0x28, 0x8006, "Apple Watch SE (40mm)" },
389 { "Watch5,10", "n140bap", 0x2A, 0x8006, "Apple Watch SE (44mm)" },
390 { "Watch5,11", "n142sap", 0x2C, 0x8006, "Apple Watch SE (40mm Cellular)" },
391 { "Watch5,12", "n142bap", 0x2E, 0x8006, "Apple Watch SE (44mm Cellular)" },
392 { "Watch6,1", "n157sap", 0x08, 0x8301, "Apple Watch Series 6 (40mm)" },
393 { "Watch6,2", "n157bap", 0x0A, 0x8301, "Apple Watch Series 6 (44mm)" },
394 { "Watch6,3", "n158sap", 0x0C, 0x8301, "Apple Watch Series 6 (40mm Cellular)" },
395 { "Watch6,4", "n158bap", 0x0E, 0x8301, "Apple Watch Series 6 (44mm Cellular)" },
396 { "Watch6,6", "n187sap", 0x10, 0x8301, "Apple Watch Series 7 (41mm)" },
397 { "Watch6,7", "n187bap", 0x12, 0x8301, "Apple Watch Series 7 (45mm)" },
398 { "Watch6,8", "n188sap", 0x14, 0x8301, "Apple Watch Series 7 (41mm Cellular)" },
399 { "Watch6,9", "n188bap", 0x16, 0x8301, "Apple Watch Series 7 (45mm Cellular)" },
400 { "Watch6,10", "n143sap", 0x28, 0x8301, "Apple Watch SE 2 (40mm)" },
401 { "Watch6,11", "n143bap", 0x2A, 0x8301, "Apple Watch SE 2 (44mm)" },
402 { "Watch6,12", "n149sap", 0x2C, 0x8301, "Apple Watch SE 2 (40mm Cellular)" },
403 { "Watch6,13", "n149bap", 0x2E, 0x8301, "Apple Watch SE 2 (44mm Cellular)" },
404 { "Watch6,14", "n197sap", 0x30, 0x8301, "Apple Watch Series 8 (41mm)" },
405 { "Watch6,15", "n197bap", 0x32, 0x8301, "Apple Watch Series 8 (45mm)" },
406 { "Watch6,16", "n198sap", 0x34, 0x8301, "Apple Watch Series 8 (41mm Cellular)" },
407 { "Watch6,17", "n198bap", 0x36, 0x8301, "Apple Watch Series 8 (45mm Cellular)" },
408 { "Watch6,18", "n199ap", 0x26, 0x8301, "Apple Watch Ultra" },
409 { "Watch7,1", "n207sap", 0x08, 0x8310, "Apple Watch Series 9 (41mm)" },
410 { "Watch7,2", "n207bap", 0x0A, 0x8310, "Apple Watch Series 9 (45mm)" },
411 { "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
412 { "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
413 { "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" },
414 { "Watch7,8", "n217sap", 0x10, 0x8310, "Apple Watch Series 10 (42mm)" },
415 { "Watch7,9", "n217bap", 0x12, 0x8310, "Apple Watch Series 10 (46mm)" },
416 { "Watch7,10", "n218sap", 0x14, 0x8310, "Apple Watch Series 10 (42mm Cellular)" },
417 { "Watch7,11", "n218bap", 0x16, 0x8310, "Apple Watch Series 10 (46mm Cellular)" },
418 { "Watch7,12", "n230ap", 0x22, 0x8310, "Apple Watch Ultra 3" },
419 { "Watch7,13", "n243sap", 0x28, 0x8310, "Apple Watch SE 3 (40mm)" },
420 { "Watch7,14", "n243bap", 0x2A, 0x8310, "Apple Watch SE 3 (44mm)" },
421 { "Watch7,15", "n244sap", 0x2C, 0x8310, "Apple Watch SE 3 (40mm Cellular)" },
422 { "Watch7,16", "n244bap", 0x2E, 0x8310, "Apple Watch SE 3 (44mm Cellular)" },
423 { "Watch7,17", "n227sap", 0x18, 0x8310, "Apple Watch Series 11 (42mm)" },
424 { "Watch7,18", "n227bap", 0x1A, 0x8310, "Apple Watch Series 11 (46mm)" },
425 { "Watch7,19", "n228sap", 0x1C, 0x8310, "Apple Watch Series 11 (42mm Cellular)" },
426 { "Watch7,20", "n228bap", 0x1E, 0x8310, "Apple Watch Series 11 (46mm Cellular)" },
427 /* Apple Silicon Macs */
428 { "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
429 { "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
430 { "MacBookPro17,1", "j293ap", 0x24, 0x8103, "MacBook Pro (M1, 13-inch, 2020)" },
431 { "MacBookPro18,1", "j316sap", 0x0A, 0x6000, "MacBook Pro (M1 Pro, 16-inch, 2021)" },
432 { "MacBookPro18,2", "j316cap", 0x0A, 0x6001, "MacBook Pro (M1 Max, 16-inch, 2021)" },
433 { "MacBookPro18,3", "j314sap", 0x08, 0x6000, "MacBook Pro (M1 Pro, 14-inch, 2021)" },
434 { "MacBookPro18,4", "j314cap", 0x08, 0x6001, "MacBook Pro (M1 Max, 14-inch, 2021)" },
435 { "MacBookAir10,1", "j313ap", 0x26, 0x8103, "MacBook Air (M1, 2020)" },
436 { "iMac21,1", "j456ap", 0x28, 0x8103, "iMac 24-inch (M1, Two Ports, 2021)" },
437 { "iMac21,2", "j457ap", 0x2A, 0x8103, "iMac 24-inch (M1, Four Ports, 2021)" },
438 { "Mac13,1", "j375cap", 0x04, 0x6001, "Mac Studio (M1 Max, 2022)" },
439 { "Mac13,2", "j375dap", 0x0C, 0x6002, "Mac Studio (M1 Ultra, 2022)" },
440 { "Mac14,2", "j413ap", 0x28, 0x8112, "MacBook Air (M2, 2022)" },
441 { "Mac14,7", "j493ap", 0x2A, 0x8112, "MacBook Pro (M2, 13-inch, 2022)" },
442 { "Mac14,3", "j473ap", 0x24, 0x8112, "Mac mini (M2, 2023)" },
443 { "Mac14,5", "j414cap", 0x04, 0x6021, "MacBook Pro (14-inch, M2 Max, 2023)" },
444 { "Mac14,6", "j416cap", 0x06, 0x6021, "MacBook Pro (16-inch, M2 Max, 2023)" },
445 { "Mac14,8", "j180dap", 0x08, 0x6022, "Mac Pro (2023)" },
446 { "Mac14,9", "j414sap", 0x04, 0x6020, "MacBook Pro (14-inch, M2 Pro, 2023)" },
447 { "Mac14,10", "j416sap", 0x06, 0x6020, "MacBook Pro (16-inch, M2 Pro, 2023)" },
448 { "Mac14,12", "j474sap", 0x02, 0x6020, "Mac mini (M2 Pro, 2023)" },
449 { "Mac14,13", "j475cap", 0x0A, 0x6021, "Mac Studio (M2 Max, 2023)" },
450 { "Mac14,14", "j475dap", 0x0A, 0x6022, "Mac Studio (M2 Ultra, 2023)" },
451 { "Mac14,15", "j415ap", 0x2E, 0x8112, "MacBook Air (M2, 15-inch, 2023)" },
452 { "Mac15,3", "j504ap", 0x22, 0x8122, "MacBook Pro (14-inch, M3, Nov 2023)" },
453 { "Mac15,4", "j433ap", 0x28, 0x8122, "iMac 24-inch (M3, Two Ports, 2023)" },
454 { "Mac15,5", "j434ap", 0x2A, 0x8122, "iMac 24-inch (M3, Four Ports, 2023)" },
455 { "Mac15,6", "j514sap", 0x04, 0x6030, "MacBook Pro (14-inch, M3 Pro, Nov 2023)" },
456 { "Mac15,7", "j516sap", 0x06, 0x6030, "MacBook Pro (16-inch, M3 Pro, Nov 2023)" },
457 { "Mac15,8", "j514cap", 0x44, 0x6031, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
458 { "Mac15,9", "j516cap", 0x46, 0x6031, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
459 { "Mac15,10", "j514map", 0x44, 0x6034, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
460 { "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
461 { "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
462 { "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" },
463 { "Mac15,14", "j575dap", 0x44, 0x6032, "Mac Studio (M3 Ultra, 2025)" },
464 { "Mac16,1", "j604ap", 0x22, 0x8132, "MacBook Pro (14-inch, M4, Nov 2024)" },
465 { "Mac16,2", "j623ap", 0x24, 0x8132, "iMac 24-inch (M4, Two Ports, 2024)" },
466 { "Mac16,3", "j624ap", 0x26, 0x8132, "iMac 24-inch (M4, Four Ports, 2024)" },
467 { "Mac16,5", "j616cap", 0x06, 0x6041, "MacBook Pro (16-inch, M4 Max, Nov 2024)" },
468 { "Mac16,6", "j614cap", 0x04, 0x6041, "MacBook Pro (14-inch, M4 Max, Nov 2024)" },
469 { "Mac16,7", "j616sap", 0x06, 0x6040, "MacBook Pro (16-inch, M4 Pro, Nov 2024)" },
470 { "Mac16,8", "j614sap", 0x04, 0x6040, "MacBook Pro (14-inch, M4 Pro, Nov 2024)" },
471 { "Mac16,9", "j575cap", 0x02, 0x6041, "Mac Studio (M4 Max, 2025)" },
472 { "Mac16,10", "j773gap", 0x2A, 0x8132, "Mac mini (M4, 2024)" },
473 { "Mac16,11", "j773sap", 0x02, 0x6040, "Mac mini (M4 Pro, 2024)" },
474 { "Mac16,12", "j713ap", 0x2C, 0x8132, "MacBook Air (13-inch, M4, 2025)" },
475 { "Mac16,13", "j715ap", 0x2E, 0x8132, "MacBook Air (15-inch, M4, 2025)" },
476 { "Mac17,2", "j704ap", 0x22, 0x8142, "MacBook Pro (14-inch, M5, 2025)" },
477 /* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
478 { "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
479 /* Apple T2 Coprocessor */
480 { "iBridge2,1", "j137ap", 0x0A, 0x8012, "Apple T2 iMacPro1,1 (j137)" },
481 { "iBridge2,3", "j680ap", 0x0B, 0x8012, "Apple T2 MacBookPro15,1 (j680)" },
482 { "iBridge2,4", "j132ap", 0x0C, 0x8012, "Apple T2 MacBookPro15,2 (j132)" },
483 { "iBridge2,5", "j174ap", 0x0E, 0x8012, "Apple T2 Macmini8,1 (j174)" },
484 { "iBridge2,6", "j160ap", 0x0F, 0x8012, "Apple T2 MacPro7,1 (j160)" },
485 { "iBridge2,7", "j780ap", 0x07, 0x8012, "Apple T2 MacBookPro15,3 (j780)" },
486 { "iBridge2,8", "j140kap", 0x17, 0x8012, "Apple T2 MacBookAir8,1 (j140k)" },
487 { "iBridge2,10", "j213ap", 0x18, 0x8012, "Apple T2 MacBookPro15,4 (j213)" },
488 { "iBridge2,12", "j140aap", 0x37, 0x8012, "Apple T2 MacBookAir8,2 (j140a)" },
489 { "iBridge2,14", "j152fap", 0x3A, 0x8012, "Apple T2 MacBookPro16,1 (j152f)" },
490 { "iBridge2,15", "j230kap", 0x3F, 0x8012, "Apple T2 MacBookAir9,1 (j230k)" },
491 { "iBridge2,16", "j214kap", 0x3E, 0x8012, "Apple T2 MacBookPro16,2 (j214k)" },
492 { "iBridge2,19", "j185ap", 0x22, 0x8012, "Apple T2 iMac20,1 (j185)" },
493 { "iBridge2,20", "j185fap", 0x23, 0x8012, "Apple T2 iMac20,2 (j185f)" },
494 { "iBridge2,21", "j223ap", 0x3B, 0x8012, "Apple T2 MacBookPro16,3 (j223)" },
495 { "iBridge2,22", "j215ap", 0x38, 0x8012, "Apple T2 MacBookPro16,4 (j215)" },
496 /* Apple Displays */
497 { "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
498 /* Apple Vision Pro */
499 { "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
500 { "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" },
501 /* Private Cloud Compute Research Environment */
502 { "iPhone99,11", "vresearch101ap", 0x90, 0xFE01, "iPhone 99,11" },
503 { NULL, NULL, -1, -1, NULL }
504};
505
506#ifndef USE_DUMMY
507static unsigned int crc32_lookup_t1[256] = {
508 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
509 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
510 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
511 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
512 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
513 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
514 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
515 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
516 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
517 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
518 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
519 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
520 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
521 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
522 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
523 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
524 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
525 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
526 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
527 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
528 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
529 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
530 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
531 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
532 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
533 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
534 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
535 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
536 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
537 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
538 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
539 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
540 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
541 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
542 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
543 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
544 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
545 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
546 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
547 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
548 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
549 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
550 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
551 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
552 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
553 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
554 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
555 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
556 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
557 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
558 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
559 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
560 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
561 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
562 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
563 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
564 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
565 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
566 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
567 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
568 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
569 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
570 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
571 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
572};
573
574#define crc32_step(a,b) \
575 a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
576
577#ifdef _WIN32
578#pragma pack(1)
579typedef struct {
580 uint16_t vid;
581 uint16_t pid;
582 uint32_t unk;
583 char nonces[255];
584 char serial[255];
585 char manufacturer[255];
586 char product[255];
587} KIS_device_info;
588
589typedef struct {
590 uint8_t data[0x4000];
591 uint32_t size;
592 uint32_t unused;
593 uint64_t address;
594} KIS_upload_chunk;
595#pragma pack()
596#else
597#pragma pack(1)
598typedef struct {
599 uint16_t sequence; // A sequence number
600 uint8_t version; // Protocol version
601 uint8_t portal; // The "portal" to connect to
602 uint8_t argCount; // Number of arguments
603 uint8_t indexLo; // An index
604 uint8_t indexHiRplSizeLo; // High 2 bits of index + low 6 bytes of reply size
605 uint8_t rplSizeHi; // Reply size high bits, number of words the device should send
606 uint32_t reqSize; // Size of the complete request, including the arguments and payload, excluding the header
607 // Followed by arguments and payload data
608} KIS_req_header;
609
610typedef struct {
611 KIS_req_header hdr;
612 uint32_t value;
613} KIS_config_wr32;
614
615typedef struct {
616 uint8_t bLength ; ///< Size of this descriptor in bytes.
617 uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
618 uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
619
620 uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
621 uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
622 uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
623 uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
624
625 uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
626 uint16_t idProduct ; ///< Product ID (assigned by the manufacturer).
627 uint16_t bcdDevice ; ///< Device release number in binary-coded decimal.
628 uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer.
629 uint8_t iProduct ; ///< Index of string descriptor describing product.
630 uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number.
631
632 uint8_t bNumConfigurations ; ///< Number of possible configurations.
633} usb_device_descriptor;
634
635typedef struct {
636 KIS_req_header hdr;
637 union {
638 struct {
639 uint32_t tag;
640 uint32_t unk1;
641 uint32_t maxUploadSize;
642 uint32_t maxDownloadSize; // maybe???
643 uint64_t rambase;
644 uint32_t nonceOffset;
645 uint32_t pad;
646 uint8_t unkpad[0x20];
647 usb_device_descriptor deviceDescriptor;
648 };
649 uint8_t deviceInfo[0x300];
650 };
651 uint32_t rspsize;
652 uint32_t statuscode;
653} KIS_device_info;
654
655typedef struct {
656 KIS_req_header hdr;
657 uint64_t address;
658 uint32_t size;
659 uint8_t data[0x4000];
660} KIS_upload_chunk;
661
662typedef struct {
663 KIS_req_header hdr;
664 uint32_t size; // Number of bytes read/written
665 uint32_t status;
666} KIS_generic_reply;
667#pragma pack()
668#endif
669
670#pragma pack(1)
671typedef struct {
672 uint16_t cmdcode;
673#define MSG_VERSION_QUERY 0x000
674#define MSG_ECHO 0x801
675#define MSG_DUMP_BUFFER 0x802
676#define MSG_SEND_COMMAND 0x803
677#define MSG_READ_FILE 0x804
678#define MSG_SEND_FILE 0x805
679#define MSG_CRC 0x807
680#define MSG_ACK 0x808
681#define MSG_REJECT 0x809
682 uint16_t magic; // always 0x1234
683 uint32_t size;
684 uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files
685} legacyCMD;
686#pragma pack()
687
688static THREAD_T th_event_handler = THREAD_T_NULL;
689struct collection listeners;
690static mutex_t listener_mutex;
691struct collection devices;
692static mutex_t device_mutex;
693#ifndef _WIN32
694#ifdef HAVE_IOKIT
695static CFRunLoopRef iokit_runloop = NULL;
696#else
697static libusb_context* irecv_hotplug_ctx = NULL;
698#endif
699#endif
700
701static void _irecv_deinit(void)
702{
703#ifndef USE_DUMMY
704#ifndef _WIN32
705#ifndef HAVE_IOKIT
706 if (libirecovery_context != NULL) {
707 libusb_exit(libirecovery_context);
708 libirecovery_context = NULL;
709 }
710#endif
711#endif
712 collection_free(&listeners);
713 mutex_destroy(&listener_mutex);
714#endif
715}
716
717INITIALIZER(_irecv_init)
718{
719 char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
720 if (dbglvl) {
721 libirecovery_debug = strtol(dbglvl, NULL, 0);
722 irecv_set_debug_level(libirecovery_debug);
723 }
724#ifndef USE_DUMMY
725#ifndef _WIN32
726#ifndef HAVE_IOKIT
727 libusb_init(&libirecovery_context);
728#endif
729#endif
730 collection_init(&listeners);
731 mutex_init(&listener_mutex);
732#endif
733 atexit(_irecv_deinit);
734}
735
736#ifdef HAVE_IOKIT
737static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
738{
739 IOReturn result;
740 IOUSBDevRequest request;
741 unsigned char descriptor[256];
742
743 request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
744 request.bRequest = kUSBRqGetDescriptor;
745 request.wValue = (kUSBStringDesc << 8); // | desc_index;
746 request.wIndex = 0; // All languages 0x409; // language
747 request.wLength = sizeof(descriptor) - 1;
748 request.pData = descriptor;
749 request.wLenDone = 0;
750
751 result = (*client->handle)->DeviceRequest(client->handle, &request);
752 if (result == kIOReturnNoDevice)
753 return IRECV_E_NO_DEVICE;
754 if (result == kIOReturnNotOpen)
755 return IRECV_E_USB_STATUS;
756 if (result != kIOReturnSuccess)
757 return IRECV_E_UNKNOWN_ERROR;
32 758
33int irecv_write_file(const char* filename, const void* data, size_t size); 759 if (descriptor[0] >= 4) { // && descriptor[2] == 0x9 && descriptor[3] == 0x4) {
34int irecv_read_file(const char* filename, char** data, uint32_t* size); 760
761 request.wValue = (kUSBStringDesc << 8) | desc_index;
762 request.wIndex = descriptor[2] + (descriptor[3] << 8);
763 request.wLenDone = 0;
764 result = (*client->handle)->DeviceRequest(client->handle, &request);
765
766 if (result == kIOReturnNoDevice)
767 return IRECV_E_NO_DEVICE;
768 if (result == kIOReturnNotOpen)
769 return IRECV_E_USB_STATUS;
770 if (result != kIOReturnSuccess)
771 return IRECV_E_UNKNOWN_ERROR;
772
773 int i = 2, j = 0;
774 for ( ; i < descriptor[0]; i += 2, j += 1) {
775 buffer[j] = descriptor[i];
776 }
777 buffer[j] = 0;
778
779 return request.wLenDone;
780 }
781 return IRECV_E_UNKNOWN_ERROR;
782}
783#endif
784
785static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
786{
787#ifndef _WIN32
788#ifdef HAVE_IOKIT
789 return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size);
790#else
791 return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size);
792#endif
793#else
794 irecv_error_t ret;
795 unsigned short langid = 0;
796 unsigned char data[256];
797 int di, si;
798 memset(data, 0, sizeof(data));
799 memset(buffer, 0, size);
800
801 ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data)-1, USB_TIMEOUT);
802
803 if (ret < 0) return ret;
804 if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
805 if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR;
806
807 for (di = 0, si = 2; si < data[0]; si += 2) {
808 if (di >= (size - 1)) break;
809 if (data[si + 1]) {
810 /* high byte */
811 buffer[di++] = '?';
812 } else {
813 buffer[di++] = data[si];
814 }
815 }
816 buffer[di] = 0;
817
818 return di;
819#endif
820}
821
822static void irecv_load_device_info_from_iboot_string(irecv_client_t client, const char* iboot_string)
823{
824 if (!client || !iboot_string) {
825 return;
826 }
827
828 memset(&client->device_info, '\0', sizeof(struct irecv_device_info));
829
830 client->device_info.serial_string = strdup(iboot_string);
831
832 char* ptr;
833
834 ptr = strstr(iboot_string, "CPID:");
835 if (ptr != NULL) {
836 sscanf(ptr, "CPID:%x", &client->device_info.cpid);
837 client->device_info.have_cpid = 1;
838 } else {
839 // early iOS 1 doesn't identify itself
840 client->device_info.cpid = 0x8900;
841 }
842
843 ptr = strstr(iboot_string, "CPRV:");
844 if (ptr != NULL) {
845 sscanf(ptr, "CPRV:%x", &client->device_info.cprv);
846 client->device_info.have_cprv = 1;
847 }
848
849 ptr = strstr(iboot_string, "CPFM:");
850 if (ptr != NULL) {
851 sscanf(ptr, "CPFM:%x", &client->device_info.cpfm);
852 client->device_info.have_cpfm = 1;
853 }
854
855 ptr = strstr(iboot_string, "SCEP:");
856 if (ptr != NULL) {
857 sscanf(ptr, "SCEP:%x", &client->device_info.scep);
858 client->device_info.have_scep = 1;
859 }
860
861 ptr = strstr(iboot_string, "BDID:");
862 if (ptr != NULL) {
863 uint64_t bdid = 0;
864 sscanf(ptr, "BDID:%" SCNx64, &bdid);
865 client->device_info.bdid = (unsigned int)bdid;
866 client->device_info.have_bdid = 1;
867 }
868
869 ptr = strstr(iboot_string, "ECID:");
870 if (ptr != NULL) {
871 sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid);
872 client->device_info.have_ecid = 1;
873 }
874
875 ptr = strstr(iboot_string, "IBFL:");
876 if (ptr != NULL) {
877 sscanf(ptr, "IBFL:%x", &client->device_info.ibfl);
878 client->device_info.have_ibfl = 1;
879 }
880
881 char tmp[256];
882 tmp[0] = '\0';
883 ptr = strstr(iboot_string, "SRNM:[");
884 if (ptr != NULL) {
885 sscanf(ptr, "SRNM:[%s]", tmp);
886 ptr = strrchr(tmp, ']');
887 if (ptr != NULL) {
888 *ptr = '\0';
889 }
890 client->device_info.srnm = strdup(tmp);
891 }
892
893 tmp[0] = '\0';
894 ptr = strstr(iboot_string, "IMEI:[");
895 if (ptr != NULL) {
896 sscanf(ptr, "IMEI:[%s]", tmp);
897 ptr = strrchr(tmp, ']');
898 if (ptr != NULL) {
899 *ptr = '\0';
900 }
901 client->device_info.imei = strdup(tmp);
902 }
903
904 tmp[0] = '\0';
905 ptr = strstr(iboot_string, "SRTG:[");
906 if (ptr != NULL) {
907 sscanf(ptr, "SRTG:[%s]", tmp);
908 ptr = strrchr(tmp, ']');
909 if (ptr != NULL) {
910 *ptr = '\0';
911 }
912 client->device_info.srtg = strdup(tmp);
913 }
914
915 client->device_info.pid = client->mode;
916 if (client->isKIS) {
917 client->device_info.pid = KIS_PRODUCT_ID;
918 }
919}
920
921static void irecv_copy_nonce_with_tag_from_buffer(const char* tag, unsigned char** nonce, unsigned int* nonce_size, const char *buf)
922{
923 int taglen = strlen(tag);
924 int nlen = 0;
925 const char* nonce_string = NULL;
926 const char* p = buf;
927 char* colon = NULL;
928 do {
929 colon = strchr(p, ':');
930 if (!colon)
931 break;
932 if (colon-taglen < p) {
933 break;
934 }
935 char *space = strchr(colon, ' ');
936 if (strncmp(colon-taglen, tag, taglen) == 0) {
937 p = colon+1;
938 if (!space) {
939 nlen = strlen(p);
940 } else {
941 nlen = space-p;
942 }
943 nonce_string = p;
944 nlen/=2;
945 break;
946 } else {
947 if (!space) {
948 break;
949 } else {
950 p = space+1;
951 }
952 }
953 } while (colon);
954
955 if (nlen == 0) {
956 debug("%s: WARNING: couldn't find tag %s in string %s\n", __func__, tag, buf);
957 return;
958 }
959
960 unsigned char *nn = malloc(nlen);
961 if (!nn) {
962 return;
963 }
35 964
36irecv_error_t irecv_open(irecv_client_t* pclient) {
37 int i = 0; 965 int i = 0;
38 char serial[256]; 966 for (i = 0; i < nlen; i++) {
967 int val = 0;
968 if (sscanf(nonce_string+(i*2), "%02X", &val) == 1) {
969 nn[i] = (unsigned char)val;
970 } else {
971 debug("%s: ERROR: unexpected data in nonce result (%2s)\n", __func__, nonce_string+(i*2));
972 break;
973 }
974 }
975
976 if (i != nlen) {
977 debug("%s: ERROR: unable to parse nonce\n", __func__);
978 free(nn);
979 return;
980 }
981
982 *nonce = nn;
983 *nonce_size = nlen;
984}
985
986static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, unsigned char** nonce, unsigned int* nonce_size)
987{
988 if (!client || !tag) {
989 return;
990 }
991
992 char buf[256];
993 int len = 0;
994
995 *nonce = NULL;
996 *nonce_size = 0;
997
998 memset(buf, 0, sizeof(buf));
999 len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*)buf, sizeof(buf)-1);
1000 if (len < 0) {
1001 debug("%s: got length: %d\n", __func__, len);
1002 return;
1003 }
1004
1005 buf[len] = 0;
1006
1007 irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf);
1008}
1009
1010#ifndef _WIN32
1011static irecv_error_t irecv_kis_request_init(KIS_req_header *hdr, uint8_t portal, uint16_t index, size_t argCount, size_t payloadSize, size_t rplWords)
1012{
1013 if (argCount > UINT8_MAX) {
1014 return IRECV_E_INVALID_INPUT;
1015 }
1016
1017 if (index >= (1 << 10)) {
1018 return IRECV_E_INVALID_INPUT;
1019 }
1020
1021 if (rplWords >= (1 << 14)) {
1022 return IRECV_E_INVALID_INPUT;
1023 }
1024
1025 size_t reqSize = payloadSize + (argCount << 2);
1026 if (reqSize > UINT32_MAX) {
1027 return IRECV_E_INVALID_INPUT;
1028 }
1029
1030 hdr->sequence = 0; // Doesn't matter
1031 hdr->version = 0xA0;
1032 hdr->portal = portal;
1033 hdr->argCount = (uint8_t) argCount;
1034 hdr->indexLo = (uint8_t) (index & 0xFF);
1035 hdr->indexHiRplSizeLo = (uint8_t) (((index >> 8) & 0x3) | ((rplWords << 2) & 0xFC));
1036 hdr->rplSizeHi = (uint8_t) ((rplWords >> 6) & 0xFF);
1037 hdr->reqSize = (uint32_t) reqSize;
1038
1039 return IRECV_E_SUCCESS;
1040}
1041
1042static irecv_error_t irecv_kis_request(irecv_client_t client, KIS_req_header *req, size_t reqSize, KIS_req_header *rpl, size_t *rplSize)
1043{
1044 int endpoint = 0;
1045 switch (req->portal) {
1046 case KIS_PORTAL_CONFIG:
1047 endpoint = 1;
1048 break;
1049 case KIS_PORTAL_RSM:
1050 endpoint = 3;
1051 break;
1052 default:
1053 debug("Don't know which endpoint to use for portal %d\n", req->portal);
1054 return IRECV_E_INVALID_INPUT;
1055 }
1056
1057 int sent = 0;
1058 irecv_error_t err = irecv_usb_bulk_transfer(client, endpoint, (unsigned char *) req, reqSize, &sent, USB_TIMEOUT);
1059 if (err != IRECV_E_SUCCESS) {
1060 debug("[send] irecv_usb_bulk_transfer failed, error %d\n", err);
1061 return err;
1062 }
1063
1064 if ((size_t) sent != reqSize) {
1065 debug("sent != reqSize\n");
1066 return IRECV_E_USB_UPLOAD;
1067 }
1068
1069 int rcvd = 0;
1070 err = irecv_usb_bulk_transfer(client, endpoint | 0x80, (unsigned char *) rpl, *rplSize, &rcvd, USB_TIMEOUT);
1071 if (err != IRECV_E_SUCCESS) {
1072 debug("[rcv] irecv_usb_bulk_transfer failed, error %d\n", err);
1073 return err;
1074 }
1075
1076 *rplSize = rcvd;
1077
1078 return IRECV_E_SUCCESS;
1079}
1080
1081static irecv_error_t irecv_kis_config_write32(irecv_client_t client, uint8_t portal, uint16_t index, uint32_t value)
1082{
1083 KIS_config_wr32 req = {};
1084 KIS_generic_reply rpl = {};
1085 irecv_error_t err = irecv_kis_request_init(&req.hdr, portal, index, 1, 0, 1);
1086 if (err != IRECV_E_SUCCESS) {
1087 debug("Failed to init KIS request, error %d\n", err);
1088 return err;
1089 }
1090
1091 req.value = value;
1092
1093 size_t rplSize = sizeof(rpl);
1094 err = irecv_kis_request(client, &req.hdr, sizeof(req), &rpl.hdr, &rplSize);
1095 if (err != IRECV_E_SUCCESS) {
1096 debug("Failed to send KIS request, error %d\n", err);
1097 return err;
1098 }
1099
1100 if (rpl.size != 4) {
1101 debug("Failed to write config, %d bytes written, status %d\n", rpl.size, rpl.status);
1102 return err;
1103 }
1104
1105 return IRECV_E_SUCCESS;
1106}
1107
1108static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, size_t buf_size)
1109{
1110 off *= 4;
1111
1112 size_t inputSize = sizeof(KIS_device_info) - sizeof(KIS_req_header);
1113
1114 if ((off + 2) > inputSize)
1115 return 0;
1116
1117 uint8_t len = di->deviceInfo[off];
1118 uint8_t type = di->deviceInfo[off + 1];
1119
1120 if (len & 1)
1121 return 0;
1122
1123 if (len/2 >= buf_size)
1124 return 0;
1125
1126 if ((off + 2 + len) > inputSize)
1127 return 0;
1128
1129 if (type != 3)
1130 return 0;
1131
1132 buf[len >> 1] = 0;
1133 for (size_t i = 0; i < len; i += 2) {
1134 buf[i >> 1] = di->deviceInfo[i + off + 2];
1135 }
1136
1137 return len/2;
1138}
1139#endif
1140
1141static irecv_error_t irecv_kis_init(irecv_client_t client)
1142{
1143#ifndef _WIN32
1144 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL);
1145 if (err != IRECV_E_SUCCESS) {
1146 debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err);
1147 return err;
1148 }
1149
1150 err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_B, KIS_ENABLE_B_VAL);
1151 if (err != IRECV_E_SUCCESS) {
1152 debug("Failed to write to KIS_INDEX_ENABLE_B, error %d\n", err);
1153 return err;
1154 }
1155#endif
1156 client->isKIS = 1;
1157
1158 return IRECV_E_SUCCESS;
1159}
1160
1161static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
1162{
1163 debug("Loading device info in KIS mode...\n");
1164#ifdef _WIN32
1165 KIS_device_info kisInfo;
1166 DWORD transferred = 0;
1167 int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL);
1168 if (ret) {
1169 debug("Serial: %s\n", kisInfo.serial);
1170 irecv_load_device_info_from_iboot_string(client, kisInfo.serial);
1171 debug("Manufacturer: %s\n", kisInfo.manufacturer);
1172 debug("Product: %s\n", kisInfo.product);
1173 debug("Nonces: %s\n", kisInfo.nonces);
1174 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, kisInfo.nonces);
1175 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, kisInfo.nonces);
1176 debug("VID: 0x%04x\n", kisInfo.vid);
1177 debug("PID: 0x%04x\n", kisInfo.pid);
1178 }
1179 client->mode = kisInfo.pid;
1180#else
1181 KIS_req_header req = {};
1182 KIS_device_info di = {};
1183 irecv_error_t err = irecv_kis_request_init(&req, KIS_PORTAL_RSM, KIS_INDEX_GET_INFO, 0, 0, sizeof(di.deviceInfo)/4);
1184 if (err != IRECV_E_SUCCESS) {
1185 debug("Failed to init KIS request, error %d\n", err);
1186 return err;
1187 }
1188
1189 size_t rcvSize = sizeof(di);
1190 err = irecv_kis_request(client, &req, sizeof(req), &di.hdr, &rcvSize);
1191 if (err != IRECV_E_SUCCESS) {
1192 debug("Failed to send KIS request, error %d\n", err);
1193 return err;
1194 }
1195
1196 char buf[0x100];
1197 int len = 0;
1198
1199 len = irecv_kis_read_string(&di, di.deviceDescriptor.iSerialNumber, buf, sizeof(buf));
1200 if (len == 0)
1201 return IRECV_E_INVALID_INPUT;
1202 debug("Serial: %s\n", buf);
1203
1204 irecv_load_device_info_from_iboot_string(client, buf);
1205
1206 len = irecv_kis_read_string(&di, di.deviceDescriptor.iManufacturer, buf, sizeof(buf));
1207 if (len == 0)
1208 return IRECV_E_INVALID_INPUT;
1209 debug("Manufacturer: %s\n", buf);
1210
1211 len = irecv_kis_read_string(&di, di.deviceDescriptor.iProduct, buf, sizeof(buf));
1212 if (len == 0)
1213 return IRECV_E_INVALID_INPUT;
1214 debug("Product: %s\n", buf);
1215
1216 len = irecv_kis_read_string(&di, di.nonceOffset, buf, sizeof(buf));
1217 if (len == 0)
1218 return IRECV_E_INVALID_INPUT;
1219 debug("Nonces: %s\n", buf);
1220
1221 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, buf);
1222 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, buf);
1223
1224 debug("VID: 0x%04x\n", di.deviceDescriptor.idVendor);
1225 debug("PID: 0x%04x\n", di.deviceDescriptor.idProduct);
1226
1227 client->mode = di.deviceDescriptor.idProduct;
1228#endif
1229 return IRECV_E_SUCCESS;
1230}
1231
1232#ifdef _WIN32
1233static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
1234static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
1235static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}};
1236static const GUID GUID_DEVINTERFACE_PORTDFU = {0xAF633FF1L, 0x1170, 0x4CA6, {0xAE, 0x9E, 0x08, 0xD0, 0x01, 0x42, 0x1E, 0xAA}};
1237
1238typedef struct usb_control_request {
1239 uint8_t bmRequestType;
1240 uint8_t bRequest;
1241 uint16_t wValue;
1242 uint16_t wIndex;
1243 uint16_t wLength;
1244
1245 char data[];
1246} usb_control_request;
1247
1248static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid)
1249{
1250 int found = 0;
1251 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
1252 irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
1253 memset(_client, 0, sizeof(struct irecv_client_private));
1254
1255 int k;
1256 for (k = 0; !found && guids[k]; k++) {
1257 DWORD i;
1258 SP_DEVICE_INTERFACE_DATA currentInterface;
1259 HDEVINFO usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1260 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
1261 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1262 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
1263 _client->handle = INVALID_HANDLE_VALUE;
1264 DWORD requiredSize = 0;
1265 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
1266 SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
1267 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
1268 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
1269 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
1270 free(details);
1271 continue;
1272 }
1273
1274 unsigned int pid = 0;
1275 unsigned int vid = 0;
1276 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid) != 2) {
1277 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
1278 free(details);
1279 continue;
1280 }
1281 if (vid != APPLE_VENDOR_ID) {
1282 free(details);
1283 continue;
1284 }
1285
1286 // make sure the current device is actually in the right mode for the given driver interface
1287 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
1288 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
1289 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
1290 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
1291 ) {
1292 free(details);
1293 continue;
1294 }
1295 if (guids[k] == &GUID_DEVINTERFACE_KIS) {
1296 pid = KIS_PRODUCT_ID;
1297 }
1298
1299 _client->handle = CreateFileA(details->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
1300 if (_client->handle == INVALID_HANDLE_VALUE) {
1301 debug("%s: Failed to open device path %s: %d\n", __func__, details->DevicePath, (int)GetLastError());
1302 free(details);
1303 continue;
1304 }
1305 _client->mode = pid;
1306
1307 if (ecid == IRECV_K_WTF_MODE) {
1308 if (_client->mode != IRECV_K_WTF_MODE) {
1309 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1310 CloseHandle(_client->handle);
1311 free(details);
1312 continue;
1313 } else {
1314 ecid = 0;
1315 }
1316 }
1317
1318 if ((ecid != 0) && (_client->mode == IRECV_K_WTF_MODE)) {
1319 /* we can't get ecid in WTF mode */
1320 CloseHandle(_client->handle);
1321 free(details);
1322 continue;
1323 }
1324
1325 char serial_str[256];
1326 serial_str[0] = '\0';
1327
1328 if (_client->mode != KIS_PRODUCT_ID) {
1329 char *p = (char*)details->DevicePath;
1330 while ((p = strstr(p, "\\usb"))) {
1331 if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1)
1332 break;
1333 p += 4;
1334 }
1335 free(details);
1336
1337 if (serial_str[0] == '\0') {
1338 CloseHandle(_client->handle);
1339 continue;
1340 }
1341 p = strchr(serial_str, '#');
1342 if (p) {
1343 *p = '\0';
1344 }
1345
1346 unsigned int j;
1347 for (j = 0; j < strlen(serial_str); j++) {
1348 if (serial_str[j] == '_') {
1349 serial_str[j] = ' ';
1350 } else {
1351 serial_str[j] = toupper(serial_str[j]);
1352 }
1353 }
1354
1355 irecv_load_device_info_from_iboot_string(_client, serial_str);
1356 }
1357
1358 if (ecid != 0 && _client->mode != KIS_PRODUCT_ID) {
1359 if (_client->device_info.ecid != ecid) {
1360 CloseHandle(_client->handle);
1361 continue;
1362 }
1363 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
1364 }
1365 found = 1;
1366 break;
1367 }
1368 SetupDiDestroyDeviceInfoList(usbDevices);
1369 }
1370
1371 if (!found) {
1372 irecv_close(_client);
1373 return IRECV_E_UNABLE_TO_CONNECT;
1374 }
1375
1376 *client = _client;
1377
1378 return IRECV_E_SUCCESS;
1379}
1380#endif
1381
1382#ifdef HAVE_IOKIT
1383static void iokit_cfdictionary_set_short(CFMutableDictionaryRef dict, const void *key, SInt16 value)
1384{
1385 CFNumberRef numberRef;
1386
1387 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &value);
1388 if (numberRef) {
1389 CFDictionarySetValue(dict, key, numberRef);
1390 CFRelease(numberRef);
1391 }
1392}
1393#endif
1394
1395static int check_context(irecv_client_t client)
1396{
1397 if (client == NULL || client->handle == NULL) {
1398 return IRECV_E_NO_DEVICE;
1399 }
1400
1401 return IRECV_E_SUCCESS;
1402}
1403#endif
1404
1405#ifndef USE_DUMMY
1406#ifdef HAVE_IOKIT
1407static int iokit_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout)
1408{
1409 IOReturn result;
1410 IOUSBDevRequestTO req;
1411
1412 bzero(&req, sizeof(req));
1413 req.bmRequestType = bm_request_type;
1414 req.bRequest = b_request;
1415 req.wValue = OSSwapLittleToHostInt16(w_value);
1416 req.wIndex = OSSwapLittleToHostInt16(w_index);
1417 req.wLength = OSSwapLittleToHostInt16(w_length);
1418 req.pData = data;
1419 req.noDataTimeout = timeout;
1420 req.completionTimeout = timeout;
1421
1422 result = (*client->handle)->DeviceRequestTO(client->handle, &req);
1423 switch (result) {
1424 case kIOReturnSuccess: return req.wLenDone;
1425 case kIOUSBPipeStalled: return IRECV_E_PIPE;
1426 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
1427 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
1428 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
1429 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1430 default:
1431 return IRECV_E_UNKNOWN_ERROR;
1432 }
1433}
1434#else
1435#ifdef __APPLE__
1436 void dummy_callback(void) { }
1437#endif
1438#endif
1439#endif
1440
1441int irecv_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int timeout)
1442{
1443#ifdef USE_DUMMY
1444 return IRECV_E_UNSUPPORTED;
1445#else
1446#ifndef _WIN32
1447#ifdef HAVE_IOKIT
1448 return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1449#else
1450 return libusb_control_transfer(client->handle, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1451#endif
1452#else
1453 DWORD count = 0;
1454 BOOL bRet;
1455 OVERLAPPED overlapped;
1456
1457 if (data == NULL)
1458 w_length = 0;
1459
1460 usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + w_length);
1461 packet->bmRequestType = bm_request_type;
1462 packet->bRequest = b_request;
1463 packet->wValue = w_value;
1464 packet->wIndex = w_index;
1465 packet->wLength = w_length;
1466
1467 if (bm_request_type < 0x80 && w_length > 0) {
1468 memcpy(packet->data, data, w_length);
1469 }
1470
1471 memset(&overlapped, 0, sizeof(overlapped));
1472 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1473 DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + w_length, packet, sizeof(usb_control_request) + w_length, NULL, &overlapped);
1474 WaitForSingleObject(overlapped.hEvent, timeout);
1475 bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE);
1476 CloseHandle(overlapped.hEvent);
1477 if (!bRet) {
1478 CancelIo(client->handle);
1479 free(packet);
1480 return -1;
1481 }
1482
1483 count -= sizeof(usb_control_request);
1484 if (count > 0) {
1485 if (bm_request_type >= 0x80) {
1486 memcpy(data, packet->data, count);
1487 }
1488 }
1489 free(packet);
1490
1491 return count;
1492#endif
1493#endif
1494}
1495
1496#ifndef WIN32
1497#ifdef HAVE_IOKIT
1498
1499static void iokit_async_cb(void *refcon, kern_return_t ret, void *arg_0)
1500{
1501 struct irecv_async_transfer* transfer = refcon;
1502
1503 if(transfer != NULL) {
1504 transfer->ret = ret;
1505 memcpy(&transfer->len, &arg_0, sizeof(transfer->len));
1506 CFRunLoopStop(CFRunLoopGetCurrent());
1507 }
1508}
1509
1510static int iokit_async_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, struct irecv_async_transfer* transfer)
1511{
1512 IOReturn result;
1513 IOUSBDevRequest req;
1514
1515 bzero(&req, sizeof(req));
1516 req.bmRequestType = bm_request_type;
1517 req.bRequest = b_request;
1518 req.wValue = OSSwapLittleToHostInt16(w_value);
1519 req.wIndex = OSSwapLittleToHostInt16(w_index);
1520 req.wLength = OSSwapLittleToHostInt16(w_length);
1521 req.pData = data;
1522 result = (*client->handle)->DeviceRequestAsync(client->handle, &req, iokit_async_cb, transfer);
1523 switch (result) {
1524 case kIOReturnSuccess: return IRECV_E_SUCCESS;
1525 case kIOUSBPipeStalled: return IRECV_E_PIPE;
1526 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
1527 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
1528 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
1529 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1530 default:
1531 return IRECV_E_UNKNOWN_ERROR;
1532 }
1533}
1534
1535#else
1536
1537static void async_cb(struct libusb_transfer* usb_transfer) {
1538 struct irecv_async_transfer* transfer = usb_transfer->user_data;
1539 transfer->ret = usb_transfer->status;
1540 transfer->len += usb_transfer->actual_length;
1541}
1542
1543#endif
1544#endif
1545
1546IRECV_API int irecv_async_usb_control_transfer(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length) {
1547#ifdef USE_DUMMY
1548 return IRECV_E_UNSUPPORTED;
1549#else
1550#ifndef _WIN32
1551#ifdef HAVE_IOKIT
1552 return iokit_async_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, NULL);
1553#else
1554 irecv_error_t error;
1555
1556 unsigned char* buffer = malloc(w_length + 8);
1557 if(!buffer) {
1558 return IRECV_E_OUT_OF_MEMORY;
1559 }
1560 struct libusb_transfer* usb_transfer = libusb_alloc_transfer(0);
1561 if(!usb_transfer) {
1562 free(buffer);
1563 return IRECV_E_OUT_OF_MEMORY;
1564 }
1565 memcpy((buffer + 8), data, w_length);
1566 libusb_fill_control_setup(buffer, bm_request_type, b_request, w_value, w_index, w_length);
1567 libusb_fill_control_transfer(usb_transfer, client->handle, buffer, NULL, NULL, 0);
1568 usb_transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
1569 error = libusb_submit_transfer(usb_transfer);
1570 if(error != 0) {
1571 free(buffer);
1572 return error;
1573 }
1574 free(buffer);
1575 return IRECV_E_SUCCESS;
1576#endif
1577#else
1578 return IRECV_E_UNSUPPORTED;
1579#endif
1580#endif
1581
1582}
1583
1584IRECV_API int irecv_async_usb_control_transfer_with_cancel(irecv_client_t client, uint8_t bm_request_type, uint8_t b_request, uint16_t w_value, uint16_t w_index, unsigned char *data, uint16_t w_length, unsigned int u_time) {
1585#ifdef USE_DUMMY
1586 return IRECV_E_UNSUPPORTED;
1587#else
1588#ifndef _WIN32
1589 irecv_error_t error;
1590 struct irecv_async_transfer transfer;
1591 bzero(&transfer, sizeof(struct irecv_async_transfer));
1592
1593#ifdef HAVE_IOKIT
1594
1595 error = iokit_async_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, &transfer);
1596 if(error != IRECV_E_SUCCESS) {
1597 return error;
1598 }
1599 usleep(u_time);
1600 error = (*client->handle)->USBDeviceAbortPipeZero(client->handle);
1601 if(error != kIOReturnSuccess) {
1602 return IRECV_E_UNKNOWN_ERROR;
1603 }
1604 while(transfer.ret != kIOReturnAborted){
1605 CFRunLoopRun();
1606 }
1607 return transfer.len;
1608#else
1609 unsigned char* buffer = malloc(w_length + 8);
1610 if(!buffer) {
1611 return IRECV_E_OUT_OF_MEMORY;
1612 }
1613 struct libusb_transfer* usb_transfer = libusb_alloc_transfer(0);
1614 if(!usb_transfer) {
1615 free(buffer);
1616 return IRECV_E_OUT_OF_MEMORY;
1617 }
1618 memcpy((buffer + 8), data, w_length);
1619 libusb_fill_control_setup(buffer, bm_request_type, b_request, w_value, w_index, w_length);
1620 libusb_fill_control_transfer(usb_transfer, client->handle, buffer, async_cb, &transfer, 0);
1621 usb_transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
1622 error = libusb_submit_transfer(usb_transfer);
1623 if(error != 0) {
1624 free(buffer);
1625 return error;
1626 }
1627
1628 usleep(u_time);
1629
1630 error = libusb_cancel_transfer(usb_transfer);
1631 if(error != 0) {
1632 free(buffer);
1633 return error;
1634 }
1635 free(buffer);
1636
1637 while(transfer.ret != LIBUSB_TRANSFER_CANCELLED){
1638 libusb_handle_events_completed(libirecovery_context, NULL);
1639 }
1640 return transfer.len;
1641#endif
1642#else
1643 return IRECV_E_UNSUPPORTED;
1644#endif
1645#endif
1646}
1647
1648#ifndef USE_DUMMY
1649#ifdef HAVE_IOKIT
1650static int iokit_usb_bulk_transfer(irecv_client_t client,
1651 unsigned char endpoint,
1652 unsigned char *data,
1653 int length,
1654 int *transferred,
1655 unsigned int timeout)
1656{
1657 IOReturn result;
1658 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1659 UInt32 size = length;
1660 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1661 UInt8 numEndpoints;
1662
1663 if (!intf) return IRECV_E_USB_INTERFACE;
1664
1665 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1666
1667 if (result != kIOReturnSuccess)
1668 return IRECV_E_USB_INTERFACE;
1669
1670 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1671 UInt8 direction = 0;
1672 UInt8 number = 0;
1673 UInt8 transferType = 0;
1674 UInt16 maxPacketSize = 0;
1675 UInt8 interval = 0;
1676
1677 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1678 if (result != kIOReturnSuccess)
1679 continue;
1680
1681 if (direction == 3)
1682 direction = isUSBIn;
1683
1684 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1685 continue;
1686
1687 // Just because
1688 result = (*intf)->GetPipeStatus(intf, pipeRef);
1689 switch (result) {
1690 case kIOReturnSuccess: break;
1691 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1692 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1693 default: return IRECV_E_USB_STATUS;
1694 }
1695
1696 // Do the transfer
1697 if (isUSBIn) {
1698 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1699 if (result != kIOReturnSuccess)
1700 return IRECV_E_PIPE;
1701 *transferred = size;
1702
1703 return IRECV_E_SUCCESS;
1704 }
1705 else {
1706 // IOUSBInterfaceClass::interfaceWritePipe (intf?, pipeRef==1, data, size=0x8000)
1707 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1708 if (result != kIOReturnSuccess)
1709 return IRECV_E_PIPE;
1710 *transferred = size;
1711
1712 return IRECV_E_SUCCESS;
1713 }
1714 }
1715
1716 return IRECV_E_USB_INTERFACE;
1717}
1718
1719static int iokit_usb_interrupt_transfer(irecv_client_t client,
1720 unsigned char endpoint,
1721 unsigned char *data,
1722 int length,
1723 int *transferred,
1724 unsigned int timeout)
1725{
1726 IOReturn result;
1727 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1728 UInt32 size = length;
1729 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1730 UInt8 numEndpoints;
1731
1732 if (!intf) return IRECV_E_USB_INTERFACE;
1733
1734 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1735
1736 if (result != kIOReturnSuccess)
1737 return IRECV_E_USB_INTERFACE;
1738
1739 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1740 UInt8 direction = 0;
1741 UInt8 number = 0;
1742 UInt8 transferType = 0;
1743 UInt16 maxPacketSize = 0;
1744 UInt8 interval = 0;
1745
1746 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1747 if (result != kIOReturnSuccess)
1748 continue;
1749
1750 if (direction == 3)
1751 direction = isUSBIn;
1752
1753 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1754 continue;
1755
1756 // Just because
1757 result = (*intf)->GetPipeStatus(intf, pipeRef);
1758 switch (result) {
1759 case kIOReturnSuccess: break;
1760 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1761 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1762 default: return IRECV_E_USB_STATUS;
1763 }
1764
1765 // Do the transfer
1766 if (isUSBIn) {
1767 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1768 if (result != kIOReturnSuccess)
1769 return IRECV_E_PIPE;
1770 *transferred = size;
1771
1772 return IRECV_E_SUCCESS;
1773 }
1774 else {
1775 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1776 if (result != kIOReturnSuccess)
1777 return IRECV_E_PIPE;
1778 *transferred = size;
1779
1780 return IRECV_E_SUCCESS;
1781 }
1782 }
1783
1784 return IRECV_E_USB_INTERFACE;
1785}
1786#endif
1787#endif
1788
1789int irecv_usb_bulk_transfer(irecv_client_t client,
1790 unsigned char endpoint,
1791 unsigned char *data,
1792 int length,
1793 int *transferred,
1794 unsigned int timeout)
1795{
1796#ifdef USE_DUMMY
1797 return IRECV_E_UNSUPPORTED;
1798#else
1799 int ret;
1800
1801#ifndef _WIN32
1802#ifdef HAVE_IOKIT
1803 return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout);
1804#else
1805 ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout);
1806 if (ret < 0) {
1807 libusb_clear_halt(client->handle, endpoint);
1808 }
1809#endif
1810#else
1811 if (endpoint==0x4) {
1812 ret = DeviceIoControl(client->handle, 0x2201B6, data, length, data, length, (PDWORD) transferred, NULL);
1813 } else {
1814 ret = 0;
1815 }
1816 ret = (ret==0) ? -1 : 0;
1817#endif
1818
1819 return ret;
1820#endif
1821}
1822
1823IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client,
1824 unsigned char endpoint,
1825 unsigned char *data,
1826 int length,
1827 int *transferred,
1828 unsigned int timeout)
1829{
1830#ifdef USE_DUMMY
1831 return IRECV_E_UNSUPPORTED;
1832#else
1833 int ret;
1834
1835#ifndef _WIN32
1836#ifdef HAVE_IOKIT
1837 return iokit_usb_interrupt_transfer(client, endpoint, data, length, transferred, timeout);
1838#else
1839 ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout);
1840 if (ret < 0) {
1841 libusb_clear_halt(client->handle, endpoint);
1842 }
1843#endif
1844#else
1845 // win32
1846 return IRECV_E_UNSUPPORTED;
1847#endif
1848
1849 return ret;
1850#endif
1851}
1852
1853#ifndef USE_DUMMY
1854#ifdef HAVE_IOKIT
1855static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service)
1856{
1857 IOReturn result;
1858 irecv_client_t client;
1859 SInt32 score;
1860 UInt16 mode;
1861 UInt32 locationID;
1862 IOCFPlugInInterface **plug = NULL;
1863 CFStringRef serialString;
1864
1865 client = (irecv_client_t) calloc( 1, sizeof(struct irecv_client_private));
1866
1867 // Create the plug-in
1868 result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score);
1869 if (result != kIOReturnSuccess) {
1870 IOObjectRelease(service);
1871 free(client);
1872 return IRECV_E_UNKNOWN_ERROR;
1873 }
1874
1875 // Cache the serial string before discarding the service. The service object
1876 // has a cached copy, so a request to the hardware device is not required.
1877 char serial_str[256];
1878 serial_str[0] = '\0';
1879 serialString = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
1880 if (serialString) {
1881 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
1882 CFRelease(serialString);
1883 }
1884 irecv_load_device_info_from_iboot_string(client, serial_str);
1885
1886 IOObjectRelease(service);
1887
1888 // Create the device interface
1889 result = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&(client->handle));
1890 IODestroyPlugInInterface(plug);
1891 if (result != kIOReturnSuccess) {
1892 free(client);
1893 return IRECV_E_UNKNOWN_ERROR;
1894 }
1895
1896 (*client->handle)->GetDeviceProduct(client->handle, &mode);
1897 (*client->handle)->GetLocationID(client->handle, &locationID);
1898 client->mode = mode;
1899 debug("opening device %04x:%04x @ %#010x...\n", kAppleVendorID, client->mode, locationID);
1900
1901 result = (*client->handle)->USBDeviceOpenSeize(client->handle);
1902 if (result != kIOReturnSuccess) {
1903 (*client->handle)->Release(client->handle);
1904 free(client);
1905 return IRECV_E_UNABLE_TO_CONNECT;
1906 }
1907
1908 *pclient = client;
1909 return IRECV_E_SUCCESS;
1910}
1911
1912static io_iterator_t iokit_usb_get_iterator_for_pid(UInt16 pid)
1913{
1914 IOReturn result;
1915 io_iterator_t iterator;
1916 CFMutableDictionaryRef matchingDict;
1917
1918 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1919 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
1920 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pid);
1921
1922 result = IOServiceGetMatchingServices(MACH_PORT_NULL, matchingDict, &iterator);
1923 if (result != kIOReturnSuccess)
1924 return IO_OBJECT_NULL;
1925
1926 return iterator;
1927}
1928
1929static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1930{
1931 io_service_t service, ret_service;
1932 io_iterator_t iterator;
1933 CFStringRef usbSerial = NULL;
1934 CFStringRef ecidString = NULL;
1935 CFRange range;
1936
1937 UInt16 wtf_pids[] = { IRECV_K_WTF_MODE, 0};
1938 UInt16 all_pids[] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_PORT_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, KIS_PRODUCT_ID, 0 };
1939 UInt16 *pids = all_pids;
1940 int i;
1941
1942 if (pclient == NULL) {
1943 debug("%s: pclient parameter is null\n", __func__);
1944 return IRECV_E_INVALID_INPUT;
1945 }
1946 if (ecid == IRECV_K_WTF_MODE) {
1947 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1948 pids = wtf_pids;
1949 ecid = 0;
1950 }
1951 if (ecid > 0) {
1952 ecidString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%llX"), ecid);
1953 if (ecidString == NULL) {
1954 debug("%s: failed to create ECID string\n", __func__);
1955 return IRECV_E_UNABLE_TO_CONNECT;
1956 }
1957 }
1958
1959 *pclient = NULL;
1960 ret_service = IO_OBJECT_NULL;
1961
1962 for (i = 0; (pids[i] > 0 && ret_service == IO_OBJECT_NULL) ; i++) {
1963
1964 iterator = iokit_usb_get_iterator_for_pid(pids[i]);
1965 if (iterator) {
1966 while ((service = IOIteratorNext(iterator))) {
1967
1968 if (ecid == 0) {
1969 ret_service = service;
1970 break;
1971 }
1972
1973 if (pids[i] == KIS_PRODUCT_ID) {
1974 // In KIS Mode, we have to open the device in order to get
1975 // it's ECID
1976 irecv_error_t err = iokit_usb_open_service(pclient, service);
1977 if (err != IRECV_E_SUCCESS) {
1978 debug("%s: failed to open KIS device\n", __func__);
1979 continue;
1980 }
1981
1982 if (ecidString)
1983 CFRelease(ecidString);
1984
1985 return IRECV_E_SUCCESS;
1986 }
1987
1988 usbSerial = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
1989 if (usbSerial == NULL) {
1990 debug("%s: failed to create USB serial string property\n", __func__);
1991 IOObjectRelease(service);
1992 continue;
1993 }
1994
1995 range = CFStringFind(usbSerial, ecidString, kCFCompareCaseInsensitive);
1996 if (range.location == kCFNotFound) {
1997 IOObjectRelease(service);
1998 } else {
1999 ret_service = service;
2000 break;
2001 }
2002 }
2003 if (usbSerial) {
2004 CFRelease(usbSerial);
2005 usbSerial = NULL;
2006 }
2007 IOObjectRelease(iterator);
2008 }
2009 }
2010
2011 if (ecidString)
2012 CFRelease(ecidString);
2013
2014 if (ret_service == IO_OBJECT_NULL)
2015 return IRECV_E_UNABLE_TO_CONNECT;
2016
2017 return iokit_usb_open_service(pclient, ret_service);
2018}
2019#endif
2020
2021#ifndef _WIN32
2022#ifndef HAVE_IOKIT
2023static irecv_error_t libusb_usb_open_handle_with_descriptor_and_ecid(irecv_client_t *pclient, struct libusb_device_handle *usb_handle, struct libusb_device_descriptor *usb_descriptor, uint64_t ecid)
2024{
2025 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
2026 if (client == NULL) {
2027 libusb_close(usb_handle);
2028 return IRECV_E_OUT_OF_MEMORY;
2029 }
2030
2031 memset(client, '\0', sizeof(struct irecv_client_private));
2032 client->usb_interface = 0;
2033 client->handle = usb_handle;
2034 client->mode = usb_descriptor->idProduct;
2035
2036 if (client->mode != KIS_PRODUCT_ID) {
2037 char serial_str[256];
2038 memset(serial_str, 0, sizeof(serial_str));
2039 irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
2040 irecv_load_device_info_from_iboot_string(client, serial_str);
2041 }
2042
2043 if (ecid != 0 && client->mode != KIS_PRODUCT_ID) {
2044 if (client->device_info.ecid != ecid) {
2045 irecv_close(client);
2046 return IRECV_E_NO_DEVICE; //wrong device
2047 }
2048 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
2049 }
2050
2051 *pclient = client;
2052 return IRECV_E_SUCCESS;
2053}
2054
2055static irecv_error_t libusb_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
2056{
2057 irecv_error_t ret = IRECV_E_UNABLE_TO_CONNECT;
2058 int i = 0;
39 struct libusb_device* usb_device = NULL; 2059 struct libusb_device* usb_device = NULL;
40 struct libusb_device** usb_device_list = NULL; 2060 struct libusb_device** usb_device_list = NULL;
41 struct libusb_device_handle* usb_handle = NULL;
42 struct libusb_device_descriptor usb_descriptor; 2061 struct libusb_device_descriptor usb_descriptor;
43 2062
44 *pclient = NULL; 2063 *pclient = NULL;
45 libusb_init(&libirecovery_context);
46 if(libirecovery_debug) {
47 irecv_set_debug_level(libirecovery_debug);
48 }
49
50 irecv_error_t error = IRECV_E_SUCCESS;
51 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list); 2064 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list);
52 for (i = 0; i < usb_device_count; i++) { 2065 for (i = 0; i < usb_device_count; i++) {
53 usb_device = usb_device_list[i]; 2066 usb_device = usb_device_list[i];
54 libusb_get_device_descriptor(usb_device, &usb_descriptor); 2067 libusb_get_device_descriptor(usb_device, &usb_descriptor);
55 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { 2068 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) {
56 /* verify this device is in a mode we understand */ 2069 /* verify this device is in a mode we understand */
57 if (usb_descriptor.idProduct == kRecoveryMode1 || 2070 if (usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_1 ||
58 usb_descriptor.idProduct == kRecoveryMode2 || 2071 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_2 ||
59 usb_descriptor.idProduct == kRecoveryMode3 || 2072 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_3 ||
60 usb_descriptor.idProduct == kRecoveryMode4 || 2073 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_4 ||
61 usb_descriptor.idProduct == kDfuMode) { 2074 usb_descriptor.idProduct == IRECV_K_WTF_MODE ||
2075 usb_descriptor.idProduct == IRECV_K_DFU_MODE ||
2076 usb_descriptor.idProduct == IRECV_K_PORT_DFU_MODE ||
2077 usb_descriptor.idProduct == KIS_PRODUCT_ID) {
2078
2079 if (ecid == IRECV_K_WTF_MODE) {
2080 if (usb_descriptor.idProduct != IRECV_K_WTF_MODE) {
2081 /* special ecid case, ignore !IRECV_K_WTF_MODE */
2082 continue;
2083 } else {
2084 ecid = 0;
2085 }
2086 }
2087
2088 if ((ecid != 0) && (usb_descriptor.idProduct == IRECV_K_WTF_MODE)) {
2089 /* we can't get ecid in WTF mode */
2090 continue;
2091 }
62 2092
63 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); 2093 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct);
64 2094
65 libusb_open(usb_device, &usb_handle); 2095 struct libusb_device_handle* usb_handle = NULL;
66 if (usb_handle == NULL) { 2096 int libusb_error = libusb_open(usb_device, &usb_handle);
67 libusb_free_device_list(usb_device_list, 1); 2097 if (usb_handle == NULL || libusb_error != 0) {
2098 debug("%s: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
2099
68 libusb_close(usb_handle); 2100 libusb_close(usb_handle);
69 libusb_exit(libirecovery_context); 2101 if (ecid != 0) {
2102 continue;
2103 }
2104 libusb_free_device_list(usb_device_list, 1);
70 return IRECV_E_UNABLE_TO_CONNECT; 2105 return IRECV_E_UNABLE_TO_CONNECT;
71 } 2106 }
72 libusb_free_device_list(usb_device_list, 1);
73 2107
74 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client)); 2108 ret = libusb_usb_open_handle_with_descriptor_and_ecid(pclient, usb_handle, &usb_descriptor, ecid);
75 if (client == NULL) { 2109 if (ret == IRECV_E_SUCCESS) {
76 libusb_close(usb_handle); 2110 break;
77 libusb_exit(libirecovery_context);
78 return IRECV_E_OUT_OF_MEMORY;
79 } 2111 }
2112 }
2113 }
2114 }
2115 libusb_free_device_list(usb_device_list, 1);
2116 return ret;
2117}
2118#endif
2119#endif
2120#endif
2121
2122irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
2123{
2124#ifdef USE_DUMMY
2125 return IRECV_E_UNSUPPORTED;
2126#else
2127 irecv_error_t error = IRECV_E_UNABLE_TO_CONNECT;
2128
2129 if (libirecovery_debug) {
2130 irecv_set_debug_level(libirecovery_debug);
2131 }
2132#ifndef _WIN32
2133#ifdef HAVE_IOKIT
2134 error = iokit_open_with_ecid(pclient, ecid);
2135#else
2136 error = libusb_open_with_ecid(pclient, ecid);
2137#endif
2138#else
2139 error = win32_open_with_ecid(pclient, ecid);
2140#endif
2141 irecv_client_t client = *pclient;
2142 if (error != IRECV_E_SUCCESS) {
2143 irecv_close(client);
2144 return error;
2145 }
80 2146
81 memset(client, '\0', sizeof(struct irecv_client)); 2147 error = irecv_usb_set_configuration(client, 1);
82 client->interface = 0; 2148 if (error != IRECV_E_SUCCESS) {
83 client->handle = usb_handle; 2149 debug("Failed to set configuration, error %d\n", error);
84 client->mode = usb_descriptor.idProduct; 2150 irecv_close(client);
2151 return error;
2152 }
85 2153
86 error = irecv_set_configuration(client, 1); 2154#ifdef HAVE_IOKIT
87 if (error != IRECV_E_SUCCESS) { 2155 error = (*client->handle)->CreateDeviceAsyncEventSource(client->handle, &client->async_event_source);
88 return error; 2156 if (error != IRECV_E_SUCCESS) {
89 } 2157 free(client);
2158 return error;
2159 }
2160 CFRunLoopAddSource(CFRunLoopGetCurrent(), client->async_event_source, kCFRunLoopDefaultMode);
2161#endif
2162
2163 if (client->mode == IRECV_K_DFU_MODE || client->mode == IRECV_K_PORT_DFU_MODE || client->mode == IRECV_K_WTF_MODE || client->mode == KIS_PRODUCT_ID) {
2164 error = irecv_usb_set_interface(client, 0, 0);
2165 } else {
2166 error = irecv_usb_set_interface(client, 0, 0);
2167 if (error == IRECV_E_SUCCESS && client->mode > IRECV_K_RECOVERY_MODE_2) {
2168 error = irecv_usb_set_interface(client, 1, 1);
2169 }
2170 }
90 2171
91 error = irecv_set_interface(client, 1, 1); 2172 if (error != IRECV_E_SUCCESS) {
92 if (error != IRECV_E_SUCCESS) { 2173 debug("Failed to set interface, error %d\n", error);
93 return error; 2174 irecv_close(client);
94 } 2175 return error;
2176 }
95 2177
96 /* cache usb serial */ 2178 if (client->mode == KIS_PRODUCT_ID) {
97 libusb_get_string_descriptor_ascii(client->handle, usb_descriptor.iSerialNumber, client->serial, 255); 2179 error = irecv_kis_init(client);
2180 if (error != IRECV_E_SUCCESS) {
2181 debug("irecv_kis_init failed, error %d\n", error);
2182 irecv_close(client);
2183 return error;
2184 }
98 2185
99 *pclient = client; 2186 error = irecv_kis_load_device_info(client);
100 return IRECV_E_SUCCESS; 2187 if (error != IRECV_E_SUCCESS) {
101 } 2188 debug("irecv_kis_load_device_info failed, error %d\n", error);
2189 irecv_close(client);
2190 return error;
2191 }
2192 if (ecid != 0 && client->device_info.ecid != ecid) {
2193 irecv_close(client);
2194 return IRECV_E_NO_DEVICE; //wrong device
102 } 2195 }
2196 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
2197 } else {
2198 irecv_copy_nonce_with_tag(client, "NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size);
2199 irecv_copy_nonce_with_tag(client, "SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size);
103 } 2200 }
104 2201
105 return IRECV_E_UNABLE_TO_CONNECT; 2202 if (error == IRECV_E_SUCCESS) {
2203 if ((*pclient)->connected_callback != NULL) {
2204 irecv_event_t event;
2205 event.size = 0;
2206 event.data = NULL;
2207 event.progress = 0;
2208 event.type = IRECV_CONNECTED;
2209 (*pclient)->connected_callback(*pclient, &event);
2210 }
2211 }
2212 return error;
2213#endif
106} 2214}
107 2215
108irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) { 2216irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration)
109 if (client == NULL || client->handle == NULL) { 2217{
2218#ifdef USE_DUMMY
2219 return IRECV_E_UNSUPPORTED;
2220#else
2221 if (check_context(client) != IRECV_E_SUCCESS)
110 return IRECV_E_NO_DEVICE; 2222 return IRECV_E_NO_DEVICE;
111 }
112 2223
2224#ifndef _WIN32
113 debug("Setting to configuration %d\n", configuration); 2225 debug("Setting to configuration %d\n", configuration);
114 2226
2227#ifdef HAVE_IOKIT
2228 IOReturn result;
2229
2230 result = (*client->handle)->SetConfiguration(client->handle, configuration);
2231 if (result != kIOReturnSuccess) {
2232 debug("error setting configuration: %#x\n", result);
2233 return IRECV_E_USB_CONFIGURATION;
2234 }
2235#else
115 int current = 0; 2236 int current = 0;
116 libusb_get_configuration(client->handle, &current); 2237 libusb_get_configuration(client->handle, &current);
117 if (current != configuration) { 2238 if (current != configuration) {
@@ -119,45 +2240,199 @@ irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration)
119 return IRECV_E_USB_CONFIGURATION; 2240 return IRECV_E_USB_CONFIGURATION;
120 } 2241 }
121 } 2242 }
2243#endif
2244 client->usb_config = configuration;
2245#endif
122 2246
123 client->config = configuration;
124 return IRECV_E_SUCCESS; 2247 return IRECV_E_SUCCESS;
2248#endif
125} 2249}
126 2250
127irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) { 2251#ifndef USE_DUMMY
128 if (client == NULL || client->handle == NULL) { 2252#ifdef HAVE_IOKIT
129 return IRECV_E_NO_DEVICE; 2253static IOReturn iokit_usb_get_interface(IOUSBDeviceInterface320 **device, uint8_t ifc, io_service_t *usbInterfacep)
2254{
2255 IOUSBFindInterfaceRequest request;
2256 uint8_t current_interface;
2257 kern_return_t kresult;
2258 io_iterator_t interface_iterator;
2259
2260 *usbInterfacep = IO_OBJECT_NULL;
2261
2262 request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
2263 request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
2264 request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
2265 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
2266
2267 kresult = (*device)->CreateInterfaceIterator(device, &request, &interface_iterator);
2268 if (kresult)
2269 return kresult;
2270
2271 for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) {
2272 *usbInterfacep = IOIteratorNext(interface_iterator);
2273 if (current_interface != ifc)
2274 (void) IOObjectRelease (*usbInterfacep);
130 } 2275 }
2276 IOObjectRelease(interface_iterator);
131 2277
132 if (client->interface == interface) { 2278 return kIOReturnSuccess;
133 return IRECV_E_SUCCESS; 2279}
2280
2281static irecv_error_t iokit_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
2282{
2283 IOReturn result;
2284 io_service_t interface_service = IO_OBJECT_NULL;
2285 IOCFPlugInInterface **plugInInterface = NULL;
2286 SInt32 score;
2287
2288 // Close current interface
2289 if (client->usbInterface) {
2290 result = (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
2291 result = (*client->usbInterface)->Release(client->usbInterface);
2292 client->usbInterface = NULL;
2293 }
2294
2295 result = iokit_usb_get_interface(client->handle, usb_interface, &interface_service);
2296 if (result != kIOReturnSuccess) {
2297 debug("failed to find requested interface: %d\n", usb_interface);
2298 return IRECV_E_USB_INTERFACE;
2299 }
2300
2301 result = IOCreatePlugInInterfaceForService(interface_service, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
2302 IOObjectRelease(interface_service);
2303 if (result != kIOReturnSuccess) {
2304 debug("error creating plug-in interface: %#x\n", result);
2305 return IRECV_E_USB_INTERFACE;
2306 }
2307
2308 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300), (LPVOID)&client->usbInterface);
2309 IODestroyPlugInInterface(plugInInterface);
2310 if (result != kIOReturnSuccess) {
2311 debug("error creating interface interface: %#x\n", result);
2312 return IRECV_E_USB_INTERFACE;
134 } 2313 }
135 2314
136 debug("Setting to interface %d:%d\n", interface, alt_interface); 2315 result = (*client->usbInterface)->USBInterfaceOpen(client->usbInterface);
137 if (libusb_claim_interface(client->handle, interface) < 0) { 2316 if (result != kIOReturnSuccess) {
2317 debug("error opening interface: %#x\n", result);
138 return IRECV_E_USB_INTERFACE; 2318 return IRECV_E_USB_INTERFACE;
139 } 2319 }
140 2320
141 if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) { 2321 if (usb_interface == 1) {
2322 result = (*client->usbInterface)->SetAlternateInterface(client->usbInterface, usb_alt_interface);
2323 if (result != kIOReturnSuccess) {
2324 debug("error setting alternate interface: %#x\n", result);
2325 return IRECV_E_USB_INTERFACE;
2326 }
2327 }
2328
2329 return IRECV_E_SUCCESS;
2330}
2331#endif
2332#endif
2333
2334irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
2335{
2336#ifdef USE_DUMMY
2337 return IRECV_E_UNSUPPORTED;
2338#else
2339 if (check_context(client) != IRECV_E_SUCCESS)
2340 return IRECV_E_NO_DEVICE;
2341
2342 debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface);
2343#ifndef _WIN32
2344#ifdef HAVE_IOKIT
2345 if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) {
142 return IRECV_E_USB_INTERFACE; 2346 return IRECV_E_USB_INTERFACE;
143 } 2347 }
2348#else
2349 if (libusb_claim_interface(client->handle, usb_interface) < 0) {
2350 return IRECV_E_USB_INTERFACE;
2351 }
2352
2353 if (usb_interface == 1) {
2354 if (libusb_set_interface_alt_setting(client->handle, usb_interface, usb_alt_interface) < 0) {
2355 return IRECV_E_USB_INTERFACE;
2356 }
2357 }
2358#endif
2359#else
2360 if (usb_interface == 1) {
2361 if (irecv_usb_control_transfer(client, 0, 0x0B, usb_alt_interface, usb_interface, NULL, 0, USB_TIMEOUT) < 0) {
2362 return IRECV_E_USB_INTERFACE;
2363 }
2364 }
2365#endif
2366 client->usb_interface = usb_interface;
2367 client->usb_alt_interface = usb_alt_interface;
144 2368
145 client->interface = interface;
146 client->alt_interface = alt_interface;
147 return IRECV_E_SUCCESS; 2369 return IRECV_E_SUCCESS;
2370#endif
148} 2371}
149 2372
150irecv_error_t irecv_reset(irecv_client_t client) { 2373irecv_error_t irecv_reset(irecv_client_t client)
151 if (client == NULL || client->handle == NULL) { 2374{
2375#ifdef USE_DUMMY
2376 return IRECV_E_UNSUPPORTED;
2377#else
2378 if (check_context(client) != IRECV_E_SUCCESS)
152 return IRECV_E_NO_DEVICE; 2379 return IRECV_E_NO_DEVICE;
2380
2381#ifndef _WIN32
2382#ifdef HAVE_IOKIT
2383 IOReturn result;
2384
2385 result = (*client->handle)->ResetDevice(client->handle);
2386 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2387 debug("error sending device reset: %#x\n", result);
2388 return IRECV_E_UNKNOWN_ERROR;
153 } 2389 }
154 2390
2391 result = (*client->handle)->USBDeviceReEnumerate(client->handle, 0);
2392 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2393 debug("error re-enumerating device: %#x (ignored)\n", result);
2394 }
2395#else
155 libusb_reset_device(client->handle); 2396 libusb_reset_device(client->handle);
2397#endif
2398#else
2399 DWORD count;
2400 DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL);
2401#endif
156 2402
157 return IRECV_E_SUCCESS; 2403 return IRECV_E_SUCCESS;
2404#endif
2405}
2406
2407irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, uint64_t ecid, int attempts)
2408{
2409#ifdef USE_DUMMY
2410 return IRECV_E_UNSUPPORTED;
2411#else
2412 int i;
2413
2414 for (i = 0; i < attempts; i++) {
2415 if (*pclient) {
2416 irecv_close(*pclient);
2417 *pclient = NULL;
2418 }
2419 if (irecv_open_with_ecid(pclient, ecid) != IRECV_E_SUCCESS) {
2420 debug("Connection failed. Waiting 1 sec before retry.\n");
2421 sleep(1);
2422 } else {
2423 return IRECV_E_SUCCESS;
2424 }
2425 }
2426
2427 return IRECV_E_UNABLE_TO_CONNECT;
2428#endif
158} 2429}
159 2430
160irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { 2431irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data)
2432{
2433#ifdef USE_DUMMY
2434 return IRECV_E_UNSUPPORTED;
2435#else
161 switch(type) { 2436 switch(type) {
162 case IRECV_RECEIVED: 2437 case IRECV_RECEIVED:
163 client->received_callback = callback; 2438 client->received_callback = callback;
@@ -165,9 +2440,11 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
165 2440
166 case IRECV_PROGRESS: 2441 case IRECV_PROGRESS:
167 client->progress_callback = callback; 2442 client->progress_callback = callback;
2443 break;
168 2444
169 case IRECV_CONNECTED: 2445 case IRECV_CONNECTED:
170 client->connected_callback = callback; 2446 client->connected_callback = callback;
2447 break;
171 2448
172 case IRECV_PRECOMMAND: 2449 case IRECV_PRECOMMAND:
173 client->precommand_callback = callback; 2450 client->precommand_callback = callback;
@@ -179,15 +2456,21 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
179 2456
180 case IRECV_DISCONNECTED: 2457 case IRECV_DISCONNECTED:
181 client->disconnected_callback = callback; 2458 client->disconnected_callback = callback;
2459 break;
182 2460
183 default: 2461 default:
184 return IRECV_E_UNKNOWN_ERROR; 2462 return IRECV_E_UNKNOWN_ERROR;
185 } 2463 }
186 2464
187 return IRECV_E_SUCCESS; 2465 return IRECV_E_SUCCESS;
2466#endif
188} 2467}
189 2468
190irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { 2469irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type)
2470{
2471#ifdef USE_DUMMY
2472 return IRECV_E_UNSUPPORTED;
2473#else
191 switch(type) { 2474 switch(type) {
192 case IRECV_RECEIVED: 2475 case IRECV_RECEIVED:
193 client->received_callback = NULL; 2476 client->received_callback = NULL;
@@ -195,9 +2478,11 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
195 2478
196 case IRECV_PROGRESS: 2479 case IRECV_PROGRESS:
197 client->progress_callback = NULL; 2480 client->progress_callback = NULL;
2481 break;
198 2482
199 case IRECV_CONNECTED: 2483 case IRECV_CONNECTED:
200 client->connected_callback = NULL; 2484 client->connected_callback = NULL;
2485 break;
201 2486
202 case IRECV_PRECOMMAND: 2487 case IRECV_PRECOMMAND:
203 client->precommand_callback = NULL; 2488 client->precommand_callback = NULL;
@@ -209,17 +2494,871 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
209 2494
210 case IRECV_DISCONNECTED: 2495 case IRECV_DISCONNECTED:
211 client->disconnected_callback = NULL; 2496 client->disconnected_callback = NULL;
2497 break;
212 2498
213 default: 2499 default:
214 return IRECV_E_UNKNOWN_ERROR; 2500 return IRECV_E_UNKNOWN_ERROR;
215 } 2501 }
216 2502
217 return IRECV_E_SUCCESS; 2503 return IRECV_E_SUCCESS;
2504#endif
2505}
2506
2507#ifndef USE_DUMMY
2508struct irecv_device_event_context {
2509 irecv_device_event_cb_t callback;
2510 void *user_data;
2511};
2512
2513struct irecv_usb_device_info {
2514 struct irecv_device_info device_info;
2515 enum irecv_mode mode;
2516 uint32_t location;
2517 int alive;
2518};
2519
2520#ifdef _WIN32
2521struct irecv_win_dev_ctx {
2522 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
2523 uint32_t location;
2524};
2525#else
2526#ifdef HAVE_IOKIT
2527struct irecv_iokit_dev_ctx {
2528 io_service_t device;
2529 IOUSBDeviceInterface **dev;
2530};
2531#endif
2532#endif
2533
2534static int _irecv_is_recovery_device(void *device)
2535{
2536 uint16_t vendor_id = 0;
2537 uint16_t product_id = 0;
2538#ifdef _WIN32
2539 const char *path = (const char*)device;
2540 unsigned int vendor = 0;
2541 unsigned int product = 0;
2542 if (sscanf(path, "\\usb#vid_%04x&pid_%04x#", &vendor, &product) != 2) {
2543 return 0;
2544 }
2545 vendor_id = (uint16_t)vendor;
2546 product_id = (uint16_t)product;
2547#else
2548#ifdef HAVE_IOKIT
2549 kern_return_t kr;
2550 IOUSBDeviceInterface **dev = device;
2551 kr = (*dev)->GetDeviceVendor(dev, &vendor_id);
2552 if (kr != kIOReturnSuccess) {
2553 debug("%s: Failed to get vendor id\n", __func__);
2554 return 0;
2555 }
2556 kr = (*dev)->GetDeviceProduct(dev, &product_id);
2557 if (kr != kIOReturnSuccess) {
2558 debug("%s: Failed to get product id\n", __func__);
2559 return 0;
2560 }
2561#else
2562 libusb_device *device_ = (libusb_device*)device;
2563 struct libusb_device_descriptor devdesc;
2564 int libusb_error;
2565
2566 libusb_error = libusb_get_device_descriptor(device_, &devdesc);
2567 if (libusb_error != 0) {
2568 debug("%s: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2569 return 0;
2570 }
2571 vendor_id = devdesc.idVendor;
2572 product_id = devdesc.idProduct;
2573#endif
2574#endif
2575
2576 if (vendor_id != APPLE_VENDOR_ID) {
2577 return 0;
2578 }
2579
2580 switch (product_id) {
2581 case IRECV_K_DFU_MODE:
2582 case IRECV_K_WTF_MODE:
2583 case IRECV_K_RECOVERY_MODE_1:
2584 case IRECV_K_RECOVERY_MODE_2:
2585 case IRECV_K_RECOVERY_MODE_3:
2586 case IRECV_K_RECOVERY_MODE_4:
2587 case IRECV_K_PORT_DFU_MODE:
2588 case KIS_PRODUCT_ID:
2589 break;
2590 default:
2591 return 0;
2592 }
2593 return 1;
2594}
2595
2596static void* _irecv_handle_device_add(void *userdata)
2597{
2598 struct irecv_client_private client_loc;
2599 char serial_str[256];
2600 uint32_t location = 0;
2601 uint16_t product_id = 0;
2602 irecv_error_t error = 0;
2603 irecv_client_t client = NULL;
2604
2605 memset(serial_str, 0, sizeof(serial_str));
2606#ifdef _WIN32
2607 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata;
2608 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details;
2609 LPSTR result = (LPSTR)details->DevicePath;
2610 location = win_ctx->location;
2611
2612 unsigned int pid = 0;
2613
2614 if (strncmp(result, "\\\\?\\kis#", 8) == 0) {
2615 pid = KIS_PRODUCT_ID;
2616 } else {
2617 char *p = result;
2618 while ((p = strstr(p, "\\usb"))) {
2619 if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2)
2620 break;
2621 p += 4;
2622 }
2623
2624 if (serial_str[0] == '\0') {
2625 debug("%s: ERROR: failed to parse DevicePath?!\n", __func__);
2626 return NULL;
2627 }
2628
2629 if (!_irecv_is_recovery_device(p)) {
2630 return NULL;
2631 }
2632 }
2633
2634 product_id = (uint16_t)pid;
2635
2636 if (product_id == KIS_PRODUCT_ID) {
2637 client = (irecv_client_t)malloc(sizeof(struct irecv_client_private));
2638 if (client == NULL) {
2639 debug("%s: Failed to allocate memory\n", __func__);
2640 return NULL;
2641 }
2642 memset(client, '\0', sizeof(struct irecv_client_private));
2643 client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
2644 if (client->handle == INVALID_HANDLE_VALUE) {
2645 debug("%s: Failed to open device path %s\n", __func__, result);
2646 free(client);
2647 return NULL;
2648 }
2649 client->mode = pid;
2650 } else {
2651 char* p = strchr(serial_str, '#');
2652 if (p) {
2653 *p = '\0';
2654 }
2655
2656 unsigned int j;
2657 for (j = 0; j < strlen(serial_str); j++) {
2658 if (serial_str[j] == '_') {
2659 serial_str[j] = ' ';
2660 } else {
2661 serial_str[j] = toupper(serial_str[j]);
2662 }
2663 }
2664 }
2665
2666#else /* !_WIN32 */
2667#ifdef HAVE_IOKIT
2668 struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata;
2669 io_service_t device = iokit_ctx->device;
2670 IOUSBDeviceInterface **dev = iokit_ctx->dev;
2671
2672 if (!device) {
2673 debug("%s: ERROR: no device?!\n", __func__);
2674 return NULL;
2675 }
2676 if (!dev) {
2677 debug("%s: ERROR: no device interface?!\n", __func__);
2678 return NULL;
2679 }
2680
2681 (*dev)->GetDeviceProduct(dev, &product_id);
2682 if (!product_id) {
2683 debug("%s: ERROR: could not get product id?!\n", __func__);
2684 return NULL;
2685 }
2686 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2687 if (locationNum) {
2688 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2689 CFRelease(locationNum);
2690 }
2691 if (!location) {
2692 debug("%s: ERROR: could not get locationID?!\n", __func__);
2693 return NULL;
2694 }
2695
2696 if (product_id == KIS_PRODUCT_ID) {
2697 IOObjectRetain(device);
2698 int i = 0;
2699 for (i = 0; i < 10; i++) {
2700 error = iokit_usb_open_service(&client, device);
2701 if (error == IRECV_E_SUCCESS) {
2702 break;
2703 }
2704 debug("%s: Could not open KIS device, retrying...\n", __func__);
2705 usleep(500000);
2706 }
2707 if (error != IRECV_E_SUCCESS) {
2708 debug("%s: ERROR: could not open KIS device!\n", __func__);
2709 return NULL;
2710 }
2711 product_id = client->mode;
2712 } else {
2713 CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
2714 if (serialString) {
2715 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
2716 CFRelease(serialString);
2717 }
2718 }
2719#else /* !HAVE_IOKIT */
2720 libusb_device *device = (libusb_device*)userdata;
2721 struct libusb_device_descriptor devdesc;
2722 struct libusb_device_handle* usb_handle = NULL;
2723 int libusb_error;
2724
2725 libusb_error = libusb_get_device_descriptor(device, &devdesc);
2726 if (libusb_error != 0) {
2727 debug("%s: ERROR: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2728 return NULL;
2729 }
2730 product_id = devdesc.idProduct;
2731
2732 uint8_t bus = libusb_get_bus_number(device);
2733 uint8_t address = libusb_get_device_address(device);
2734 location = (bus << 16) | address;
2735
2736 libusb_error = libusb_open(device, &usb_handle);
2737 if (usb_handle == NULL || libusb_error != 0) {
2738 debug("%s: ERROR: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
2739 libusb_close(usb_handle);
2740 return 0;
2741 }
2742
2743 if (product_id == KIS_PRODUCT_ID) {
2744 error = libusb_usb_open_handle_with_descriptor_and_ecid(&client, usb_handle, &devdesc, 0);
2745 if (error != IRECV_E_SUCCESS) {
2746 debug("%s: ERROR: could not open KIS device!\n", __func__);
2747 return NULL;
2748 }
2749
2750 product_id = client->mode;
2751 } else {
2752 libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
2753 if (libusb_error < 0) {
2754 debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2755 return 0;
2756 }
2757 libusb_close(usb_handle);
2758 }
2759#endif /* !HAVE_IOKIT */
2760#endif /* !_WIN32 */
2761 memset(&client_loc, '\0', sizeof(client_loc));
2762 if (product_id == KIS_PRODUCT_ID) {
2763 int i = 0;
2764 for (i = 0; i < 10; i++) {
2765 error = irecv_usb_set_configuration(client, 1);
2766 if (error == IRECV_E_SUCCESS) {
2767 break;
2768 }
2769 debug("Failed to set configuration, error %d, retrying...\n", error);
2770 usleep(500000);
2771 }
2772 if (error != IRECV_E_SUCCESS) {
2773 debug("Failed to set configuration, error %d\n", error);
2774 irecv_close(client);
2775 return NULL;
2776 }
2777
2778 for (i = 0; i < 10; i++) {
2779 error = irecv_usb_set_interface(client, 0, 0);
2780 if (error == IRECV_E_SUCCESS) {
2781 break;
2782 }
2783 debug("Failed to set interface, error %d, retrying...\n", error);
2784 usleep(500000);
2785 }
2786 if (error != IRECV_E_SUCCESS) {
2787 debug("Failed to set interface, error %d\n", error);
2788 irecv_close(client);
2789 return NULL;
2790 }
2791
2792 error = irecv_kis_init(client);
2793 if (error != IRECV_E_SUCCESS) {
2794 debug("irecv_kis_init failed, error %d\n", error);
2795 irecv_close(client);
2796 return NULL;
2797 }
2798
2799 error = irecv_kis_load_device_info(client);
2800 if (error != IRECV_E_SUCCESS) {
2801 debug("irecv_kis_load_device_info failed, error %d\n", error);
2802 irecv_close(client);
2803 return NULL;
2804 }
2805 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
2806 strncpy(serial_str, client->device_info.serial_string, 255);
2807 product_id = client->mode;
2808 client_loc.isKIS = 1;
2809 }
2810 if (client) {
2811 irecv_close(client);
2812 }
2813
2814 client_loc.mode = product_id;
2815 irecv_load_device_info_from_iboot_string(&client_loc, serial_str);
2816
2817 struct irecv_usb_device_info *usb_dev_info = (struct irecv_usb_device_info*)malloc(sizeof(struct irecv_usb_device_info));
2818 memcpy(&(usb_dev_info->device_info), &(client_loc.device_info), sizeof(struct irecv_device_info));
2819 usb_dev_info->location = location;
2820 usb_dev_info->alive = 1;
2821 usb_dev_info->mode = client_loc.mode;
2822
2823 collection_add(&devices, usb_dev_info);
2824
2825 irecv_device_event_t dev_event;
2826 dev_event.type = IRECV_DEVICE_ADD;
2827 dev_event.mode = client_loc.mode;
2828 dev_event.device_info = &(usb_dev_info->device_info);
2829
2830 mutex_lock(&listener_mutex);
2831 FOREACH(struct irecv_device_event_context* context, &listeners) {
2832 context->callback(&dev_event, context->user_data);
2833 } ENDFOREACH
2834 mutex_unlock(&listener_mutex);
2835
2836 return NULL;
2837}
2838
2839static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
2840{
2841 irecv_device_event_t dev_event;
2842 dev_event.type = IRECV_DEVICE_REMOVE;
2843 dev_event.mode = devinfo->mode;
2844 dev_event.device_info = &(devinfo->device_info);
2845 mutex_lock(&listener_mutex);
2846 FOREACH(struct irecv_device_event_context* context, &listeners) {
2847 context->callback(&dev_event, context->user_data);
2848 } ENDFOREACH
2849 mutex_unlock(&listener_mutex);
2850 free(devinfo->device_info.srnm);
2851 devinfo->device_info.srnm = NULL;
2852 free(devinfo->device_info.imei);
2853 devinfo->device_info.imei = NULL;
2854 free(devinfo->device_info.srtg);
2855 devinfo->device_info.srtg = NULL;
2856 free(devinfo->device_info.serial_string);
2857 devinfo->device_info.serial_string = NULL;
2858 devinfo->alive = 0;
2859 collection_remove(&devices, devinfo);
2860 free(devinfo);
2861}
2862
2863#ifndef _WIN32
2864#ifdef HAVE_IOKIT
2865static void iokit_device_added(void *refcon, io_iterator_t iterator)
2866{
2867 kern_return_t kr;
2868 io_service_t device;
2869 IOCFPlugInInterface **plugInInterface = NULL;
2870 IOUSBDeviceInterface **dev = NULL;
2871 HRESULT result;
2872 SInt32 score;
2873
2874 while ((device = IOIteratorNext(iterator))) {
2875 kr = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
2876 if ((kIOReturnSuccess != kr) || !plugInInterface) {
2877 debug("%s: ERROR: Unable to create a plug-in (%08x)\n", __func__, kr);
2878 IOObjectRelease(device);
2879 continue;
2880 }
2881 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&dev);
2882 (*plugInInterface)->Release(plugInInterface);
2883
2884 if (result || !dev) {
2885 debug("%s: ERROR: Couldn't create a device interface (%08x)\n", __func__, (int)result);
2886 IOObjectRelease(device);
2887 continue;
2888 }
2889
2890 if (!_irecv_is_recovery_device(dev)) {
2891 (void) (*dev)->Release(dev);
2892 IOObjectRelease(device);
2893 continue;
2894 }
2895
2896 struct irecv_iokit_dev_ctx idev;
2897 idev.device = device;
2898 idev.dev = dev;
2899 _irecv_handle_device_add(&idev);
2900 (void) (*dev)->Release(dev);
2901 IOObjectRelease(device);
2902 }
2903}
2904
2905static void iokit_device_removed(void *refcon, io_iterator_t iterator)
2906{
2907 io_service_t device;
2908
2909 while ((device = IOIteratorNext(iterator))) {
2910 uint32_t location = 0;
2911 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2912 if (locationNum) {
2913 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2914 CFRelease(locationNum);
2915 }
2916 IOObjectRelease(device);
2917
2918 if (!location) {
2919 continue;
2920 }
2921
2922 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2923 if (devinfo->location == location) {
2924 _irecv_handle_device_remove(devinfo);
2925 break;
2926 }
2927 } ENDFOREACH
2928 }
2929}
2930#else /* !HAVE_IOKIT */
2931#ifdef HAVE_LIBUSB_HOTPLUG_API
2932static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
2933{
2934 if (!_irecv_is_recovery_device(device)) {
2935 return 0;
2936 }
2937 if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
2938 THREAD_T th_device;
2939 if (thread_new(&th_device, _irecv_handle_device_add, device) != 0) {
2940 debug("%s: FATAL: failed to create thread to handle device add\n", __func__);
2941 return 0;
2942 }
2943 thread_detach(th_device);
2944 } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
2945 uint8_t bus = libusb_get_bus_number(device);
2946 uint8_t address = libusb_get_device_address(device);
2947 uint32_t location = (bus << 16) | address;
2948 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2949 if (devinfo->location == location) {
2950 _irecv_handle_device_remove(devinfo);
2951 break;
2952 }
2953 } ENDFOREACH
2954 }
2955
2956 return 0;
2957}
2958#endif /* HAVE_LIBUSB_HOTPLUG_API */
2959#endif /* !HAVE_IOKIT */
2960#endif /* !_WIN32 */
2961
2962struct _irecv_event_handler_info {
2963 cond_t startup_cond;
2964 mutex_t startup_mutex;
2965};
2966
2967static void *_irecv_event_handler(void* data)
2968{
2969 struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data;
2970#ifdef _WIN32
2971 struct collection newDevices;
2972 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
2973 int running = 1;
2974
2975 collection_init(&newDevices);
2976
2977 mutex_lock(&(info->startup_mutex));
2978 cond_signal(&(info->startup_cond));
2979 mutex_unlock(&(info->startup_mutex));
2980
2981 do {
2982 SP_DEVICE_INTERFACE_DATA currentInterface;
2983 HDEVINFO usbDevices;
2984 DWORD i;
2985 int k;
2986
2987 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2988 devinfo->alive = 0;
2989 } ENDFOREACH
2990
2991 for (k = 0; guids[k]; k++) {
2992 usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
2993 if (!usbDevices) {
2994 debug("%s: ERROR: SetupDiGetClassDevs failed\n", __func__);
2995 // cleanup/free newDevices
2996 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
2997 free(win_ctx->details);
2998 collection_remove(&newDevices, win_ctx);
2999 free(win_ctx);
3000 } ENDFOREACH
3001 collection_free(&newDevices);
3002 return NULL;
3003 }
3004
3005
3006 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
3007 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3008 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
3009 DWORD requiredSize = 0;
3010 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
3011 SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
3012 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
3013 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
3014 SP_DEVINFO_DATA devinfodata;
3015 devinfodata.cbSize = sizeof(SP_DEVINFO_DATA);
3016 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, &devinfodata)) {
3017 free(details);
3018 continue;
3019 }
3020
3021 DWORD sz = REG_SZ;
3022 char driver[256];
3023 driver[0] = '\0';
3024 if (!SetupDiGetDeviceRegistryPropertyA(usbDevices, &devinfodata, SPDRP_DRIVER, &sz, (PBYTE)driver, sizeof(driver), NULL)) {
3025 debug("%s: ERROR: Failed to get driver key\n", __func__);
3026 free(details);
3027 continue;
3028 }
3029
3030 char *p = strrchr(driver, '\\');
3031 if (!p) {
3032 debug("%s: ERROR: Failed to parse device location\n", __func__);
3033 free(details);
3034 continue;
3035 }
3036 p++;
3037 uint32_t location = 0;
3038 if (!*p || strlen(p) < 4) {
3039 debug("%s: ERROR: Driver location suffix too short\n", __func__);
3040 free(details);
3041 continue;
3042 }
3043 memcpy(&location, p, 4);
3044 int found = 0;
3045
3046 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3047 if (devinfo->location == location) {
3048 devinfo->alive = 1;
3049 found = 1;
3050 break;
3051 }
3052 } ENDFOREACH
3053
3054 unsigned int pid = 0;
3055 unsigned int vid = 0;
3056 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid)!= 2) {
3057 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
3058 free(details);
3059 continue;
3060 }
3061 if (vid != APPLE_VENDOR_ID) {
3062 free(details);
3063 continue;
3064 }
3065
3066 // make sure the current device is actually in the right mode for the given driver interface
3067 int skip = 0;
3068 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
3069 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
3070 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
3071 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
3072 ) {
3073 skip = 1;
3074 }
3075
3076 if (!found && !skip) {
3077 // Add device to newDevices list, and deliver the notification later, when removed devices are first handled.
3078 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)malloc(sizeof(struct irecv_win_dev_ctx));
3079 win_ctx->details = details;
3080 win_ctx->location = location;
3081 collection_add(&newDevices, win_ctx);
3082 details = NULL;
3083 }
3084 free(details);
3085 }
3086 SetupDiDestroyDeviceInfoList(usbDevices);
3087 }
3088
3089 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3090 if (!devinfo->alive) {
3091 debug("%s: removed ecid: %016" PRIx64 ", location: %d\n",__func__, (uint64_t)devinfo->device_info.ecid, devinfo->location);
3092 _irecv_handle_device_remove(devinfo);
3093 }
3094 } ENDFOREACH
3095
3096 // handle newly added devices and remove from local list
3097 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
3098 debug("%s: found new: %s, location: %d\n", __func__, win_ctx->details->DevicePath, win_ctx->location);
3099 _irecv_handle_device_add(win_ctx);
3100 free(win_ctx->details);
3101 collection_remove(&newDevices, win_ctx);
3102 free(win_ctx);
3103 } ENDFOREACH
3104
3105 Sleep(500);
3106 mutex_lock(&listener_mutex);
3107 if (collection_count(&listeners) == 0) {
3108 running = 0;
3109 }
3110 mutex_unlock(&listener_mutex);
3111 } while (running);
3112
3113 collection_free(&newDevices);
3114#else /* !_WIN32 */
3115#ifdef HAVE_IOKIT
3116 kern_return_t kr;
3117
3118 IONotificationPortRef notifyPort = IONotificationPortCreate(MACH_PORT_NULL);
3119 CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notifyPort);
3120 iokit_runloop = CFRunLoopGetCurrent();
3121 CFRunLoopAddSource(iokit_runloop, runLoopSource, kCFRunLoopDefaultMode);
3122
3123 uint16_t pids[9] = { IRECV_K_WTF_MODE, IRECV_K_DFU_MODE, IRECV_K_RECOVERY_MODE_1, IRECV_K_RECOVERY_MODE_2, IRECV_K_RECOVERY_MODE_3, IRECV_K_RECOVERY_MODE_4, IRECV_K_PORT_DFU_MODE, KIS_PRODUCT_ID, 0 };
3124 int i = 0;
3125 while (pids[i] > 0) {
3126 CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
3127 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
3128 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pids[i]);
3129
3130 matchingDict = (CFMutableDictionaryRef)CFRetain(matchingDict);
3131
3132 io_iterator_t devAddedIter;
3133 kr = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, iokit_device_added, NULL, &devAddedIter);
3134 if (kr != kIOReturnSuccess) {
3135 debug("%s: Failed to register device add notification callback\n", __func__);
3136 }
3137 iokit_device_added(NULL, devAddedIter);
3138
3139 io_iterator_t devRemovedIter;
3140 kr = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, iokit_device_removed, NULL, &devRemovedIter);
3141 if (kr != kIOReturnSuccess) {
3142 debug("%s: Failed to register device remove notification callback\n", __func__);
3143 }
3144 iokit_device_removed(NULL, devRemovedIter);
3145
3146 i++;
3147 }
3148
3149 mutex_lock(&(info->startup_mutex));
3150 cond_signal(&(info->startup_cond));
3151 mutex_unlock(&(info->startup_mutex));
3152
3153 CFRunLoopRun();
3154
3155#else /* !HAVE_IOKIT */
3156#ifdef HAVE_LIBUSB_HOTPLUG_API
3157 static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
3158 libusb_hotplug_register_callback(irecv_hotplug_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_ENUMERATE, APPLE_VENDOR_ID, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, _irecv_usb_hotplug_cb, NULL, &usb_hotplug_cb_handle);
3159 int running = 1;
3160
3161 mutex_lock(&(info->startup_mutex));
3162 cond_signal(&(info->startup_cond));
3163 mutex_unlock(&(info->startup_mutex));
3164
3165 do {
3166 struct timeval tv;
3167 tv.tv_sec = tv.tv_usec = 0;
3168 libusb_handle_events_timeout(irecv_hotplug_ctx, &tv);
3169
3170 mutex_lock(&listener_mutex);
3171 if (collection_count(&listeners) == 0) {
3172 running = 0;
3173 }
3174 mutex_unlock(&listener_mutex);
3175
3176 usleep(100000);
3177 } while (running);
3178 libusb_hotplug_deregister_callback(irecv_hotplug_ctx, usb_hotplug_cb_handle);
3179#else /* !HAVE_LIBUSB_HOTPLUG_API */
3180 int i, cnt;
3181 libusb_device **devs;
3182 int running = 1;
3183
3184 mutex_lock(&(info->startup_mutex));
3185 cond_signal(&(info->startup_cond));
3186 mutex_unlock(&(info->startup_mutex));
3187
3188 do {
3189 cnt = libusb_get_device_list(irecv_hotplug_ctx, &devs);
3190 if (cnt < 0) {
3191 debug("%s: FATAL: Failed to get device list: %s\n", __func__, libusb_error_name(cnt));
3192 return NULL;
3193 }
3194
3195 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3196 devinfo->alive = 0;
3197 } ENDFOREACH
3198
3199 for (i = 0; i < cnt; i++) {
3200 libusb_device *dev = devs[i];
3201 if (!_irecv_is_recovery_device(dev)) {
3202 continue;
3203 }
3204 uint8_t bus = libusb_get_bus_number(dev);
3205 uint8_t address = libusb_get_device_address(dev);
3206 uint32_t location = (bus << 16) | address;
3207 int found = 0;
3208 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3209 if (devinfo->location == location) {
3210 devinfo->alive = 1;
3211 found = 1;
3212 break;
3213 }
3214 } ENDFOREACH
3215 if (!found) {
3216 _irecv_handle_device_add(dev);
3217 }
3218 }
3219
3220 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3221 if (!devinfo->alive) {
3222 _irecv_handle_device_remove(devinfo);
3223 }
3224 } ENDFOREACH
3225
3226 libusb_free_device_list(devs, 1);
3227
3228 mutex_lock(&listener_mutex);
3229 if (collection_count(&listeners) == 0) {
3230 running = 0;
3231 }
3232 mutex_unlock(&listener_mutex);
3233 if (!running)
3234 break;
3235 usleep(500000);
3236 } while (running);
3237#endif /* !HAVE_LIBUSB_HOTPLUG_API */
3238#endif /* !HAVE_IOKIT */
3239#endif /* !_WIN32 */
3240 return NULL;
3241}
3242#endif /* !USE_DUMMY */
3243
3244irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data)
3245{
3246#ifdef USE_DUMMY
3247 return IRECV_E_UNSUPPORTED;
3248#else
3249 if (!context || !callback)
3250 return IRECV_E_INVALID_INPUT;
3251
3252 struct irecv_device_event_context* _context = malloc(sizeof(struct irecv_device_event_context));
3253 if (!_context) {
3254 return IRECV_E_OUT_OF_MEMORY;
3255 }
3256
3257 _context->callback = callback;
3258 _context->user_data = user_data;
3259
3260 mutex_lock(&listener_mutex);
3261 collection_add(&listeners, _context);
3262
3263 if (th_event_handler == THREAD_T_NULL || !thread_alive(th_event_handler)) {
3264 mutex_unlock(&listener_mutex);
3265 struct _irecv_event_handler_info info;
3266 cond_init(&info.startup_cond);
3267 mutex_init(&info.startup_mutex);
3268#ifndef _WIN32
3269#ifndef HAVE_IOKIT
3270 libusb_init(&irecv_hotplug_ctx);
3271#endif
3272#endif
3273 collection_init(&devices);
3274 mutex_init(&device_mutex);
3275 mutex_lock(&info.startup_mutex);
3276 if (thread_new(&th_event_handler, _irecv_event_handler, &info) == 0) {
3277 cond_wait(&info.startup_cond, &info.startup_mutex);
3278 }
3279 mutex_unlock(&info.startup_mutex);
3280 cond_destroy(&info.startup_cond);
3281 mutex_destroy(&info.startup_mutex);
3282 } else {
3283 /* send DEVICE_ADD events to the new listener */
3284 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3285 if (devinfo && devinfo->alive) {
3286 irecv_device_event_t ev;
3287 ev.type = IRECV_DEVICE_ADD;
3288 ev.mode = devinfo->mode;
3289 ev.device_info = &(devinfo->device_info);
3290 _context->callback(&ev, _context->user_data);
3291 }
3292 } ENDFOREACH
3293 mutex_unlock(&listener_mutex);
3294 }
3295
3296 *context = _context;
3297
3298 return IRECV_E_SUCCESS;
3299#endif
3300}
3301
3302irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context)
3303{
3304#ifdef USE_DUMMY
3305 return IRECV_E_UNSUPPORTED;
3306#else
3307 if (!context)
3308 return IRECV_E_INVALID_INPUT;
3309
3310 mutex_lock(&listener_mutex);
3311 collection_remove(&listeners, context);
3312 int num = collection_count(&listeners);
3313 mutex_unlock(&listener_mutex);
3314
3315 if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) {
3316#ifdef HAVE_IOKIT
3317 if (iokit_runloop) {
3318 while (!CFRunLoopIsWaiting(iokit_runloop)) usleep(420);
3319 CFRunLoopStop(iokit_runloop);
3320 iokit_runloop = NULL;
3321 }
3322#endif
3323 thread_join(th_event_handler);
3324 thread_free(th_event_handler);
3325 th_event_handler = THREAD_T_NULL;
3326 mutex_lock(&device_mutex);
3327 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3328 free(devinfo->device_info.srnm);
3329 devinfo->device_info.srnm = NULL;
3330 free(devinfo->device_info.imei);
3331 devinfo->device_info.imei = NULL;
3332 free(devinfo->device_info.srtg);
3333 devinfo->device_info.srtg = NULL;
3334 free(devinfo->device_info.serial_string);
3335 devinfo->device_info.serial_string = NULL;
3336 free(devinfo);
3337 } ENDFOREACH
3338 collection_free(&devices);
3339 mutex_unlock(&device_mutex);
3340 mutex_destroy(&device_mutex);
3341#ifndef _WIN32
3342#ifndef HAVE_IOKIT
3343 libusb_exit(irecv_hotplug_ctx);
3344 irecv_hotplug_ctx = NULL;
3345#endif
3346#endif
3347 }
3348
3349 free(context);
3350
3351 return IRECV_E_SUCCESS;
3352#endif
218} 3353}
219 3354
220irecv_error_t irecv_close(irecv_client_t client) { 3355static irecv_error_t irecv_cleanup(irecv_client_t client)
3356{
3357#ifdef USE_DUMMY
3358 return IRECV_E_UNSUPPORTED;
3359#else
221 if (client != NULL) { 3360 if (client != NULL) {
222 if(client->disconnected_callback != NULL) { 3361 if (client->disconnected_callback != NULL) {
223 irecv_event_t event; 3362 irecv_event_t event;
224 event.size = 0; 3363 event.size = 0;
225 event.data = NULL; 3364 event.data = NULL;
@@ -227,114 +3366,205 @@ irecv_error_t irecv_close(irecv_client_t client) {
227 event.type = IRECV_DISCONNECTED; 3366 event.type = IRECV_DISCONNECTED;
228 client->disconnected_callback(client, &event); 3367 client->disconnected_callback(client, &event);
229 } 3368 }
230 3369#ifndef _WIN32
3370#ifdef HAVE_IOKIT
3371 if (client->usbInterface) {
3372 (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
3373 (*client->usbInterface)->Release(client->usbInterface);
3374 client->usbInterface = NULL;
3375 }
3376 if (client->handle) {
3377 (*client->handle)->USBDeviceClose(client->handle);
3378 (*client->handle)->Release(client->handle);
3379 client->handle = NULL;
3380 }
3381 if(client->async_event_source) {
3382 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), client->async_event_source, kCFRunLoopDefaultMode);
3383 CFRelease(client->async_event_source);
3384 }
3385#else
231 if (client->handle != NULL) { 3386 if (client->handle != NULL) {
232 libusb_release_interface(client->handle, client->interface); 3387 if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE) && (client->isKIS == 0)) {
3388 libusb_release_interface(client->handle, client->usb_interface);
3389 }
233 libusb_close(client->handle); 3390 libusb_close(client->handle);
234 client->handle = NULL; 3391 client->handle = NULL;
235 } 3392 }
3393#endif
3394#else
3395 CloseHandle(client->handle);
3396#endif
3397 free(client->device_info.srnm);
3398 free(client->device_info.imei);
3399 free(client->device_info.srtg);
3400 free(client->device_info.serial_string);
3401 free(client->device_info.ap_nonce);
3402 free(client->device_info.sep_nonce);
3403 }
236 3404
237 if (libirecovery_context != NULL) { 3405 return IRECV_E_SUCCESS;
238 libusb_exit(libirecovery_context); 3406#endif
239 libirecovery_context = NULL; 3407}
240 }
241 3408
3409irecv_error_t irecv_close(irecv_client_t client)
3410{
3411 irecv_error_t ret = IRECV_E_SUCCESS;
3412 if (client) {
3413 ret = irecv_cleanup(client);
242 free(client); 3414 free(client);
243 client = NULL; 3415 client = NULL;
244 } 3416 }
245 3417 return ret;
246 return IRECV_E_SUCCESS;
247} 3418}
248 3419
249void irecv_set_debug_level(int level) { 3420void irecv_set_debug_level(int level)
3421{
250 libirecovery_debug = level; 3422 libirecovery_debug = level;
251 if(libirecovery_context) { 3423#ifndef USE_DUMMY
252 libusb_set_debug(libirecovery_context, libirecovery_debug); 3424#ifndef _WIN32
3425#ifndef HAVE_IOKIT
3426 if (libirecovery_context) {
3427#if LIBUSB_API_VERSION >= 0x01000106
3428 libusb_set_option(libirecovery_context, LIBUSB_OPTION_LOG_LEVEL, libirecovery_debug > 2 ? 1: 0);
3429#else
3430 libusb_set_debug(libirecovery_context, libirecovery_debug > 2 ? 1: 0);
3431#endif
253 } 3432 }
3433#endif
3434#endif
3435#endif
254} 3436}
255 3437
256static irecv_error_t irecv_send_command_raw(irecv_client_t client, unsigned char* command) { 3438const char* irecv_version()
3439{
3440#ifndef PACKAGE_VERSION
3441#error PACKAGE_VERSION is not defined!
3442#endif
3443 return PACKAGE_VERSION;
3444}
3445
3446
3447#ifndef USE_DUMMY
3448static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* command, uint8_t b_request)
3449{
257 unsigned int length = strlen(command); 3450 unsigned int length = strlen(command);
258 if (length >= 0x100) { 3451 if (length >= 0x100) {
259 length = 0xFF; 3452 return IRECV_E_INVALID_INPUT;
260 } 3453 }
261 3454
262 if (length > 0) { 3455 if (length > 0 && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
263 int ret = libusb_control_transfer(client->handle, 0x40, 0, 0, 0, command, length + 1, 100); 3456 int bytes = 0;
264 if ((ret < 0) || (ret != (length + 1))) { 3457 irecv_error_t error = 0;
265 if (ret == LIBUSB_ERROR_PIPE) 3458#ifdef DEBUG
266 return IRECV_E_PIPE; 3459 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
267 if (ret == LIBUSB_ERROR_TIMEOUT) 3460 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
268 return IRECV_E_TIMEOUT; 3461 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
269 return IRECV_E_UNKNOWN_ERROR; 3462 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3463#endif
3464 char cmdstr[0x100] = {0};
3465 if (length & 0xf) {
3466 length &= ~0xf;
3467 length += 0x10;
270 } 3468 }
3469 snprintf(cmdstr, sizeof(cmdstr), "%s\n",command);
3470 legacyCMD cmd = {
3471 MSG_SEND_COMMAND,
3472 0x1234, //magic
3473 (uint32_t)length, //zero terminated?
3474 0x0
3475 };
3476 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3477 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3478 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3479 if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error;
3480 sleep(1); //go easy on this old device
3481 return IRECV_E_SUCCESS;
3482 }
3483
3484 if (length > 0) {
3485 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
271 } 3486 }
272 3487
273 return IRECV_E_SUCCESS; 3488 return IRECV_E_SUCCESS;
274} 3489}
3490#endif
275 3491
276irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command) { 3492irecv_error_t irecv_send_command_breq(irecv_client_t client, const char* command, uint8_t b_request)
3493{
3494#ifdef USE_DUMMY
3495 return IRECV_E_UNSUPPORTED;
3496#else
277 irecv_error_t error = 0; 3497 irecv_error_t error = 0;
278 3498
279 if (client == NULL || client->handle == NULL) { 3499 if (check_context(client) != IRECV_E_SUCCESS)
280 return IRECV_E_NO_DEVICE; 3500 return IRECV_E_NO_DEVICE;
281 }
282 3501
283 unsigned int length = strlen(command); 3502 unsigned int length = strlen(command);
284 if (length >= 0x100) { 3503 if (length >= 0x100) {
285 length = 0xFF; 3504 return IRECV_E_INVALID_INPUT;
286 } 3505 }
287 3506
288 irecv_event_t event; 3507 irecv_event_t event;
289 if(client->precommand_callback != NULL) { 3508 if (client->precommand_callback != NULL) {
290 event.size = length; 3509 event.size = length;
291 event.data = command; 3510 event.data = command;
292 event.type = IRECV_PRECOMMAND; 3511 event.type = IRECV_PRECOMMAND;
293 if(client->precommand_callback(client, &event)) { 3512 if (client->precommand_callback(client, &event)) {
294 return IRECV_E_SUCCESS; 3513 return IRECV_E_SUCCESS;
295 } 3514 }
296 } 3515 }
297 3516
298 error = irecv_send_command_raw(client, command); 3517 error = irecv_send_command_raw(client, command, b_request);
299 if (error != IRECV_E_SUCCESS) { 3518 if (error != IRECV_E_SUCCESS) {
300 debug("Failed to send command %s\n", command); 3519 debug("Failed to send command %s\n", command);
301 if (error != IRECV_E_PIPE) 3520 if (error != IRECV_E_PIPE)
302 return error; 3521 return error;
303 } 3522 }
304 3523
305 if(client->postcommand_callback != NULL) { 3524 if (client->postcommand_callback != NULL) {
306 event.size = length; 3525 event.size = length;
307 event.data = command; 3526 event.data = command;
308 event.type = IRECV_POSTCOMMAND; 3527 event.type = IRECV_POSTCOMMAND;
309 if(client->postcommand_callback(client, &event)) { 3528 if (client->postcommand_callback(client, &event)) {
310 return IRECV_E_SUCCESS; 3529 return IRECV_E_SUCCESS;
311 } 3530 }
312 } 3531 }
313 3532
314 return IRECV_E_SUCCESS; 3533 return IRECV_E_SUCCESS;
3534#endif
315} 3535}
316 3536
317irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) { 3537irecv_error_t irecv_send_command(irecv_client_t client, const char* command)
318 if (client == NULL || client->handle == NULL) { 3538{
3539 return irecv_send_command_breq(client, command, 0);
3540}
3541
3542irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, unsigned int options)
3543{
3544#ifdef USE_DUMMY
3545 return IRECV_E_UNSUPPORTED;
3546#else
3547 if (check_context(client) != IRECV_E_SUCCESS)
319 return IRECV_E_NO_DEVICE; 3548 return IRECV_E_NO_DEVICE;
320 }
321 3549
322 FILE* file = fopen(filename, "rb"); 3550 FILE* file = fopen(filename, "rb");
323 if (file == NULL) { 3551 if (file == NULL) {
324 return IRECV_E_FILE_NOT_FOUND; 3552 return IRECV_E_FILE_NOT_FOUND;
325 } 3553 }
326 3554
327 fseek(file, 0, SEEK_END); 3555 struct stat fst;
328 long length = ftell(file); 3556 if (fstat(fileno(file), &fst) < 0) {
329 fseek(file, 0, SEEK_SET); 3557 return IRECV_E_UNKNOWN_ERROR;
3558 }
3559 size_t length = fst.st_size;
330 3560
331 unsigned char* buffer = (unsigned char*) malloc(length); 3561 char* buffer = (char*)malloc(length);
332 if (buffer == NULL) { 3562 if (buffer == NULL) {
333 fclose(file); 3563 fclose(file);
334 return IRECV_E_OUT_OF_MEMORY; 3564 return IRECV_E_OUT_OF_MEMORY;
335 } 3565 }
336 3566
337 long bytes = fread(buffer, 1, length, file); 3567 size_t bytes = fread(buffer, 1, length, file);
338 fclose(file); 3568 fclose(file);
339 3569
340 if (bytes != length) { 3570 if (bytes != length) {
@@ -342,64 +3572,283 @@ irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) {
342 return IRECV_E_UNKNOWN_ERROR; 3572 return IRECV_E_UNKNOWN_ERROR;
343 } 3573 }
344 3574
345 irecv_error_t error = irecv_send_buffer(client, buffer, length); 3575 irecv_error_t error = irecv_send_buffer(client, (unsigned char*)buffer, length, options);
346 free(buffer); 3576 free(buffer);
3577
347 return error; 3578 return error;
3579#endif
348} 3580}
349 3581
350irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { 3582#ifndef USE_DUMMY
351 if (client == NULL || client->handle == NULL) { 3583static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status)
3584{
3585 if (check_context(client) != IRECV_E_SUCCESS) {
352 *status = 0; 3586 *status = 0;
353 return IRECV_E_NO_DEVICE; 3587 return IRECV_E_NO_DEVICE;
354 } 3588 }
355 3589
356 unsigned char buffer[6]; 3590 unsigned char buffer[6];
357 memset(buffer, '\0', 6); 3591 memset(buffer, '\0', sizeof(buffer));
358 if (libusb_control_transfer(client->handle, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) { 3592 if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, sizeof(buffer), USB_TIMEOUT) != sizeof(buffer)) {
359 *status = 0; 3593 *status = 0;
360 return IRECV_E_USB_STATUS; 3594 return IRECV_E_USB_STATUS;
361 } 3595 }
362 3596
363 *status = (unsigned int) buffer[4]; 3597 *status = (unsigned int) buffer[4];
3598
3599 return IRECV_E_SUCCESS;
3600}
3601
3602static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3603{
3604 if (client->mode != IRECV_K_DFU_MODE) {
3605 return IRECV_E_UNSUPPORTED;
3606 }
3607
3608 unsigned long origLen = length;
3609
3610 KIS_upload_chunk *chunk = calloc(1, sizeof(KIS_upload_chunk));
3611 uint64_t address = 0;
3612 while (length) {
3613 unsigned long toUpload = length;
3614 if (toUpload > 0x4000)
3615 toUpload = 0x4000;
3616
3617#ifdef _WIN32
3618 memcpy(chunk->data, buffer, toUpload);
3619 chunk->size = toUpload;
3620 chunk->address = address;
3621#else
3622 irecv_error_t error = irecv_kis_request_init(&chunk->hdr, KIS_PORTAL_RSM, KIS_INDEX_UPLOAD, 3, toUpload, 0);
3623 if (error != IRECV_E_SUCCESS) {
3624 free(chunk);
3625 debug("Failed to init chunk header, error %d\n", error);
3626 return error;
3627 }
3628
3629 chunk->address = address;
3630 chunk->size = toUpload;
3631 memcpy(chunk->data, buffer, toUpload);
3632#endif
3633
3634#ifdef _WIN32
3635 DWORD transferred = 0;
3636 int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL);
3637 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3638#else
3639 KIS_generic_reply reply;
3640 size_t rcvSize = sizeof(reply);
3641 error = irecv_kis_request(client, &chunk->hdr, sizeof(*chunk) - (0x4000 - toUpload), &reply.hdr, &rcvSize);
3642#endif
3643 if (error != IRECV_E_SUCCESS) {
3644 free(chunk);
3645 debug("Failed to upload chunk, error %d\n", error);
3646 return error;
3647 }
3648
3649 address += toUpload;
3650 buffer += toUpload;
3651 length -= toUpload;
3652
3653 if (client->progress_callback != NULL) {
3654 irecv_event_t event;
3655 event.progress = ((double) (origLen - length) / (double) origLen) * 100.0;
3656 event.type = IRECV_PROGRESS;
3657 event.data = (char*)"Uploading";
3658 event.size = origLen - length;
3659 client->progress_callback(client, &event);
3660 } else {
3661 debug("Sent: %lu bytes - %lu of %lu\n", toUpload, origLen - length, origLen);
3662 }
3663 }
3664 free(chunk);
3665
3666 if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) {
3667#ifdef _WIN32
3668 DWORD amount = (DWORD)origLen;
3669 DWORD transferred = 0;
3670 int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL);
3671 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3672#else
3673 irecv_error_t error = irecv_kis_config_write32(client, KIS_PORTAL_RSM, KIS_INDEX_BOOT_IMG, origLen);
3674#endif
3675 if (error != IRECV_E_SUCCESS) {
3676 debug("Failed to boot image, error %d\n", error);
3677 return error;
3678 }
3679 }
3680
364 return IRECV_E_SUCCESS; 3681 return IRECV_E_SUCCESS;
365} 3682}
3683#endif
3684
3685irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3686{
3687#ifdef USE_DUMMY
3688 return IRECV_E_UNSUPPORTED;
3689#else
3690 if (client->isKIS)
3691 return irecv_kis_send_buffer(client, buffer, length, options);
366 3692
367irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length) {
368 irecv_error_t error = 0; 3693 irecv_error_t error = 0;
369 int recovery_mode = (client->mode != kDfuMode); 3694 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
3695 int legacy_recovery_mode = 0;
3696 int isiOS2 = 0;
3697
3698 if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3699#ifdef DEBUG
3700 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
3701 int bytes = 0;
3702 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
3703 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
3704 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3705#endif
3706 legacy_recovery_mode = 1;
3707 }
370 3708
371 if (client == NULL || client->handle == NULL) { 3709 if (recovery_mode && !legacy_recovery_mode) {
372 return IRECV_E_NO_DEVICE; 3710 // we are in recovery mode and we are not dealing with iOS 1.x
3711 if ((client->device_info.cpid == 0x8900 || client->device_info.cpid == 0x8720) && !client->device_info.have_ibfl) {
3712 // iOS 2.x doesn't have IBFL tag, but iOS 3 does
3713 // Also, to avoid false activation of this codepath, restrict it to the only two CPID which can run iOS 2
3714 recovery_mode = 0; // iOS 2 recovery mode works same as DFU mode
3715 isiOS2 = 1;
3716 options |= IRECV_SEND_OPT_DFU_NOTIFY_FINISH;
3717 }
373 } 3718 }
374 3719
375 int packet_size = recovery_mode ? 0x4000: 0x800; 3720 if (check_context(client) != IRECV_E_SUCCESS)
3721 return IRECV_E_NO_DEVICE;
3722
3723 unsigned int h1 = 0xFFFFFFFF;
3724 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
3725 int dfu_crc = 1;
3726 int packet_size = recovery_mode ? 0x8000 : 0x800;
3727 if (legacy_recovery_mode) packet_size = 0x200;
3728 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
3729 packet_size = 0x40;
3730 dfu_crc = 0;
3731 }
376 int last = length % packet_size; 3732 int last = length % packet_size;
377 int packets = length / packet_size; 3733 int packets = length / packet_size;
3734
378 if (last != 0) { 3735 if (last != 0) {
379 packets++; 3736 packets++;
3737 } else {
3738 last = packet_size;
380 } 3739 }
381 3740
382 /* initiate transfer */ 3741 /* initiate transfer */
383 if (recovery_mode) { 3742 if (legacy_recovery_mode) {
384 error = libusb_control_transfer(client->handle, 0x41, 0, 0, 0, NULL, 0, 1000); 3743 int bytes = 0;
385 if (error != IRECV_E_SUCCESS) { 3744 uint32_t loadaddr0x8900 = 0x09000000;
386 return error; 3745 const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR");
3746 if (ios1_overwrite_loadaddr) {
3747 sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900);
3748 debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900);
3749 }
3750 legacyCMD cmd = {
3751 MSG_SEND_FILE,
3752 0x1234, //magic
3753 (uint32_t)length,
3754 loadaddr0x8900
3755 };
3756 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3757 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3758 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3759 } else if (recovery_mode) {
3760 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
3761 } else {
3762 uint8_t state = 0;
3763 if (irecv_usb_control_transfer(client, 0xa1, 5, 0, 0, (unsigned char*)&state, 1, USB_TIMEOUT) == 1) {
3764 error = IRECV_E_SUCCESS;
3765 } else {
3766 return IRECV_E_USB_UPLOAD;
3767 }
3768 switch (state) {
3769 case 2:
3770 /* DFU IDLE */
3771 break;
3772 case 8:
3773 /* DFU WAIT RESET */
3774 if (!isiOS2) {
3775 debug("Unexpected state %d in non-iOS2 mode!, issuing ABORT\n", state);
3776 irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT);
3777 error = IRECV_E_USB_UPLOAD;
3778 }
3779 break;
3780 case 10:
3781 debug("DFU ERROR, issuing CLRSTATUS\n");
3782 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT);
3783 error = IRECV_E_USB_UPLOAD;
3784 break;
3785 default:
3786 debug("Unexpected state %d, issuing ABORT\n", state);
3787 irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT);
3788 error = IRECV_E_USB_UPLOAD;
3789 break;
387 } 3790 }
388 } 3791 }
389 3792
3793 if (error != IRECV_E_SUCCESS) {
3794 return error;
3795 }
3796
390 int i = 0; 3797 int i = 0;
391 double progress = 0;
392 unsigned long count = 0; 3798 unsigned long count = 0;
393 unsigned int status = 0; 3799 unsigned int status = 0;
394 int bytes = 0; 3800 int bytes = 0;
395 for (i = 0; i < packets; i++) { 3801 for (i = 0; i < packets; i++) {
396 int size = (i + 1) < packets ? packet_size : last; 3802 int size = (i + 1) < packets ? packet_size : last;
397 3803
398 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ 3804 if (legacy_recovery_mode) {
399 if (recovery_mode) { 3805 // Use interrupt transfer for legacy devices
400 error = libusb_bulk_transfer(client->handle, 0x04, &buffer[i * packet_size], size, &bytes, 1000); 3806 error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
3807 } else if (recovery_mode) {
3808 // Use bulk transfer for recovery mode
3809 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
401 } else { 3810 } else {
402 bytes = libusb_control_transfer(client->handle, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000); 3811 // Use control transfer for DFU and WTF mode
3812 if (dfu_crc) {
3813 int j;
3814 for (j = 0; j < size; j++) {
3815 crc32_step(h1, buffer[i*packet_size + j]);
3816 }
3817 }
3818 if (dfu_crc && i+1 == packets) {
3819 int j;
3820 if (size+16 > packet_size) {
3821 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3822 if (bytes != size) {
3823 return IRECV_E_USB_UPLOAD;
3824 }
3825 count += size;
3826 size = 0;
3827 }
3828 for (j = 0; j < 2; j++) {
3829 crc32_step(h1, dfu_xbuf[j*6 + 0]);
3830 crc32_step(h1, dfu_xbuf[j*6 + 1]);
3831 crc32_step(h1, dfu_xbuf[j*6 + 2]);
3832 crc32_step(h1, dfu_xbuf[j*6 + 3]);
3833 crc32_step(h1, dfu_xbuf[j*6 + 4]);
3834 crc32_step(h1, dfu_xbuf[j*6 + 5]);
3835 }
3836
3837 char* newbuf = (char*)malloc(size + 16);
3838 if (size > 0) {
3839 memcpy(newbuf, &buffer[i * packet_size], size);
3840 }
3841 memcpy(newbuf+size, dfu_xbuf, 12);
3842 newbuf[size+12] = h1 & 0xFF;
3843 newbuf[size+13] = (h1 >> 8) & 0xFF;
3844 newbuf[size+14] = (h1 >> 16) & 0xFF;
3845 newbuf[size+15] = (h1 >> 24) & 0xFF;
3846 size += 16;
3847 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, (unsigned char*)newbuf, size, USB_TIMEOUT);
3848 free(newbuf);
3849 } else {
3850 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3851 }
403 } 3852 }
404 3853
405 if (bytes != size) { 3854 if (bytes != size) {
@@ -415,44 +3864,92 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
415 } 3864 }
416 3865
417 if (!recovery_mode && status != 5) { 3866 if (!recovery_mode && status != 5) {
418 return IRECV_E_USB_UPLOAD; 3867 int retry = 0;
3868
3869 while (retry++ < 20) {
3870 irecv_get_status(client, &status);
3871 if (status == 5) {
3872 break;
3873 }
3874 sleep(1);
3875 }
3876
3877 if (status != 5) {
3878 return IRECV_E_USB_UPLOAD;
3879 }
419 } 3880 }
420 3881
421 count += size; 3882 count += size;
422 if(client->progress_callback != NULL) { 3883 if (client->progress_callback != NULL) {
423 irecv_event_t event; 3884 irecv_event_t event;
424 event.progress = ((double) count/ (double) length) * 100.0; 3885 event.progress = ((double) count/ (double) length) * 100.0;
425 event.type = IRECV_PROGRESS; 3886 event.type = IRECV_PROGRESS;
426 event.data = "Uploading"; 3887 event.data = (char*)"Uploading";
427 event.size = count; 3888 event.size = count;
428 client->progress_callback(client, &event); 3889 client->progress_callback(client, &event);
429 } else { 3890 } else {
430 debug("Sent: %d bytes - %d of %d\n", bytes, count, length); 3891 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
431 } 3892 }
432 } 3893 }
433 3894
434 if (!recovery_mode) { 3895 if (recovery_mode && length % 512 == 0) {
435 libusb_control_transfer(client->handle, 0x21, 1, 0, 0, buffer, 0, 1000); 3896 /* send a ZLP */
436 for (i = 0; i < 3; i++) { 3897 bytes = 0;
3898 irecv_usb_bulk_transfer(client, 0x04, buffer, 0, &bytes, USB_TIMEOUT);
3899 }
3900
3901 if ((options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) && !recovery_mode) {
3902 irecv_usb_control_transfer(client, 0x21, 1, packets, 0, (unsigned char*) buffer, 0, USB_TIMEOUT);
3903
3904 for (i = 0; i < 2; i++) {
437 error = irecv_get_status(client, &status); 3905 error = irecv_get_status(client, &status);
438 if (error != IRECV_E_SUCCESS) { 3906 if (error != IRECV_E_SUCCESS) {
439 return error; 3907 return error;
440 } 3908 }
441 } 3909 }
3910
3911 if ((options & IRECV_SEND_OPT_DFU_FORCE_ZLP)) {
3912 /* we send a pseudo ZLP here just in case */
3913 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
3914 }
3915
3916 irecv_reset(client);
3917
3918 if (isiOS2) {
3919 irecv_reconnect(client, 0);
3920 }
3921 }
3922
3923 if (legacy_recovery_mode) {
3924 irecv_reconnect(client, 0);
3925 char cmdstr[0x100] = {0};
3926 snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length);
3927 irecv_send_command(client, cmdstr);
442 } 3928 }
443 3929
444 return IRECV_E_SUCCESS; 3930 return IRECV_E_SUCCESS;
3931#endif
445} 3932}
446 3933
447irecv_error_t irecv_receive(irecv_client_t client) { 3934irecv_error_t irecv_receive(irecv_client_t client)
448 unsigned char buffer[BUFFER_SIZE]; 3935{
3936#ifdef USE_DUMMY
3937 return IRECV_E_UNSUPPORTED;
3938#else
3939 char buffer[BUFFER_SIZE];
449 memset(buffer, '\0', BUFFER_SIZE); 3940 memset(buffer, '\0', BUFFER_SIZE);
450 if (client == NULL || client->handle == NULL) { 3941
3942 if (check_context(client) != IRECV_E_SUCCESS)
451 return IRECV_E_NO_DEVICE; 3943 return IRECV_E_NO_DEVICE;
452 }
453 3944
454 int bytes = 0; 3945 int bytes = 0;
455 while (libusb_bulk_transfer(client->handle, 0x81, buffer, BUFFER_SIZE, &bytes, 100) == 0) { 3946 while (1) {
3947 irecv_usb_set_interface(client, 1, 1);
3948 int r = irecv_usb_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 500);
3949 irecv_usb_set_interface(client, 0, 0);
3950 if (r != 0) {
3951 break;
3952 }
456 if (bytes > 0) { 3953 if (bytes > 0) {
457 if (client->received_callback != NULL) { 3954 if (client->received_callback != NULL) {
458 irecv_event_t event; 3955 irecv_event_t event;
@@ -460,155 +3957,295 @@ irecv_error_t irecv_receive(irecv_client_t client) {
460 event.data = buffer; 3957 event.data = buffer;
461 event.type = IRECV_RECEIVED; 3958 event.type = IRECV_RECEIVED;
462 if (client->received_callback(client, &event) != 0) { 3959 if (client->received_callback(client, &event) != 0) {
463 return IRECV_E_SUCCESS; 3960 break;
464 } 3961 }
465 } 3962 }
466 } else break; 3963 } else break;
467 } 3964 }
468
469 return IRECV_E_SUCCESS; 3965 return IRECV_E_SUCCESS;
3966#endif
470} 3967}
471 3968
472irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { 3969irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value)
3970{
3971#ifdef USE_DUMMY
3972 return IRECV_E_UNSUPPORTED;
3973#else
473 char command[256]; 3974 char command[256];
474 if (client == NULL || client->handle == NULL) { 3975
3976 if (check_context(client) != IRECV_E_SUCCESS)
475 return IRECV_E_NO_DEVICE; 3977 return IRECV_E_NO_DEVICE;
476 }
477 3978
478 *value = NULL; 3979 *value = NULL;
479 3980
480 if(variable == NULL) { 3981 if (variable == NULL) {
481 return IRECV_E_UNKNOWN_ERROR; 3982 return IRECV_E_INVALID_INPUT;
3983 }
3984
3985 if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3986 debug("iOS 1 doesn't support getenv\n");
3987 return IRECV_E_UNSUPPORTED;
482 } 3988 }
483 3989
484 memset(command, '\0', sizeof(command)); 3990 memset(command, '\0', sizeof(command));
485 snprintf(command, sizeof(command)-1, "getenv %s", variable); 3991 snprintf(command, sizeof(command)-1, "getenv %s", variable);
486 irecv_error_t error = irecv_send_command_raw(client, command); 3992 irecv_error_t error = irecv_send_command_raw(client, command, 0);
487 if(error == IRECV_E_PIPE) 3993 if (error == IRECV_E_PIPE) {
488 return IRECV_E_SUCCESS; 3994 return IRECV_E_SUCCESS;
489 if(error != IRECV_E_SUCCESS) 3995 }
3996
3997 if (error != IRECV_E_SUCCESS) {
490 return error; 3998 return error;
3999 }
491 4000
492 unsigned char* response = (unsigned char*) malloc(256); 4001 int rsize = 256;
4002 char* response = (char*) malloc(rsize);
493 if (response == NULL) { 4003 if (response == NULL) {
494 return IRECV_E_OUT_OF_MEMORY; 4004 return IRECV_E_OUT_OF_MEMORY;
495 } 4005 }
496 4006
497 memset(response, '\0', 256); 4007 memset(response, '\0', rsize);
498 int ret = libusb_control_transfer(client->handle, 0xC0, 0, 0, 0, response, 255, 500); 4008 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
499 if (ret < 0)
500 return IRECV_E_UNKNOWN_ERROR;
501 4009
502 *value = response; 4010 *value = response;
4011
503 return IRECV_E_SUCCESS; 4012 return IRECV_E_SUCCESS;
4013#endif
504} 4014}
505 4015
506irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { 4016irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value)
507 if (client == NULL || client->handle == NULL) { 4017{
4018#ifdef USE_DUMMY
4019 return IRECV_E_UNSUPPORTED;
4020#else
4021 if (check_context(client) != IRECV_E_SUCCESS)
508 return IRECV_E_NO_DEVICE; 4022 return IRECV_E_NO_DEVICE;
509 }
510 4023
511 unsigned char* cpid_string = strstr(client->serial, "CPID:"); 4024 *value = 0;
512 if (cpid_string == NULL) { 4025
513 *cpid = 0; 4026 int rsize = 256;
514 return IRECV_E_UNKNOWN_ERROR; 4027 char* response = (char*) malloc(rsize);
4028 if (response == NULL) {
4029 return IRECV_E_OUT_OF_MEMORY;
515 } 4030 }
516 sscanf(cpid_string, "CPID:%d", cpid); 4031
4032 memset(response, '\0', rsize);
4033 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
4034
4035 *value = (unsigned int) *response;
517 4036
518 return IRECV_E_SUCCESS; 4037 return IRECV_E_SUCCESS;
4038#endif
519} 4039}
520 4040
521irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) { 4041irecv_error_t irecv_get_mode(irecv_client_t client, int* mode)
522 if (client == NULL || client->handle == NULL) { 4042{
4043#ifdef USE_DUMMY
4044 return IRECV_E_UNSUPPORTED;
4045#else
4046 if (check_context(client) != IRECV_E_SUCCESS)
523 return IRECV_E_NO_DEVICE; 4047 return IRECV_E_NO_DEVICE;
524 }
525 4048
526 unsigned char* bdid_string = strstr(client->serial, "BDID:"); 4049 *mode = client->mode;
527 if (bdid_string == NULL) {
528 *bdid = 0;
529 return IRECV_E_UNKNOWN_ERROR;
530 }
531 sscanf(bdid_string, "BDID:%d", bdid);
532 4050
533 return IRECV_E_SUCCESS; 4051 return IRECV_E_SUCCESS;
4052#endif
534} 4053}
535 4054
536irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { 4055const struct irecv_device_info* irecv_get_device_info(irecv_client_t client)
537 if (client == NULL || client->handle == NULL) { 4056{
538 return IRECV_E_NO_DEVICE; 4057#ifdef USE_DUMMY
539 } 4058 return NULL;
540 4059#else
541 unsigned char* ecid_string = strstr(client->serial, "ECID:"); 4060 if (check_context(client) != IRECV_E_SUCCESS)
542 if (ecid_string == NULL) { 4061 return NULL;
543 *ecid = 0;
544 return IRECV_E_UNKNOWN_ERROR;
545 }
546 sscanf(ecid_string, "ECID:%qX", ecid);
547 4062
548 return IRECV_E_SUCCESS; 4063 return &client->device_info;
4064#endif
549} 4065}
550 4066
551irecv_error_t irecv_send_exploit(irecv_client_t client) { 4067#ifndef USE_DUMMY
552 if (client == NULL || client->handle == NULL) { 4068#ifdef HAVE_IOKIT
4069static void *iokit_limera1n_usb_submit_request(void *argv)
4070{
4071 void **args = argv;
4072 IOUSBDeviceInterface320 **dev = args[0];
4073 IOUSBDevRequest *req = args[1];
4074
4075 IOReturn result = (*dev)->DeviceRequest(dev, req);
4076 if (result != kIOReturnSuccess)
4077 debug("%s result: %#x\n", __func__, result);
4078
4079 return NULL;
4080}
4081#endif
4082#endif
4083
4084irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client)
4085{
4086#ifdef USE_DUMMY
4087 return IRECV_E_UNSUPPORTED;
4088#else
4089 if (check_context(client) != IRECV_E_SUCCESS)
553 return IRECV_E_NO_DEVICE; 4090 return IRECV_E_NO_DEVICE;
4091
4092#ifdef HAVE_IOKIT
4093 IOReturn result;
4094 IOUSBDevRequestTO req;
4095 bzero(&req, sizeof(req));
4096
4097 req.bmRequestType = 0x21;
4098 req.bRequest = 2;
4099 req.wValue = 0;
4100 req.wIndex = 0;
4101 req.wLength = 0;
4102 req.pData = NULL;
4103 req.noDataTimeout = USB_TIMEOUT;
4104 req.completionTimeout = USB_TIMEOUT;
4105
4106 // The original version uses an async request, but we don't have an async event
4107 // source set up. The hack relies on aborting the transaction before it times out,
4108 // which can be accomplished by sending on another thread.
4109
4110 void *args[2] = { client->handle, &req };
4111 THREAD_T thread;
4112 thread_new(&thread, iokit_limera1n_usb_submit_request, args);
4113
4114 usleep(5 * 1000);
4115 result = (*client->handle)->USBDeviceAbortPipeZero(client->handle);
4116 if (result != kIOReturnSuccess)
4117 debug("USBDeviceAbortPipeZero returned %#x\n", result);
4118
4119 switch (result) {
4120 case kIOReturnSuccess: return req.wLenDone;
4121 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
4122 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
4123 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
4124 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
4125 default:
4126 return IRECV_E_UNKNOWN_ERROR;
554 } 4127 }
4128#else
4129 irecv_usb_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, USB_TIMEOUT);
4130#endif
555 4131
556 libusb_control_transfer(client->handle, 0x21, 2, 0, 0, NULL, 0, 100);
557 return IRECV_E_SUCCESS; 4132 return IRECV_E_SUCCESS;
4133#endif
558} 4134}
559 4135
560irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) { 4136irecv_error_t irecv_execute_script(irecv_client_t client, const char* script)
4137{
4138#ifdef USE_DUMMY
4139 return IRECV_E_UNSUPPORTED;
4140#else
561 irecv_error_t error = IRECV_E_SUCCESS; 4141 irecv_error_t error = IRECV_E_SUCCESS;
562 if (client == NULL || client->handle == NULL) { 4142 if (check_context(client) != IRECV_E_SUCCESS)
563 return IRECV_E_NO_DEVICE; 4143 return IRECV_E_NO_DEVICE;
564 }
565 4144
566 int file_size = 0; 4145 char* body = strdup(script);
567 char* file_data = NULL; 4146 char* line = strtok(body, "\n");
568 if(irecv_read_file(filename, &file_data, &file_size) < 0) {
569 return IRECV_E_FILE_NOT_FOUND;
570 }
571 4147
572 char* line = strtok(file_data, "\n"); 4148 while (line != NULL) {
573 while(line != NULL) { 4149 if (line[0] != '#') {
574 if(line[0] != '#') {
575 error = irecv_send_command(client, line); 4150 error = irecv_send_command(client, line);
576 if(error != IRECV_E_SUCCESS) { 4151 if (error != IRECV_E_SUCCESS) {
577 return error; 4152 break;
578 } 4153 }
579 4154
580 error = irecv_receive(client); 4155 error = irecv_receive(client);
581 if(error != IRECV_E_SUCCESS) { 4156 if (error != IRECV_E_SUCCESS) {
582 return error; 4157 break;
583 } 4158 }
584 } 4159 }
585 line = strtok(NULL, "\n"); 4160 line = strtok(NULL, "\n");
586 } 4161 }
587 4162
4163 free(body);
4164
4165 return error;
4166#endif
4167}
4168
4169irecv_error_t irecv_saveenv(irecv_client_t client)
4170{
4171#ifdef USE_DUMMY
4172 return IRECV_E_UNSUPPORTED;
4173#else
4174 irecv_error_t error = irecv_send_command_raw(client, "saveenv", 0);
4175 if (error != IRECV_E_SUCCESS) {
4176 return error;
4177 }
4178
588 return IRECV_E_SUCCESS; 4179 return IRECV_E_SUCCESS;
4180#endif
589} 4181}
590 4182
591irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { 4183irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value)
4184{
4185#ifdef USE_DUMMY
4186 return IRECV_E_UNSUPPORTED;
4187#else
592 char command[256]; 4188 char command[256];
593 if (client == NULL || client->handle == NULL) { 4189
4190 if (check_context(client) != IRECV_E_SUCCESS)
594 return IRECV_E_NO_DEVICE; 4191 return IRECV_E_NO_DEVICE;
595 }
596 4192
597 if(variable == NULL || value == NULL) { 4193 if (variable == NULL || value == NULL) {
598 return IRECV_E_UNKNOWN_ERROR; 4194 return IRECV_E_UNKNOWN_ERROR;
599 } 4195 }
600 4196
601 memset(command, '\0', sizeof(command)); 4197 memset(command, '\0', sizeof(command));
602 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); 4198 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value);
603 irecv_error_t error = irecv_send_command_raw(client, command); 4199 irecv_error_t error = irecv_send_command_raw(client, command, 0);
604 if(error != IRECV_E_SUCCESS) { 4200 if (error != IRECV_E_SUCCESS) {
605 return error; 4201 return error;
606 } 4202 }
607 4203
608 return IRECV_E_SUCCESS; 4204 return IRECV_E_SUCCESS;
4205#endif
609} 4206}
610 4207
611const char* irecv_strerror(irecv_error_t error) { 4208irecv_error_t irecv_setenv_np(irecv_client_t client, const char* variable, const char* value)
4209{
4210#ifdef USE_DUMMY
4211 return IRECV_E_UNSUPPORTED;
4212#else
4213 char command[256];
4214
4215 if (check_context(client) != IRECV_E_SUCCESS)
4216 return IRECV_E_NO_DEVICE;
4217
4218 if (variable == NULL || value == NULL) {
4219 return IRECV_E_UNKNOWN_ERROR;
4220 }
4221
4222 memset(command, '\0', sizeof(command));
4223 snprintf(command, sizeof(command)-1, "setenvnp %s %s", variable, value);
4224 irecv_error_t error = irecv_send_command_raw(client, command, 0);
4225 if (error != IRECV_E_SUCCESS) {
4226 return error;
4227 }
4228
4229 return IRECV_E_SUCCESS;
4230#endif
4231}
4232
4233irecv_error_t irecv_reboot(irecv_client_t client)
4234{
4235#ifdef USE_DUMMY
4236 return IRECV_E_UNSUPPORTED;
4237#else
4238 irecv_error_t error = irecv_send_command_raw(client, "reboot", 0);
4239 if (error != IRECV_E_SUCCESS) {
4240 return error;
4241 }
4242
4243 return IRECV_E_SUCCESS;
4244#endif
4245}
4246
4247const char* irecv_strerror(irecv_error_t error)
4248{
612 switch (error) { 4249 switch (error) {
613 case IRECV_E_SUCCESS: 4250 case IRECV_E_SUCCESS:
614 return "Command completed successfully"; 4251 return "Command completed successfully";
@@ -646,6 +4283,9 @@ const char* irecv_strerror(irecv_error_t error) {
646 case IRECV_E_TIMEOUT: 4283 case IRECV_E_TIMEOUT:
647 return "Timeout talking to device"; 4284 return "Timeout talking to device";
648 4285
4286 case IRECV_E_UNSUPPORTED:
4287 return "Operation unsupported by driver";
4288
649 default: 4289 default:
650 return "Unknown error"; 4290 return "Unknown error";
651 } 4291 }
@@ -653,64 +4293,220 @@ const char* irecv_strerror(irecv_error_t error) {
653 return NULL; 4293 return NULL;
654} 4294}
655 4295
656int irecv_write_file(const char* filename, const void* data, size_t size) { 4296irecv_error_t irecv_reset_counters(irecv_client_t client)
657 size_t bytes = 0; 4297{
658 FILE* file = NULL; 4298#ifdef USE_DUMMY
4299 return IRECV_E_UNSUPPORTED;
4300#else
4301 if (check_context(client) != IRECV_E_SUCCESS)
4302 return IRECV_E_NO_DEVICE;
659 4303
660 debug("Writing data to %s\n", filename); 4304 if ((client->mode == IRECV_K_DFU_MODE) || (client->mode == IRECV_K_PORT_DFU_MODE) || (client->mode == IRECV_K_WTF_MODE)) {
661 file = fopen(filename, "wb"); 4305 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, 0, 0, USB_TIMEOUT);
662 if (file == NULL) {
663 error("read_file: Unable to open file %s\n", filename);
664 return -1;
665 } 4306 }
666 4307
667 bytes = fwrite(data, 1, size, file); 4308 return IRECV_E_SUCCESS;
668 fclose(file); 4309#endif
4310}
669 4311
670 if (bytes != size) { 4312irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length)
671 error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size); 4313{
672 return -1; 4314#ifdef USE_DUMMY
4315 return IRECV_E_UNSUPPORTED;
4316#else
4317 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
4318
4319 if (check_context(client) != IRECV_E_SUCCESS)
4320 return IRECV_E_NO_DEVICE;
4321
4322 int packet_size = recovery_mode ? 0x2000: 0x800;
4323 int last = length % packet_size;
4324 int packets = length / packet_size;
4325 if (last != 0) {
4326 packets++;
4327 } else {
4328 last = packet_size;
4329 }
4330
4331 int i = 0;
4332 int bytes = 0;
4333 unsigned long count = 0;
4334 for (i = 0; i < packets; i++) {
4335 unsigned short size = (i+1) < packets ? packet_size : last;
4336 bytes = irecv_usb_control_transfer(client, 0xA1, 2, 0, 0, (unsigned char*)&buffer[i * packet_size], size, USB_TIMEOUT);
4337
4338 if (bytes != size) {
4339 return IRECV_E_USB_UPLOAD;
4340 }
4341
4342 count += size;
4343 if (client->progress_callback != NULL) {
4344 irecv_event_t event;
4345 event.progress = ((double) count/ (double) length) * 100.0;
4346 event.type = IRECV_PROGRESS;
4347 event.data = (char*)"Downloading";
4348 event.size = count;
4349 client->progress_callback(client, &event);
4350 } else {
4351 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
4352 }
4353 }
4354
4355 return IRECV_E_SUCCESS;
4356#endif
4357}
4358
4359irecv_error_t irecv_finish_transfer(irecv_client_t client)
4360{
4361#ifdef USE_DUMMY
4362 return IRECV_E_UNSUPPORTED;
4363#else
4364 int i = 0;
4365 unsigned int status = 0;
4366
4367 if (check_context(client) != IRECV_E_SUCCESS)
4368 return IRECV_E_NO_DEVICE;
4369
4370 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
4371
4372 for (i = 0; i < 3; i++){
4373 irecv_get_status(client, &status);
673 } 4374 }
674 4375
675 return size; 4376 irecv_reset(client);
4377
4378 return IRECV_E_SUCCESS;
4379#endif
676} 4380}
677 4381
678int irecv_read_file(const char* filename, char** data, uint32_t* size) { 4382irecv_device_t irecv_devices_get_all(void)
679 size_t bytes = 0; 4383{
680 size_t length = 0; 4384 return irecv_devices;
681 FILE* file = NULL; 4385}
682 char* buffer = NULL;
683 debug("Reading data from %s\n", filename);
684 4386
685 *size = 0; 4387irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device)
686 *data = NULL; 4388{
4389#ifdef USE_DUMMY
4390 return IRECV_E_UNSUPPORTED;
4391#else
4392 int i = 0;
687 4393
688 file = fopen(filename, "rb"); 4394 if (!client || !device)
689 if (file == NULL) { 4395 return IRECV_E_INVALID_INPUT;
690 error("read_file: File %s not found\n", filename); 4396
691 return -1; 4397 *device = NULL;
4398
4399 if (client->device_info.cpid == 0) {
4400 return IRECV_E_UNKNOWN_ERROR;
692 } 4401 }
693 4402
694 fseek(file, 0, SEEK_END); 4403 unsigned int cpid_match = client->device_info.cpid;
695 length = ftell(file); 4404 unsigned int bdid_match = client->device_info.bdid;
696 rewind(file); 4405 if (client->mode == IRECV_K_PORT_DFU_MODE) {
4406 cpid_match = (client->device_info.bdid >> 8) & 0xFFFF;
4407 bdid_match = (client->device_info.bdid >> 24) & 0xFF;
4408 }
697 4409
698 buffer = (char*) malloc(length); 4410 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
699 if(buffer == NULL) { 4411 if (irecv_devices[i].chip_id == cpid_match && irecv_devices[i].board_id == bdid_match) {
700 error("ERROR: Out of memory\n"); 4412 *device = &irecv_devices[i];
701 fclose(file); 4413 return IRECV_E_SUCCESS;
702 return -1; 4414 }
703 } 4415 }
704 bytes = fread(buffer, 1, length, file);
705 fclose(file);
706 4416
707 if(bytes != length) { 4417 return IRECV_E_NO_DEVICE;
708 error("ERROR: Unable to read entire file\n"); 4418#endif
709 free(buffer); 4419}
710 return -1; 4420
4421irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device)
4422{
4423 int i = 0;
4424
4425 if (!product_type || !device)
4426 return IRECV_E_INVALID_INPUT;
4427
4428 *device = NULL;
4429
4430 for (i = 0; irecv_devices[i].product_type != NULL; i++) {
4431 if (!strcmp(product_type, irecv_devices[i].product_type)) {
4432 *device = &irecv_devices[i];
4433 return IRECV_E_SUCCESS;
4434 }
711 } 4435 }
712 4436
713 *size = length; 4437 return IRECV_E_NO_DEVICE;
714 *data = buffer; 4438}
715 return 0; 4439
4440irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device)
4441{
4442 int i = 0;
4443
4444 if (!hardware_model || !device)
4445 return IRECV_E_INVALID_INPUT;
4446
4447 *device = NULL;
4448
4449 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
4450 if (!strcasecmp(hardware_model, irecv_devices[i].hardware_model)) {
4451 *device = &irecv_devices[i];
4452 return IRECV_E_SUCCESS;
4453 }
4454 }
4455
4456 return IRECV_E_NO_DEVICE;
4457}
4458
4459irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
4460{
4461#ifdef USE_DUMMY
4462 return NULL;
4463#else
4464 irecv_error_t error = 0;
4465 irecv_client_t new_client = NULL;
4466 irecv_event_cb_t progress_callback = client->progress_callback;
4467 irecv_event_cb_t received_callback = client->received_callback;
4468 irecv_event_cb_t connected_callback = client->connected_callback;
4469 irecv_event_cb_t precommand_callback = client->precommand_callback;
4470 irecv_event_cb_t postcommand_callback = client->postcommand_callback;
4471 irecv_event_cb_t disconnected_callback = client->disconnected_callback;
4472
4473 uint64_t ecid = client->device_info.ecid;
4474
4475 if (check_context(client) == IRECV_E_SUCCESS) {
4476 irecv_cleanup(client);
4477 }
4478
4479 if (initial_pause > 0) {
4480 debug("Waiting %d seconds for the device to pop up...\n", initial_pause);
4481 sleep(initial_pause);
4482 }
4483
4484 error = irecv_open_with_ecid_and_attempts(&new_client, ecid, 10);
4485 if (error != IRECV_E_SUCCESS) {
4486 return NULL;
4487 }
4488
4489 new_client->progress_callback = progress_callback;
4490 new_client->received_callback = received_callback;
4491 new_client->connected_callback = connected_callback;
4492 new_client->precommand_callback = precommand_callback;
4493 new_client->postcommand_callback = postcommand_callback;
4494 new_client->disconnected_callback = disconnected_callback;
4495
4496 // keep old handle valid
4497 memcpy(client, new_client, sizeof(*client));
4498 free(new_client);
4499 new_client = client;
4500
4501 if (new_client->connected_callback != NULL) {
4502 irecv_event_t event;
4503 event.size = 0;
4504 event.data = NULL;
4505 event.progress = 0;
4506 event.type = IRECV_CONNECTED;
4507 new_client->connected_callback(new_client, &event);
4508 }
4509
4510 return new_client;
4511#endif
716} 4512}
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..15e2eb9
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,16 @@
1if BUILD_TOOLS
2AM_CPPFLAGS = -I$(top_srcdir)/include
3
4AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS)
5AM_LDFLAGS = $(libusb_LIBS)
6
7bin_PROGRAMS = irecovery
8
9irecovery_SOURCES = irecovery.c
10irecovery_CFLAGS = $(AM_CFLAGS)
11irecovery_LDFLAGS = $(AM_LDFLAGS)
12if HAVE_READLINE
13irecovery_LDFLAGS += -lreadline
14endif
15irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la
16endif
diff --git a/tools/irecovery.c b/tools/irecovery.c
new file mode 100644
index 0000000..b293324
--- /dev/null
+++ b/tools/irecovery.c
@@ -0,0 +1,760 @@
1/*
2 * irecovery.c
3 * Software frontend for iBoot/iBSS communication with iOS devices
4 *
5 * Copyright (c) 2012-2023 Nikias Bassen <nikias@gmx.li>
6 * Copyright (c) 2012-2015 Martin Szulecki <martin.szulecki@libimobiledevice.org>
7 * Copyright (c) 2010-2011 Chronic-Dev Team
8 * Copyright (c) 2010-2011 Joshua Hill
9 * Copyright (c) 2008-2011 Nicolas Haunold
10 *
11 * All rights reserved. This program and the accompanying materials
12 * are made available under the terms of the GNU Lesser General Public License
13 * (LGPL) version 2.1 which accompanies this distribution, and is available at
14 * http://www.gnu.org/licenses/lgpl-2.1.html
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#define TOOL_NAME "irecovery"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <string.h>
32#include <getopt.h>
33#include <inttypes.h>
34#include <ctype.h>
35#include <libirecovery.h>
36#ifdef HAVE_READLINE
37#include <readline/readline.h>
38#include <readline/history.h>
39#else
40#ifndef _WIN32
41#include <termios.h>
42#endif
43#endif
44
45#ifdef _WIN32
46#include <windows.h>
47#include <conio.h>
48#define sleep(n) Sleep(1000 * n)
49#endif
50
51#define FILE_HISTORY_PATH ".irecovery"
52#define debug(...) if (verbose) fprintf(stderr, __VA_ARGS__)
53
54enum {
55 kNoAction,
56 kResetDevice,
57 kStartShell,
58 kSendCommand,
59 kSendFile,
60 kSendExploit,
61 kSendScript,
62 kShowMode,
63 kRebootToNormalMode,
64 kQueryInfo,
65 kListDevices
66};
67
68static unsigned int quit = 0;
69static unsigned int verbose = 0;
70
71void print_progress_bar(double progress);
72int received_cb(irecv_client_t client, const irecv_event_t* event);
73int progress_cb(irecv_client_t client, const irecv_event_t* event);
74int precommand_cb(irecv_client_t client, const irecv_event_t* event);
75int postcommand_cb(irecv_client_t client, const irecv_event_t* event);
76
77static void shell_usage()
78{
79 printf("Usage:\n");
80 printf(" /upload FILE\t\tsend FILE to device\n");
81 printf(" /limera1n [FILE]\trun limera1n exploit and send optional payload from FILE\n");
82 printf(" /deviceinfo\t\tprint device information (ECID, IMEI, etc.)\n");
83 printf(" /help\t\t\tshow this help\n");
84 printf(" /exit\t\t\texit interactive shell\n");
85}
86
87static const char* mode_to_str(int mode)
88{
89 switch (mode) {
90 case IRECV_K_RECOVERY_MODE_1:
91 case IRECV_K_RECOVERY_MODE_2:
92 case IRECV_K_RECOVERY_MODE_3:
93 case IRECV_K_RECOVERY_MODE_4:
94 return "Recovery";
95 break;
96 case IRECV_K_DFU_MODE:
97 return "DFU";
98 break;
99 case IRECV_K_PORT_DFU_MODE:
100 return "Port DFU";
101 break;
102 case IRECV_K_WTF_MODE:
103 return "WTF";
104 break;
105 default:
106 return "Unknown";
107 break;
108 }
109}
110
111static void buffer_read_from_filename(const char *filename, char **buffer, uint64_t *length)
112{
113 FILE *f;
114 uint64_t size;
115
116 *length = 0;
117
118 f = fopen(filename, "rb");
119 if (!f) {
120 return;
121 }
122
123 fseek(f, 0, SEEK_END);
124 size = ftell(f);
125 rewind(f);
126
127 if (size == 0) {
128 fclose(f);
129 return;
130 }
131
132 *buffer = (char*)malloc(sizeof(char)*(size+1));
133 fread(*buffer, sizeof(char), size, f);
134 fclose(f);
135
136 *length = size;
137}
138
139static void print_hex(unsigned char *buf, size_t len)
140{
141 size_t i;
142 for (i = 0; i < len; i++) {
143 printf("%02x", buf[i]);
144 }
145}
146
147static void print_device_info(irecv_client_t client)
148{
149 int ret, mode;
150 irecv_device_t device = NULL;
151 const struct irecv_device_info *devinfo = irecv_get_device_info(client);
152 if (devinfo) {
153 printf("CPID: 0x%04x\n", devinfo->cpid);
154 printf("CPRV: 0x%02x\n", devinfo->cprv);
155 printf("BDID: 0x%02x\n", devinfo->bdid);
156 printf("ECID: 0x%016" PRIx64 "\n", devinfo->ecid);
157 printf("CPFM: 0x%02x\n", devinfo->cpfm);
158 printf("SCEP: 0x%02x\n", devinfo->scep);
159 printf("IBFL: 0x%02x\n", devinfo->ibfl);
160 printf("SRTG: %s\n", (devinfo->srtg) ? devinfo->srtg : "N/A");
161 printf("SRNM: %s\n", (devinfo->srnm) ? devinfo->srnm : "N/A");
162 printf("IMEI: %s\n", (devinfo->imei) ? devinfo->imei : "N/A");
163 printf("NONC: ");
164 if (devinfo->ap_nonce) {
165 print_hex(devinfo->ap_nonce, devinfo->ap_nonce_size);
166 } else {
167 printf("N/A");
168 }
169 printf("\n");
170 printf("SNON: ");
171 if (devinfo->sep_nonce) {
172 print_hex(devinfo->sep_nonce, devinfo->sep_nonce_size);
173 } else {
174 printf("N/A");
175 }
176 printf("\n");
177 char* p = strstr(devinfo->serial_string, "PWND:[");
178 if (p) {
179 p+=6;
180 char* pend = strchr(p, ']');
181 if (pend) {
182 printf("PWND: %.*s\n", (int)(pend-p), p);
183 }
184 }
185 } else {
186 printf("Could not get device info?!\n");
187 }
188
189 ret = irecv_get_mode(client, &mode);
190 if (ret == IRECV_E_SUCCESS) {
191 switch (devinfo->pid) {
192 case 0x1881:
193 printf("MODE: DFU via Debug USB (KIS)\n");
194 break;
195 default:
196 printf("MODE: %s\n", mode_to_str(mode));
197 break;
198 }
199 }
200
201 irecv_devices_get_device_by_client(client, &device);
202 if (device) {
203 printf("PRODUCT: %s\n", device->product_type);
204 printf("MODEL: %s\n", device->hardware_model);
205 printf("NAME: %s\n", device->display_name);
206 }
207}
208
209static void print_devices()
210{
211 struct irecv_device *devices = irecv_devices_get_all();
212 struct irecv_device *device = NULL;
213 int i = 0;
214
215 for (i = 0; devices[i].product_type != NULL; i++) {
216 device = &devices[i];
217
218 printf("%s %s 0x%02x 0x%04x %s\n", device->product_type, device->hardware_model, device->board_id, device->chip_id, device->display_name);
219 }
220}
221
222static int _is_breq_command(const char* cmd)
223{
224 return (
225 !strcmp(cmd, "go")
226 || !strcmp(cmd, "bootx")
227 || !strcmp(cmd, "reboot")
228 || !strcmp(cmd, "memboot")
229 );
230}
231
232static void parse_command(irecv_client_t client, unsigned char* command, unsigned int size)
233{
234 char* cmd = strdup((char*)command);
235 char* action = strtok(cmd, " ");
236
237 if (!strcmp(cmd, "/exit")) {
238 quit = 1;
239 } else if (!strcmp(cmd, "/help")) {
240 shell_usage();
241 } else if (!strcmp(cmd, "/upload")) {
242 char* filename = strtok(NULL, " ");
243 debug("Uploading file %s\n", filename);
244 if (filename != NULL) {
245 irecv_send_file(client, filename, 0);
246 }
247 } else if (!strcmp(cmd, "/deviceinfo")) {
248 print_device_info(client);
249 } else if (!strcmp(cmd, "/limera1n")) {
250 char* filename = strtok(NULL, " ");
251 debug("Sending limera1n payload %s\n", filename);
252 if (filename != NULL) {
253 irecv_send_file(client, filename, 0);
254 }
255 irecv_trigger_limera1n_exploit(client);
256 } else if (!strcmp(cmd, "/execute")) {
257 char* filename = strtok(NULL, " ");
258 debug("Executing script %s\n", filename);
259 if (filename != NULL) {
260 char* buffer = NULL;
261 uint64_t buffer_length = 0;
262 buffer_read_from_filename(filename, &buffer, &buffer_length);
263 if (buffer) {
264 buffer[buffer_length] = '\0';
265 irecv_execute_script(client, buffer);
266 free(buffer);
267 } else {
268 printf("Could not read file '%s'\n", filename);
269 }
270 }
271 } else {
272 printf("Unsupported command %s. Use /help to get a list of available commands.\n", cmd);
273 }
274
275 free(action);
276}
277
278static void load_command_history()
279{
280#ifdef HAVE_READLINE
281 read_history(FILE_HISTORY_PATH);
282#endif
283}
284
285static void append_command_to_history(const char* cmd)
286{
287#ifdef HAVE_READLINE
288 add_history(cmd);
289 write_history(FILE_HISTORY_PATH);
290#endif
291}
292
293#ifndef HAVE_READLINE
294#ifdef _WIN32
295#define BS_CC '\b'
296#else
297#define BS_CC 0x7f
298#define getch getchar
299#endif
300static void get_input(char *buf, int maxlen)
301{
302 int len = 0;
303 int c;
304
305 while ((c = getch())) {
306 if ((c == '\r') || (c == '\n')) {
307 break;
308 }
309 if (isprint(c)) {
310 if (len < maxlen-1)
311 buf[len++] = c;
312 } else if (c == BS_CC) {
313 if (len > 0) {
314 fputs("\b \b", stdout);
315 len--;
316 }
317 }
318 }
319 buf[len] = 0;
320}
321#endif
322
323static void init_shell(irecv_client_t client)
324{
325 irecv_error_t error = 0;
326 load_command_history();
327 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
328 irecv_event_subscribe(client, IRECV_RECEIVED, &received_cb, NULL);
329 irecv_event_subscribe(client, IRECV_PRECOMMAND, &precommand_cb, NULL);
330 irecv_event_subscribe(client, IRECV_POSTCOMMAND, &postcommand_cb, NULL);
331 while (!quit) {
332 error = irecv_receive(client);
333 if (error != IRECV_E_SUCCESS) {
334 debug("%s\n", irecv_strerror(error));
335 break;
336 }
337#ifdef HAVE_READLINE
338 char* cmd = readline("> ");
339#else
340 char cmdbuf[4096];
341 const char* cmd = &cmdbuf[0];
342 printf("> ");
343 fflush(stdout);
344 get_input(cmdbuf, sizeof(cmdbuf));
345#endif
346 if (cmd && *cmd) {
347 if (_is_breq_command(cmd)) {
348 error = irecv_send_command_breq(client, cmd, 1);
349 } else {
350 error = irecv_send_command(client, cmd);
351 }
352 if (error != IRECV_E_SUCCESS) {
353 quit = 1;
354 }
355
356 append_command_to_history(cmd);
357 }
358#ifdef HAVE_READLINE
359 free(cmd);
360#endif
361 }
362}
363
364int received_cb(irecv_client_t client, const irecv_event_t* event)
365{
366 if (event->type == IRECV_RECEIVED) {
367 int i = 0;
368 int size = event->size;
369 const char* data = event->data;
370 for (i = 0; i < size; i++) {
371 printf("%c", data[i]);
372 }
373 }
374
375 return 0;
376}
377
378int precommand_cb(irecv_client_t client, const irecv_event_t* event)
379{
380 if (event->type == IRECV_PRECOMMAND) {
381 if (event->data[0] == '/') {
382 parse_command(client, (unsigned char*)event->data, event->size);
383 return -1;
384 }
385 }
386
387 return 0;
388}
389
390int postcommand_cb(irecv_client_t client, const irecv_event_t* event)
391{
392 char* value = NULL;
393 char* action = NULL;
394 char* command = NULL;
395 char* argument = NULL;
396 irecv_error_t error = IRECV_E_SUCCESS;
397
398 if (event->type == IRECV_POSTCOMMAND) {
399 command = strdup(event->data);
400 action = strtok(command, " ");
401 if (!strcmp(action, "getenv")) {
402 argument = strtok(NULL, " ");
403 error = irecv_getenv(client, argument, &value);
404 if (error != IRECV_E_SUCCESS) {
405 debug("%s\n", irecv_strerror(error));
406 free(command);
407 return error;
408 }
409 printf("%s\n", value);
410 free(value);
411 }
412
413 if (!strcmp(action, "reboot")) {
414 quit = 1;
415 }
416 }
417
418 free(command);
419
420 return 0;
421}
422
423int progress_cb(irecv_client_t client, const irecv_event_t* event)
424{
425 if (event->type == IRECV_PROGRESS) {
426 print_progress_bar(event->progress);
427 }
428
429 return 0;
430}
431
432void print_progress_bar(double progress)
433{
434 int i = 0;
435
436 if (progress < 0) {
437 return;
438 }
439
440 if (progress > 100) {
441 progress = 100;
442 }
443
444 printf("\r[");
445
446 for (i = 0; i < 50; i++) {
447 if (i < progress / 2) {
448 printf("=");
449 } else {
450 printf(" ");
451 }
452 }
453
454 printf("] %3.1f%%", progress);
455
456 fflush(stdout);
457
458 if (progress == 100) {
459 printf("\n");
460 }
461}
462
463static void print_usage(int argc, char **argv)
464{
465 char *name = NULL;
466 name = strrchr(argv[0], '/');
467 printf("Usage: %s [OPTIONS]\n", (name ? name + 1: argv[0]));
468 printf("\n");
469 printf("Interact with an iOS device in DFU or recovery mode.\n");
470 printf("\n");
471 printf("OPTIONS:\n");
472 printf(" -i, --ecid ECID\tconnect to specific device by its ECID\n");
473 printf(" -c, --command CMD\trun CMD on device\n");
474 printf(" -m, --mode\t\tprint current device mode\n");
475 printf(" -f, --file FILE\tsend file to device\n");
476 printf(" -k, --payload FILE\tsend limera1n usb exploit payload from FILE\n");
477 printf(" -r, --reset\t\treset client\n");
478 printf(" -n, --normal\t\treboot device into normal mode (exit recovery loop)\n");
479 printf(" -e, --script FILE\texecutes recovery script from FILE\n");
480 printf(" -s, --shell\t\tstart an interactive shell\n");
481 printf(" -q, --query\t\tquery device info\n");
482 printf(" -a, --devices\t\tlist information for all known devices\n");
483 printf(" -v, --verbose\t\tenable verbose output, repeat for higher verbosity\n");
484 printf(" -h, --help\t\tprints this usage information\n");
485 printf(" -V, --version\t\tprints version information\n");
486 printf("\n");
487 printf("Homepage: <" PACKAGE_URL ">\n");
488 printf("Bug Reports: <" PACKAGE_BUGREPORT ">\n");
489}
490
491int main(int argc, char* argv[])
492{
493 static struct option longopts[] = {
494 { "ecid", required_argument, NULL, 'i' },
495 { "command", required_argument, NULL, 'c' },
496 { "mode", no_argument, NULL, 'm' },
497 { "file", required_argument, NULL, 'f' },
498 { "payload", required_argument, NULL, 'k' },
499 { "reset", no_argument, NULL, 'r' },
500 { "normal", no_argument, NULL, 'n' },
501 { "script", required_argument, NULL, 'e' },
502 { "shell", no_argument, NULL, 's' },
503 { "query", no_argument, NULL, 'q' },
504 { "devices", no_argument, NULL, 'a' },
505 { "verbose", no_argument, NULL, 'v' },
506 { "help", no_argument, NULL, 'h' },
507 { "version", no_argument, NULL, 'V' },
508 { NULL, 0, NULL, 0 }
509 };
510 int i = 0;
511 int opt = 0;
512 int action = kNoAction;
513 uint64_t ecid = 0;
514 int mode = -1;
515 char* argument = NULL;
516 irecv_error_t error = 0;
517
518 char* buffer = NULL;
519 uint64_t buffer_length = 0;
520
521 if (argc == 1) {
522 print_usage(argc, argv);
523 return 0;
524 }
525
526 while ((opt = getopt_long(argc, argv, "i:vVhrsmnc:f:e:k:qa", longopts, NULL)) > 0) {
527 switch (opt) {
528 case 'i':
529 if (optarg) {
530 char* tail = NULL;
531 ecid = strtoull(optarg, &tail, 0);
532 if (tail && (tail[0] != '\0')) {
533 ecid = 0;
534 }
535 if (ecid == 0) {
536 fprintf(stderr, "ERROR: Could not parse ECID from argument '%s'\n", optarg);
537 return -1;
538 }
539 }
540 break;
541
542 case 'v':
543 verbose += 1;
544 break;
545
546 case 'h':
547 print_usage(argc, argv);
548 return 0;
549
550 case 'm':
551 action = kShowMode;
552 break;
553
554 case 'n':
555 action = kRebootToNormalMode;
556 break;
557
558 case 'r':
559 action = kResetDevice;
560 break;
561
562 case 's':
563 action = kStartShell;
564 break;
565
566 case 'f':
567 action = kSendFile;
568 argument = optarg;
569 break;
570
571 case 'c':
572 action = kSendCommand;
573 argument = optarg;
574 break;
575
576 case 'k':
577 action = kSendExploit;
578 argument = optarg;
579 break;
580
581 case 'e':
582 action = kSendScript;
583 argument = optarg;
584 break;
585
586 case 'q':
587 action = kQueryInfo;
588 break;
589
590 case 'a':
591 action = kListDevices;
592 print_devices();
593 return 0;
594
595 case 'V':
596 printf("%s %s", TOOL_NAME, PACKAGE_VERSION);
597#ifdef HAVE_READLINE
598 printf(" (readline)");
599#endif
600 printf("\n");
601 return 0;
602
603 default:
604 fprintf(stderr, "Unknown argument\n");
605 return -1;
606 }
607 }
608
609 if (action == kNoAction) {
610 fprintf(stderr, "ERROR: Missing action option\n");
611 print_usage(argc, argv);
612 return -1;
613 }
614
615 if (verbose)
616 irecv_set_debug_level(verbose);
617
618 irecv_client_t client = NULL;
619 for (i = 0; i <= 5; i++) {
620 debug("Attempting to connect... \n");
621
622 irecv_error_t err = irecv_open_with_ecid(&client, ecid);
623 if (err == IRECV_E_UNSUPPORTED) {
624 fprintf(stderr, "ERROR: %s\n", irecv_strerror(err));
625 return -1;
626 }
627 else if (err != IRECV_E_SUCCESS)
628 sleep(1);
629 else
630 break;
631
632 if (i == 5) {
633 fprintf(stderr, "ERROR: %s\n", irecv_strerror(err));
634 return -1;
635 }
636 }
637
638 irecv_device_t device = NULL;
639 irecv_devices_get_device_by_client(client, &device);
640 if (device)
641 debug("Connected to %s, model %s, cpid 0x%04x, bdid 0x%02x\n", device->product_type, device->hardware_model, device->chip_id, device->board_id);
642
643 const struct irecv_device_info *devinfo = irecv_get_device_info(client);
644
645 switch (action) {
646 case kResetDevice:
647 irecv_reset(client);
648 break;
649
650 case kSendFile:
651 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
652 error = irecv_send_file(client, argument, IRECV_SEND_OPT_DFU_NOTIFY_FINISH);
653 debug("%s\n", irecv_strerror(error));
654 break;
655
656 case kSendCommand:
657 if (devinfo->pid == 0x1881) {
658 printf("Shell is not available in Debug USB (KIS) mode.\n");
659 break;
660 }
661 if (_is_breq_command(argument)) {
662 error = irecv_send_command_breq(client, argument, 1);
663 } else {
664 error = irecv_send_command(client, argument);
665 }
666 debug("%s\n", irecv_strerror(error));
667 break;
668
669 case kSendExploit:
670 if (devinfo->pid == 0x1881) {
671 printf("Shell is not available in Debug USB (KIS) mode.\n");
672 break;
673 }
674 if (argument != NULL) {
675 irecv_event_subscribe(client, IRECV_PROGRESS, &progress_cb, NULL);
676 error = irecv_send_file(client, argument, 0);
677 if (error != IRECV_E_SUCCESS) {
678 debug("%s\n", irecv_strerror(error));
679 break;
680 }
681 }
682 error = irecv_trigger_limera1n_exploit(client);
683 debug("%s\n", irecv_strerror(error));
684 break;
685
686 case kStartShell:
687 if (devinfo->pid == 0x1881) {
688 printf("This feature is not supported in Debug USB (KIS) mode.\n");
689 break;
690 }
691 init_shell(client);
692 break;
693
694 case kSendScript:
695 if (devinfo->pid == 0x1881) {
696 printf("This feature is not supported in Debug USB (KIS) mode.\n");
697 break;
698 }
699 buffer_read_from_filename(argument, &buffer, &buffer_length);
700 if (buffer) {
701 buffer[buffer_length] = '\0';
702
703 error = irecv_execute_script(client, buffer);
704 if (error != IRECV_E_SUCCESS) {
705 debug("%s\n", irecv_strerror(error));
706 }
707
708 free(buffer);
709 } else {
710 fprintf(stderr, "Could not read file '%s'\n", argument);
711 }
712 break;
713
714 case kShowMode: {
715 irecv_get_mode(client, &mode);
716 printf("%s Mode", mode_to_str(mode));
717 if (devinfo->pid == 0x1881) {
718 printf(" via Debug USB (KIS)");
719 }
720 printf("\n");
721 break;
722 }
723 case kRebootToNormalMode:
724 if (devinfo->pid == 0x1881) {
725 printf("This feature is not supported in Debug USB (KIS) mode.\n");
726 break;
727 }
728 error = irecv_setenv(client, "auto-boot", "true");
729 if (error != IRECV_E_SUCCESS) {
730 debug("%s\n", irecv_strerror(error));
731 break;
732 }
733
734 error = irecv_saveenv(client);
735 if (error != IRECV_E_SUCCESS) {
736 debug("%s\n", irecv_strerror(error));
737 break;
738 }
739
740 error = irecv_reboot(client);
741 if (error != IRECV_E_SUCCESS) {
742 debug("%s\n", irecv_strerror(error));
743 } else {
744 debug("%s\n", irecv_strerror(error));
745 }
746 break;
747
748 case kQueryInfo:
749 print_device_info(client);
750 break;
751
752 default:
753 fprintf(stderr, "Unknown action\n");
754 break;
755 }
756
757 irecv_close(client);
758
759 return 0;
760}
diff --git a/udev/39-libirecovery.rules.in b/udev/39-libirecovery.rules.in
new file mode 100644
index 0000000..ea9f93a
--- /dev/null
+++ b/udev/39-libirecovery.rules.in
@@ -0,0 +1,8 @@
1# Handle iOS devices in DFU and Recovery mode - for use with libirecovery
2
3# Change group and permissions of iOS devices in DFU, legacy WTF, and Recovery mode
4ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="122[27]|128[0-3]", @udev_activation_rule@
5
6# Handle checkra1n DFU mode
7ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="1338", @udev_activation_rule@
8
diff --git a/udev/Makefile.am b/udev/Makefile.am
new file mode 100644
index 0000000..2a7ad98
--- /dev/null
+++ b/udev/Makefile.am
@@ -0,0 +1,21 @@
1if WITH_UDEV
2edit = \
3 $(SED) -r \
4 -e 's|@udev_activation_rule[@]|$(udev_activation_rule)|g' \
5 < $< > $@ || rm $@
6
7udevrules_DATA = \
8 39-libirecovery.rules
9
1039-libirecovery.rules: 39-libirecovery.rules.in
11 $(edit)
12
13EXTRA_DIST = \
14 39-libirecovery.rules.in
15
16MAINTAINERCLEANFILES = \
17 39-libirecovery.rules
18
19CLEANFILES = \
20 39-libirecovery.rules
21endif