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.c4329
-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, 6580 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..093d431
--- /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])
94AM_CONDITIONAL(HAVE_READLINE, test "x$have_readline" = "xyes")
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..ca96181 100644
--- a/src/libirecovery.c
+++ b/src/libirecovery.c
@@ -1,117 +1,2249 @@
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 #ifndef USE_DUMMY
108 enum libusb_transfer_status ret;
109 #endif
110 #endif
111};
112
113#endif
114
115struct irecv_client_private {
116 int debug;
117 int usb_config;
118 int usb_interface;
119 int usb_alt_interface;
120 unsigned int mode;
121 int isKIS;
122 struct irecv_device_info device_info;
123#ifndef USE_DUMMY
124#ifndef _WIN32
125#ifndef HAVE_IOKIT
126 libusb_device_handle* handle;
127#else
128 IOUSBDeviceInterface320 **handle;
129 IOUSBInterfaceInterface300 **usbInterface;
130 CFRunLoopSourceRef async_event_source;
131#endif
132#else
133 HANDLE handle;
134#endif
135 irecv_event_cb_t progress_callback;
136 irecv_event_cb_t received_callback;
137 irecv_event_cb_t connected_callback;
138 irecv_event_cb_t precommand_callback;
139 irecv_event_cb_t postcommand_callback;
140 irecv_event_cb_t disconnected_callback;
141#endif
142};
143
144#define USB_TIMEOUT 10000
145#define APPLE_VENDOR_ID 0x05AC
146
147// KIS
148#define KIS_PRODUCT_ID 0x1881
149
150#define KIS_PORTAL_CONFIG 0x01
151#define KIS_PORTAL_RSM 0x10
152
153#define KIS_INDEX_UPLOAD 0x0D
154#define KIS_INDEX_ENABLE_A 0x0A // macOS writes to this
155#define KIS_INDEX_ENABLE_B 0x14 // macOS writes to this
156#define KIS_INDEX_GET_INFO 0x100
157#define KIS_INDEX_BOOT_IMG 0x103
158
159#define KIS_ENABLE_A_VAL 0x21 // Value to write to KIS_INDEX_ENABLE_A
160#define KIS_ENABLE_B_VAL 0x01 // Value to write to KIS_INDEX_ENABLE_B
161
27#define BUFFER_SIZE 0x1000 162#define BUFFER_SIZE 0x1000
28#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__) 163#define debug(...) if (libirecovery_debug) fprintf(stderr, __VA_ARGS__)
29 164
30static int libirecovery_debug = 0; 165static int libirecovery_debug = 0;
166#ifndef USE_DUMMY
167#ifndef _WIN32
168#ifndef HAVE_IOKIT
31static libusb_context* libirecovery_context = NULL; 169static libusb_context* libirecovery_context = NULL;
170#endif
171#endif
172#endif
173
174static struct irecv_device irecv_devices[] = {
175 /* iPhone */
176 { "iPhone1,1", "m68ap", 0x00, 0x8900, "iPhone 2G" },
177 { "iPhone1,2", "n82ap", 0x04, 0x8900, "iPhone 3G" },
178 { "iPhone2,1", "n88ap", 0x00, 0x8920, "iPhone 3Gs" },
179 { "iPhone3,1", "n90ap", 0x00, 0x8930, "iPhone 4 (GSM)" },
180 { "iPhone3,2", "n90bap", 0x04, 0x8930, "iPhone 4 (GSM) R2 2012" },
181 { "iPhone3,3", "n92ap", 0x06, 0x8930, "iPhone 4 (CDMA)" },
182 { "iPhone4,1", "n94ap", 0x08, 0x8940, "iPhone 4s" },
183 { "iPhone5,1", "n41ap", 0x00, 0x8950, "iPhone 5 (GSM)" },
184 { "iPhone5,2", "n42ap", 0x02, 0x8950, "iPhone 5 (Global)" },
185 { "iPhone5,3", "n48ap", 0x0a, 0x8950, "iPhone 5c (GSM)" },
186 { "iPhone5,4", "n49ap", 0x0e, 0x8950, "iPhone 5c (Global)" },
187 { "iPhone6,1", "n51ap", 0x00, 0x8960, "iPhone 5s (GSM)" },
188 { "iPhone6,2", "n53ap", 0x02, 0x8960, "iPhone 5s (Global)" },
189 { "iPhone7,1", "n56ap", 0x04, 0x7000, "iPhone 6 Plus" },
190 { "iPhone7,2", "n61ap", 0x06, 0x7000, "iPhone 6" },
191 { "iPhone8,1", "n71ap", 0x04, 0x8000, "iPhone 6s" },
192 { "iPhone8,1", "n71map", 0x04, 0x8003, "iPhone 6s" },
193 { "iPhone8,2", "n66ap", 0x06, 0x8000, "iPhone 6s Plus" },
194 { "iPhone8,2", "n66map", 0x06, 0x8003, "iPhone 6s Plus" },
195 { "iPhone8,4", "n69ap", 0x02, 0x8003, "iPhone SE (1st gen)" },
196 { "iPhone8,4", "n69uap", 0x02, 0x8000, "iPhone SE (1st gen)" },
197 { "iPhone9,1", "d10ap", 0x08, 0x8010, "iPhone 7 (Global)" },
198 { "iPhone9,2", "d11ap", 0x0a, 0x8010, "iPhone 7 Plus (Global)" },
199 { "iPhone9,3", "d101ap", 0x0c, 0x8010, "iPhone 7 (GSM)" },
200 { "iPhone9,4", "d111ap", 0x0e, 0x8010, "iPhone 7 Plus (GSM)" },
201 { "iPhone10,1", "d20ap", 0x02, 0x8015, "iPhone 8 (Global)" },
202 { "iPhone10,2", "d21ap", 0x04, 0x8015, "iPhone 8 Plus (Global)" },
203 { "iPhone10,3", "d22ap", 0x06, 0x8015, "iPhone X (Global)" },
204 { "iPhone10,4", "d201ap", 0x0a, 0x8015, "iPhone 8 (GSM)" },
205 { "iPhone10,5", "d211ap", 0x0c, 0x8015, "iPhone 8 Plus (GSM)" },
206 { "iPhone10,6", "d221ap", 0x0e, 0x8015, "iPhone X (GSM)" },
207 { "iPhone11,2", "d321ap", 0x0e, 0x8020, "iPhone XS" },
208 { "iPhone11,4", "d331ap", 0x0a, 0x8020, "iPhone XS Max (China)" },
209 { "iPhone11,6", "d331pap", 0x1a, 0x8020, "iPhone XS Max" },
210 { "iPhone11,8", "n841ap", 0x0c, 0x8020, "iPhone XR" },
211 { "iPhone12,1", "n104ap", 0x04, 0x8030, "iPhone 11" },
212 { "iPhone12,3", "d421ap", 0x06, 0x8030, "iPhone 11 Pro" },
213 { "iPhone12,5", "d431ap", 0x02, 0x8030, "iPhone 11 Pro Max" },
214 { "iPhone12,8", "d79ap", 0x10, 0x8030, "iPhone SE (2nd gen)" },
215 { "iPhone13,1", "d52gap", 0x0A, 0x8101, "iPhone 12 mini" },
216 { "iPhone13,2", "d53gap", 0x0C, 0x8101, "iPhone 12" },
217 { "iPhone13,3", "d53pap", 0x0E, 0x8101, "iPhone 12 Pro" },
218 { "iPhone13,4", "d54pap", 0x08, 0x8101, "iPhone 12 Pro Max" },
219 { "iPhone14,2", "d63ap", 0x0C, 0x8110, "iPhone 13 Pro" },
220 { "iPhone14,3", "d64ap", 0x0E, 0x8110, "iPhone 13 Pro Max" },
221 { "iPhone14,4", "d16ap", 0x08, 0x8110, "iPhone 13 mini" },
222 { "iPhone14,5", "d17ap", 0x0A, 0x8110, "iPhone 13" },
223 { "iPhone14,6", "d49ap", 0x10, 0x8110, "iPhone SE (3rd gen)" },
224 { "iPhone14,7", "d27ap", 0x18, 0x8110, "iPhone 14" },
225 { "iPhone14,8", "d28ap", 0x1A, 0x8110, "iPhone 14 Plus" },
226 { "iPhone15,2", "d73ap", 0x0C, 0x8120, "iPhone 14 Pro" },
227 { "iPhone15,3", "d74ap", 0x0E, 0x8120, "iPhone 14 Pro Max" },
228 { "iPhone15,4", "d37ap", 0x08, 0x8120, "iPhone 15" },
229 { "iPhone15,5", "d38ap", 0x0A, 0x8120, "iPhone 15 Plus" },
230 { "iPhone16,1", "d83ap", 0x04, 0x8130, "iPhone 15 Pro" },
231 { "iPhone16,2", "d84ap", 0x06, 0x8130, "iPhone 15 Pro Max" },
232 { "iPhone17,1", "d93ap", 0x0C, 0x8140, "iPhone 16 Pro" },
233 { "iPhone17,2", "d94ap", 0x0E, 0x8140, "iPhone 16 Pro Max" },
234 { "iPhone17,3", "d47ap", 0x08, 0x8140, "iPhone 16" },
235 { "iPhone17,4", "d48ap", 0x0A, 0x8140, "iPhone 16 Plus" },
236 { "iPhone17,5", "v59ap", 0x04, 0x8140, "iPhone 16e" },
237 { "iPhone18,1", "v53ap", 0x0C, 0x8150, "iPhone 17 Pro" },
238 { "iPhone18,2", "v54ap", 0x0E, 0x8150, "iPhone 17 Pro Max" },
239 { "iPhone18,3", "v57ap", 0x08, 0x8150, "iPhone 17" },
240 { "iPhone18,4", "d23ap", 0x0A, 0x8150, "iPhone Air" },
241 { "iPhone18,5", "v159ap", 0x16, 0x8150, "iPhone 17e" },
242 /* iPod */
243 { "iPod1,1", "n45ap", 0x02, 0x8900, "iPod Touch (1st gen)" },
244 { "iPod2,1", "n72ap", 0x00, 0x8720, "iPod Touch (2nd gen)" },
245 { "iPod3,1", "n18ap", 0x02, 0x8922, "iPod Touch (3rd gen)" },
246 { "iPod4,1", "n81ap", 0x08, 0x8930, "iPod Touch (4th gen)" },
247 { "iPod5,1", "n78ap", 0x00, 0x8942, "iPod Touch (5th gen)" },
248 { "iPod7,1", "n102ap", 0x10, 0x7000, "iPod Touch (6th gen)" },
249 { "iPod9,1", "n112ap", 0x16, 0x8010, "iPod Touch (7th gen)" },
250 /* iPad */
251 { "iPad1,1", "k48ap", 0x02, 0x8930, "iPad" },
252 { "iPad2,1", "k93ap", 0x04, 0x8940, "iPad 2 (WiFi)" },
253 { "iPad2,2", "k94ap", 0x06, 0x8940, "iPad 2 (GSM)" },
254 { "iPad2,3", "k95ap", 0x02, 0x8940, "iPad 2 (CDMA)" },
255 { "iPad2,4", "k93aap", 0x06, 0x8942, "iPad 2 (WiFi) R2 2012" },
256 { "iPad2,5", "p105ap", 0x0a, 0x8942, "iPad mini (WiFi)" },
257 { "iPad2,6", "p106ap", 0x0c, 0x8942, "iPad mini (GSM)" },
258 { "iPad2,7", "p107ap", 0x0e, 0x8942, "iPad mini (Global)" },
259 { "iPad3,1", "j1ap", 0x00, 0x8945, "iPad (3rd gen, WiFi)" },
260 { "iPad3,2", "j2ap", 0x02, 0x8945, "iPad (3rd gen, CDMA)" },
261 { "iPad3,3", "j2aap", 0x04, 0x8945, "iPad (3rd gen, GSM)" },
262 { "iPad3,4", "p101ap", 0x00, 0x8955, "iPad (4th gen, WiFi)" },
263 { "iPad3,5", "p102ap", 0x02, 0x8955, "iPad (4th gen, GSM)" },
264 { "iPad3,6", "p103ap", 0x04, 0x8955, "iPad (4th gen, Global)" },
265 { "iPad4,1", "j71ap", 0x10, 0x8960, "iPad Air (WiFi)" },
266 { "iPad4,2", "j72ap", 0x12, 0x8960, "iPad Air (Cellular)" },
267 { "iPad4,3", "j73ap", 0x14, 0x8960, "iPad Air (China)" },
268 { "iPad4,4", "j85ap", 0x0a, 0x8960, "iPad mini 2 (WiFi)" },
269 { "iPad4,5", "j86ap", 0x0c, 0x8960, "iPad mini 2 (Cellular)" },
270 { "iPad4,6", "j87ap", 0x0e, 0x8960, "iPad mini 2 (China)" },
271 { "iPad4,7", "j85map", 0x32, 0x8960, "iPad mini 3 (WiFi)" },
272 { "iPad4,8", "j86map", 0x34, 0x8960, "iPad mini 3 (Cellular)" },
273 { "iPad4,9", "j87map", 0x36, 0x8960, "iPad mini 3 (China)" },
274 { "iPad5,1", "j96ap", 0x08, 0x7000, "iPad mini 4 (WiFi)" },
275 { "iPad5,2", "j97ap", 0x0A, 0x7000, "iPad mini 4 (Cellular)" },
276 { "iPad5,3", "j81ap", 0x06, 0x7001, "iPad Air 2 (WiFi)" },
277 { "iPad5,4", "j82ap", 0x02, 0x7001, "iPad Air 2 (Cellular)" },
278 { "iPad6,3", "j127ap", 0x08, 0x8001, "iPad Pro 9.7-inch (WiFi)" },
279 { "iPad6,4", "j128ap", 0x0a, 0x8001, "iPad Pro 9.7-inch (Cellular)" },
280 { "iPad6,7", "j98aap", 0x10, 0x8001, "iPad Pro 12.9-inch (1st gen, WiFi)" },
281 { "iPad6,8", "j99aap", 0x12, 0x8001, "iPad Pro 12.9-inch (1st gen, Cellular)" },
282 { "iPad6,11", "j71sap", 0x10, 0x8000, "iPad (5th gen, WiFi)" },
283 { "iPad6,11", "j71tap", 0x10, 0x8003, "iPad (5th gen, WiFi)" },
284 { "iPad6,12", "j72sap", 0x12, 0x8000, "iPad (5th gen, Cellular)" },
285 { "iPad6,12", "j72tap", 0x12, 0x8003, "iPad (5th gen, Cellular)" },
286 { "iPad7,1", "j120ap", 0x0C, 0x8011, "iPad Pro 12.9-inch (2nd gen, WiFi)" },
287 { "iPad7,2", "j121ap", 0x0E, 0x8011, "iPad Pro 12.9-inch (2nd gen, Cellular)" },
288 { "iPad7,3", "j207ap", 0x04, 0x8011, "iPad Pro 10.5-inch (WiFi)" },
289 { "iPad7,4", "j208ap", 0x06, 0x8011, "iPad Pro 10.5-inch (Cellular)" },
290 { "iPad7,5", "j71bap", 0x18, 0x8010, "iPad (6th gen, WiFi)" },
291 { "iPad7,6", "j72bap", 0x1A, 0x8010, "iPad (6th gen, Cellular)" },
292 { "iPad7,11", "j171ap", 0x1C, 0x8010, "iPad (7th gen, WiFi)" },
293 { "iPad7,12", "j172ap", 0x1E, 0x8010, "iPad (7th gen, Cellular)" },
294 { "iPad8,1", "j317ap", 0x0C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi)" },
295 { "iPad8,2", "j317xap", 0x1C, 0x8027, "iPad Pro 11-inch (1st gen, WiFi, 1TB)" },
296 { "iPad8,3", "j318ap", 0x0E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular)" },
297 { "iPad8,4", "j318xap", 0x1E, 0x8027, "iPad Pro 11-inch (1st gen, Cellular, 1TB)" },
298 { "iPad8,5", "j320ap", 0x08, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi)" },
299 { "iPad8,6", "j320xap", 0x18, 0x8027, "iPad Pro 12.9-inch (3rd gen, WiFi, 1TB)" },
300 { "iPad8,7", "j321ap", 0x0A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular)" },
301 { "iPad8,8", "j321xap", 0x1A, 0x8027, "iPad Pro 12.9-inch (3rd gen, Cellular, 1TB)" },
302 { "iPad8,9", "j417ap", 0x3C, 0x8027, "iPad Pro 11-inch (2nd gen, WiFi)" },
303 { "iPad8,10", "j418ap", 0x3E, 0x8027, "iPad Pro 11-inch (2nd gen, Cellular)" },
304 { "iPad8,11", "j420ap", 0x38, 0x8027, "iPad Pro 12.9-inch (4th gen, WiFi)" },
305 { "iPad8,12", "j421ap", 0x3A, 0x8027, "iPad Pro 12.9-inch (4th gen, Cellular)" },
306 { "iPad11,1", "j210ap", 0x14, 0x8020, "iPad mini (5th gen, WiFi)" },
307 { "iPad11,2", "j211ap", 0x16, 0x8020, "iPad mini (5th gen, Cellular)" },
308 { "iPad11,3", "j217ap", 0x1C, 0x8020, "iPad Air (3rd gen, WiFi)" },
309 { "iPad11,4", "j218ap", 0x1E, 0x8020, "iPad Air (3rd gen, Cellular)" },
310 { "iPad11,6", "j171aap", 0x24, 0x8020, "iPad (8th gen, WiFi)" },
311 { "iPad11,7", "j172aap", 0x26, 0x8020, "iPad (8th gen, Cellular)" },
312 { "iPad12,1", "j181ap", 0x18, 0x8030, "iPad (9th gen, WiFi)" },
313 { "iPad12,2", "j182ap", 0x1A, 0x8030, "iPad (9th gen, Cellular)" },
314 { "iPad13,1", "j307ap", 0x04, 0x8101, "iPad Air (4th gen, WiFi)" },
315 { "iPad13,2", "j308ap", 0x06, 0x8101, "iPad Air (4th gen, Cellular)" },
316 { "iPad13,4", "j517ap", 0x08, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi)" },
317 { "iPad13,5", "j517xap", 0x0A, 0x8103, "iPad Pro 11-inch (3rd gen, WiFi, 2TB)" },
318 { "iPad13,6", "j518ap", 0x0C, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular)" },
319 { "iPad13,7", "j518xap", 0x0E, 0x8103, "iPad Pro 11-inch (3rd gen, Cellular, 2TB)" },
320 { "iPad13,8", "j522ap", 0x18, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi)" },
321 { "iPad13,9", "j522xap", 0x1A, 0x8103, "iPad Pro 12.9-inch (5th gen, WiFi, 2TB)" },
322 { "iPad13,10", "j523ap", 0x1C, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular)" },
323 { "iPad13,11", "j523xap", 0x1E, 0x8103, "iPad Pro 12.9-inch (5th gen, Cellular, 2TB)" },
324 { "iPad13,16", "j407ap", 0x10, 0x8103, "iPad Air (5th gen, WiFi)" },
325 { "iPad13,17", "j408ap", 0x12, 0x8103, "iPad Air (5th gen, Cellular)" },
326 { "iPad13,18", "j271ap", 0x14, 0x8101, "iPad (10th gen, WiFi)" },
327 { "iPad13,19", "j272ap", 0x16, 0x8101, "iPad (10th gen, Cellular)" },
328 { "iPad14,1", "j310ap", 0x04, 0x8110, "iPad mini (6th gen, WiFi)" },
329 { "iPad14,2", "j311ap", 0x06, 0x8110, "iPad mini (6th gen, Cellular)" },
330 { "iPad14,3", "j617ap", 0x08, 0x8112, "iPad Pro 11-inch (4th gen, WiFi)" },
331 { "iPad14,4", "j618ap", 0x0A, 0x8112, "iPad Pro 11-inch (4th gen, Cellular)" },
332 { "iPad14,5", "j620ap", 0x0C, 0x8112, "iPad Pro 12.9-inch (6th gen, WiFi)" },
333 { "iPad14,6", "j621ap", 0x0E, 0x8112, "iPad Pro 12.9-inch (6th gen, Cellular)" },
334 { "iPad14,8", "j507ap", 0x10, 0x8112, "iPad Air 11-inch (M2, WiFi)" },
335 { "iPad14,9", "j508ap", 0x12, 0x8112, "iPad Air 11-inch (M2, Cellular)" },
336 { "iPad14,10", "j537ap", 0x14, 0x8112, "iPad Air 13-inch (M2, WiFi)" },
337 { "iPad14,11", "j538ap", 0x16, 0x8112, "iPad Air 13-inch (M2, Cellular)" },
338 { "iPad15,3", "j607ap", 0x08, 0x8122, "iPad Air 11-inch (M3, WiFi)" },
339 { "iPad15,4", "j608ap", 0x0A, 0x8122, "iPad Air 11-inch (M3, Cellular)" },
340 { "iPad15,5", "j637ap", 0x0C, 0x8122, "iPad Air 13-inch (M3, WiFi)" },
341 { "iPad15,6", "j638ap", 0x0E, 0x8122, "iPad Air 13-inch (M3, Cellular)" },
342 { "iPad15,7", "j481ap", 0x10, 0x8120, "iPad (A16, WiFi)" },
343 { "iPad15,8", "j482ap", 0x12, 0x8120, "iPad (A16, Cellular)" },
344 { "iPad16,1", "j410ap", 0x08, 0x8130, "iPad mini (A17 Pro, WiFi)" },
345 { "iPad16,2", "j411ap", 0x0A, 0x8130, "iPad mini (A17 Pro, Cellular)" },
346 { "iPad16,3", "j717ap", 0x08, 0x8132, "iPad Pro 11-inch (M4, WiFi)" },
347 { "iPad16,4", "j718ap", 0x0A, 0x8132, "iPad Pro 11-inch (M4, Cellular)" },
348 { "iPad16,5", "j720ap", 0x0C, 0x8132, "iPad Pro 13-inch (M4, WiFi)" },
349 { "iPad16,6", "j721ap", 0x0E, 0x8132, "iPad Pro 13-inch (M4, Cellular)" },
350 { "iPad16,8", "j707ap", 0x10, 0x8132, "iPad Air 11-inch (M4, Wi-Fi)" },
351 { "iPad16,9", "j708ap", 0x12, 0x8132, "iPad Air 11-inch (M4, Cellular)" },
352 { "iPad16,10", "j737ap", 0x14, 0x8132, "iPad Air 13-inch (M4, Wi-Fi)" },
353 { "iPad16,11", "j738ap", 0x16, 0x8132, "iPad Air 13-inch (M4, Cellular)" },
354 { "iPad17,1", "j817ap", 0x08, 0x8142, "iPad Pro 11-inch (M5, WiFi)" },
355 { "iPad17,2", "j818ap", 0x0A, 0x8142, "iPad Pro 11-inch (M5, Cellular)" },
356 { "iPad17,3", "j820ap", 0x0C, 0x8142, "iPad Pro 13-inch (M5, WiFi)" },
357 { "iPad17,4", "j821ap", 0x0E, 0x8142, "iPad Pro 13-inch (M5, Cellular)" },
358 /* Apple TV */
359 { "AppleTV2,1", "k66ap", 0x10, 0x8930, "Apple TV 2" },
360 { "AppleTV3,1", "j33ap", 0x08, 0x8942, "Apple TV 3" },
361 { "AppleTV3,2", "j33iap", 0x00, 0x8947, "Apple TV 3 (2013)" },
362 { "AppleTV5,3", "j42dap", 0x34, 0x7000, "Apple TV 4" },
363 { "AppleTV6,2", "j105aap", 0x02, 0x8011, "Apple TV 4K" },
364 { "AppleTV11,1", "j305ap", 0x08, 0x8020, "Apple TV 4K (2nd gen)" },
365 { "AppleTV14,1", "j255ap", 0x02, 0x8110, "Apple TV 4K (3rd gen)" },
366 /* HomePod */
367 { "AudioAccessory1,1", "b238aap", 0x38, 0x7000, "HomePod (1st gen)" },
368 { "AudioAccessory1,2", "b238ap", 0x1A, 0x7000, "HomePod (1st gen)" },
369 { "AudioAccessory5,1", "b520ap", 0x22, 0x8006, "HomePod mini" },
370 { "AudioAccessory6,1", "b620ap", 0x18, 0x8301, "HomePod (2nd gen)" },
371 /* Apple Watch */
372 { "Watch1,1", "n27aap", 0x02, 0x7002, "Apple Watch 38mm (1st gen)" },
373 { "Watch1,2", "n28aap", 0x04, 0x7002, "Apple Watch 42mm (1st gen)" },
374 { "Watch2,6", "n27dap", 0x02, 0x8002, "Apple Watch Series 1 (38mm)" },
375 { "Watch2,7", "n28dap", 0x04, 0x8002, "Apple Watch Series 1 (42mm)" },
376 { "Watch2,3", "n74ap", 0x0C, 0x8002, "Apple Watch Series 2 (38mm)" },
377 { "Watch2,4", "n75ap", 0x0E, 0x8002, "Apple Watch Series 2 (42mm)" },
378 { "Watch3,1", "n111sap", 0x1C, 0x8004, "Apple Watch Series 3 (38mm Cellular)" },
379 { "Watch3,2", "n111bap", 0x1E, 0x8004, "Apple Watch Series 3 (42mm Cellular)" },
380 { "Watch3,3", "n121sap", 0x18, 0x8004, "Apple Watch Series 3 (38mm)" },
381 { "Watch3,4", "n121bap", 0x1A, 0x8004, "Apple Watch Series 3 (42mm)" },
382 { "Watch4,1", "n131sap", 0x08, 0x8006, "Apple Watch Series 4 (40mm)" },
383 { "Watch4,2", "n131bap", 0x0A, 0x8006, "Apple Watch Series 4 (44mm)" },
384 { "Watch4,3", "n141sap", 0x0C, 0x8006, "Apple Watch Series 4 (40mm Cellular)" },
385 { "Watch4,4", "n141bap", 0x0E, 0x8006, "Apple Watch Series 4 (44mm Cellular)" },
386 { "Watch5,1", "n144sap", 0x10, 0x8006, "Apple Watch Series 5 (40mm)" },
387 { "Watch5,2", "n144bap", 0x12, 0x8006, "Apple Watch Series 5 (44mm)" },
388 { "Watch5,3", "n146sap", 0x14, 0x8006, "Apple Watch Series 5 (40mm Cellular)" },
389 { "Watch5,4", "n146bap", 0x16, 0x8006, "Apple Watch Series 5 (44mm Cellular)" },
390 { "Watch5,9", "n140sap", 0x28, 0x8006, "Apple Watch SE (40mm)" },
391 { "Watch5,10", "n140bap", 0x2A, 0x8006, "Apple Watch SE (44mm)" },
392 { "Watch5,11", "n142sap", 0x2C, 0x8006, "Apple Watch SE (40mm Cellular)" },
393 { "Watch5,12", "n142bap", 0x2E, 0x8006, "Apple Watch SE (44mm Cellular)" },
394 { "Watch6,1", "n157sap", 0x08, 0x8301, "Apple Watch Series 6 (40mm)" },
395 { "Watch6,2", "n157bap", 0x0A, 0x8301, "Apple Watch Series 6 (44mm)" },
396 { "Watch6,3", "n158sap", 0x0C, 0x8301, "Apple Watch Series 6 (40mm Cellular)" },
397 { "Watch6,4", "n158bap", 0x0E, 0x8301, "Apple Watch Series 6 (44mm Cellular)" },
398 { "Watch6,6", "n187sap", 0x10, 0x8301, "Apple Watch Series 7 (41mm)" },
399 { "Watch6,7", "n187bap", 0x12, 0x8301, "Apple Watch Series 7 (45mm)" },
400 { "Watch6,8", "n188sap", 0x14, 0x8301, "Apple Watch Series 7 (41mm Cellular)" },
401 { "Watch6,9", "n188bap", 0x16, 0x8301, "Apple Watch Series 7 (45mm Cellular)" },
402 { "Watch6,10", "n143sap", 0x28, 0x8301, "Apple Watch SE 2 (40mm)" },
403 { "Watch6,11", "n143bap", 0x2A, 0x8301, "Apple Watch SE 2 (44mm)" },
404 { "Watch6,12", "n149sap", 0x2C, 0x8301, "Apple Watch SE 2 (40mm Cellular)" },
405 { "Watch6,13", "n149bap", 0x2E, 0x8301, "Apple Watch SE 2 (44mm Cellular)" },
406 { "Watch6,14", "n197sap", 0x30, 0x8301, "Apple Watch Series 8 (41mm)" },
407 { "Watch6,15", "n197bap", 0x32, 0x8301, "Apple Watch Series 8 (45mm)" },
408 { "Watch6,16", "n198sap", 0x34, 0x8301, "Apple Watch Series 8 (41mm Cellular)" },
409 { "Watch6,17", "n198bap", 0x36, 0x8301, "Apple Watch Series 8 (45mm Cellular)" },
410 { "Watch6,18", "n199ap", 0x26, 0x8301, "Apple Watch Ultra" },
411 { "Watch7,1", "n207sap", 0x08, 0x8310, "Apple Watch Series 9 (41mm)" },
412 { "Watch7,2", "n207bap", 0x0A, 0x8310, "Apple Watch Series 9 (45mm)" },
413 { "Watch7,3", "n208sap", 0x0C, 0x8310, "Apple Watch Series 9 (41mm Cellular)" },
414 { "Watch7,4", "n208bap", 0x0E, 0x8310, "Apple Watch Series 9 (45mm Cellular)" },
415 { "Watch7,5", "n210ap", 0x02, 0x8310, "Apple Watch Ultra 2" },
416 { "Watch7,8", "n217sap", 0x10, 0x8310, "Apple Watch Series 10 (42mm)" },
417 { "Watch7,9", "n217bap", 0x12, 0x8310, "Apple Watch Series 10 (46mm)" },
418 { "Watch7,10", "n218sap", 0x14, 0x8310, "Apple Watch Series 10 (42mm Cellular)" },
419 { "Watch7,11", "n218bap", 0x16, 0x8310, "Apple Watch Series 10 (46mm Cellular)" },
420 { "Watch7,12", "n230ap", 0x22, 0x8310, "Apple Watch Ultra 3" },
421 { "Watch7,13", "n243sap", 0x28, 0x8310, "Apple Watch SE 3 (40mm)" },
422 { "Watch7,14", "n243bap", 0x2A, 0x8310, "Apple Watch SE 3 (44mm)" },
423 { "Watch7,15", "n244sap", 0x2C, 0x8310, "Apple Watch SE 3 (40mm Cellular)" },
424 { "Watch7,16", "n244bap", 0x2E, 0x8310, "Apple Watch SE 3 (44mm Cellular)" },
425 { "Watch7,17", "n227sap", 0x18, 0x8310, "Apple Watch Series 11 (42mm)" },
426 { "Watch7,18", "n227bap", 0x1A, 0x8310, "Apple Watch Series 11 (46mm)" },
427 { "Watch7,19", "n228sap", 0x1C, 0x8310, "Apple Watch Series 11 (42mm Cellular)" },
428 { "Watch7,20", "n228bap", 0x1E, 0x8310, "Apple Watch Series 11 (46mm Cellular)" },
429 /* Apple Silicon Macs */
430 { "ADP3,2", "j273aap", 0x42, 0x8027, "Developer Transition Kit (2020)" },
431 { "Macmini9,1", "j274ap", 0x22, 0x8103, "Mac mini (M1, 2020)" },
432 { "MacBookPro17,1", "j293ap", 0x24, 0x8103, "MacBook Pro (M1, 13-inch, 2020)" },
433 { "MacBookPro18,1", "j316sap", 0x0A, 0x6000, "MacBook Pro (M1 Pro, 16-inch, 2021)" },
434 { "MacBookPro18,2", "j316cap", 0x0A, 0x6001, "MacBook Pro (M1 Max, 16-inch, 2021)" },
435 { "MacBookPro18,3", "j314sap", 0x08, 0x6000, "MacBook Pro (M1 Pro, 14-inch, 2021)" },
436 { "MacBookPro18,4", "j314cap", 0x08, 0x6001, "MacBook Pro (M1 Max, 14-inch, 2021)" },
437 { "MacBookAir10,1", "j313ap", 0x26, 0x8103, "MacBook Air (M1, 2020)" },
438 { "iMac21,1", "j456ap", 0x28, 0x8103, "iMac 24-inch (M1, Two Ports, 2021)" },
439 { "iMac21,2", "j457ap", 0x2A, 0x8103, "iMac 24-inch (M1, Four Ports, 2021)" },
440 { "Mac13,1", "j375cap", 0x04, 0x6001, "Mac Studio (M1 Max, 2022)" },
441 { "Mac13,2", "j375dap", 0x0C, 0x6002, "Mac Studio (M1 Ultra, 2022)" },
442 { "Mac14,2", "j413ap", 0x28, 0x8112, "MacBook Air (M2, 2022)" },
443 { "Mac14,7", "j493ap", 0x2A, 0x8112, "MacBook Pro (M2, 13-inch, 2022)" },
444 { "Mac14,3", "j473ap", 0x24, 0x8112, "Mac mini (M2, 2023)" },
445 { "Mac14,5", "j414cap", 0x04, 0x6021, "MacBook Pro (14-inch, M2 Max, 2023)" },
446 { "Mac14,6", "j416cap", 0x06, 0x6021, "MacBook Pro (16-inch, M2 Max, 2023)" },
447 { "Mac14,8", "j180dap", 0x08, 0x6022, "Mac Pro (2023)" },
448 { "Mac14,9", "j414sap", 0x04, 0x6020, "MacBook Pro (14-inch, M2 Pro, 2023)" },
449 { "Mac14,10", "j416sap", 0x06, 0x6020, "MacBook Pro (16-inch, M2 Pro, 2023)" },
450 { "Mac14,12", "j474sap", 0x02, 0x6020, "Mac mini (M2 Pro, 2023)" },
451 { "Mac14,13", "j475cap", 0x0A, 0x6021, "Mac Studio (M2 Max, 2023)" },
452 { "Mac14,14", "j475dap", 0x0A, 0x6022, "Mac Studio (M2 Ultra, 2023)" },
453 { "Mac14,15", "j415ap", 0x2E, 0x8112, "MacBook Air (M2, 15-inch, 2023)" },
454 { "Mac15,3", "j504ap", 0x22, 0x8122, "MacBook Pro (14-inch, M3, Nov 2023)" },
455 { "Mac15,4", "j433ap", 0x28, 0x8122, "iMac 24-inch (M3, Two Ports, 2023)" },
456 { "Mac15,5", "j434ap", 0x2A, 0x8122, "iMac 24-inch (M3, Four Ports, 2023)" },
457 { "Mac15,6", "j514sap", 0x04, 0x6030, "MacBook Pro (14-inch, M3 Pro, Nov 2023)" },
458 { "Mac15,7", "j516sap", 0x06, 0x6030, "MacBook Pro (16-inch, M3 Pro, Nov 2023)" },
459 { "Mac15,8", "j514cap", 0x44, 0x6031, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
460 { "Mac15,9", "j516cap", 0x46, 0x6031, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
461 { "Mac15,10", "j514map", 0x44, 0x6034, "MacBook Pro (14-inch, M3 Max, Nov 2023)" },
462 { "Mac15,11", "j516map", 0x46, 0x6034, "MacBook Pro (16-inch, M3 Max, Nov 2023)" },
463 { "Mac15,12", "j613ap", 0x30, 0x8122, "MacBook Air (13-inch, M3, 2024)" },
464 { "Mac15,13", "j615ap", 0x32, 0x8122, "MacBook Air (15-inch, M3, 2024)" },
465 { "Mac15,14", "j575dap", 0x44, 0x6032, "Mac Studio (M3 Ultra, 2025)" },
466 { "Mac16,1", "j604ap", 0x22, 0x8132, "MacBook Pro (14-inch, M4, Nov 2024)" },
467 { "Mac16,2", "j623ap", 0x24, 0x8132, "iMac 24-inch (M4, Two Ports, 2024)" },
468 { "Mac16,3", "j624ap", 0x26, 0x8132, "iMac 24-inch (M4, Four Ports, 2024)" },
469 { "Mac16,5", "j616cap", 0x06, 0x6041, "MacBook Pro (16-inch, M4 Max, Nov 2024)" },
470 { "Mac16,6", "j614cap", 0x04, 0x6041, "MacBook Pro (14-inch, M4 Max, Nov 2024)" },
471 { "Mac16,7", "j616sap", 0x06, 0x6040, "MacBook Pro (16-inch, M4 Pro, Nov 2024)" },
472 { "Mac16,8", "j614sap", 0x04, 0x6040, "MacBook Pro (14-inch, M4 Pro, Nov 2024)" },
473 { "Mac16,9", "j575cap", 0x02, 0x6041, "Mac Studio (M4 Max, 2025)" },
474 { "Mac16,10", "j773gap", 0x2A, 0x8132, "Mac mini (M4, 2024)" },
475 { "Mac16,11", "j773sap", 0x02, 0x6040, "Mac mini (M4 Pro, 2024)" },
476 { "Mac16,12", "j713ap", 0x2C, 0x8132, "MacBook Air (13-inch, M4, 2025)" },
477 { "Mac16,13", "j715ap", 0x2E, 0x8132, "MacBook Air (15-inch, M4, 2025)" },
478 { "Mac17,2", "j704ap", 0x22, 0x8142, "MacBook Pro (14-inch, M5, 2025)" },
479 { "Mac17,3", "j813ap", 0x24, 0x8142, "MacBook Air (13-inch, M5, 2026)" },
480 { "Mac17,4", "j815ap", 0x26, 0x8142, "MacBook Air (15-inch, M5, 2026)" },
481 { "Mac17,5", "j700ap", 0x64, 0x8140, "MacBook Neo" },
482 { "Mac17,6", "j716cap", 0x0E, 0x6050, "MacBook Pro (16-inch, M5 Max, 2026)" },
483 { "Mac17,7", "j714cap", 0x0A, 0x6050, "MacBook Pro (14-inch, M5 Max, 2026)" },
484 { "Mac17,8", "j716sap", 0x0C, 0x6050, "MacBook Pro (16-inch, M5 Pro, 2026)" },
485 { "Mac17,9", "j714sap", 0x08, 0x6050, "MacBook Pro (14-inch, M5 Pro, 2026)" },
486 /* Apple Silicon VMs (supported by Virtualization.framework on macOS 12) */
487 { "VirtualMac2,1", "vma2macosap", 0x20, 0xFE00, "Apple Virtual Machine 1" },
488 /* Apple T2 Coprocessor */
489 { "iBridge2,1", "j137ap", 0x0A, 0x8012, "Apple T2 iMacPro1,1 (j137)" },
490 { "iBridge2,3", "j680ap", 0x0B, 0x8012, "Apple T2 MacBookPro15,1 (j680)" },
491 { "iBridge2,4", "j132ap", 0x0C, 0x8012, "Apple T2 MacBookPro15,2 (j132)" },
492 { "iBridge2,5", "j174ap", 0x0E, 0x8012, "Apple T2 Macmini8,1 (j174)" },
493 { "iBridge2,6", "j160ap", 0x0F, 0x8012, "Apple T2 MacPro7,1 (j160)" },
494 { "iBridge2,7", "j780ap", 0x07, 0x8012, "Apple T2 MacBookPro15,3 (j780)" },
495 { "iBridge2,8", "j140kap", 0x17, 0x8012, "Apple T2 MacBookAir8,1 (j140k)" },
496 { "iBridge2,10", "j213ap", 0x18, 0x8012, "Apple T2 MacBookPro15,4 (j213)" },
497 { "iBridge2,12", "j140aap", 0x37, 0x8012, "Apple T2 MacBookAir8,2 (j140a)" },
498 { "iBridge2,14", "j152fap", 0x3A, 0x8012, "Apple T2 MacBookPro16,1 (j152f)" },
499 { "iBridge2,15", "j230kap", 0x3F, 0x8012, "Apple T2 MacBookAir9,1 (j230k)" },
500 { "iBridge2,16", "j214kap", 0x3E, 0x8012, "Apple T2 MacBookPro16,2 (j214k)" },
501 { "iBridge2,19", "j185ap", 0x22, 0x8012, "Apple T2 iMac20,1 (j185)" },
502 { "iBridge2,20", "j185fap", 0x23, 0x8012, "Apple T2 iMac20,2 (j185f)" },
503 { "iBridge2,21", "j223ap", 0x3B, 0x8012, "Apple T2 MacBookPro16,3 (j223)" },
504 { "iBridge2,22", "j215ap", 0x38, 0x8012, "Apple T2 MacBookPro16,4 (j215)" },
505 /* Apple Displays */
506 { "AppleDisplay2,1", "j327ap", 0x22, 0x8030, "Studio Display" },
507 /* Apple Vision Pro */
508 { "RealityDevice14,1", "n301ap", 0x42, 0x8112, "Apple Vision Pro" },
509 { "RealityDevice17,1", "n301aap", 0x42, 0x8142, "Apple Vision Pro (M5)" },
510 /* Private Cloud Compute Research Environment */
511 { "iPhone99,11", "vresearch101ap", 0x90, 0xFE01, "iPhone 99,11" },
512 { NULL, NULL, -1, -1, NULL }
513};
514
515#ifndef USE_DUMMY
516static unsigned int crc32_lookup_t1[256] = {
517 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
518 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
519 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
520 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
521 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
522 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
523 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
524 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
525 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
526 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
527 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
528 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
529 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
530 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
531 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
532 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
533 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
534 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
535 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
536 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
537 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
538 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
539 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
540 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
541 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
542 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
543 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
544 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
545 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
546 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
547 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
548 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
549 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
550 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
551 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
552 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
553 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
554 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
555 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
556 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
557 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
558 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
559 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
560 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
561 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
562 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
563 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
564 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
565 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
566 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
567 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
568 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
569 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
570 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
571 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
572 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
573 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
574 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
575 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
576 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
577 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
578 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
579 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
580 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
581};
582
583#define crc32_step(a,b) \
584 a = (crc32_lookup_t1[(a & 0xFF) ^ ((unsigned char)b)] ^ (a >> 8))
585
586#ifdef _WIN32
587#pragma pack(1)
588typedef struct {
589 uint16_t vid;
590 uint16_t pid;
591 uint32_t unk;
592 char nonces[255];
593 char serial[255];
594 char manufacturer[255];
595 char product[255];
596} KIS_device_info;
597
598typedef struct {
599 uint8_t data[0x4000];
600 uint32_t size;
601 uint32_t unused;
602 uint64_t address;
603} KIS_upload_chunk;
604#pragma pack()
605#else
606#pragma pack(1)
607typedef struct {
608 uint16_t sequence; // A sequence number
609 uint8_t version; // Protocol version
610 uint8_t portal; // The "portal" to connect to
611 uint8_t argCount; // Number of arguments
612 uint8_t indexLo; // An index
613 uint8_t indexHiRplSizeLo; // High 2 bits of index + low 6 bytes of reply size
614 uint8_t rplSizeHi; // Reply size high bits, number of words the device should send
615 uint32_t reqSize; // Size of the complete request, including the arguments and payload, excluding the header
616 // Followed by arguments and payload data
617} KIS_req_header;
618
619typedef struct {
620 KIS_req_header hdr;
621 uint32_t value;
622} KIS_config_wr32;
623
624typedef struct {
625 uint8_t bLength ; ///< Size of this descriptor in bytes.
626 uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
627 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.
628
629 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.
630 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.
631 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.
632 uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
633
634 uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
635 uint16_t idProduct ; ///< Product ID (assigned by the manufacturer).
636 uint16_t bcdDevice ; ///< Device release number in binary-coded decimal.
637 uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer.
638 uint8_t iProduct ; ///< Index of string descriptor describing product.
639 uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number.
640
641 uint8_t bNumConfigurations ; ///< Number of possible configurations.
642} usb_device_descriptor;
643
644typedef struct {
645 KIS_req_header hdr;
646 union {
647 struct {
648 uint32_t tag;
649 uint32_t unk1;
650 uint32_t maxUploadSize;
651 uint32_t maxDownloadSize; // maybe???
652 uint64_t rambase;
653 uint32_t nonceOffset;
654 uint32_t pad;
655 uint8_t unkpad[0x20];
656 usb_device_descriptor deviceDescriptor;
657 };
658 uint8_t deviceInfo[0x300];
659 };
660 uint32_t rspsize;
661 uint32_t statuscode;
662} KIS_device_info;
663
664typedef struct {
665 KIS_req_header hdr;
666 uint64_t address;
667 uint32_t size;
668 uint8_t data[0x4000];
669} KIS_upload_chunk;
670
671typedef struct {
672 KIS_req_header hdr;
673 uint32_t size; // Number of bytes read/written
674 uint32_t status;
675} KIS_generic_reply;
676#pragma pack()
677#endif
678
679#pragma pack(1)
680typedef struct {
681 uint16_t cmdcode;
682#define MSG_VERSION_QUERY 0x000
683#define MSG_ECHO 0x801
684#define MSG_DUMP_BUFFER 0x802
685#define MSG_SEND_COMMAND 0x803
686#define MSG_READ_FILE 0x804
687#define MSG_SEND_FILE 0x805
688#define MSG_CRC 0x807
689#define MSG_ACK 0x808
690#define MSG_REJECT 0x809
691 uint16_t magic; // always 0x1234
692 uint32_t size;
693 uint32_t loadaddr; // 0x0 for commands, 0x09000000 for files
694} legacyCMD;
695#pragma pack()
696
697static THREAD_T th_event_handler = THREAD_T_NULL;
698struct collection listeners;
699static mutex_t listener_mutex;
700struct collection devices;
701static mutex_t device_mutex;
702#ifndef _WIN32
703#ifdef HAVE_IOKIT
704static CFRunLoopRef iokit_runloop = NULL;
705#else
706static libusb_context* irecv_hotplug_ctx = NULL;
707#endif
708#endif
709
710static void _irecv_deinit(void)
711{
712#ifndef USE_DUMMY
713#ifndef _WIN32
714#ifndef HAVE_IOKIT
715 if (libirecovery_context != NULL) {
716 libusb_exit(libirecovery_context);
717 libirecovery_context = NULL;
718 }
719#endif
720#endif
721 collection_free(&listeners);
722 mutex_destroy(&listener_mutex);
723#endif
724}
725
726INITIALIZER(_irecv_init)
727{
728 char* dbglvl = getenv("LIBIRECOVERY_DEBUG_LEVEL");
729 if (dbglvl) {
730 libirecovery_debug = strtol(dbglvl, NULL, 0);
731 irecv_set_debug_level(libirecovery_debug);
732 }
733#ifndef USE_DUMMY
734#ifndef _WIN32
735#ifndef HAVE_IOKIT
736 libusb_init(&libirecovery_context);
737#endif
738#endif
739 collection_init(&listeners);
740 mutex_init(&listener_mutex);
741#endif
742 atexit(_irecv_deinit);
743}
744
745#ifdef HAVE_IOKIT
746static int iokit_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
747{
748 IOReturn result;
749 IOUSBDevRequest request;
750 unsigned char descriptor[256];
751
752 request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
753 request.bRequest = kUSBRqGetDescriptor;
754 request.wValue = (kUSBStringDesc << 8); // | desc_index;
755 request.wIndex = 0; // All languages 0x409; // language
756 request.wLength = sizeof(descriptor) - 1;
757 request.pData = descriptor;
758 request.wLenDone = 0;
759
760 result = (*client->handle)->DeviceRequest(client->handle, &request);
761 if (result == kIOReturnNoDevice)
762 return IRECV_E_NO_DEVICE;
763 if (result == kIOReturnNotOpen)
764 return IRECV_E_USB_STATUS;
765 if (result != kIOReturnSuccess)
766 return IRECV_E_UNKNOWN_ERROR;
32 767
33int irecv_write_file(const char* filename, const void* data, size_t size); 768 if (descriptor[0] >= 4) { // && descriptor[2] == 0x9 && descriptor[3] == 0x4) {
34int irecv_read_file(const char* filename, char** data, uint32_t* size); 769
770 request.wValue = (kUSBStringDesc << 8) | desc_index;
771 request.wIndex = descriptor[2] + (descriptor[3] << 8);
772 request.wLenDone = 0;
773 result = (*client->handle)->DeviceRequest(client->handle, &request);
774
775 if (result == kIOReturnNoDevice)
776 return IRECV_E_NO_DEVICE;
777 if (result == kIOReturnNotOpen)
778 return IRECV_E_USB_STATUS;
779 if (result != kIOReturnSuccess)
780 return IRECV_E_UNKNOWN_ERROR;
781
782 int i = 2, j = 0;
783 for ( ; i < descriptor[0]; i += 2, j += 1) {
784 buffer[j] = descriptor[i];
785 }
786 buffer[j] = 0;
787
788 return request.wLenDone;
789 }
790 return IRECV_E_UNKNOWN_ERROR;
791}
792#endif
793
794static int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size)
795{
796#ifndef _WIN32
797#ifdef HAVE_IOKIT
798 return iokit_get_string_descriptor_ascii(client, desc_index, buffer, size);
799#else
800 return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size);
801#endif
802#else
803 irecv_error_t ret;
804 unsigned short langid = 0;
805 unsigned char data[256];
806 int di, si;
807 memset(data, 0, sizeof(data));
808 memset(buffer, 0, size);
809
810 ret = irecv_usb_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data)-1, USB_TIMEOUT);
811
812 if (ret < 0) return ret;
813 if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
814 if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR;
815
816 for (di = 0, si = 2; si < data[0]; si += 2) {
817 if (di >= (size - 1)) break;
818 if (data[si + 1]) {
819 /* high byte */
820 buffer[di++] = '?';
821 } else {
822 buffer[di++] = data[si];
823 }
824 }
825 buffer[di] = 0;
826
827 return di;
828#endif
829}
830
831static void irecv_load_device_info_from_iboot_string(irecv_client_t client, const char* iboot_string)
832{
833 if (!client || !iboot_string) {
834 return;
835 }
836
837 memset(&client->device_info, '\0', sizeof(struct irecv_device_info));
838
839 client->device_info.serial_string = strdup(iboot_string);
840
841 char* ptr;
842
843 ptr = strstr(iboot_string, "CPID:");
844 if (ptr != NULL) {
845 sscanf(ptr, "CPID:%x", &client->device_info.cpid);
846 client->device_info.have_cpid = 1;
847 } else {
848 // early iOS 1 doesn't identify itself
849 client->device_info.cpid = 0x8900;
850 }
851
852 ptr = strstr(iboot_string, "CPRV:");
853 if (ptr != NULL) {
854 sscanf(ptr, "CPRV:%x", &client->device_info.cprv);
855 client->device_info.have_cprv = 1;
856 }
857
858 ptr = strstr(iboot_string, "CPFM:");
859 if (ptr != NULL) {
860 sscanf(ptr, "CPFM:%x", &client->device_info.cpfm);
861 client->device_info.have_cpfm = 1;
862 }
863
864 ptr = strstr(iboot_string, "SCEP:");
865 if (ptr != NULL) {
866 sscanf(ptr, "SCEP:%x", &client->device_info.scep);
867 client->device_info.have_scep = 1;
868 }
869
870 ptr = strstr(iboot_string, "BDID:");
871 if (ptr != NULL) {
872 uint64_t bdid = 0;
873 sscanf(ptr, "BDID:%" SCNx64, &bdid);
874 client->device_info.bdid = (unsigned int)bdid;
875 client->device_info.have_bdid = 1;
876 }
877
878 ptr = strstr(iboot_string, "ECID:");
879 if (ptr != NULL) {
880 sscanf(ptr, "ECID:%" SCNx64, &client->device_info.ecid);
881 client->device_info.have_ecid = 1;
882 }
883
884 ptr = strstr(iboot_string, "IBFL:");
885 if (ptr != NULL) {
886 sscanf(ptr, "IBFL:%x", &client->device_info.ibfl);
887 client->device_info.have_ibfl = 1;
888 }
889
890 char tmp[256];
891 tmp[0] = '\0';
892 ptr = strstr(iboot_string, "SRNM:[");
893 if (ptr != NULL) {
894 sscanf(ptr, "SRNM:[%s]", tmp);
895 ptr = strrchr(tmp, ']');
896 if (ptr != NULL) {
897 *ptr = '\0';
898 }
899 client->device_info.srnm = strdup(tmp);
900 }
901
902 tmp[0] = '\0';
903 ptr = strstr(iboot_string, "IMEI:[");
904 if (ptr != NULL) {
905 sscanf(ptr, "IMEI:[%s]", tmp);
906 ptr = strrchr(tmp, ']');
907 if (ptr != NULL) {
908 *ptr = '\0';
909 }
910 client->device_info.imei = strdup(tmp);
911 }
912
913 tmp[0] = '\0';
914 ptr = strstr(iboot_string, "SRTG:[");
915 if (ptr != NULL) {
916 sscanf(ptr, "SRTG:[%s]", tmp);
917 ptr = strrchr(tmp, ']');
918 if (ptr != NULL) {
919 *ptr = '\0';
920 }
921 client->device_info.srtg = strdup(tmp);
922 }
923
924 client->device_info.pid = client->mode;
925 if (client->isKIS) {
926 client->device_info.pid = KIS_PRODUCT_ID;
927 }
928}
929
930static void irecv_copy_nonce_with_tag_from_buffer(const char* tag, unsigned char** nonce, unsigned int* nonce_size, const char *buf)
931{
932 int taglen = strlen(tag);
933 int nlen = 0;
934 const char* nonce_string = NULL;
935 const char* p = buf;
936 char* colon = NULL;
937 do {
938 colon = strchr(p, ':');
939 if (!colon)
940 break;
941 if (colon-taglen < p) {
942 break;
943 }
944 char *space = strchr(colon, ' ');
945 if (strncmp(colon-taglen, tag, taglen) == 0) {
946 p = colon+1;
947 if (!space) {
948 nlen = strlen(p);
949 } else {
950 nlen = space-p;
951 }
952 nonce_string = p;
953 nlen/=2;
954 break;
955 } else {
956 if (!space) {
957 break;
958 } else {
959 p = space+1;
960 }
961 }
962 } while (colon);
963
964 if (nlen == 0) {
965 debug("%s: WARNING: couldn't find tag %s in string %s\n", __func__, tag, buf);
966 return;
967 }
968
969 unsigned char *nn = malloc(nlen);
970 if (!nn) {
971 return;
972 }
35 973
36irecv_error_t irecv_open(irecv_client_t* pclient) {
37 int i = 0; 974 int i = 0;
38 char serial[256]; 975 for (i = 0; i < nlen; i++) {
976 int val = 0;
977 if (sscanf(nonce_string+(i*2), "%02X", &val) == 1) {
978 nn[i] = (unsigned char)val;
979 } else {
980 debug("%s: ERROR: unexpected data in nonce result (%2s)\n", __func__, nonce_string+(i*2));
981 break;
982 }
983 }
984
985 if (i != nlen) {
986 debug("%s: ERROR: unable to parse nonce\n", __func__);
987 free(nn);
988 return;
989 }
990
991 *nonce = nn;
992 *nonce_size = nlen;
993}
994
995static void irecv_copy_nonce_with_tag(irecv_client_t client, const char* tag, unsigned char** nonce, unsigned int* nonce_size)
996{
997 if (!client || !tag) {
998 return;
999 }
1000
1001 char buf[256];
1002 int len = 0;
1003
1004 *nonce = NULL;
1005 *nonce_size = 0;
1006
1007 memset(buf, 0, sizeof(buf));
1008 len = irecv_get_string_descriptor_ascii(client, 1, (unsigned char*)buf, sizeof(buf)-1);
1009 if (len < 0) {
1010 debug("%s: got length: %d\n", __func__, len);
1011 return;
1012 }
1013
1014 buf[len] = 0;
1015
1016 irecv_copy_nonce_with_tag_from_buffer(tag,nonce,nonce_size,buf);
1017}
1018
1019#ifndef _WIN32
1020static 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)
1021{
1022 if (argCount > UINT8_MAX) {
1023 return IRECV_E_INVALID_INPUT;
1024 }
1025
1026 if (index >= (1 << 10)) {
1027 return IRECV_E_INVALID_INPUT;
1028 }
1029
1030 if (rplWords >= (1 << 14)) {
1031 return IRECV_E_INVALID_INPUT;
1032 }
1033
1034 size_t reqSize = payloadSize + (argCount << 2);
1035 if (reqSize > UINT32_MAX) {
1036 return IRECV_E_INVALID_INPUT;
1037 }
1038
1039 hdr->sequence = 0; // Doesn't matter
1040 hdr->version = 0xA0;
1041 hdr->portal = portal;
1042 hdr->argCount = (uint8_t) argCount;
1043 hdr->indexLo = (uint8_t) (index & 0xFF);
1044 hdr->indexHiRplSizeLo = (uint8_t) (((index >> 8) & 0x3) | ((rplWords << 2) & 0xFC));
1045 hdr->rplSizeHi = (uint8_t) ((rplWords >> 6) & 0xFF);
1046 hdr->reqSize = (uint32_t) reqSize;
1047
1048 return IRECV_E_SUCCESS;
1049}
1050
1051static irecv_error_t irecv_kis_request(irecv_client_t client, KIS_req_header *req, size_t reqSize, KIS_req_header *rpl, size_t *rplSize)
1052{
1053 int endpoint = 0;
1054 switch (req->portal) {
1055 case KIS_PORTAL_CONFIG:
1056 endpoint = 1;
1057 break;
1058 case KIS_PORTAL_RSM:
1059 endpoint = 3;
1060 break;
1061 default:
1062 debug("Don't know which endpoint to use for portal %d\n", req->portal);
1063 return IRECV_E_INVALID_INPUT;
1064 }
1065
1066 int sent = 0;
1067 irecv_error_t err = irecv_usb_bulk_transfer(client, endpoint, (unsigned char *) req, reqSize, &sent, USB_TIMEOUT);
1068 if (err != IRECV_E_SUCCESS) {
1069 debug("[send] irecv_usb_bulk_transfer failed, error %d\n", err);
1070 return err;
1071 }
1072
1073 if ((size_t) sent != reqSize) {
1074 debug("sent != reqSize\n");
1075 return IRECV_E_USB_UPLOAD;
1076 }
1077
1078 int rcvd = 0;
1079 err = irecv_usb_bulk_transfer(client, endpoint | 0x80, (unsigned char *) rpl, *rplSize, &rcvd, USB_TIMEOUT);
1080 if (err != IRECV_E_SUCCESS) {
1081 debug("[rcv] irecv_usb_bulk_transfer failed, error %d\n", err);
1082 return err;
1083 }
1084
1085 *rplSize = rcvd;
1086
1087 return IRECV_E_SUCCESS;
1088}
1089
1090static irecv_error_t irecv_kis_config_write32(irecv_client_t client, uint8_t portal, uint16_t index, uint32_t value)
1091{
1092 KIS_config_wr32 req = {};
1093 KIS_generic_reply rpl = {};
1094 irecv_error_t err = irecv_kis_request_init(&req.hdr, portal, index, 1, 0, 1);
1095 if (err != IRECV_E_SUCCESS) {
1096 debug("Failed to init KIS request, error %d\n", err);
1097 return err;
1098 }
1099
1100 req.value = value;
1101
1102 size_t rplSize = sizeof(rpl);
1103 err = irecv_kis_request(client, &req.hdr, sizeof(req), &rpl.hdr, &rplSize);
1104 if (err != IRECV_E_SUCCESS) {
1105 debug("Failed to send KIS request, error %d\n", err);
1106 return err;
1107 }
1108
1109 if (rpl.size != 4) {
1110 debug("Failed to write config, %d bytes written, status %d\n", rpl.size, rpl.status);
1111 return err;
1112 }
1113
1114 return IRECV_E_SUCCESS;
1115}
1116
1117static int irecv_kis_read_string(KIS_device_info *di, size_t off, char *buf, size_t buf_size)
1118{
1119 off *= 4;
1120
1121 size_t inputSize = sizeof(KIS_device_info) - sizeof(KIS_req_header);
1122
1123 if ((off + 2) > inputSize)
1124 return 0;
1125
1126 uint8_t len = di->deviceInfo[off];
1127 uint8_t type = di->deviceInfo[off + 1];
1128
1129 if (len & 1)
1130 return 0;
1131
1132 if (len/2 >= buf_size)
1133 return 0;
1134
1135 if ((off + 2 + len) > inputSize)
1136 return 0;
1137
1138 if (type != 3)
1139 return 0;
1140
1141 buf[len >> 1] = 0;
1142 for (size_t i = 0; i < len; i += 2) {
1143 buf[i >> 1] = di->deviceInfo[i + off + 2];
1144 }
1145
1146 return len/2;
1147}
1148#endif
1149
1150static irecv_error_t irecv_kis_init(irecv_client_t client)
1151{
1152#ifndef _WIN32
1153 irecv_error_t err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_A, KIS_ENABLE_A_VAL);
1154 if (err != IRECV_E_SUCCESS) {
1155 debug("Failed to write to KIS_INDEX_ENABLE_A, error %d\n", err);
1156 return err;
1157 }
1158
1159 err = irecv_kis_config_write32(client, KIS_PORTAL_CONFIG, KIS_INDEX_ENABLE_B, KIS_ENABLE_B_VAL);
1160 if (err != IRECV_E_SUCCESS) {
1161 debug("Failed to write to KIS_INDEX_ENABLE_B, error %d\n", err);
1162 return err;
1163 }
1164#endif
1165 client->isKIS = 1;
1166
1167 return IRECV_E_SUCCESS;
1168}
1169
1170static irecv_error_t irecv_kis_load_device_info(irecv_client_t client)
1171{
1172 debug("Loading device info in KIS mode...\n");
1173#ifdef _WIN32
1174 KIS_device_info kisInfo;
1175 DWORD transferred = 0;
1176 int ret = DeviceIoControl(client->handle, 0x220004, NULL, 0, &kisInfo, sizeof(kisInfo), (PDWORD)&transferred, NULL);
1177 if (ret) {
1178 debug("Serial: %s\n", kisInfo.serial);
1179 irecv_load_device_info_from_iboot_string(client, kisInfo.serial);
1180 debug("Manufacturer: %s\n", kisInfo.manufacturer);
1181 debug("Product: %s\n", kisInfo.product);
1182 debug("Nonces: %s\n", kisInfo.nonces);
1183 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, kisInfo.nonces);
1184 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, kisInfo.nonces);
1185 debug("VID: 0x%04x\n", kisInfo.vid);
1186 debug("PID: 0x%04x\n", kisInfo.pid);
1187 }
1188 client->mode = kisInfo.pid;
1189#else
1190 KIS_req_header req = {};
1191 KIS_device_info di = {};
1192 irecv_error_t err = irecv_kis_request_init(&req, KIS_PORTAL_RSM, KIS_INDEX_GET_INFO, 0, 0, sizeof(di.deviceInfo)/4);
1193 if (err != IRECV_E_SUCCESS) {
1194 debug("Failed to init KIS request, error %d\n", err);
1195 return err;
1196 }
1197
1198 size_t rcvSize = sizeof(di);
1199 err = irecv_kis_request(client, &req, sizeof(req), &di.hdr, &rcvSize);
1200 if (err != IRECV_E_SUCCESS) {
1201 debug("Failed to send KIS request, error %d\n", err);
1202 return err;
1203 }
1204
1205 char buf[0x100];
1206 int len = 0;
1207
1208 len = irecv_kis_read_string(&di, di.deviceDescriptor.iSerialNumber, buf, sizeof(buf));
1209 if (len == 0)
1210 return IRECV_E_INVALID_INPUT;
1211 debug("Serial: %s\n", buf);
1212
1213 irecv_load_device_info_from_iboot_string(client, buf);
1214
1215 len = irecv_kis_read_string(&di, di.deviceDescriptor.iManufacturer, buf, sizeof(buf));
1216 if (len == 0)
1217 return IRECV_E_INVALID_INPUT;
1218 debug("Manufacturer: %s\n", buf);
1219
1220 len = irecv_kis_read_string(&di, di.deviceDescriptor.iProduct, buf, sizeof(buf));
1221 if (len == 0)
1222 return IRECV_E_INVALID_INPUT;
1223 debug("Product: %s\n", buf);
1224
1225 len = irecv_kis_read_string(&di, di.nonceOffset, buf, sizeof(buf));
1226 if (len == 0)
1227 return IRECV_E_INVALID_INPUT;
1228 debug("Nonces: %s\n", buf);
1229
1230 irecv_copy_nonce_with_tag_from_buffer("NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size, buf);
1231 irecv_copy_nonce_with_tag_from_buffer("SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size, buf);
1232
1233 debug("VID: 0x%04x\n", di.deviceDescriptor.idVendor);
1234 debug("PID: 0x%04x\n", di.deviceDescriptor.idProduct);
1235
1236 client->mode = di.deviceDescriptor.idProduct;
1237#endif
1238 return IRECV_E_SUCCESS;
1239}
1240
1241#ifdef _WIN32
1242static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
1243static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
1244static const GUID GUID_DEVINTERFACE_KIS = {0xB36F4137L, 0xF4EF, 0x4BFC, {0xA2, 0x5A, 0xC2, 0x41, 0x07, 0x68, 0xEE, 0x37}};
1245static const GUID GUID_DEVINTERFACE_PORTDFU = {0xAF633FF1L, 0x1170, 0x4CA6, {0xAE, 0x9E, 0x08, 0xD0, 0x01, 0x42, 0x1E, 0xAA}};
1246
1247typedef struct usb_control_request {
1248 uint8_t bmRequestType;
1249 uint8_t bRequest;
1250 uint16_t wValue;
1251 uint16_t wIndex;
1252 uint16_t wLength;
1253
1254 char data[];
1255} usb_control_request;
1256
1257static irecv_error_t win32_open_with_ecid(irecv_client_t* client, uint64_t ecid)
1258{
1259 int found = 0;
1260 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
1261 irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
1262 memset(_client, 0, sizeof(struct irecv_client_private));
1263
1264 int k;
1265 for (k = 0; !found && guids[k]; k++) {
1266 DWORD i;
1267 SP_DEVICE_INTERFACE_DATA currentInterface;
1268 HDEVINFO usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
1269 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
1270 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
1271 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
1272 _client->handle = INVALID_HANDLE_VALUE;
1273 DWORD requiredSize = 0;
1274 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
1275 SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
1276 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
1277 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
1278 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
1279 free(details);
1280 continue;
1281 }
1282
1283 unsigned int pid = 0;
1284 unsigned int vid = 0;
1285 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid) != 2) {
1286 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
1287 free(details);
1288 continue;
1289 }
1290 if (vid != APPLE_VENDOR_ID) {
1291 free(details);
1292 continue;
1293 }
1294
1295 // make sure the current device is actually in the right mode for the given driver interface
1296 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
1297 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
1298 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
1299 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
1300 ) {
1301 free(details);
1302 continue;
1303 }
1304 if (guids[k] == &GUID_DEVINTERFACE_KIS) {
1305 pid = KIS_PRODUCT_ID;
1306 }
1307
1308 _client->handle = CreateFileA(details->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
1309 if (_client->handle == INVALID_HANDLE_VALUE) {
1310 debug("%s: Failed to open device path %s: %d\n", __func__, details->DevicePath, (int)GetLastError());
1311 free(details);
1312 continue;
1313 }
1314 _client->mode = pid;
1315
1316 if (ecid == IRECV_K_WTF_MODE) {
1317 if (_client->mode != IRECV_K_WTF_MODE) {
1318 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1319 CloseHandle(_client->handle);
1320 free(details);
1321 continue;
1322 } else {
1323 ecid = 0;
1324 }
1325 }
1326
1327 if ((ecid != 0) && (_client->mode == IRECV_K_WTF_MODE)) {
1328 /* we can't get ecid in WTF mode */
1329 CloseHandle(_client->handle);
1330 free(details);
1331 continue;
1332 }
1333
1334 char serial_str[256];
1335 serial_str[0] = '\0';
1336
1337 if (_client->mode != KIS_PRODUCT_ID) {
1338 char *p = (char*)details->DevicePath;
1339 while ((p = strstr(p, "\\usb"))) {
1340 if (sscanf(p, "\\usb#vid_05ac&pid_%*04x#%s", serial_str) == 1)
1341 break;
1342 p += 4;
1343 }
1344 free(details);
1345
1346 if (serial_str[0] == '\0') {
1347 CloseHandle(_client->handle);
1348 continue;
1349 }
1350 p = strchr(serial_str, '#');
1351 if (p) {
1352 *p = '\0';
1353 }
1354
1355 unsigned int j;
1356 for (j = 0; j < strlen(serial_str); j++) {
1357 if (serial_str[j] == '_') {
1358 serial_str[j] = ' ';
1359 } else {
1360 serial_str[j] = toupper(serial_str[j]);
1361 }
1362 }
1363
1364 irecv_load_device_info_from_iboot_string(_client, serial_str);
1365 }
1366
1367 if (ecid != 0 && _client->mode != KIS_PRODUCT_ID) {
1368 if (_client->device_info.ecid != ecid) {
1369 CloseHandle(_client->handle);
1370 continue;
1371 }
1372 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
1373 }
1374 found = 1;
1375 break;
1376 }
1377 SetupDiDestroyDeviceInfoList(usbDevices);
1378 }
1379
1380 if (!found) {
1381 irecv_close(_client);
1382 return IRECV_E_UNABLE_TO_CONNECT;
1383 }
1384
1385 *client = _client;
1386
1387 return IRECV_E_SUCCESS;
1388}
1389#endif
1390
1391#ifdef HAVE_IOKIT
1392static void iokit_cfdictionary_set_short(CFMutableDictionaryRef dict, const void *key, SInt16 value)
1393{
1394 CFNumberRef numberRef;
1395
1396 numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &value);
1397 if (numberRef) {
1398 CFDictionarySetValue(dict, key, numberRef);
1399 CFRelease(numberRef);
1400 }
1401}
1402#endif
1403
1404static int check_context(irecv_client_t client)
1405{
1406 if (client == NULL || client->handle == NULL) {
1407 return IRECV_E_NO_DEVICE;
1408 }
1409
1410 return IRECV_E_SUCCESS;
1411}
1412#endif
1413
1414#ifndef USE_DUMMY
1415#ifdef HAVE_IOKIT
1416static 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)
1417{
1418 IOReturn result;
1419 IOUSBDevRequestTO req;
1420
1421 bzero(&req, sizeof(req));
1422 req.bmRequestType = bm_request_type;
1423 req.bRequest = b_request;
1424 req.wValue = OSSwapLittleToHostInt16(w_value);
1425 req.wIndex = OSSwapLittleToHostInt16(w_index);
1426 req.wLength = OSSwapLittleToHostInt16(w_length);
1427 req.pData = data;
1428 req.noDataTimeout = timeout;
1429 req.completionTimeout = timeout;
1430
1431 result = (*client->handle)->DeviceRequestTO(client->handle, &req);
1432 switch (result) {
1433 case kIOReturnSuccess: return req.wLenDone;
1434 case kIOUSBPipeStalled: return IRECV_E_PIPE;
1435 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
1436 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
1437 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
1438 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1439 default:
1440 return IRECV_E_UNKNOWN_ERROR;
1441 }
1442}
1443#else
1444#ifdef __APPLE__
1445 void dummy_callback(void) { }
1446#endif
1447#endif
1448#endif
1449
1450int 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)
1451{
1452#ifdef USE_DUMMY
1453 return IRECV_E_UNSUPPORTED;
1454#else
1455#ifndef _WIN32
1456#ifdef HAVE_IOKIT
1457 return iokit_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1458#else
1459 return libusb_control_transfer(client->handle, bm_request_type, b_request, w_value, w_index, data, w_length, timeout);
1460#endif
1461#else
1462 DWORD count = 0;
1463 BOOL bRet;
1464 OVERLAPPED overlapped;
1465
1466 if (data == NULL)
1467 w_length = 0;
1468
1469 usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + w_length);
1470 packet->bmRequestType = bm_request_type;
1471 packet->bRequest = b_request;
1472 packet->wValue = w_value;
1473 packet->wIndex = w_index;
1474 packet->wLength = w_length;
1475
1476 if (bm_request_type < 0x80 && w_length > 0) {
1477 memcpy(packet->data, data, w_length);
1478 }
1479
1480 memset(&overlapped, 0, sizeof(overlapped));
1481 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1482 DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + w_length, packet, sizeof(usb_control_request) + w_length, NULL, &overlapped);
1483 WaitForSingleObject(overlapped.hEvent, timeout);
1484 bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE);
1485 CloseHandle(overlapped.hEvent);
1486 if (!bRet) {
1487 CancelIo(client->handle);
1488 free(packet);
1489 return -1;
1490 }
1491
1492 count -= sizeof(usb_control_request);
1493 if (count > 0) {
1494 if (bm_request_type >= 0x80) {
1495 memcpy(data, packet->data, count);
1496 }
1497 }
1498 free(packet);
1499
1500 return count;
1501#endif
1502#endif
1503}
1504
1505#ifndef WIN32
1506#ifdef HAVE_IOKIT
1507
1508static void iokit_async_cb(void *refcon, kern_return_t ret, void *arg_0)
1509{
1510 struct irecv_async_transfer* transfer = refcon;
1511
1512 if(transfer != NULL) {
1513 transfer->ret = ret;
1514 memcpy(&transfer->len, &arg_0, sizeof(transfer->len));
1515 CFRunLoopStop(CFRunLoopGetCurrent());
1516 }
1517}
1518
1519static 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)
1520{
1521 IOReturn result;
1522 IOUSBDevRequest req;
1523
1524 bzero(&req, sizeof(req));
1525 req.bmRequestType = bm_request_type;
1526 req.bRequest = b_request;
1527 req.wValue = OSSwapLittleToHostInt16(w_value);
1528 req.wIndex = OSSwapLittleToHostInt16(w_index);
1529 req.wLength = OSSwapLittleToHostInt16(w_length);
1530 req.pData = data;
1531 result = (*client->handle)->DeviceRequestAsync(client->handle, &req, iokit_async_cb, transfer);
1532 switch (result) {
1533 case kIOReturnSuccess: return IRECV_E_SUCCESS;
1534 case kIOUSBPipeStalled: return IRECV_E_PIPE;
1535 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
1536 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
1537 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
1538 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1539 default:
1540 return IRECV_E_UNKNOWN_ERROR;
1541 }
1542}
1543
1544#else
1545#ifndef USE_DUMMY
1546
1547static void async_cb(struct libusb_transfer* usb_transfer) {
1548 struct irecv_async_transfer* transfer = usb_transfer->user_data;
1549 transfer->ret = usb_transfer->status;
1550 transfer->len += usb_transfer->actual_length;
1551}
1552
1553#endif
1554#endif
1555#endif
1556
1557IRECV_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) {
1558#ifdef USE_DUMMY
1559 return IRECV_E_UNSUPPORTED;
1560#else
1561#ifndef _WIN32
1562#ifdef HAVE_IOKIT
1563 return iokit_async_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, NULL);
1564#else
1565 irecv_error_t error;
1566
1567 unsigned char* buffer = malloc(w_length + 8);
1568 if(!buffer) {
1569 return IRECV_E_OUT_OF_MEMORY;
1570 }
1571 struct libusb_transfer* usb_transfer = libusb_alloc_transfer(0);
1572 if(!usb_transfer) {
1573 free(buffer);
1574 return IRECV_E_OUT_OF_MEMORY;
1575 }
1576 memcpy((buffer + 8), data, w_length);
1577 libusb_fill_control_setup(buffer, bm_request_type, b_request, w_value, w_index, w_length);
1578 libusb_fill_control_transfer(usb_transfer, client->handle, buffer, NULL, NULL, 0);
1579 usb_transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
1580 error = libusb_submit_transfer(usb_transfer);
1581 if(error != 0) {
1582 free(buffer);
1583 return error;
1584 }
1585 free(buffer);
1586 return IRECV_E_SUCCESS;
1587#endif
1588#else
1589 return IRECV_E_UNSUPPORTED;
1590#endif
1591#endif
1592
1593}
1594
1595IRECV_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) {
1596#ifdef USE_DUMMY
1597 return IRECV_E_UNSUPPORTED;
1598#else
1599#ifndef _WIN32
1600 irecv_error_t error;
1601 struct irecv_async_transfer transfer;
1602 bzero(&transfer, sizeof(struct irecv_async_transfer));
1603
1604#ifdef HAVE_IOKIT
1605
1606 error = iokit_async_usb_control_transfer(client, bm_request_type, b_request, w_value, w_index, data, w_length, &transfer);
1607 if(error != IRECV_E_SUCCESS) {
1608 return error;
1609 }
1610 usleep(u_time);
1611 error = (*client->handle)->USBDeviceAbortPipeZero(client->handle);
1612 if(error != kIOReturnSuccess) {
1613 return IRECV_E_UNKNOWN_ERROR;
1614 }
1615 while(transfer.ret != kIOReturnAborted){
1616 CFRunLoopRun();
1617 }
1618 return transfer.len;
1619#else
1620 unsigned char* buffer = malloc(w_length + 8);
1621 if(!buffer) {
1622 return IRECV_E_OUT_OF_MEMORY;
1623 }
1624 struct libusb_transfer* usb_transfer = libusb_alloc_transfer(0);
1625 if(!usb_transfer) {
1626 free(buffer);
1627 return IRECV_E_OUT_OF_MEMORY;
1628 }
1629 memcpy((buffer + 8), data, w_length);
1630 libusb_fill_control_setup(buffer, bm_request_type, b_request, w_value, w_index, w_length);
1631 libusb_fill_control_transfer(usb_transfer, client->handle, buffer, async_cb, &transfer, 0);
1632 usb_transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
1633 error = libusb_submit_transfer(usb_transfer);
1634 if(error != 0) {
1635 free(buffer);
1636 return error;
1637 }
1638
1639 usleep(u_time);
1640
1641 error = libusb_cancel_transfer(usb_transfer);
1642 if(error != 0) {
1643 free(buffer);
1644 return error;
1645 }
1646 free(buffer);
1647
1648 while(transfer.ret != LIBUSB_TRANSFER_CANCELLED){
1649 libusb_handle_events_completed(libirecovery_context, NULL);
1650 }
1651 return transfer.len;
1652#endif
1653#else
1654 return IRECV_E_UNSUPPORTED;
1655#endif
1656#endif
1657}
1658
1659#ifndef USE_DUMMY
1660#ifdef HAVE_IOKIT
1661static int iokit_usb_bulk_transfer(irecv_client_t client,
1662 unsigned char endpoint,
1663 unsigned char *data,
1664 int length,
1665 int *transferred,
1666 unsigned int timeout)
1667{
1668 IOReturn result;
1669 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1670 UInt32 size = length;
1671 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1672 UInt8 numEndpoints;
1673
1674 if (!intf) return IRECV_E_USB_INTERFACE;
1675
1676 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1677
1678 if (result != kIOReturnSuccess)
1679 return IRECV_E_USB_INTERFACE;
1680
1681 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1682 UInt8 direction = 0;
1683 UInt8 number = 0;
1684 UInt8 transferType = 0;
1685 UInt16 maxPacketSize = 0;
1686 UInt8 interval = 0;
1687
1688 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1689 if (result != kIOReturnSuccess)
1690 continue;
1691
1692 if (direction == 3)
1693 direction = isUSBIn;
1694
1695 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1696 continue;
1697
1698 // Just because
1699 result = (*intf)->GetPipeStatus(intf, pipeRef);
1700 switch (result) {
1701 case kIOReturnSuccess: break;
1702 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1703 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1704 default: return IRECV_E_USB_STATUS;
1705 }
1706
1707 // Do the transfer
1708 if (isUSBIn) {
1709 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1710 if (result != kIOReturnSuccess)
1711 return IRECV_E_PIPE;
1712 *transferred = size;
1713
1714 return IRECV_E_SUCCESS;
1715 }
1716 else {
1717 // IOUSBInterfaceClass::interfaceWritePipe (intf?, pipeRef==1, data, size=0x8000)
1718 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1719 if (result != kIOReturnSuccess)
1720 return IRECV_E_PIPE;
1721 *transferred = size;
1722
1723 return IRECV_E_SUCCESS;
1724 }
1725 }
1726
1727 return IRECV_E_USB_INTERFACE;
1728}
1729
1730static int iokit_usb_interrupt_transfer(irecv_client_t client,
1731 unsigned char endpoint,
1732 unsigned char *data,
1733 int length,
1734 int *transferred,
1735 unsigned int timeout)
1736{
1737 IOReturn result;
1738 IOUSBInterfaceInterface300 **intf = client->usbInterface;
1739 UInt32 size = length;
1740 UInt8 isUSBIn = (endpoint & kUSBbEndpointDirectionMask) != 0;
1741 UInt8 numEndpoints;
1742
1743 if (!intf) return IRECV_E_USB_INTERFACE;
1744
1745 result = (*intf)->GetNumEndpoints(intf, &numEndpoints);
1746
1747 if (result != kIOReturnSuccess)
1748 return IRECV_E_USB_INTERFACE;
1749
1750 for (UInt8 pipeRef = 0; pipeRef <= numEndpoints; pipeRef++) {
1751 UInt8 direction = 0;
1752 UInt8 number = 0;
1753 UInt8 transferType = 0;
1754 UInt16 maxPacketSize = 0;
1755 UInt8 interval = 0;
1756
1757 result = (*intf)->GetPipeProperties(intf, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval);
1758 if (result != kIOReturnSuccess)
1759 continue;
1760
1761 if (direction == 3)
1762 direction = isUSBIn;
1763
1764 if (number != (endpoint & ~kUSBbEndpointDirectionMask) || direction != isUSBIn)
1765 continue;
1766
1767 // Just because
1768 result = (*intf)->GetPipeStatus(intf, pipeRef);
1769 switch (result) {
1770 case kIOReturnSuccess: break;
1771 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
1772 case kIOReturnNotOpen: return IRECV_E_UNABLE_TO_CONNECT;
1773 default: return IRECV_E_USB_STATUS;
1774 }
1775
1776 // Do the transfer
1777 if (isUSBIn) {
1778 result = (*intf)->ReadPipeTO(intf, pipeRef, data, &size, timeout, timeout);
1779 if (result != kIOReturnSuccess)
1780 return IRECV_E_PIPE;
1781 *transferred = size;
1782
1783 return IRECV_E_SUCCESS;
1784 }
1785 else {
1786 result = (*intf)->WritePipeTO(intf, pipeRef, data, size, timeout, timeout);
1787 if (result != kIOReturnSuccess)
1788 return IRECV_E_PIPE;
1789 *transferred = size;
1790
1791 return IRECV_E_SUCCESS;
1792 }
1793 }
1794
1795 return IRECV_E_USB_INTERFACE;
1796}
1797#endif
1798#endif
1799
1800int irecv_usb_bulk_transfer(irecv_client_t client,
1801 unsigned char endpoint,
1802 unsigned char *data,
1803 int length,
1804 int *transferred,
1805 unsigned int timeout)
1806{
1807#ifdef USE_DUMMY
1808 return IRECV_E_UNSUPPORTED;
1809#else
1810 int ret;
1811
1812#ifndef _WIN32
1813#ifdef HAVE_IOKIT
1814 return iokit_usb_bulk_transfer(client, endpoint, data, length, transferred, timeout);
1815#else
1816 ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout);
1817 if (ret < 0) {
1818 libusb_clear_halt(client->handle, endpoint);
1819 }
1820#endif
1821#else
1822 if (endpoint==0x4) {
1823 ret = DeviceIoControl(client->handle, 0x2201B6, data, length, data, length, (PDWORD) transferred, NULL);
1824 } else {
1825 ret = 0;
1826 }
1827 ret = (ret==0) ? -1 : 0;
1828#endif
1829
1830 return ret;
1831#endif
1832}
1833
1834IRECV_API int irecv_usb_interrupt_transfer(irecv_client_t client,
1835 unsigned char endpoint,
1836 unsigned char *data,
1837 int length,
1838 int *transferred,
1839 unsigned int timeout)
1840{
1841#ifdef USE_DUMMY
1842 return IRECV_E_UNSUPPORTED;
1843#else
1844 int ret;
1845
1846#ifndef _WIN32
1847#ifdef HAVE_IOKIT
1848 return iokit_usb_interrupt_transfer(client, endpoint, data, length, transferred, timeout);
1849#else
1850 ret = libusb_interrupt_transfer(client->handle, endpoint, data, length, transferred, timeout);
1851 if (ret < 0) {
1852 libusb_clear_halt(client->handle, endpoint);
1853 }
1854#endif
1855#else
1856 // win32
1857 return IRECV_E_UNSUPPORTED;
1858#endif
1859
1860 return ret;
1861#endif
1862}
1863
1864#ifndef USE_DUMMY
1865#ifdef HAVE_IOKIT
1866static irecv_error_t iokit_usb_open_service(irecv_client_t *pclient, io_service_t service)
1867{
1868 IOReturn result;
1869 irecv_client_t client;
1870 SInt32 score;
1871 UInt16 mode;
1872 UInt32 locationID;
1873 IOCFPlugInInterface **plug = NULL;
1874 CFStringRef serialString;
1875
1876 client = (irecv_client_t) calloc( 1, sizeof(struct irecv_client_private));
1877
1878 // Create the plug-in
1879 result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score);
1880 if (result != kIOReturnSuccess) {
1881 IOObjectRelease(service);
1882 free(client);
1883 return IRECV_E_UNKNOWN_ERROR;
1884 }
1885
1886 // Cache the serial string before discarding the service. The service object
1887 // has a cached copy, so a request to the hardware device is not required.
1888 char serial_str[256];
1889 serial_str[0] = '\0';
1890 serialString = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
1891 if (serialString) {
1892 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
1893 CFRelease(serialString);
1894 }
1895 irecv_load_device_info_from_iboot_string(client, serial_str);
1896
1897 IOObjectRelease(service);
1898
1899 // Create the device interface
1900 result = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&(client->handle));
1901 IODestroyPlugInInterface(plug);
1902 if (result != kIOReturnSuccess) {
1903 free(client);
1904 return IRECV_E_UNKNOWN_ERROR;
1905 }
1906
1907 (*client->handle)->GetDeviceProduct(client->handle, &mode);
1908 (*client->handle)->GetLocationID(client->handle, &locationID);
1909 client->mode = mode;
1910 debug("opening device %04x:%04x @ %#010x...\n", kAppleVendorID, client->mode, locationID);
1911
1912 result = (*client->handle)->USBDeviceOpenSeize(client->handle);
1913 if (result != kIOReturnSuccess) {
1914 (*client->handle)->Release(client->handle);
1915 free(client);
1916 return IRECV_E_UNABLE_TO_CONNECT;
1917 }
1918
1919 *pclient = client;
1920 return IRECV_E_SUCCESS;
1921}
1922
1923static io_iterator_t iokit_usb_get_iterator_for_pid(UInt16 pid)
1924{
1925 IOReturn result;
1926 io_iterator_t iterator;
1927 CFMutableDictionaryRef matchingDict;
1928
1929 matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1930 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
1931 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pid);
1932
1933 result = IOServiceGetMatchingServices(MACH_PORT_NULL, matchingDict, &iterator);
1934 if (result != kIOReturnSuccess)
1935 return IO_OBJECT_NULL;
1936
1937 return iterator;
1938}
1939
1940static irecv_error_t iokit_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
1941{
1942 io_service_t service, ret_service;
1943 io_iterator_t iterator;
1944 CFStringRef usbSerial = NULL;
1945 CFStringRef ecidString = NULL;
1946 CFRange range;
1947
1948 UInt16 wtf_pids[] = { IRECV_K_WTF_MODE, 0};
1949 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 };
1950 UInt16 *pids = all_pids;
1951 int i;
1952
1953 if (pclient == NULL) {
1954 debug("%s: pclient parameter is null\n", __func__);
1955 return IRECV_E_INVALID_INPUT;
1956 }
1957 if (ecid == IRECV_K_WTF_MODE) {
1958 /* special ecid case, ignore !IRECV_K_WTF_MODE */
1959 pids = wtf_pids;
1960 ecid = 0;
1961 }
1962 if (ecid > 0) {
1963 ecidString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%llX"), ecid);
1964 if (ecidString == NULL) {
1965 debug("%s: failed to create ECID string\n", __func__);
1966 return IRECV_E_UNABLE_TO_CONNECT;
1967 }
1968 }
1969
1970 *pclient = NULL;
1971 ret_service = IO_OBJECT_NULL;
1972
1973 for (i = 0; (pids[i] > 0 && ret_service == IO_OBJECT_NULL) ; i++) {
1974
1975 iterator = iokit_usb_get_iterator_for_pid(pids[i]);
1976 if (iterator) {
1977 while ((service = IOIteratorNext(iterator))) {
1978
1979 if (ecid == 0) {
1980 ret_service = service;
1981 break;
1982 }
1983
1984 if (pids[i] == KIS_PRODUCT_ID) {
1985 // In KIS Mode, we have to open the device in order to get
1986 // it's ECID
1987 irecv_error_t err = iokit_usb_open_service(pclient, service);
1988 if (err != IRECV_E_SUCCESS) {
1989 debug("%s: failed to open KIS device\n", __func__);
1990 continue;
1991 }
1992
1993 if (ecidString)
1994 CFRelease(ecidString);
1995
1996 return IRECV_E_SUCCESS;
1997 }
1998
1999 usbSerial = IORegistryEntryCreateCFProperty(service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
2000 if (usbSerial == NULL) {
2001 debug("%s: failed to create USB serial string property\n", __func__);
2002 IOObjectRelease(service);
2003 continue;
2004 }
2005
2006 range = CFStringFind(usbSerial, ecidString, kCFCompareCaseInsensitive);
2007 if (range.location == kCFNotFound) {
2008 IOObjectRelease(service);
2009 } else {
2010 ret_service = service;
2011 break;
2012 }
2013 }
2014 if (usbSerial) {
2015 CFRelease(usbSerial);
2016 usbSerial = NULL;
2017 }
2018 IOObjectRelease(iterator);
2019 }
2020 }
2021
2022 if (ecidString)
2023 CFRelease(ecidString);
2024
2025 if (ret_service == IO_OBJECT_NULL)
2026 return IRECV_E_UNABLE_TO_CONNECT;
2027
2028 return iokit_usb_open_service(pclient, ret_service);
2029}
2030#endif
2031
2032#ifndef _WIN32
2033#ifndef HAVE_IOKIT
2034static 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)
2035{
2036 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client_private));
2037 if (client == NULL) {
2038 libusb_close(usb_handle);
2039 return IRECV_E_OUT_OF_MEMORY;
2040 }
2041
2042 memset(client, '\0', sizeof(struct irecv_client_private));
2043 client->usb_interface = 0;
2044 client->handle = usb_handle;
2045 client->mode = usb_descriptor->idProduct;
2046
2047 if (client->mode != KIS_PRODUCT_ID) {
2048 char serial_str[256];
2049 memset(serial_str, 0, sizeof(serial_str));
2050 irecv_get_string_descriptor_ascii(client, usb_descriptor->iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
2051 irecv_load_device_info_from_iboot_string(client, serial_str);
2052 }
2053
2054 if (ecid != 0 && client->mode != KIS_PRODUCT_ID) {
2055 if (client->device_info.ecid != ecid) {
2056 irecv_close(client);
2057 return IRECV_E_NO_DEVICE; //wrong device
2058 }
2059 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)ecid);
2060 }
2061
2062 *pclient = client;
2063 return IRECV_E_SUCCESS;
2064}
2065
2066static irecv_error_t libusb_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
2067{
2068 irecv_error_t ret = IRECV_E_UNABLE_TO_CONNECT;
2069 int i = 0;
39 struct libusb_device* usb_device = NULL; 2070 struct libusb_device* usb_device = NULL;
40 struct libusb_device** usb_device_list = NULL; 2071 struct libusb_device** usb_device_list = NULL;
41 struct libusb_device_handle* usb_handle = NULL;
42 struct libusb_device_descriptor usb_descriptor; 2072 struct libusb_device_descriptor usb_descriptor;
43 2073
44 *pclient = NULL; 2074 *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); 2075 int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list);
52 for (i = 0; i < usb_device_count; i++) { 2076 for (i = 0; i < usb_device_count; i++) {
53 usb_device = usb_device_list[i]; 2077 usb_device = usb_device_list[i];
54 libusb_get_device_descriptor(usb_device, &usb_descriptor); 2078 libusb_get_device_descriptor(usb_device, &usb_descriptor);
55 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) { 2079 if (usb_descriptor.idVendor == APPLE_VENDOR_ID) {
56 /* verify this device is in a mode we understand */ 2080 /* verify this device is in a mode we understand */
57 if (usb_descriptor.idProduct == kRecoveryMode1 || 2081 if (usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_1 ||
58 usb_descriptor.idProduct == kRecoveryMode2 || 2082 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_2 ||
59 usb_descriptor.idProduct == kRecoveryMode3 || 2083 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_3 ||
60 usb_descriptor.idProduct == kRecoveryMode4 || 2084 usb_descriptor.idProduct == IRECV_K_RECOVERY_MODE_4 ||
61 usb_descriptor.idProduct == kDfuMode) { 2085 usb_descriptor.idProduct == IRECV_K_WTF_MODE ||
2086 usb_descriptor.idProduct == IRECV_K_DFU_MODE ||
2087 usb_descriptor.idProduct == IRECV_K_PORT_DFU_MODE ||
2088 usb_descriptor.idProduct == KIS_PRODUCT_ID) {
2089
2090 if (ecid == IRECV_K_WTF_MODE) {
2091 if (usb_descriptor.idProduct != IRECV_K_WTF_MODE) {
2092 /* special ecid case, ignore !IRECV_K_WTF_MODE */
2093 continue;
2094 } else {
2095 ecid = 0;
2096 }
2097 }
2098
2099 if ((ecid != 0) && (usb_descriptor.idProduct == IRECV_K_WTF_MODE)) {
2100 /* we can't get ecid in WTF mode */
2101 continue;
2102 }
62 2103
63 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct); 2104 debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct);
64 2105
65 libusb_open(usb_device, &usb_handle); 2106 struct libusb_device_handle* usb_handle = NULL;
66 if (usb_handle == NULL) { 2107 int libusb_error = libusb_open(usb_device, &usb_handle);
67 libusb_free_device_list(usb_device_list, 1); 2108 if (usb_handle == NULL || libusb_error != 0) {
2109 debug("%s: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
2110
68 libusb_close(usb_handle); 2111 libusb_close(usb_handle);
69 libusb_exit(libirecovery_context); 2112 if (ecid != 0) {
2113 continue;
2114 }
2115 libusb_free_device_list(usb_device_list, 1);
70 return IRECV_E_UNABLE_TO_CONNECT; 2116 return IRECV_E_UNABLE_TO_CONNECT;
71 } 2117 }
72 libusb_free_device_list(usb_device_list, 1);
73 2118
74 irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client)); 2119 ret = libusb_usb_open_handle_with_descriptor_and_ecid(pclient, usb_handle, &usb_descriptor, ecid);
75 if (client == NULL) { 2120 if (ret == IRECV_E_SUCCESS) {
76 libusb_close(usb_handle); 2121 break;
77 libusb_exit(libirecovery_context);
78 return IRECV_E_OUT_OF_MEMORY;
79 } 2122 }
2123 }
2124 }
2125 }
2126 libusb_free_device_list(usb_device_list, 1);
2127 return ret;
2128}
2129#endif
2130#endif
2131#endif
2132
2133irecv_error_t irecv_open_with_ecid(irecv_client_t* pclient, uint64_t ecid)
2134{
2135#ifdef USE_DUMMY
2136 return IRECV_E_UNSUPPORTED;
2137#else
2138 irecv_error_t error = IRECV_E_UNABLE_TO_CONNECT;
2139
2140 if (libirecovery_debug) {
2141 irecv_set_debug_level(libirecovery_debug);
2142 }
2143#ifndef _WIN32
2144#ifdef HAVE_IOKIT
2145 error = iokit_open_with_ecid(pclient, ecid);
2146#else
2147 error = libusb_open_with_ecid(pclient, ecid);
2148#endif
2149#else
2150 error = win32_open_with_ecid(pclient, ecid);
2151#endif
2152 irecv_client_t client = *pclient;
2153 if (error != IRECV_E_SUCCESS) {
2154 irecv_close(client);
2155 return error;
2156 }
80 2157
81 memset(client, '\0', sizeof(struct irecv_client)); 2158 error = irecv_usb_set_configuration(client, 1);
82 client->interface = 0; 2159 if (error != IRECV_E_SUCCESS) {
83 client->handle = usb_handle; 2160 debug("Failed to set configuration, error %d\n", error);
84 client->mode = usb_descriptor.idProduct; 2161 irecv_close(client);
2162 return error;
2163 }
85 2164
86 error = irecv_set_configuration(client, 1); 2165#ifdef HAVE_IOKIT
87 if (error != IRECV_E_SUCCESS) { 2166 error = (*client->handle)->CreateDeviceAsyncEventSource(client->handle, &client->async_event_source);
88 return error; 2167 if (error != IRECV_E_SUCCESS) {
89 } 2168 free(client);
2169 return error;
2170 }
2171 CFRunLoopAddSource(CFRunLoopGetCurrent(), client->async_event_source, kCFRunLoopDefaultMode);
2172#endif
2173
2174 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) {
2175 error = irecv_usb_set_interface(client, 0, 0);
2176 } else {
2177 error = irecv_usb_set_interface(client, 0, 0);
2178 if (error == IRECV_E_SUCCESS && client->mode > IRECV_K_RECOVERY_MODE_2) {
2179 error = irecv_usb_set_interface(client, 1, 1);
2180 }
2181 }
90 2182
91 error = irecv_set_interface(client, 1, 1); 2183 if (error != IRECV_E_SUCCESS) {
92 if (error != IRECV_E_SUCCESS) { 2184 debug("Failed to set interface, error %d\n", error);
93 return error; 2185 irecv_close(client);
94 } 2186 return error;
2187 }
95 2188
96 /* cache usb serial */ 2189 if (client->mode == KIS_PRODUCT_ID) {
97 libusb_get_string_descriptor_ascii(client->handle, usb_descriptor.iSerialNumber, client->serial, 255); 2190 error = irecv_kis_init(client);
2191 if (error != IRECV_E_SUCCESS) {
2192 debug("irecv_kis_init failed, error %d\n", error);
2193 irecv_close(client);
2194 return error;
2195 }
98 2196
99 *pclient = client; 2197 error = irecv_kis_load_device_info(client);
100 return IRECV_E_SUCCESS; 2198 if (error != IRECV_E_SUCCESS) {
101 } 2199 debug("irecv_kis_load_device_info failed, error %d\n", error);
2200 irecv_close(client);
2201 return error;
2202 }
2203 if (ecid != 0 && client->device_info.ecid != ecid) {
2204 irecv_close(client);
2205 return IRECV_E_NO_DEVICE; //wrong device
102 } 2206 }
2207 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
2208 } else {
2209 irecv_copy_nonce_with_tag(client, "NONC", &client->device_info.ap_nonce, &client->device_info.ap_nonce_size);
2210 irecv_copy_nonce_with_tag(client, "SNON", &client->device_info.sep_nonce, &client->device_info.sep_nonce_size);
103 } 2211 }
104 2212
105 return IRECV_E_UNABLE_TO_CONNECT; 2213 if (error == IRECV_E_SUCCESS) {
2214 if ((*pclient)->connected_callback != NULL) {
2215 irecv_event_t event;
2216 event.size = 0;
2217 event.data = NULL;
2218 event.progress = 0;
2219 event.type = IRECV_CONNECTED;
2220 (*pclient)->connected_callback(*pclient, &event);
2221 }
2222 }
2223 return error;
2224#endif
106} 2225}
107 2226
108irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) { 2227irecv_error_t irecv_usb_set_configuration(irecv_client_t client, int configuration)
109 if (client == NULL || client->handle == NULL) { 2228{
2229#ifdef USE_DUMMY
2230 return IRECV_E_UNSUPPORTED;
2231#else
2232 if (check_context(client) != IRECV_E_SUCCESS)
110 return IRECV_E_NO_DEVICE; 2233 return IRECV_E_NO_DEVICE;
111 }
112 2234
2235#ifndef _WIN32
113 debug("Setting to configuration %d\n", configuration); 2236 debug("Setting to configuration %d\n", configuration);
114 2237
2238#ifdef HAVE_IOKIT
2239 IOReturn result;
2240
2241 result = (*client->handle)->SetConfiguration(client->handle, configuration);
2242 if (result != kIOReturnSuccess) {
2243 debug("error setting configuration: %#x\n", result);
2244 return IRECV_E_USB_CONFIGURATION;
2245 }
2246#else
115 int current = 0; 2247 int current = 0;
116 libusb_get_configuration(client->handle, &current); 2248 libusb_get_configuration(client->handle, &current);
117 if (current != configuration) { 2249 if (current != configuration) {
@@ -119,45 +2251,199 @@ irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration)
119 return IRECV_E_USB_CONFIGURATION; 2251 return IRECV_E_USB_CONFIGURATION;
120 } 2252 }
121 } 2253 }
2254#endif
2255 client->usb_config = configuration;
2256#endif
122 2257
123 client->config = configuration;
124 return IRECV_E_SUCCESS; 2258 return IRECV_E_SUCCESS;
2259#endif
125} 2260}
126 2261
127irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) { 2262#ifndef USE_DUMMY
128 if (client == NULL || client->handle == NULL) { 2263#ifdef HAVE_IOKIT
129 return IRECV_E_NO_DEVICE; 2264static IOReturn iokit_usb_get_interface(IOUSBDeviceInterface320 **device, uint8_t ifc, io_service_t *usbInterfacep)
2265{
2266 IOUSBFindInterfaceRequest request;
2267 uint8_t current_interface;
2268 kern_return_t kresult;
2269 io_iterator_t interface_iterator;
2270
2271 *usbInterfacep = IO_OBJECT_NULL;
2272
2273 request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
2274 request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
2275 request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
2276 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
2277
2278 kresult = (*device)->CreateInterfaceIterator(device, &request, &interface_iterator);
2279 if (kresult)
2280 return kresult;
2281
2282 for ( current_interface = 0 ; current_interface <= ifc ; current_interface++ ) {
2283 *usbInterfacep = IOIteratorNext(interface_iterator);
2284 if (current_interface != ifc)
2285 (void) IOObjectRelease (*usbInterfacep);
130 } 2286 }
2287 IOObjectRelease(interface_iterator);
131 2288
132 if (client->interface == interface) { 2289 return kIOReturnSuccess;
133 return IRECV_E_SUCCESS; 2290}
2291
2292static irecv_error_t iokit_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
2293{
2294 IOReturn result;
2295 io_service_t interface_service = IO_OBJECT_NULL;
2296 IOCFPlugInInterface **plugInInterface = NULL;
2297 SInt32 score;
2298
2299 // Close current interface
2300 if (client->usbInterface) {
2301 result = (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
2302 result = (*client->usbInterface)->Release(client->usbInterface);
2303 client->usbInterface = NULL;
2304 }
2305
2306 result = iokit_usb_get_interface(client->handle, usb_interface, &interface_service);
2307 if (result != kIOReturnSuccess) {
2308 debug("failed to find requested interface: %d\n", usb_interface);
2309 return IRECV_E_USB_INTERFACE;
2310 }
2311
2312 result = IOCreatePlugInInterfaceForService(interface_service, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
2313 IOObjectRelease(interface_service);
2314 if (result != kIOReturnSuccess) {
2315 debug("error creating plug-in interface: %#x\n", result);
2316 return IRECV_E_USB_INTERFACE;
2317 }
2318
2319 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300), (LPVOID)&client->usbInterface);
2320 IODestroyPlugInInterface(plugInInterface);
2321 if (result != kIOReturnSuccess) {
2322 debug("error creating interface interface: %#x\n", result);
2323 return IRECV_E_USB_INTERFACE;
134 } 2324 }
135 2325
136 debug("Setting to interface %d:%d\n", interface, alt_interface); 2326 result = (*client->usbInterface)->USBInterfaceOpen(client->usbInterface);
137 if (libusb_claim_interface(client->handle, interface) < 0) { 2327 if (result != kIOReturnSuccess) {
2328 debug("error opening interface: %#x\n", result);
138 return IRECV_E_USB_INTERFACE; 2329 return IRECV_E_USB_INTERFACE;
139 } 2330 }
140 2331
141 if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) { 2332 if (usb_interface == 1) {
2333 result = (*client->usbInterface)->SetAlternateInterface(client->usbInterface, usb_alt_interface);
2334 if (result != kIOReturnSuccess) {
2335 debug("error setting alternate interface: %#x\n", result);
2336 return IRECV_E_USB_INTERFACE;
2337 }
2338 }
2339
2340 return IRECV_E_SUCCESS;
2341}
2342#endif
2343#endif
2344
2345irecv_error_t irecv_usb_set_interface(irecv_client_t client, int usb_interface, int usb_alt_interface)
2346{
2347#ifdef USE_DUMMY
2348 return IRECV_E_UNSUPPORTED;
2349#else
2350 if (check_context(client) != IRECV_E_SUCCESS)
2351 return IRECV_E_NO_DEVICE;
2352
2353 debug("Setting to interface %d:%d\n", usb_interface, usb_alt_interface);
2354#ifndef _WIN32
2355#ifdef HAVE_IOKIT
2356 if (iokit_usb_set_interface(client, usb_interface, usb_alt_interface) < 0) {
142 return IRECV_E_USB_INTERFACE; 2357 return IRECV_E_USB_INTERFACE;
143 } 2358 }
2359#else
2360 if (libusb_claim_interface(client->handle, usb_interface) < 0) {
2361 return IRECV_E_USB_INTERFACE;
2362 }
2363
2364 if (usb_interface == 1) {
2365 if (libusb_set_interface_alt_setting(client->handle, usb_interface, usb_alt_interface) < 0) {
2366 return IRECV_E_USB_INTERFACE;
2367 }
2368 }
2369#endif
2370#else
2371 if (usb_interface == 1) {
2372 if (irecv_usb_control_transfer(client, 0, 0x0B, usb_alt_interface, usb_interface, NULL, 0, USB_TIMEOUT) < 0) {
2373 return IRECV_E_USB_INTERFACE;
2374 }
2375 }
2376#endif
2377 client->usb_interface = usb_interface;
2378 client->usb_alt_interface = usb_alt_interface;
144 2379
145 client->interface = interface;
146 client->alt_interface = alt_interface;
147 return IRECV_E_SUCCESS; 2380 return IRECV_E_SUCCESS;
2381#endif
148} 2382}
149 2383
150irecv_error_t irecv_reset(irecv_client_t client) { 2384irecv_error_t irecv_reset(irecv_client_t client)
151 if (client == NULL || client->handle == NULL) { 2385{
2386#ifdef USE_DUMMY
2387 return IRECV_E_UNSUPPORTED;
2388#else
2389 if (check_context(client) != IRECV_E_SUCCESS)
152 return IRECV_E_NO_DEVICE; 2390 return IRECV_E_NO_DEVICE;
2391
2392#ifndef _WIN32
2393#ifdef HAVE_IOKIT
2394 IOReturn result;
2395
2396 result = (*client->handle)->ResetDevice(client->handle);
2397 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2398 debug("error sending device reset: %#x\n", result);
2399 return IRECV_E_UNKNOWN_ERROR;
153 } 2400 }
154 2401
2402 result = (*client->handle)->USBDeviceReEnumerate(client->handle, 0);
2403 if (result != kIOReturnSuccess && result != kIOReturnNotResponding) {
2404 debug("error re-enumerating device: %#x (ignored)\n", result);
2405 }
2406#else
155 libusb_reset_device(client->handle); 2407 libusb_reset_device(client->handle);
2408#endif
2409#else
2410 DWORD count;
2411 DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL);
2412#endif
156 2413
157 return IRECV_E_SUCCESS; 2414 return IRECV_E_SUCCESS;
2415#endif
2416}
2417
2418irecv_error_t irecv_open_with_ecid_and_attempts(irecv_client_t* pclient, uint64_t ecid, int attempts)
2419{
2420#ifdef USE_DUMMY
2421 return IRECV_E_UNSUPPORTED;
2422#else
2423 int i;
2424
2425 for (i = 0; i < attempts; i++) {
2426 if (*pclient) {
2427 irecv_close(*pclient);
2428 *pclient = NULL;
2429 }
2430 if (irecv_open_with_ecid(pclient, ecid) != IRECV_E_SUCCESS) {
2431 debug("Connection failed. Waiting 1 sec before retry.\n");
2432 sleep(1);
2433 } else {
2434 return IRECV_E_SUCCESS;
2435 }
2436 }
2437
2438 return IRECV_E_UNABLE_TO_CONNECT;
2439#endif
158} 2440}
159 2441
160irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) { 2442irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data)
2443{
2444#ifdef USE_DUMMY
2445 return IRECV_E_UNSUPPORTED;
2446#else
161 switch(type) { 2447 switch(type) {
162 case IRECV_RECEIVED: 2448 case IRECV_RECEIVED:
163 client->received_callback = callback; 2449 client->received_callback = callback;
@@ -165,9 +2451,11 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
165 2451
166 case IRECV_PROGRESS: 2452 case IRECV_PROGRESS:
167 client->progress_callback = callback; 2453 client->progress_callback = callback;
2454 break;
168 2455
169 case IRECV_CONNECTED: 2456 case IRECV_CONNECTED:
170 client->connected_callback = callback; 2457 client->connected_callback = callback;
2458 break;
171 2459
172 case IRECV_PRECOMMAND: 2460 case IRECV_PRECOMMAND:
173 client->precommand_callback = callback; 2461 client->precommand_callback = callback;
@@ -179,15 +2467,21 @@ irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type
179 2467
180 case IRECV_DISCONNECTED: 2468 case IRECV_DISCONNECTED:
181 client->disconnected_callback = callback; 2469 client->disconnected_callback = callback;
2470 break;
182 2471
183 default: 2472 default:
184 return IRECV_E_UNKNOWN_ERROR; 2473 return IRECV_E_UNKNOWN_ERROR;
185 } 2474 }
186 2475
187 return IRECV_E_SUCCESS; 2476 return IRECV_E_SUCCESS;
2477#endif
188} 2478}
189 2479
190irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) { 2480irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type)
2481{
2482#ifdef USE_DUMMY
2483 return IRECV_E_UNSUPPORTED;
2484#else
191 switch(type) { 2485 switch(type) {
192 case IRECV_RECEIVED: 2486 case IRECV_RECEIVED:
193 client->received_callback = NULL; 2487 client->received_callback = NULL;
@@ -195,9 +2489,11 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
195 2489
196 case IRECV_PROGRESS: 2490 case IRECV_PROGRESS:
197 client->progress_callback = NULL; 2491 client->progress_callback = NULL;
2492 break;
198 2493
199 case IRECV_CONNECTED: 2494 case IRECV_CONNECTED:
200 client->connected_callback = NULL; 2495 client->connected_callback = NULL;
2496 break;
201 2497
202 case IRECV_PRECOMMAND: 2498 case IRECV_PRECOMMAND:
203 client->precommand_callback = NULL; 2499 client->precommand_callback = NULL;
@@ -209,17 +2505,871 @@ irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type ty
209 2505
210 case IRECV_DISCONNECTED: 2506 case IRECV_DISCONNECTED:
211 client->disconnected_callback = NULL; 2507 client->disconnected_callback = NULL;
2508 break;
212 2509
213 default: 2510 default:
214 return IRECV_E_UNKNOWN_ERROR; 2511 return IRECV_E_UNKNOWN_ERROR;
215 } 2512 }
216 2513
217 return IRECV_E_SUCCESS; 2514 return IRECV_E_SUCCESS;
2515#endif
2516}
2517
2518#ifndef USE_DUMMY
2519struct irecv_device_event_context {
2520 irecv_device_event_cb_t callback;
2521 void *user_data;
2522};
2523
2524struct irecv_usb_device_info {
2525 struct irecv_device_info device_info;
2526 enum irecv_mode mode;
2527 uint32_t location;
2528 int alive;
2529};
2530
2531#ifdef _WIN32
2532struct irecv_win_dev_ctx {
2533 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
2534 uint32_t location;
2535};
2536#else
2537#ifdef HAVE_IOKIT
2538struct irecv_iokit_dev_ctx {
2539 io_service_t device;
2540 IOUSBDeviceInterface **dev;
2541};
2542#endif
2543#endif
2544
2545static int _irecv_is_recovery_device(void *device)
2546{
2547 uint16_t vendor_id = 0;
2548 uint16_t product_id = 0;
2549#ifdef _WIN32
2550 const char *path = (const char*)device;
2551 unsigned int vendor = 0;
2552 unsigned int product = 0;
2553 if (sscanf(path, "\\usb#vid_%04x&pid_%04x#", &vendor, &product) != 2) {
2554 return 0;
2555 }
2556 vendor_id = (uint16_t)vendor;
2557 product_id = (uint16_t)product;
2558#else
2559#ifdef HAVE_IOKIT
2560 kern_return_t kr;
2561 IOUSBDeviceInterface **dev = device;
2562 kr = (*dev)->GetDeviceVendor(dev, &vendor_id);
2563 if (kr != kIOReturnSuccess) {
2564 debug("%s: Failed to get vendor id\n", __func__);
2565 return 0;
2566 }
2567 kr = (*dev)->GetDeviceProduct(dev, &product_id);
2568 if (kr != kIOReturnSuccess) {
2569 debug("%s: Failed to get product id\n", __func__);
2570 return 0;
2571 }
2572#else
2573 libusb_device *device_ = (libusb_device*)device;
2574 struct libusb_device_descriptor devdesc;
2575 int libusb_error;
2576
2577 libusb_error = libusb_get_device_descriptor(device_, &devdesc);
2578 if (libusb_error != 0) {
2579 debug("%s: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2580 return 0;
2581 }
2582 vendor_id = devdesc.idVendor;
2583 product_id = devdesc.idProduct;
2584#endif
2585#endif
2586
2587 if (vendor_id != APPLE_VENDOR_ID) {
2588 return 0;
2589 }
2590
2591 switch (product_id) {
2592 case IRECV_K_DFU_MODE:
2593 case IRECV_K_WTF_MODE:
2594 case IRECV_K_RECOVERY_MODE_1:
2595 case IRECV_K_RECOVERY_MODE_2:
2596 case IRECV_K_RECOVERY_MODE_3:
2597 case IRECV_K_RECOVERY_MODE_4:
2598 case IRECV_K_PORT_DFU_MODE:
2599 case KIS_PRODUCT_ID:
2600 break;
2601 default:
2602 return 0;
2603 }
2604 return 1;
2605}
2606
2607static void* _irecv_handle_device_add(void *userdata)
2608{
2609 struct irecv_client_private client_loc;
2610 char serial_str[256];
2611 uint32_t location = 0;
2612 uint16_t product_id = 0;
2613 irecv_error_t error = 0;
2614 irecv_client_t client = NULL;
2615
2616 memset(serial_str, 0, sizeof(serial_str));
2617#ifdef _WIN32
2618 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)userdata;
2619 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details = win_ctx->details;
2620 LPSTR result = (LPSTR)details->DevicePath;
2621 location = win_ctx->location;
2622
2623 unsigned int pid = 0;
2624
2625 if (strncmp(result, "\\\\?\\kis#", 8) == 0) {
2626 pid = KIS_PRODUCT_ID;
2627 } else {
2628 char *p = result;
2629 while ((p = strstr(p, "\\usb"))) {
2630 if (sscanf(p, "\\usb#vid_05ac&pid_%04x#%s", &pid, serial_str) == 2)
2631 break;
2632 p += 4;
2633 }
2634
2635 if (serial_str[0] == '\0') {
2636 debug("%s: ERROR: failed to parse DevicePath?!\n", __func__);
2637 return NULL;
2638 }
2639
2640 if (!_irecv_is_recovery_device(p)) {
2641 return NULL;
2642 }
2643 }
2644
2645 product_id = (uint16_t)pid;
2646
2647 if (product_id == KIS_PRODUCT_ID) {
2648 client = (irecv_client_t)malloc(sizeof(struct irecv_client_private));
2649 if (client == NULL) {
2650 debug("%s: Failed to allocate memory\n", __func__);
2651 return NULL;
2652 }
2653 memset(client, '\0', sizeof(struct irecv_client_private));
2654 client->handle = CreateFileA(result, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
2655 if (client->handle == INVALID_HANDLE_VALUE) {
2656 debug("%s: Failed to open device path %s\n", __func__, result);
2657 free(client);
2658 return NULL;
2659 }
2660 client->mode = pid;
2661 } else {
2662 char* p = strchr(serial_str, '#');
2663 if (p) {
2664 *p = '\0';
2665 }
2666
2667 unsigned int j;
2668 for (j = 0; j < strlen(serial_str); j++) {
2669 if (serial_str[j] == '_') {
2670 serial_str[j] = ' ';
2671 } else {
2672 serial_str[j] = toupper(serial_str[j]);
2673 }
2674 }
2675 }
2676
2677#else /* !_WIN32 */
2678#ifdef HAVE_IOKIT
2679 struct irecv_iokit_dev_ctx* iokit_ctx = (struct irecv_iokit_dev_ctx*)userdata;
2680 io_service_t device = iokit_ctx->device;
2681 IOUSBDeviceInterface **dev = iokit_ctx->dev;
2682
2683 if (!device) {
2684 debug("%s: ERROR: no device?!\n", __func__);
2685 return NULL;
2686 }
2687 if (!dev) {
2688 debug("%s: ERROR: no device interface?!\n", __func__);
2689 return NULL;
2690 }
2691
2692 (*dev)->GetDeviceProduct(dev, &product_id);
2693 if (!product_id) {
2694 debug("%s: ERROR: could not get product id?!\n", __func__);
2695 return NULL;
2696 }
2697 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2698 if (locationNum) {
2699 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2700 CFRelease(locationNum);
2701 }
2702 if (!location) {
2703 debug("%s: ERROR: could not get locationID?!\n", __func__);
2704 return NULL;
2705 }
2706
2707 if (product_id == KIS_PRODUCT_ID) {
2708 IOObjectRetain(device);
2709 int i = 0;
2710 for (i = 0; i < 10; i++) {
2711 error = iokit_usb_open_service(&client, device);
2712 if (error == IRECV_E_SUCCESS) {
2713 break;
2714 }
2715 debug("%s: Could not open KIS device, retrying...\n", __func__);
2716 usleep(500000);
2717 }
2718 if (error != IRECV_E_SUCCESS) {
2719 debug("%s: ERROR: could not open KIS device!\n", __func__);
2720 return NULL;
2721 }
2722 product_id = client->mode;
2723 } else {
2724 CFStringRef serialString = (CFStringRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
2725 if (serialString) {
2726 CFStringGetCString(serialString, serial_str, sizeof(serial_str), kCFStringEncodingUTF8);
2727 CFRelease(serialString);
2728 }
2729 }
2730#else /* !HAVE_IOKIT */
2731 libusb_device *device = (libusb_device*)userdata;
2732 struct libusb_device_descriptor devdesc;
2733 struct libusb_device_handle* usb_handle = NULL;
2734 int libusb_error;
2735
2736 libusb_error = libusb_get_device_descriptor(device, &devdesc);
2737 if (libusb_error != 0) {
2738 debug("%s: ERROR: failed to get device descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2739 return NULL;
2740 }
2741 product_id = devdesc.idProduct;
2742
2743 uint8_t bus = libusb_get_bus_number(device);
2744 uint8_t address = libusb_get_device_address(device);
2745 location = (bus << 16) | address;
2746
2747 libusb_error = libusb_open(device, &usb_handle);
2748 if (usb_handle == NULL || libusb_error != 0) {
2749 debug("%s: ERROR: can't connect to device: %s\n", __func__, libusb_error_name(libusb_error));
2750 libusb_close(usb_handle);
2751 return 0;
2752 }
2753
2754 if (product_id == KIS_PRODUCT_ID) {
2755 error = libusb_usb_open_handle_with_descriptor_and_ecid(&client, usb_handle, &devdesc, 0);
2756 if (error != IRECV_E_SUCCESS) {
2757 debug("%s: ERROR: could not open KIS device!\n", __func__);
2758 return NULL;
2759 }
2760
2761 product_id = client->mode;
2762 } else {
2763 libusb_error = libusb_get_string_descriptor_ascii(usb_handle, devdesc.iSerialNumber, (unsigned char*)serial_str, sizeof(serial_str)-1);
2764 if (libusb_error < 0) {
2765 debug("%s: Failed to get string descriptor: %s\n", __func__, libusb_error_name(libusb_error));
2766 return 0;
2767 }
2768 libusb_close(usb_handle);
2769 }
2770#endif /* !HAVE_IOKIT */
2771#endif /* !_WIN32 */
2772 memset(&client_loc, '\0', sizeof(client_loc));
2773 if (product_id == KIS_PRODUCT_ID) {
2774 int i = 0;
2775 for (i = 0; i < 10; i++) {
2776 error = irecv_usb_set_configuration(client, 1);
2777 if (error == IRECV_E_SUCCESS) {
2778 break;
2779 }
2780 debug("Failed to set configuration, error %d, retrying...\n", error);
2781 usleep(500000);
2782 }
2783 if (error != IRECV_E_SUCCESS) {
2784 debug("Failed to set configuration, error %d\n", error);
2785 irecv_close(client);
2786 return NULL;
2787 }
2788
2789 for (i = 0; i < 10; i++) {
2790 error = irecv_usb_set_interface(client, 0, 0);
2791 if (error == IRECV_E_SUCCESS) {
2792 break;
2793 }
2794 debug("Failed to set interface, error %d, retrying...\n", error);
2795 usleep(500000);
2796 }
2797 if (error != IRECV_E_SUCCESS) {
2798 debug("Failed to set interface, error %d\n", error);
2799 irecv_close(client);
2800 return NULL;
2801 }
2802
2803 error = irecv_kis_init(client);
2804 if (error != IRECV_E_SUCCESS) {
2805 debug("irecv_kis_init failed, error %d\n", error);
2806 irecv_close(client);
2807 return NULL;
2808 }
2809
2810 error = irecv_kis_load_device_info(client);
2811 if (error != IRECV_E_SUCCESS) {
2812 debug("irecv_kis_load_device_info failed, error %d\n", error);
2813 irecv_close(client);
2814 return NULL;
2815 }
2816 debug("found device with ECID %016" PRIx64 "\n", (uint64_t)client->device_info.ecid);
2817 strncpy(serial_str, client->device_info.serial_string, 255);
2818 product_id = client->mode;
2819 client_loc.isKIS = 1;
2820 }
2821 if (client) {
2822 irecv_close(client);
2823 }
2824
2825 client_loc.mode = product_id;
2826 irecv_load_device_info_from_iboot_string(&client_loc, serial_str);
2827
2828 struct irecv_usb_device_info *usb_dev_info = (struct irecv_usb_device_info*)malloc(sizeof(struct irecv_usb_device_info));
2829 memcpy(&(usb_dev_info->device_info), &(client_loc.device_info), sizeof(struct irecv_device_info));
2830 usb_dev_info->location = location;
2831 usb_dev_info->alive = 1;
2832 usb_dev_info->mode = client_loc.mode;
2833
2834 collection_add(&devices, usb_dev_info);
2835
2836 irecv_device_event_t dev_event;
2837 dev_event.type = IRECV_DEVICE_ADD;
2838 dev_event.mode = client_loc.mode;
2839 dev_event.device_info = &(usb_dev_info->device_info);
2840
2841 mutex_lock(&listener_mutex);
2842 FOREACH(struct irecv_device_event_context* context, &listeners) {
2843 context->callback(&dev_event, context->user_data);
2844 } ENDFOREACH
2845 mutex_unlock(&listener_mutex);
2846
2847 return NULL;
2848}
2849
2850static void _irecv_handle_device_remove(struct irecv_usb_device_info *devinfo)
2851{
2852 irecv_device_event_t dev_event;
2853 dev_event.type = IRECV_DEVICE_REMOVE;
2854 dev_event.mode = devinfo->mode;
2855 dev_event.device_info = &(devinfo->device_info);
2856 mutex_lock(&listener_mutex);
2857 FOREACH(struct irecv_device_event_context* context, &listeners) {
2858 context->callback(&dev_event, context->user_data);
2859 } ENDFOREACH
2860 mutex_unlock(&listener_mutex);
2861 free(devinfo->device_info.srnm);
2862 devinfo->device_info.srnm = NULL;
2863 free(devinfo->device_info.imei);
2864 devinfo->device_info.imei = NULL;
2865 free(devinfo->device_info.srtg);
2866 devinfo->device_info.srtg = NULL;
2867 free(devinfo->device_info.serial_string);
2868 devinfo->device_info.serial_string = NULL;
2869 devinfo->alive = 0;
2870 collection_remove(&devices, devinfo);
2871 free(devinfo);
2872}
2873
2874#ifndef _WIN32
2875#ifdef HAVE_IOKIT
2876static void iokit_device_added(void *refcon, io_iterator_t iterator)
2877{
2878 kern_return_t kr;
2879 io_service_t device;
2880 IOCFPlugInInterface **plugInInterface = NULL;
2881 IOUSBDeviceInterface **dev = NULL;
2882 HRESULT result;
2883 SInt32 score;
2884
2885 while ((device = IOIteratorNext(iterator))) {
2886 kr = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
2887 if ((kIOReturnSuccess != kr) || !plugInInterface) {
2888 debug("%s: ERROR: Unable to create a plug-in (%08x)\n", __func__, kr);
2889 IOObjectRelease(device);
2890 continue;
2891 }
2892 result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320), (LPVOID *)&dev);
2893 (*plugInInterface)->Release(plugInInterface);
2894
2895 if (result || !dev) {
2896 debug("%s: ERROR: Couldn't create a device interface (%08x)\n", __func__, (int)result);
2897 IOObjectRelease(device);
2898 continue;
2899 }
2900
2901 if (!_irecv_is_recovery_device(dev)) {
2902 (void) (*dev)->Release(dev);
2903 IOObjectRelease(device);
2904 continue;
2905 }
2906
2907 struct irecv_iokit_dev_ctx idev;
2908 idev.device = device;
2909 idev.dev = dev;
2910 _irecv_handle_device_add(&idev);
2911 (void) (*dev)->Release(dev);
2912 IOObjectRelease(device);
2913 }
2914}
2915
2916static void iokit_device_removed(void *refcon, io_iterator_t iterator)
2917{
2918 io_service_t device;
2919
2920 while ((device = IOIteratorNext(iterator))) {
2921 uint32_t location = 0;
2922 CFNumberRef locationNum = (CFNumberRef)IORegistryEntryCreateCFProperty(device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
2923 if (locationNum) {
2924 CFNumberGetValue(locationNum, kCFNumberSInt32Type, &location);
2925 CFRelease(locationNum);
2926 }
2927 IOObjectRelease(device);
2928
2929 if (!location) {
2930 continue;
2931 }
2932
2933 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2934 if (devinfo->location == location) {
2935 _irecv_handle_device_remove(devinfo);
2936 break;
2937 }
2938 } ENDFOREACH
2939 }
2940}
2941#else /* !HAVE_IOKIT */
2942#ifdef HAVE_LIBUSB_HOTPLUG_API
2943static int _irecv_usb_hotplug_cb(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data)
2944{
2945 if (!_irecv_is_recovery_device(device)) {
2946 return 0;
2947 }
2948 if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
2949 THREAD_T th_device;
2950 if (thread_new(&th_device, _irecv_handle_device_add, device) != 0) {
2951 debug("%s: FATAL: failed to create thread to handle device add\n", __func__);
2952 return 0;
2953 }
2954 thread_detach(th_device);
2955 } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
2956 uint8_t bus = libusb_get_bus_number(device);
2957 uint8_t address = libusb_get_device_address(device);
2958 uint32_t location = (bus << 16) | address;
2959 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2960 if (devinfo->location == location) {
2961 _irecv_handle_device_remove(devinfo);
2962 break;
2963 }
2964 } ENDFOREACH
2965 }
2966
2967 return 0;
2968}
2969#endif /* HAVE_LIBUSB_HOTPLUG_API */
2970#endif /* !HAVE_IOKIT */
2971#endif /* !_WIN32 */
2972
2973struct _irecv_event_handler_info {
2974 cond_t startup_cond;
2975 mutex_t startup_mutex;
2976};
2977
2978static void *_irecv_event_handler(void* data)
2979{
2980 struct _irecv_event_handler_info* info = (struct _irecv_event_handler_info*)data;
2981#ifdef _WIN32
2982 struct collection newDevices;
2983 const GUID *guids[] = { &GUID_DEVINTERFACE_KIS, &GUID_DEVINTERFACE_PORTDFU, &GUID_DEVINTERFACE_DFU, &GUID_DEVINTERFACE_IBOOT, NULL };
2984 int running = 1;
2985
2986 collection_init(&newDevices);
2987
2988 mutex_lock(&(info->startup_mutex));
2989 cond_signal(&(info->startup_cond));
2990 mutex_unlock(&(info->startup_mutex));
2991
2992 do {
2993 SP_DEVICE_INTERFACE_DATA currentInterface;
2994 HDEVINFO usbDevices;
2995 DWORD i;
2996 int k;
2997
2998 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
2999 devinfo->alive = 0;
3000 } ENDFOREACH
3001
3002 for (k = 0; guids[k]; k++) {
3003 usbDevices = SetupDiGetClassDevs(guids[k], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
3004 if (!usbDevices) {
3005 debug("%s: ERROR: SetupDiGetClassDevs failed\n", __func__);
3006 // cleanup/free newDevices
3007 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
3008 free(win_ctx->details);
3009 collection_remove(&newDevices, win_ctx);
3010 free(win_ctx);
3011 } ENDFOREACH
3012 collection_free(&newDevices);
3013 return NULL;
3014 }
3015
3016
3017 memset(&currentInterface, '\0', sizeof(SP_DEVICE_INTERFACE_DATA));
3018 currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
3019 for (i = 0; usbDevices && SetupDiEnumDeviceInterfaces(usbDevices, NULL, guids[k], i, &currentInterface); i++) {
3020 DWORD requiredSize = 0;
3021 PSP_DEVICE_INTERFACE_DETAIL_DATA_A details;
3022 SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
3023 details = (PSP_DEVICE_INTERFACE_DETAIL_DATA_A) malloc(requiredSize);
3024 details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
3025 SP_DEVINFO_DATA devinfodata;
3026 devinfodata.cbSize = sizeof(SP_DEVINFO_DATA);
3027 if (!SetupDiGetDeviceInterfaceDetailA(usbDevices, &currentInterface, details, requiredSize, NULL, &devinfodata)) {
3028 free(details);
3029 continue;
3030 }
3031
3032 DWORD sz = REG_SZ;
3033 char driver[256];
3034 driver[0] = '\0';
3035 if (!SetupDiGetDeviceRegistryPropertyA(usbDevices, &devinfodata, SPDRP_DRIVER, &sz, (PBYTE)driver, sizeof(driver), NULL)) {
3036 debug("%s: ERROR: Failed to get driver key\n", __func__);
3037 free(details);
3038 continue;
3039 }
3040
3041 char *p = strrchr(driver, '\\');
3042 if (!p) {
3043 debug("%s: ERROR: Failed to parse device location\n", __func__);
3044 free(details);
3045 continue;
3046 }
3047 p++;
3048 uint32_t location = 0;
3049 if (!*p || strlen(p) < 4) {
3050 debug("%s: ERROR: Driver location suffix too short\n", __func__);
3051 free(details);
3052 continue;
3053 }
3054 memcpy(&location, p, 4);
3055 int found = 0;
3056
3057 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3058 if (devinfo->location == location) {
3059 devinfo->alive = 1;
3060 found = 1;
3061 break;
3062 }
3063 } ENDFOREACH
3064
3065 unsigned int pid = 0;
3066 unsigned int vid = 0;
3067 if (sscanf(details->DevicePath, "\\\\?\\%*3s#vid_%04x&pid_%04x", &vid, &pid)!= 2) {
3068 debug("%s: ERROR: failed to parse VID/PID! path: %s\n", __func__, details->DevicePath);
3069 free(details);
3070 continue;
3071 }
3072 if (vid != APPLE_VENDOR_ID) {
3073 free(details);
3074 continue;
3075 }
3076
3077 // make sure the current device is actually in the right mode for the given driver interface
3078 int skip = 0;
3079 if ((guids[k] == &GUID_DEVINTERFACE_DFU && pid != IRECV_K_DFU_MODE && pid != IRECV_K_WTF_MODE)
3080 || (guids[k] == &GUID_DEVINTERFACE_PORTDFU && pid != IRECV_K_PORT_DFU_MODE)
3081 || (guids[k] == &GUID_DEVINTERFACE_IBOOT && (pid < IRECV_K_RECOVERY_MODE_1 || pid > IRECV_K_RECOVERY_MODE_4))
3082 || (guids[k] == &GUID_DEVINTERFACE_KIS && pid != 1)
3083 ) {
3084 skip = 1;
3085 }
3086
3087 if (!found && !skip) {
3088 // Add device to newDevices list, and deliver the notification later, when removed devices are first handled.
3089 struct irecv_win_dev_ctx *win_ctx = (struct irecv_win_dev_ctx*)malloc(sizeof(struct irecv_win_dev_ctx));
3090 win_ctx->details = details;
3091 win_ctx->location = location;
3092 collection_add(&newDevices, win_ctx);
3093 details = NULL;
3094 }
3095 free(details);
3096 }
3097 SetupDiDestroyDeviceInfoList(usbDevices);
3098 }
3099
3100 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3101 if (!devinfo->alive) {
3102 debug("%s: removed ecid: %016" PRIx64 ", location: %d\n",__func__, (uint64_t)devinfo->device_info.ecid, devinfo->location);
3103 _irecv_handle_device_remove(devinfo);
3104 }
3105 } ENDFOREACH
3106
3107 // handle newly added devices and remove from local list
3108 FOREACH(struct irecv_win_dev_ctx *win_ctx, &newDevices) {
3109 debug("%s: found new: %s, location: %d\n", __func__, win_ctx->details->DevicePath, win_ctx->location);
3110 _irecv_handle_device_add(win_ctx);
3111 free(win_ctx->details);
3112 collection_remove(&newDevices, win_ctx);
3113 free(win_ctx);
3114 } ENDFOREACH
3115
3116 Sleep(500);
3117 mutex_lock(&listener_mutex);
3118 if (collection_count(&listeners) == 0) {
3119 running = 0;
3120 }
3121 mutex_unlock(&listener_mutex);
3122 } while (running);
3123
3124 collection_free(&newDevices);
3125#else /* !_WIN32 */
3126#ifdef HAVE_IOKIT
3127 kern_return_t kr;
3128
3129 IONotificationPortRef notifyPort = IONotificationPortCreate(MACH_PORT_NULL);
3130 CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notifyPort);
3131 iokit_runloop = CFRunLoopGetCurrent();
3132 CFRunLoopAddSource(iokit_runloop, runLoopSource, kCFRunLoopDefaultMode);
3133
3134 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 };
3135 int i = 0;
3136 while (pids[i] > 0) {
3137 CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
3138 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBVendorID), kAppleVendorID);
3139 iokit_cfdictionary_set_short(matchingDict, CFSTR(kUSBProductID), pids[i]);
3140
3141 matchingDict = (CFMutableDictionaryRef)CFRetain(matchingDict);
3142
3143 io_iterator_t devAddedIter;
3144 kr = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, iokit_device_added, NULL, &devAddedIter);
3145 if (kr != kIOReturnSuccess) {
3146 debug("%s: Failed to register device add notification callback\n", __func__);
3147 }
3148 iokit_device_added(NULL, devAddedIter);
3149
3150 io_iterator_t devRemovedIter;
3151 kr = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, iokit_device_removed, NULL, &devRemovedIter);
3152 if (kr != kIOReturnSuccess) {
3153 debug("%s: Failed to register device remove notification callback\n", __func__);
3154 }
3155 iokit_device_removed(NULL, devRemovedIter);
3156
3157 i++;
3158 }
3159
3160 mutex_lock(&(info->startup_mutex));
3161 cond_signal(&(info->startup_cond));
3162 mutex_unlock(&(info->startup_mutex));
3163
3164 CFRunLoopRun();
3165
3166#else /* !HAVE_IOKIT */
3167#ifdef HAVE_LIBUSB_HOTPLUG_API
3168 static libusb_hotplug_callback_handle usb_hotplug_cb_handle;
3169 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);
3170 int running = 1;
3171
3172 mutex_lock(&(info->startup_mutex));
3173 cond_signal(&(info->startup_cond));
3174 mutex_unlock(&(info->startup_mutex));
3175
3176 do {
3177 struct timeval tv;
3178 tv.tv_sec = tv.tv_usec = 0;
3179 libusb_handle_events_timeout(irecv_hotplug_ctx, &tv);
3180
3181 mutex_lock(&listener_mutex);
3182 if (collection_count(&listeners) == 0) {
3183 running = 0;
3184 }
3185 mutex_unlock(&listener_mutex);
3186
3187 usleep(100000);
3188 } while (running);
3189 libusb_hotplug_deregister_callback(irecv_hotplug_ctx, usb_hotplug_cb_handle);
3190#else /* !HAVE_LIBUSB_HOTPLUG_API */
3191 int i, cnt;
3192 libusb_device **devs;
3193 int running = 1;
3194
3195 mutex_lock(&(info->startup_mutex));
3196 cond_signal(&(info->startup_cond));
3197 mutex_unlock(&(info->startup_mutex));
3198
3199 do {
3200 cnt = libusb_get_device_list(irecv_hotplug_ctx, &devs);
3201 if (cnt < 0) {
3202 debug("%s: FATAL: Failed to get device list: %s\n", __func__, libusb_error_name(cnt));
3203 return NULL;
3204 }
3205
3206 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3207 devinfo->alive = 0;
3208 } ENDFOREACH
3209
3210 for (i = 0; i < cnt; i++) {
3211 libusb_device *dev = devs[i];
3212 if (!_irecv_is_recovery_device(dev)) {
3213 continue;
3214 }
3215 uint8_t bus = libusb_get_bus_number(dev);
3216 uint8_t address = libusb_get_device_address(dev);
3217 uint32_t location = (bus << 16) | address;
3218 int found = 0;
3219 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3220 if (devinfo->location == location) {
3221 devinfo->alive = 1;
3222 found = 1;
3223 break;
3224 }
3225 } ENDFOREACH
3226 if (!found) {
3227 _irecv_handle_device_add(dev);
3228 }
3229 }
3230
3231 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3232 if (!devinfo->alive) {
3233 _irecv_handle_device_remove(devinfo);
3234 }
3235 } ENDFOREACH
3236
3237 libusb_free_device_list(devs, 1);
3238
3239 mutex_lock(&listener_mutex);
3240 if (collection_count(&listeners) == 0) {
3241 running = 0;
3242 }
3243 mutex_unlock(&listener_mutex);
3244 if (!running)
3245 break;
3246 usleep(500000);
3247 } while (running);
3248#endif /* !HAVE_LIBUSB_HOTPLUG_API */
3249#endif /* !HAVE_IOKIT */
3250#endif /* !_WIN32 */
3251 return NULL;
3252}
3253#endif /* !USE_DUMMY */
3254
3255irecv_error_t irecv_device_event_subscribe(irecv_device_event_context_t *context, irecv_device_event_cb_t callback, void *user_data)
3256{
3257#ifdef USE_DUMMY
3258 return IRECV_E_UNSUPPORTED;
3259#else
3260 if (!context || !callback)
3261 return IRECV_E_INVALID_INPUT;
3262
3263 struct irecv_device_event_context* _context = malloc(sizeof(struct irecv_device_event_context));
3264 if (!_context) {
3265 return IRECV_E_OUT_OF_MEMORY;
3266 }
3267
3268 _context->callback = callback;
3269 _context->user_data = user_data;
3270
3271 mutex_lock(&listener_mutex);
3272 collection_add(&listeners, _context);
3273
3274 if (th_event_handler == THREAD_T_NULL || !thread_alive(th_event_handler)) {
3275 mutex_unlock(&listener_mutex);
3276 struct _irecv_event_handler_info info;
3277 cond_init(&info.startup_cond);
3278 mutex_init(&info.startup_mutex);
3279#ifndef _WIN32
3280#ifndef HAVE_IOKIT
3281 libusb_init(&irecv_hotplug_ctx);
3282#endif
3283#endif
3284 collection_init(&devices);
3285 mutex_init(&device_mutex);
3286 mutex_lock(&info.startup_mutex);
3287 if (thread_new(&th_event_handler, _irecv_event_handler, &info) == 0) {
3288 cond_wait(&info.startup_cond, &info.startup_mutex);
3289 }
3290 mutex_unlock(&info.startup_mutex);
3291 cond_destroy(&info.startup_cond);
3292 mutex_destroy(&info.startup_mutex);
3293 } else {
3294 /* send DEVICE_ADD events to the new listener */
3295 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3296 if (devinfo && devinfo->alive) {
3297 irecv_device_event_t ev;
3298 ev.type = IRECV_DEVICE_ADD;
3299 ev.mode = devinfo->mode;
3300 ev.device_info = &(devinfo->device_info);
3301 _context->callback(&ev, _context->user_data);
3302 }
3303 } ENDFOREACH
3304 mutex_unlock(&listener_mutex);
3305 }
3306
3307 *context = _context;
3308
3309 return IRECV_E_SUCCESS;
3310#endif
3311}
3312
3313irecv_error_t irecv_device_event_unsubscribe(irecv_device_event_context_t context)
3314{
3315#ifdef USE_DUMMY
3316 return IRECV_E_UNSUPPORTED;
3317#else
3318 if (!context)
3319 return IRECV_E_INVALID_INPUT;
3320
3321 mutex_lock(&listener_mutex);
3322 collection_remove(&listeners, context);
3323 int num = collection_count(&listeners);
3324 mutex_unlock(&listener_mutex);
3325
3326 if (num == 0 && th_event_handler != THREAD_T_NULL && thread_alive(th_event_handler)) {
3327#ifdef HAVE_IOKIT
3328 if (iokit_runloop) {
3329 while (!CFRunLoopIsWaiting(iokit_runloop)) usleep(420);
3330 CFRunLoopStop(iokit_runloop);
3331 iokit_runloop = NULL;
3332 }
3333#endif
3334 thread_join(th_event_handler);
3335 thread_free(th_event_handler);
3336 th_event_handler = THREAD_T_NULL;
3337 mutex_lock(&device_mutex);
3338 FOREACH(struct irecv_usb_device_info *devinfo, &devices) {
3339 free(devinfo->device_info.srnm);
3340 devinfo->device_info.srnm = NULL;
3341 free(devinfo->device_info.imei);
3342 devinfo->device_info.imei = NULL;
3343 free(devinfo->device_info.srtg);
3344 devinfo->device_info.srtg = NULL;
3345 free(devinfo->device_info.serial_string);
3346 devinfo->device_info.serial_string = NULL;
3347 free(devinfo);
3348 } ENDFOREACH
3349 collection_free(&devices);
3350 mutex_unlock(&device_mutex);
3351 mutex_destroy(&device_mutex);
3352#ifndef _WIN32
3353#ifndef HAVE_IOKIT
3354 libusb_exit(irecv_hotplug_ctx);
3355 irecv_hotplug_ctx = NULL;
3356#endif
3357#endif
3358 }
3359
3360 free(context);
3361
3362 return IRECV_E_SUCCESS;
3363#endif
218} 3364}
219 3365
220irecv_error_t irecv_close(irecv_client_t client) { 3366static irecv_error_t irecv_cleanup(irecv_client_t client)
3367{
3368#ifdef USE_DUMMY
3369 return IRECV_E_UNSUPPORTED;
3370#else
221 if (client != NULL) { 3371 if (client != NULL) {
222 if(client->disconnected_callback != NULL) { 3372 if (client->disconnected_callback != NULL) {
223 irecv_event_t event; 3373 irecv_event_t event;
224 event.size = 0; 3374 event.size = 0;
225 event.data = NULL; 3375 event.data = NULL;
@@ -227,114 +3377,205 @@ irecv_error_t irecv_close(irecv_client_t client) {
227 event.type = IRECV_DISCONNECTED; 3377 event.type = IRECV_DISCONNECTED;
228 client->disconnected_callback(client, &event); 3378 client->disconnected_callback(client, &event);
229 } 3379 }
230 3380#ifndef _WIN32
3381#ifdef HAVE_IOKIT
3382 if (client->usbInterface) {
3383 (*client->usbInterface)->USBInterfaceClose(client->usbInterface);
3384 (*client->usbInterface)->Release(client->usbInterface);
3385 client->usbInterface = NULL;
3386 }
3387 if (client->handle) {
3388 (*client->handle)->USBDeviceClose(client->handle);
3389 (*client->handle)->Release(client->handle);
3390 client->handle = NULL;
3391 }
3392 if(client->async_event_source) {
3393 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), client->async_event_source, kCFRunLoopDefaultMode);
3394 CFRelease(client->async_event_source);
3395 }
3396#else
231 if (client->handle != NULL) { 3397 if (client->handle != NULL) {
232 libusb_release_interface(client->handle, client->interface); 3398 if ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE) && (client->isKIS == 0)) {
3399 libusb_release_interface(client->handle, client->usb_interface);
3400 }
233 libusb_close(client->handle); 3401 libusb_close(client->handle);
234 client->handle = NULL; 3402 client->handle = NULL;
235 } 3403 }
3404#endif
3405#else
3406 CloseHandle(client->handle);
3407#endif
3408 free(client->device_info.srnm);
3409 free(client->device_info.imei);
3410 free(client->device_info.srtg);
3411 free(client->device_info.serial_string);
3412 free(client->device_info.ap_nonce);
3413 free(client->device_info.sep_nonce);
3414 }
236 3415
237 if (libirecovery_context != NULL) { 3416 return IRECV_E_SUCCESS;
238 libusb_exit(libirecovery_context); 3417#endif
239 libirecovery_context = NULL; 3418}
240 }
241 3419
3420irecv_error_t irecv_close(irecv_client_t client)
3421{
3422 irecv_error_t ret = IRECV_E_SUCCESS;
3423 if (client) {
3424 ret = irecv_cleanup(client);
242 free(client); 3425 free(client);
243 client = NULL; 3426 client = NULL;
244 } 3427 }
245 3428 return ret;
246 return IRECV_E_SUCCESS;
247} 3429}
248 3430
249void irecv_set_debug_level(int level) { 3431void irecv_set_debug_level(int level)
3432{
250 libirecovery_debug = level; 3433 libirecovery_debug = level;
251 if(libirecovery_context) { 3434#ifndef USE_DUMMY
252 libusb_set_debug(libirecovery_context, libirecovery_debug); 3435#ifndef _WIN32
3436#ifndef HAVE_IOKIT
3437 if (libirecovery_context) {
3438#if LIBUSB_API_VERSION >= 0x01000106
3439 libusb_set_option(libirecovery_context, LIBUSB_OPTION_LOG_LEVEL, libirecovery_debug > 2 ? 1: 0);
3440#else
3441 libusb_set_debug(libirecovery_context, libirecovery_debug > 2 ? 1: 0);
3442#endif
253 } 3443 }
3444#endif
3445#endif
3446#endif
254} 3447}
255 3448
256static irecv_error_t irecv_send_command_raw(irecv_client_t client, unsigned char* command) { 3449const char* irecv_version()
3450{
3451#ifndef PACKAGE_VERSION
3452#error PACKAGE_VERSION is not defined!
3453#endif
3454 return PACKAGE_VERSION;
3455}
3456
3457
3458#ifndef USE_DUMMY
3459static irecv_error_t irecv_send_command_raw(irecv_client_t client, const char* command, uint8_t b_request)
3460{
257 unsigned int length = strlen(command); 3461 unsigned int length = strlen(command);
258 if (length >= 0x100) { 3462 if (length >= 0x100) {
259 length = 0xFF; 3463 return IRECV_E_INVALID_INPUT;
260 } 3464 }
261 3465
262 if (length > 0) { 3466 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); 3467 int bytes = 0;
264 if ((ret < 0) || (ret != (length + 1))) { 3468 irecv_error_t error = 0;
265 if (ret == LIBUSB_ERROR_PIPE) 3469#ifdef DEBUG
266 return IRECV_E_PIPE; 3470 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
267 if (ret == LIBUSB_ERROR_TIMEOUT) 3471 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
268 return IRECV_E_TIMEOUT; 3472 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
269 return IRECV_E_UNKNOWN_ERROR; 3473 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3474#endif
3475 char cmdstr[0x100] = {0};
3476 if (length & 0xf) {
3477 length &= ~0xf;
3478 length += 0x10;
270 } 3479 }
3480 snprintf(cmdstr, sizeof(cmdstr), "%s\n",command);
3481 legacyCMD cmd = {
3482 MSG_SEND_COMMAND,
3483 0x1234, //magic
3484 (uint32_t)length, //zero terminated?
3485 0x0
3486 };
3487 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3488 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3489 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3490 if ((error = irecv_usb_interrupt_transfer(client, 0x02, (unsigned char*)cmdstr, length, &bytes, USB_TIMEOUT))) return error;
3491 sleep(1); //go easy on this old device
3492 return IRECV_E_SUCCESS;
3493 }
3494
3495 if (length > 0) {
3496 irecv_usb_control_transfer(client, 0x40, b_request, 0, 0, (unsigned char*) command, length + 1, USB_TIMEOUT);
271 } 3497 }
272 3498
273 return IRECV_E_SUCCESS; 3499 return IRECV_E_SUCCESS;
274} 3500}
3501#endif
275 3502
276irecv_error_t irecv_send_command(irecv_client_t client, unsigned char* command) { 3503irecv_error_t irecv_send_command_breq(irecv_client_t client, const char* command, uint8_t b_request)
3504{
3505#ifdef USE_DUMMY
3506 return IRECV_E_UNSUPPORTED;
3507#else
277 irecv_error_t error = 0; 3508 irecv_error_t error = 0;
278 3509
279 if (client == NULL || client->handle == NULL) { 3510 if (check_context(client) != IRECV_E_SUCCESS)
280 return IRECV_E_NO_DEVICE; 3511 return IRECV_E_NO_DEVICE;
281 }
282 3512
283 unsigned int length = strlen(command); 3513 unsigned int length = strlen(command);
284 if (length >= 0x100) { 3514 if (length >= 0x100) {
285 length = 0xFF; 3515 return IRECV_E_INVALID_INPUT;
286 } 3516 }
287 3517
288 irecv_event_t event; 3518 irecv_event_t event;
289 if(client->precommand_callback != NULL) { 3519 if (client->precommand_callback != NULL) {
290 event.size = length; 3520 event.size = length;
291 event.data = command; 3521 event.data = command;
292 event.type = IRECV_PRECOMMAND; 3522 event.type = IRECV_PRECOMMAND;
293 if(client->precommand_callback(client, &event)) { 3523 if (client->precommand_callback(client, &event)) {
294 return IRECV_E_SUCCESS; 3524 return IRECV_E_SUCCESS;
295 } 3525 }
296 } 3526 }
297 3527
298 error = irecv_send_command_raw(client, command); 3528 error = irecv_send_command_raw(client, command, b_request);
299 if (error != IRECV_E_SUCCESS) { 3529 if (error != IRECV_E_SUCCESS) {
300 debug("Failed to send command %s\n", command); 3530 debug("Failed to send command %s\n", command);
301 if (error != IRECV_E_PIPE) 3531 if (error != IRECV_E_PIPE)
302 return error; 3532 return error;
303 } 3533 }
304 3534
305 if(client->postcommand_callback != NULL) { 3535 if (client->postcommand_callback != NULL) {
306 event.size = length; 3536 event.size = length;
307 event.data = command; 3537 event.data = command;
308 event.type = IRECV_POSTCOMMAND; 3538 event.type = IRECV_POSTCOMMAND;
309 if(client->postcommand_callback(client, &event)) { 3539 if (client->postcommand_callback(client, &event)) {
310 return IRECV_E_SUCCESS; 3540 return IRECV_E_SUCCESS;
311 } 3541 }
312 } 3542 }
313 3543
314 return IRECV_E_SUCCESS; 3544 return IRECV_E_SUCCESS;
3545#endif
315} 3546}
316 3547
317irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) { 3548irecv_error_t irecv_send_command(irecv_client_t client, const char* command)
318 if (client == NULL || client->handle == NULL) { 3549{
3550 return irecv_send_command_breq(client, command, 0);
3551}
3552
3553irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, unsigned int options)
3554{
3555#ifdef USE_DUMMY
3556 return IRECV_E_UNSUPPORTED;
3557#else
3558 if (check_context(client) != IRECV_E_SUCCESS)
319 return IRECV_E_NO_DEVICE; 3559 return IRECV_E_NO_DEVICE;
320 }
321 3560
322 FILE* file = fopen(filename, "rb"); 3561 FILE* file = fopen(filename, "rb");
323 if (file == NULL) { 3562 if (file == NULL) {
324 return IRECV_E_FILE_NOT_FOUND; 3563 return IRECV_E_FILE_NOT_FOUND;
325 } 3564 }
326 3565
327 fseek(file, 0, SEEK_END); 3566 struct stat fst;
328 long length = ftell(file); 3567 if (fstat(fileno(file), &fst) < 0) {
329 fseek(file, 0, SEEK_SET); 3568 return IRECV_E_UNKNOWN_ERROR;
3569 }
3570 size_t length = fst.st_size;
330 3571
331 unsigned char* buffer = (unsigned char*) malloc(length); 3572 char* buffer = (char*)malloc(length);
332 if (buffer == NULL) { 3573 if (buffer == NULL) {
333 fclose(file); 3574 fclose(file);
334 return IRECV_E_OUT_OF_MEMORY; 3575 return IRECV_E_OUT_OF_MEMORY;
335 } 3576 }
336 3577
337 long bytes = fread(buffer, 1, length, file); 3578 size_t bytes = fread(buffer, 1, length, file);
338 fclose(file); 3579 fclose(file);
339 3580
340 if (bytes != length) { 3581 if (bytes != length) {
@@ -342,64 +3583,283 @@ irecv_error_t irecv_send_file(irecv_client_t client, const char* filename) {
342 return IRECV_E_UNKNOWN_ERROR; 3583 return IRECV_E_UNKNOWN_ERROR;
343 } 3584 }
344 3585
345 irecv_error_t error = irecv_send_buffer(client, buffer, length); 3586 irecv_error_t error = irecv_send_buffer(client, (unsigned char*)buffer, length, options);
346 free(buffer); 3587 free(buffer);
3588
347 return error; 3589 return error;
3590#endif
348} 3591}
349 3592
350irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) { 3593#ifndef USE_DUMMY
351 if (client == NULL || client->handle == NULL) { 3594static irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status)
3595{
3596 if (check_context(client) != IRECV_E_SUCCESS) {
352 *status = 0; 3597 *status = 0;
353 return IRECV_E_NO_DEVICE; 3598 return IRECV_E_NO_DEVICE;
354 } 3599 }
355 3600
356 unsigned char buffer[6]; 3601 unsigned char buffer[6];
357 memset(buffer, '\0', 6); 3602 memset(buffer, '\0', sizeof(buffer));
358 if (libusb_control_transfer(client->handle, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) { 3603 if (irecv_usb_control_transfer(client, 0xA1, 3, 0, 0, buffer, sizeof(buffer), USB_TIMEOUT) != sizeof(buffer)) {
359 *status = 0; 3604 *status = 0;
360 return IRECV_E_USB_STATUS; 3605 return IRECV_E_USB_STATUS;
361 } 3606 }
362 3607
363 *status = (unsigned int) buffer[4]; 3608 *status = (unsigned int) buffer[4];
3609
3610 return IRECV_E_SUCCESS;
3611}
3612
3613static irecv_error_t irecv_kis_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3614{
3615 if (client->mode != IRECV_K_DFU_MODE) {
3616 return IRECV_E_UNSUPPORTED;
3617 }
3618
3619 unsigned long origLen = length;
3620
3621 KIS_upload_chunk *chunk = calloc(1, sizeof(KIS_upload_chunk));
3622 uint64_t address = 0;
3623 while (length) {
3624 unsigned long toUpload = length;
3625 if (toUpload > 0x4000)
3626 toUpload = 0x4000;
3627
3628#ifdef _WIN32
3629 memcpy(chunk->data, buffer, toUpload);
3630 chunk->size = toUpload;
3631 chunk->address = address;
3632#else
3633 irecv_error_t error = irecv_kis_request_init(&chunk->hdr, KIS_PORTAL_RSM, KIS_INDEX_UPLOAD, 3, toUpload, 0);
3634 if (error != IRECV_E_SUCCESS) {
3635 free(chunk);
3636 debug("Failed to init chunk header, error %d\n", error);
3637 return error;
3638 }
3639
3640 chunk->address = address;
3641 chunk->size = toUpload;
3642 memcpy(chunk->data, buffer, toUpload);
3643#endif
3644
3645#ifdef _WIN32
3646 DWORD transferred = 0;
3647 int ret = DeviceIoControl(client->handle, 0x220008, chunk, sizeof(*chunk), NULL, 0, (PDWORD)&transferred, NULL);
3648 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3649#else
3650 KIS_generic_reply reply;
3651 size_t rcvSize = sizeof(reply);
3652 error = irecv_kis_request(client, &chunk->hdr, sizeof(*chunk) - (0x4000 - toUpload), &reply.hdr, &rcvSize);
3653#endif
3654 if (error != IRECV_E_SUCCESS) {
3655 free(chunk);
3656 debug("Failed to upload chunk, error %d\n", error);
3657 return error;
3658 }
3659
3660 address += toUpload;
3661 buffer += toUpload;
3662 length -= toUpload;
3663
3664 if (client->progress_callback != NULL) {
3665 irecv_event_t event;
3666 event.progress = ((double) (origLen - length) / (double) origLen) * 100.0;
3667 event.type = IRECV_PROGRESS;
3668 event.data = (char*)"Uploading";
3669 event.size = origLen - length;
3670 client->progress_callback(client, &event);
3671 } else {
3672 debug("Sent: %lu bytes - %lu of %lu\n", toUpload, origLen - length, origLen);
3673 }
3674 }
3675 free(chunk);
3676
3677 if (options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) {
3678#ifdef _WIN32
3679 DWORD amount = (DWORD)origLen;
3680 DWORD transferred = 0;
3681 int ret = DeviceIoControl(client->handle, 0x22000C, &amount, 4, NULL, 0, (PDWORD)&transferred, NULL);
3682 irecv_error_t error = (ret) ? IRECV_E_SUCCESS : IRECV_E_USB_UPLOAD;
3683#else
3684 irecv_error_t error = irecv_kis_config_write32(client, KIS_PORTAL_RSM, KIS_INDEX_BOOT_IMG, origLen);
3685#endif
3686 if (error != IRECV_E_SUCCESS) {
3687 debug("Failed to boot image, error %d\n", error);
3688 return error;
3689 }
3690 }
3691
364 return IRECV_E_SUCCESS; 3692 return IRECV_E_SUCCESS;
365} 3693}
3694#endif
3695
3696irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, unsigned int options)
3697{
3698#ifdef USE_DUMMY
3699 return IRECV_E_UNSUPPORTED;
3700#else
3701 if (client->isKIS)
3702 return irecv_kis_send_buffer(client, buffer, length, options);
366 3703
367irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length) {
368 irecv_error_t error = 0; 3704 irecv_error_t error = 0;
369 int recovery_mode = (client->mode != kDfuMode); 3705 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
3706 int legacy_recovery_mode = 0;
3707 int isiOS2 = 0;
3708
3709 if (recovery_mode && client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3710#ifdef DEBUG
3711 uint8_t buf[0x100] = {0x00, 0x00, 0x34, 0x12}; // ask how large commands should be
3712 int bytes = 0;
3713 if ((error = irecv_usb_interrupt_transfer(client, 0x04, &buf[0], 4, &bytes, USB_TIMEOUT))) return error;
3714 if ((error = irecv_usb_interrupt_transfer(client, 0x83, &buf[0], sizeof(buf), &bytes, USB_TIMEOUT))) return error;
3715 if (bytes != sizeof(legacyCMD)) return IRECV_E_UNKNOWN_ERROR;
3716#endif
3717 legacy_recovery_mode = 1;
3718 }
370 3719
371 if (client == NULL || client->handle == NULL) { 3720 if (recovery_mode && !legacy_recovery_mode) {
372 return IRECV_E_NO_DEVICE; 3721 // we are in recovery mode and we are not dealing with iOS 1.x
3722 if ((client->device_info.cpid == 0x8900 || client->device_info.cpid == 0x8720) && !client->device_info.have_ibfl) {
3723 // iOS 2.x doesn't have IBFL tag, but iOS 3 does
3724 // Also, to avoid false activation of this codepath, restrict it to the only two CPID which can run iOS 2
3725 recovery_mode = 0; // iOS 2 recovery mode works same as DFU mode
3726 isiOS2 = 1;
3727 options |= IRECV_SEND_OPT_DFU_NOTIFY_FINISH;
3728 }
373 } 3729 }
374 3730
375 int packet_size = recovery_mode ? 0x4000: 0x800; 3731 if (check_context(client) != IRECV_E_SUCCESS)
3732 return IRECV_E_NO_DEVICE;
3733
3734 unsigned int h1 = 0xFFFFFFFF;
3735 unsigned char dfu_xbuf[12] = {0xff, 0xff, 0xff, 0xff, 0xac, 0x05, 0x00, 0x01, 0x55, 0x46, 0x44, 0x10};
3736 int dfu_crc = 1;
3737 int packet_size = recovery_mode ? 0x8000 : 0x800;
3738 if (legacy_recovery_mode) packet_size = 0x200;
3739 if (!recovery_mode && (options & IRECV_SEND_OPT_DFU_SMALL_PKT)) {
3740 packet_size = 0x40;
3741 dfu_crc = 0;
3742 }
376 int last = length % packet_size; 3743 int last = length % packet_size;
377 int packets = length / packet_size; 3744 int packets = length / packet_size;
3745
378 if (last != 0) { 3746 if (last != 0) {
379 packets++; 3747 packets++;
3748 } else {
3749 last = packet_size;
380 } 3750 }
381 3751
382 /* initiate transfer */ 3752 /* initiate transfer */
383 if (recovery_mode) { 3753 if (legacy_recovery_mode) {
384 error = libusb_control_transfer(client->handle, 0x41, 0, 0, 0, NULL, 0, 1000); 3754 int bytes = 0;
385 if (error != IRECV_E_SUCCESS) { 3755 uint32_t loadaddr0x8900 = 0x09000000;
386 return error; 3756 const char *ios1_overwrite_loadaddr = getenv("LIBIRECOVERY_IOS1_OVERWRITE_LOADADDR");
3757 if (ios1_overwrite_loadaddr) {
3758 sscanf(ios1_overwrite_loadaddr, "0x%x",&loadaddr0x8900);
3759 debug("Overwriting loadaddr requested by env var. uploading to 0x%08x\n",loadaddr0x8900);
3760 }
3761 legacyCMD cmd = {
3762 MSG_SEND_FILE,
3763 0x1234, //magic
3764 (uint32_t)length,
3765 loadaddr0x8900
3766 };
3767 if ((error = irecv_usb_interrupt_transfer(client, 0x04, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3768 if ((error = irecv_usb_interrupt_transfer(client, 0x83, (unsigned char*)&cmd, sizeof(cmd), &bytes, USB_TIMEOUT))) return error;
3769 if (cmd.cmdcode != MSG_ACK) return IRECV_E_UNKNOWN_ERROR;
3770 } else if (recovery_mode) {
3771 error = irecv_usb_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, USB_TIMEOUT);
3772 } else {
3773 uint8_t state = 0;
3774 if (irecv_usb_control_transfer(client, 0xa1, 5, 0, 0, (unsigned char*)&state, 1, USB_TIMEOUT) == 1) {
3775 error = IRECV_E_SUCCESS;
3776 } else {
3777 return IRECV_E_USB_UPLOAD;
3778 }
3779 switch (state) {
3780 case 2:
3781 /* DFU IDLE */
3782 break;
3783 case 8:
3784 /* DFU WAIT RESET */
3785 if (!isiOS2) {
3786 debug("Unexpected state %d in non-iOS2 mode!, 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 }
3790 break;
3791 case 10:
3792 debug("DFU ERROR, issuing CLRSTATUS\n");
3793 irecv_usb_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, USB_TIMEOUT);
3794 error = IRECV_E_USB_UPLOAD;
3795 break;
3796 default:
3797 debug("Unexpected state %d, issuing ABORT\n", state);
3798 irecv_usb_control_transfer(client, 0x21, 6, 0, 0, NULL, 0, USB_TIMEOUT);
3799 error = IRECV_E_USB_UPLOAD;
3800 break;
387 } 3801 }
388 } 3802 }
389 3803
3804 if (error != IRECV_E_SUCCESS) {
3805 return error;
3806 }
3807
390 int i = 0; 3808 int i = 0;
391 double progress = 0;
392 unsigned long count = 0; 3809 unsigned long count = 0;
393 unsigned int status = 0; 3810 unsigned int status = 0;
394 int bytes = 0; 3811 int bytes = 0;
395 for (i = 0; i < packets; i++) { 3812 for (i = 0; i < packets; i++) {
396 int size = (i + 1) < packets ? packet_size : last; 3813 int size = (i + 1) < packets ? packet_size : last;
397 3814
398 /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */ 3815 if (legacy_recovery_mode) {
399 if (recovery_mode) { 3816 // Use interrupt transfer for legacy devices
400 error = libusb_bulk_transfer(client->handle, 0x04, &buffer[i * packet_size], size, &bytes, 1000); 3817 error = irecv_usb_interrupt_transfer(client, 0x05, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
3818 } else if (recovery_mode) {
3819 // Use bulk transfer for recovery mode
3820 error = irecv_usb_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, USB_TIMEOUT);
401 } else { 3821 } else {
402 bytes = libusb_control_transfer(client->handle, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000); 3822 // Use control transfer for DFU and WTF mode
3823 if (dfu_crc) {
3824 int j;
3825 for (j = 0; j < size; j++) {
3826 crc32_step(h1, buffer[i*packet_size + j]);
3827 }
3828 }
3829 if (dfu_crc && i+1 == packets) {
3830 int j;
3831 if (size+16 > packet_size) {
3832 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3833 if (bytes != size) {
3834 return IRECV_E_USB_UPLOAD;
3835 }
3836 count += size;
3837 size = 0;
3838 }
3839 for (j = 0; j < 2; j++) {
3840 crc32_step(h1, dfu_xbuf[j*6 + 0]);
3841 crc32_step(h1, dfu_xbuf[j*6 + 1]);
3842 crc32_step(h1, dfu_xbuf[j*6 + 2]);
3843 crc32_step(h1, dfu_xbuf[j*6 + 3]);
3844 crc32_step(h1, dfu_xbuf[j*6 + 4]);
3845 crc32_step(h1, dfu_xbuf[j*6 + 5]);
3846 }
3847
3848 char* newbuf = (char*)malloc(size + 16);
3849 if (size > 0) {
3850 memcpy(newbuf, &buffer[i * packet_size], size);
3851 }
3852 memcpy(newbuf+size, dfu_xbuf, 12);
3853 newbuf[size+12] = h1 & 0xFF;
3854 newbuf[size+13] = (h1 >> 8) & 0xFF;
3855 newbuf[size+14] = (h1 >> 16) & 0xFF;
3856 newbuf[size+15] = (h1 >> 24) & 0xFF;
3857 size += 16;
3858 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, (unsigned char*)newbuf, size, USB_TIMEOUT);
3859 free(newbuf);
3860 } else {
3861 bytes = irecv_usb_control_transfer(client, 0x21, 1, i, 0, &buffer[i * packet_size], size, USB_TIMEOUT);
3862 }
403 } 3863 }
404 3864
405 if (bytes != size) { 3865 if (bytes != size) {
@@ -415,44 +3875,92 @@ irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, un
415 } 3875 }
416 3876
417 if (!recovery_mode && status != 5) { 3877 if (!recovery_mode && status != 5) {
418 return IRECV_E_USB_UPLOAD; 3878 int retry = 0;
3879
3880 while (retry++ < 20) {
3881 irecv_get_status(client, &status);
3882 if (status == 5) {
3883 break;
3884 }
3885 sleep(1);
3886 }
3887
3888 if (status != 5) {
3889 return IRECV_E_USB_UPLOAD;
3890 }
419 } 3891 }
420 3892
421 count += size; 3893 count += size;
422 if(client->progress_callback != NULL) { 3894 if (client->progress_callback != NULL) {
423 irecv_event_t event; 3895 irecv_event_t event;
424 event.progress = ((double) count/ (double) length) * 100.0; 3896 event.progress = ((double) count/ (double) length) * 100.0;
425 event.type = IRECV_PROGRESS; 3897 event.type = IRECV_PROGRESS;
426 event.data = "Uploading"; 3898 event.data = (char*)"Uploading";
427 event.size = count; 3899 event.size = count;
428 client->progress_callback(client, &event); 3900 client->progress_callback(client, &event);
429 } else { 3901 } else {
430 debug("Sent: %d bytes - %d of %d\n", bytes, count, length); 3902 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
431 } 3903 }
432 } 3904 }
433 3905
434 if (!recovery_mode) { 3906 if (recovery_mode && length % 512 == 0) {
435 libusb_control_transfer(client->handle, 0x21, 1, 0, 0, buffer, 0, 1000); 3907 /* send a ZLP */
436 for (i = 0; i < 3; i++) { 3908 bytes = 0;
3909 irecv_usb_bulk_transfer(client, 0x04, buffer, 0, &bytes, USB_TIMEOUT);
3910 }
3911
3912 if ((options & IRECV_SEND_OPT_DFU_NOTIFY_FINISH) && !recovery_mode) {
3913 irecv_usb_control_transfer(client, 0x21, 1, packets, 0, (unsigned char*) buffer, 0, USB_TIMEOUT);
3914
3915 for (i = 0; i < 2; i++) {
437 error = irecv_get_status(client, &status); 3916 error = irecv_get_status(client, &status);
438 if (error != IRECV_E_SUCCESS) { 3917 if (error != IRECV_E_SUCCESS) {
439 return error; 3918 return error;
440 } 3919 }
441 } 3920 }
3921
3922 if ((options & IRECV_SEND_OPT_DFU_FORCE_ZLP)) {
3923 /* we send a pseudo ZLP here just in case */
3924 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
3925 }
3926
3927 irecv_reset(client);
3928
3929 if (isiOS2) {
3930 irecv_reconnect(client, 0);
3931 }
3932 }
3933
3934 if (legacy_recovery_mode) {
3935 irecv_reconnect(client, 0);
3936 char cmdstr[0x100] = {0};
3937 snprintf(cmdstr, sizeof(cmdstr), "setenv filesize %d", (int)length);
3938 irecv_send_command(client, cmdstr);
442 } 3939 }
443 3940
444 return IRECV_E_SUCCESS; 3941 return IRECV_E_SUCCESS;
3942#endif
445} 3943}
446 3944
447irecv_error_t irecv_receive(irecv_client_t client) { 3945irecv_error_t irecv_receive(irecv_client_t client)
448 unsigned char buffer[BUFFER_SIZE]; 3946{
3947#ifdef USE_DUMMY
3948 return IRECV_E_UNSUPPORTED;
3949#else
3950 char buffer[BUFFER_SIZE];
449 memset(buffer, '\0', BUFFER_SIZE); 3951 memset(buffer, '\0', BUFFER_SIZE);
450 if (client == NULL || client->handle == NULL) { 3952
3953 if (check_context(client) != IRECV_E_SUCCESS)
451 return IRECV_E_NO_DEVICE; 3954 return IRECV_E_NO_DEVICE;
452 }
453 3955
454 int bytes = 0; 3956 int bytes = 0;
455 while (libusb_bulk_transfer(client->handle, 0x81, buffer, BUFFER_SIZE, &bytes, 100) == 0) { 3957 while (1) {
3958 irecv_usb_set_interface(client, 1, 1);
3959 int r = irecv_usb_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 500);
3960 irecv_usb_set_interface(client, 0, 0);
3961 if (r != 0) {
3962 break;
3963 }
456 if (bytes > 0) { 3964 if (bytes > 0) {
457 if (client->received_callback != NULL) { 3965 if (client->received_callback != NULL) {
458 irecv_event_t event; 3966 irecv_event_t event;
@@ -460,155 +3968,295 @@ irecv_error_t irecv_receive(irecv_client_t client) {
460 event.data = buffer; 3968 event.data = buffer;
461 event.type = IRECV_RECEIVED; 3969 event.type = IRECV_RECEIVED;
462 if (client->received_callback(client, &event) != 0) { 3970 if (client->received_callback(client, &event) != 0) {
463 return IRECV_E_SUCCESS; 3971 break;
464 } 3972 }
465 } 3973 }
466 } else break; 3974 } else break;
467 } 3975 }
468
469 return IRECV_E_SUCCESS; 3976 return IRECV_E_SUCCESS;
3977#endif
470} 3978}
471 3979
472irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) { 3980irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value)
3981{
3982#ifdef USE_DUMMY
3983 return IRECV_E_UNSUPPORTED;
3984#else
473 char command[256]; 3985 char command[256];
474 if (client == NULL || client->handle == NULL) { 3986
3987 if (check_context(client) != IRECV_E_SUCCESS)
475 return IRECV_E_NO_DEVICE; 3988 return IRECV_E_NO_DEVICE;
476 }
477 3989
478 *value = NULL; 3990 *value = NULL;
479 3991
480 if(variable == NULL) { 3992 if (variable == NULL) {
481 return IRECV_E_UNKNOWN_ERROR; 3993 return IRECV_E_INVALID_INPUT;
3994 }
3995
3996 if (client->device_info.cpid == 0x8900 && !client->device_info.ecid) {
3997 debug("iOS 1 doesn't support getenv\n");
3998 return IRECV_E_UNSUPPORTED;
482 } 3999 }
483 4000
484 memset(command, '\0', sizeof(command)); 4001 memset(command, '\0', sizeof(command));
485 snprintf(command, sizeof(command)-1, "getenv %s", variable); 4002 snprintf(command, sizeof(command)-1, "getenv %s", variable);
486 irecv_error_t error = irecv_send_command_raw(client, command); 4003 irecv_error_t error = irecv_send_command_raw(client, command, 0);
487 if(error == IRECV_E_PIPE) 4004 if (error == IRECV_E_PIPE) {
488 return IRECV_E_SUCCESS; 4005 return IRECV_E_SUCCESS;
489 if(error != IRECV_E_SUCCESS) 4006 }
4007
4008 if (error != IRECV_E_SUCCESS) {
490 return error; 4009 return error;
4010 }
491 4011
492 unsigned char* response = (unsigned char*) malloc(256); 4012 int rsize = 256;
4013 char* response = (char*) malloc(rsize);
493 if (response == NULL) { 4014 if (response == NULL) {
494 return IRECV_E_OUT_OF_MEMORY; 4015 return IRECV_E_OUT_OF_MEMORY;
495 } 4016 }
496 4017
497 memset(response, '\0', 256); 4018 memset(response, '\0', rsize);
498 int ret = libusb_control_transfer(client->handle, 0xC0, 0, 0, 0, response, 255, 500); 4019 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 4020
502 *value = response; 4021 *value = response;
4022
503 return IRECV_E_SUCCESS; 4023 return IRECV_E_SUCCESS;
4024#endif
504} 4025}
505 4026
506irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) { 4027irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value)
507 if (client == NULL || client->handle == NULL) { 4028{
4029#ifdef USE_DUMMY
4030 return IRECV_E_UNSUPPORTED;
4031#else
4032 if (check_context(client) != IRECV_E_SUCCESS)
508 return IRECV_E_NO_DEVICE; 4033 return IRECV_E_NO_DEVICE;
509 }
510 4034
511 unsigned char* cpid_string = strstr(client->serial, "CPID:"); 4035 *value = 0;
512 if (cpid_string == NULL) { 4036
513 *cpid = 0; 4037 int rsize = 256;
514 return IRECV_E_UNKNOWN_ERROR; 4038 char* response = (char*) malloc(rsize);
4039 if (response == NULL) {
4040 return IRECV_E_OUT_OF_MEMORY;
515 } 4041 }
516 sscanf(cpid_string, "CPID:%d", cpid); 4042
4043 memset(response, '\0', rsize);
4044 irecv_usb_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, rsize-1, USB_TIMEOUT);
4045
4046 *value = (unsigned int) *response;
517 4047
518 return IRECV_E_SUCCESS; 4048 return IRECV_E_SUCCESS;
4049#endif
519} 4050}
520 4051
521irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) { 4052irecv_error_t irecv_get_mode(irecv_client_t client, int* mode)
522 if (client == NULL || client->handle == NULL) { 4053{
4054#ifdef USE_DUMMY
4055 return IRECV_E_UNSUPPORTED;
4056#else
4057 if (check_context(client) != IRECV_E_SUCCESS)
523 return IRECV_E_NO_DEVICE; 4058 return IRECV_E_NO_DEVICE;
524 }
525 4059
526 unsigned char* bdid_string = strstr(client->serial, "BDID:"); 4060 *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 4061
533 return IRECV_E_SUCCESS; 4062 return IRECV_E_SUCCESS;
4063#endif
534} 4064}
535 4065
536irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) { 4066const struct irecv_device_info* irecv_get_device_info(irecv_client_t client)
537 if (client == NULL || client->handle == NULL) { 4067{
538 return IRECV_E_NO_DEVICE; 4068#ifdef USE_DUMMY
539 } 4069 return NULL;
540 4070#else
541 unsigned char* ecid_string = strstr(client->serial, "ECID:"); 4071 if (check_context(client) != IRECV_E_SUCCESS)
542 if (ecid_string == NULL) { 4072 return NULL;
543 *ecid = 0;
544 return IRECV_E_UNKNOWN_ERROR;
545 }
546 sscanf(ecid_string, "ECID:%qX", ecid);
547 4073
548 return IRECV_E_SUCCESS; 4074 return &client->device_info;
4075#endif
549} 4076}
550 4077
551irecv_error_t irecv_send_exploit(irecv_client_t client) { 4078#ifndef USE_DUMMY
552 if (client == NULL || client->handle == NULL) { 4079#ifdef HAVE_IOKIT
4080static void *iokit_limera1n_usb_submit_request(void *argv)
4081{
4082 void **args = argv;
4083 IOUSBDeviceInterface320 **dev = args[0];
4084 IOUSBDevRequest *req = args[1];
4085
4086 IOReturn result = (*dev)->DeviceRequest(dev, req);
4087 if (result != kIOReturnSuccess)
4088 debug("%s result: %#x\n", __func__, result);
4089
4090 return NULL;
4091}
4092#endif
4093#endif
4094
4095irecv_error_t irecv_trigger_limera1n_exploit(irecv_client_t client)
4096{
4097#ifdef USE_DUMMY
4098 return IRECV_E_UNSUPPORTED;
4099#else
4100 if (check_context(client) != IRECV_E_SUCCESS)
553 return IRECV_E_NO_DEVICE; 4101 return IRECV_E_NO_DEVICE;
4102
4103#ifdef HAVE_IOKIT
4104 IOReturn result;
4105 IOUSBDevRequestTO req;
4106 bzero(&req, sizeof(req));
4107
4108 req.bmRequestType = 0x21;
4109 req.bRequest = 2;
4110 req.wValue = 0;
4111 req.wIndex = 0;
4112 req.wLength = 0;
4113 req.pData = NULL;
4114 req.noDataTimeout = USB_TIMEOUT;
4115 req.completionTimeout = USB_TIMEOUT;
4116
4117 // The original version uses an async request, but we don't have an async event
4118 // source set up. The hack relies on aborting the transaction before it times out,
4119 // which can be accomplished by sending on another thread.
4120
4121 void *args[2] = { client->handle, &req };
4122 THREAD_T thread;
4123 thread_new(&thread, iokit_limera1n_usb_submit_request, args);
4124
4125 usleep(5 * 1000);
4126 result = (*client->handle)->USBDeviceAbortPipeZero(client->handle);
4127 if (result != kIOReturnSuccess)
4128 debug("USBDeviceAbortPipeZero returned %#x\n", result);
4129
4130 switch (result) {
4131 case kIOReturnSuccess: return req.wLenDone;
4132 case kIOReturnTimeout: return IRECV_E_TIMEOUT;
4133 case kIOUSBTransactionTimeout: return IRECV_E_TIMEOUT;
4134 case kIOReturnNotResponding: return IRECV_E_NO_DEVICE;
4135 case kIOReturnNoDevice: return IRECV_E_NO_DEVICE;
4136 default:
4137 return IRECV_E_UNKNOWN_ERROR;
554 } 4138 }
4139#else
4140 irecv_usb_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, USB_TIMEOUT);
4141#endif
555 4142
556 libusb_control_transfer(client->handle, 0x21, 2, 0, 0, NULL, 0, 100);
557 return IRECV_E_SUCCESS; 4143 return IRECV_E_SUCCESS;
4144#endif
558} 4145}
559 4146
560irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) { 4147irecv_error_t irecv_execute_script(irecv_client_t client, const char* script)
4148{
4149#ifdef USE_DUMMY
4150 return IRECV_E_UNSUPPORTED;
4151#else
561 irecv_error_t error = IRECV_E_SUCCESS; 4152 irecv_error_t error = IRECV_E_SUCCESS;
562 if (client == NULL || client->handle == NULL) { 4153 if (check_context(client) != IRECV_E_SUCCESS)
563 return IRECV_E_NO_DEVICE; 4154 return IRECV_E_NO_DEVICE;
564 }
565 4155
566 int file_size = 0; 4156 char* body = strdup(script);
567 char* file_data = NULL; 4157 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 4158
572 char* line = strtok(file_data, "\n"); 4159 while (line != NULL) {
573 while(line != NULL) { 4160 if (line[0] != '#') {
574 if(line[0] != '#') {
575 error = irecv_send_command(client, line); 4161 error = irecv_send_command(client, line);
576 if(error != IRECV_E_SUCCESS) { 4162 if (error != IRECV_E_SUCCESS) {
577 return error; 4163 break;
578 } 4164 }
579 4165
580 error = irecv_receive(client); 4166 error = irecv_receive(client);
581 if(error != IRECV_E_SUCCESS) { 4167 if (error != IRECV_E_SUCCESS) {
582 return error; 4168 break;
583 } 4169 }
584 } 4170 }
585 line = strtok(NULL, "\n"); 4171 line = strtok(NULL, "\n");
586 } 4172 }
587 4173
4174 free(body);
4175
4176 return error;
4177#endif
4178}
4179
4180irecv_error_t irecv_saveenv(irecv_client_t client)
4181{
4182#ifdef USE_DUMMY
4183 return IRECV_E_UNSUPPORTED;
4184#else
4185 irecv_error_t error = irecv_send_command_raw(client, "saveenv", 0);
4186 if (error != IRECV_E_SUCCESS) {
4187 return error;
4188 }
4189
588 return IRECV_E_SUCCESS; 4190 return IRECV_E_SUCCESS;
4191#endif
589} 4192}
590 4193
591irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) { 4194irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value)
4195{
4196#ifdef USE_DUMMY
4197 return IRECV_E_UNSUPPORTED;
4198#else
592 char command[256]; 4199 char command[256];
593 if (client == NULL || client->handle == NULL) { 4200
4201 if (check_context(client) != IRECV_E_SUCCESS)
594 return IRECV_E_NO_DEVICE; 4202 return IRECV_E_NO_DEVICE;
595 }
596 4203
597 if(variable == NULL || value == NULL) { 4204 if (variable == NULL || value == NULL) {
598 return IRECV_E_UNKNOWN_ERROR; 4205 return IRECV_E_UNKNOWN_ERROR;
599 } 4206 }
600 4207
601 memset(command, '\0', sizeof(command)); 4208 memset(command, '\0', sizeof(command));
602 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value); 4209 snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value);
603 irecv_error_t error = irecv_send_command_raw(client, command); 4210 irecv_error_t error = irecv_send_command_raw(client, command, 0);
604 if(error != IRECV_E_SUCCESS) { 4211 if (error != IRECV_E_SUCCESS) {
605 return error; 4212 return error;
606 } 4213 }
607 4214
608 return IRECV_E_SUCCESS; 4215 return IRECV_E_SUCCESS;
4216#endif
609} 4217}
610 4218
611const char* irecv_strerror(irecv_error_t error) { 4219irecv_error_t irecv_setenv_np(irecv_client_t client, const char* variable, const char* value)
4220{
4221#ifdef USE_DUMMY
4222 return IRECV_E_UNSUPPORTED;
4223#else
4224 char command[256];
4225
4226 if (check_context(client) != IRECV_E_SUCCESS)
4227 return IRECV_E_NO_DEVICE;
4228
4229 if (variable == NULL || value == NULL) {
4230 return IRECV_E_UNKNOWN_ERROR;
4231 }
4232
4233 memset(command, '\0', sizeof(command));
4234 snprintf(command, sizeof(command)-1, "setenvnp %s %s", variable, value);
4235 irecv_error_t error = irecv_send_command_raw(client, command, 0);
4236 if (error != IRECV_E_SUCCESS) {
4237 return error;
4238 }
4239
4240 return IRECV_E_SUCCESS;
4241#endif
4242}
4243
4244irecv_error_t irecv_reboot(irecv_client_t client)
4245{
4246#ifdef USE_DUMMY
4247 return IRECV_E_UNSUPPORTED;
4248#else
4249 irecv_error_t error = irecv_send_command_raw(client, "reboot", 0);
4250 if (error != IRECV_E_SUCCESS) {
4251 return error;
4252 }
4253
4254 return IRECV_E_SUCCESS;
4255#endif
4256}
4257
4258const char* irecv_strerror(irecv_error_t error)
4259{
612 switch (error) { 4260 switch (error) {
613 case IRECV_E_SUCCESS: 4261 case IRECV_E_SUCCESS:
614 return "Command completed successfully"; 4262 return "Command completed successfully";
@@ -646,6 +4294,9 @@ const char* irecv_strerror(irecv_error_t error) {
646 case IRECV_E_TIMEOUT: 4294 case IRECV_E_TIMEOUT:
647 return "Timeout talking to device"; 4295 return "Timeout talking to device";
648 4296
4297 case IRECV_E_UNSUPPORTED:
4298 return "Operation unsupported by driver";
4299
649 default: 4300 default:
650 return "Unknown error"; 4301 return "Unknown error";
651 } 4302 }
@@ -653,64 +4304,220 @@ const char* irecv_strerror(irecv_error_t error) {
653 return NULL; 4304 return NULL;
654} 4305}
655 4306
656int irecv_write_file(const char* filename, const void* data, size_t size) { 4307irecv_error_t irecv_reset_counters(irecv_client_t client)
657 size_t bytes = 0; 4308{
658 FILE* file = NULL; 4309#ifdef USE_DUMMY
4310 return IRECV_E_UNSUPPORTED;
4311#else
4312 if (check_context(client) != IRECV_E_SUCCESS)
4313 return IRECV_E_NO_DEVICE;
659 4314
660 debug("Writing data to %s\n", filename); 4315 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"); 4316 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 } 4317 }
666 4318
667 bytes = fwrite(data, 1, size, file); 4319 return IRECV_E_SUCCESS;
668 fclose(file); 4320#endif
4321}
669 4322
670 if (bytes != size) { 4323irecv_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); 4324{
672 return -1; 4325#ifdef USE_DUMMY
4326 return IRECV_E_UNSUPPORTED;
4327#else
4328 int recovery_mode = ((client->mode != IRECV_K_DFU_MODE) && (client->mode != IRECV_K_PORT_DFU_MODE) && (client->mode != IRECV_K_WTF_MODE));
4329
4330 if (check_context(client) != IRECV_E_SUCCESS)
4331 return IRECV_E_NO_DEVICE;
4332
4333 int packet_size = recovery_mode ? 0x2000: 0x800;
4334 int last = length % packet_size;
4335 int packets = length / packet_size;
4336 if (last != 0) {
4337 packets++;
4338 } else {
4339 last = packet_size;
4340 }
4341
4342 int i = 0;
4343 int bytes = 0;
4344 unsigned long count = 0;
4345 for (i = 0; i < packets; i++) {
4346 unsigned short size = (i+1) < packets ? packet_size : last;
4347 bytes = irecv_usb_control_transfer(client, 0xA1, 2, 0, 0, (unsigned char*)&buffer[i * packet_size], size, USB_TIMEOUT);
4348
4349 if (bytes != size) {
4350 return IRECV_E_USB_UPLOAD;
4351 }
4352
4353 count += size;
4354 if (client->progress_callback != NULL) {
4355 irecv_event_t event;
4356 event.progress = ((double) count/ (double) length) * 100.0;
4357 event.type = IRECV_PROGRESS;
4358 event.data = (char*)"Downloading";
4359 event.size = count;
4360 client->progress_callback(client, &event);
4361 } else {
4362 debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
4363 }
4364 }
4365
4366 return IRECV_E_SUCCESS;
4367#endif
4368}
4369
4370irecv_error_t irecv_finish_transfer(irecv_client_t client)
4371{
4372#ifdef USE_DUMMY
4373 return IRECV_E_UNSUPPORTED;
4374#else
4375 int i = 0;
4376 unsigned int status = 0;
4377
4378 if (check_context(client) != IRECV_E_SUCCESS)
4379 return IRECV_E_NO_DEVICE;
4380
4381 irecv_usb_control_transfer(client, 0x21, 1, 0, 0, 0, 0, USB_TIMEOUT);
4382
4383 for (i = 0; i < 3; i++){
4384 irecv_get_status(client, &status);
673 } 4385 }
674 4386
675 return size; 4387 irecv_reset(client);
4388
4389 return IRECV_E_SUCCESS;
4390#endif
676} 4391}
677 4392
678int irecv_read_file(const char* filename, char** data, uint32_t* size) { 4393irecv_device_t irecv_devices_get_all(void)
679 size_t bytes = 0; 4394{
680 size_t length = 0; 4395 return irecv_devices;
681 FILE* file = NULL; 4396}
682 char* buffer = NULL;
683 debug("Reading data from %s\n", filename);
684 4397
685 *size = 0; 4398irecv_error_t irecv_devices_get_device_by_client(irecv_client_t client, irecv_device_t* device)
686 *data = NULL; 4399{
4400#ifdef USE_DUMMY
4401 return IRECV_E_UNSUPPORTED;
4402#else
4403 int i = 0;
687 4404
688 file = fopen(filename, "rb"); 4405 if (!client || !device)
689 if (file == NULL) { 4406 return IRECV_E_INVALID_INPUT;
690 error("read_file: File %s not found\n", filename); 4407
691 return -1; 4408 *device = NULL;
4409
4410 if (client->device_info.cpid == 0) {
4411 return IRECV_E_UNKNOWN_ERROR;
692 } 4412 }
693 4413
694 fseek(file, 0, SEEK_END); 4414 unsigned int cpid_match = client->device_info.cpid;
695 length = ftell(file); 4415 unsigned int bdid_match = client->device_info.bdid;
696 rewind(file); 4416 if (client->mode == IRECV_K_PORT_DFU_MODE) {
4417 cpid_match = (client->device_info.bdid >> 8) & 0xFFFF;
4418 bdid_match = (client->device_info.bdid >> 24) & 0xFF;
4419 }
697 4420
698 buffer = (char*) malloc(length); 4421 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
699 if(buffer == NULL) { 4422 if (irecv_devices[i].chip_id == cpid_match && irecv_devices[i].board_id == bdid_match) {
700 error("ERROR: Out of memory\n"); 4423 *device = &irecv_devices[i];
701 fclose(file); 4424 return IRECV_E_SUCCESS;
702 return -1; 4425 }
703 } 4426 }
704 bytes = fread(buffer, 1, length, file);
705 fclose(file);
706 4427
707 if(bytes != length) { 4428 return IRECV_E_NO_DEVICE;
708 error("ERROR: Unable to read entire file\n"); 4429#endif
709 free(buffer); 4430}
710 return -1; 4431
4432irecv_error_t irecv_devices_get_device_by_product_type(const char* product_type, irecv_device_t* device)
4433{
4434 int i = 0;
4435
4436 if (!product_type || !device)
4437 return IRECV_E_INVALID_INPUT;
4438
4439 *device = NULL;
4440
4441 for (i = 0; irecv_devices[i].product_type != NULL; i++) {
4442 if (!strcmp(product_type, irecv_devices[i].product_type)) {
4443 *device = &irecv_devices[i];
4444 return IRECV_E_SUCCESS;
4445 }
711 } 4446 }
712 4447
713 *size = length; 4448 return IRECV_E_NO_DEVICE;
714 *data = buffer; 4449}
715 return 0; 4450
4451irecv_error_t irecv_devices_get_device_by_hardware_model(const char* hardware_model, irecv_device_t* device)
4452{
4453 int i = 0;
4454
4455 if (!hardware_model || !device)
4456 return IRECV_E_INVALID_INPUT;
4457
4458 *device = NULL;
4459
4460 for (i = 0; irecv_devices[i].hardware_model != NULL; i++) {
4461 if (!strcasecmp(hardware_model, irecv_devices[i].hardware_model)) {
4462 *device = &irecv_devices[i];
4463 return IRECV_E_SUCCESS;
4464 }
4465 }
4466
4467 return IRECV_E_NO_DEVICE;
4468}
4469
4470irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause)
4471{
4472#ifdef USE_DUMMY
4473 return NULL;
4474#else
4475 irecv_error_t error = 0;
4476 irecv_client_t new_client = NULL;
4477 irecv_event_cb_t progress_callback = client->progress_callback;
4478 irecv_event_cb_t received_callback = client->received_callback;
4479 irecv_event_cb_t connected_callback = client->connected_callback;
4480 irecv_event_cb_t precommand_callback = client->precommand_callback;
4481 irecv_event_cb_t postcommand_callback = client->postcommand_callback;
4482 irecv_event_cb_t disconnected_callback = client->disconnected_callback;
4483
4484 uint64_t ecid = client->device_info.ecid;
4485
4486 if (check_context(client) == IRECV_E_SUCCESS) {
4487 irecv_cleanup(client);
4488 }
4489
4490 if (initial_pause > 0) {
4491 debug("Waiting %d seconds for the device to pop up...\n", initial_pause);
4492 sleep(initial_pause);
4493 }
4494
4495 error = irecv_open_with_ecid_and_attempts(&new_client, ecid, 10);
4496 if (error != IRECV_E_SUCCESS) {
4497 return NULL;
4498 }
4499
4500 new_client->progress_callback = progress_callback;
4501 new_client->received_callback = received_callback;
4502 new_client->connected_callback = connected_callback;
4503 new_client->precommand_callback = precommand_callback;
4504 new_client->postcommand_callback = postcommand_callback;
4505 new_client->disconnected_callback = disconnected_callback;
4506
4507 // keep old handle valid
4508 memcpy(client, new_client, sizeof(*client));
4509 free(new_client);
4510 new_client = client;
4511
4512 if (new_client->connected_callback != NULL) {
4513 irecv_event_t event;
4514 event.size = 0;
4515 event.data = NULL;
4516 event.progress = 0;
4517 event.type = IRECV_CONNECTED;
4518 new_client->connected_callback(new_client, &event);
4519 }
4520
4521 return new_client;
4522#endif
716} 4523}
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