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