summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar boxingsquirrel2011-04-30 17:31:01 -0400
committerGravatar boxingsquirrel2011-04-30 17:31:01 -0400
commit1c851c273dece4d1a6efa5a6e093557bd0e862c1 (patch)
tree36462c25af32a6bf2070a6a1fe510beed0a8efd2 /src
parentfde808212e85de310cb404ac2e577da17f8382c2 (diff)
downloadidevicerestore-1c851c273dece4d1a6efa5a6e093557bd0e862c1.tar.gz
idevicerestore-1c851c273dece4d1a6efa5a6e093557bd0e862c1.tar.bz2
Working with iOS versions up to 4.3.2; plus all currently released devices
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am12
-rw-r--r--src/asr.c6
-rw-r--r--src/common.h26
-rw-r--r--src/idevicerestore.c70
-rw-r--r--src/libirecovery.c1293
-rw-r--r--src/libirecovery.h232
-rw-r--r--src/recovery.c11
-rw-r--r--src/recovery.h6
-rw-r--r--src/tss.c7
9 files changed, 1645 insertions, 18 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index a4c8fc3..831a222 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
-libirecovery_CFLAGS = -I/usr/local/include
-libirecovery_LIBS = -L/usr/local/lib -lirecovery -lusb-1.0
+#libirecovery_CFLAGS = -I/usr/local/include
+#libirecovery_LIBS = -L/usr/local/lib -lusb-1.0
AM_CFLAGS = \
$(GLOBAL_CFLAGS) \
@@ -7,17 +7,17 @@ AM_CFLAGS = \
$(libplist_CFLAGS) \
$(libzip_CFLAGS) \
$(libcurl_CFLAGS) \
- $(libirecovery_CFLAGS)
+ #$(libirecovery_CFLAGS)
AM_LDFLAGS =\
$(libimobiledevice_LIBS) \
$(libplist_LIBS) \
$(libzip_LIBS) \
$(libcurl_LIBS) \
- $(libirecovery_LIBS)
+ #$(libirecovery_LIBS)
bin_PROGRAMS = idevicerestore
-idevicerestore_SOURCES = idevicerestore.c common.c tss.c img3.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c
+idevicerestore_SOURCES = idevicerestore.c common.c tss.c img3.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c libirecovery.c
idevicerestore_CFLAGS = $(AM_CFLAGS)
-idevicerestore_LDFLAGS = $(AM_LDFLAGS)
+idevicerestore_LDFLAGS = $(AM_LDFLAGS) -lusb-1.0
diff --git a/src/asr.c b/src/asr.c
index f48170a..7b08f55 100644
--- a/src/asr.c
+++ b/src/asr.c
@@ -106,9 +106,11 @@ int asr_send(idevice_connection_t asr, plist_t* data) {
}
debug("Sent %d bytes:\n", size);
- if (idevicerestore_debug)
+
+ // TODO: Actually figure out the problem with the commented out code below, instead of just ditching it...
+ /*if (idevicerestore_debug)
debug_plist(*data);
- free(buffer);
+ free(buffer);*/
return 0;
}
diff --git a/src/common.h b/src/common.h
index f8c0ae0..b6873a2 100644
--- a/src/common.h
+++ b/src/common.h
@@ -41,6 +41,12 @@ extern "C" {
#define CPID_IPOD3G 8922
#define CPID_IPAD1G 8930
#define CPID_IPHONE4 8930
+#define CPID_IPOD4G 8930
+#define CPID_APPLETV2 8930
+#define CPID_IPHONE42 8930
+#define CPID_IPAD21 8940
+#define CPID_IPAD22 8940
+#define CPID_IPAD23 8940
#define BDID_UNKNOWN -1
#define BDID_IPHONE2G 0
@@ -51,6 +57,12 @@ extern "C" {
#define BDID_IPOD3G 2
#define BDID_IPAD1G 2
#define BDID_IPHONE4 0
+#define BDID_IPOD4G 8
+#define BDID_APPLETV2 10
+#define BDID_IPHONE42 6
+#define BDID_IPAD21 4
+#define BDID_IPAD22 6
+#define BDID_IPAD23 2
#define DEVICE_UNKNOWN -1
#define DEVICE_IPHONE2G 0
@@ -61,6 +73,12 @@ extern "C" {
#define DEVICE_IPOD3G 5
#define DEVICE_IPAD1G 6
#define DEVICE_IPHONE4 7
+#define DEVICE_IPOD4G 8
+#define DEVICE_APPLETV2 9
+#define DEVICE_IPHONE42 10
+#define DEVICE_IPAD21 11
+#define DEVICE_IPAD22 12
+#define DEVICE_IPAD23 13
#define MODE_UNKNOWN -1
#define MODE_DFU 0
@@ -74,6 +92,8 @@ extern "C" {
#define FLAG_CUSTOM 8
#define FLAG_EXCLUDE 16
+extern int use_apple_server;
+
struct dfu_client_t;
struct normal_client_t;
struct restore_clien_t;
@@ -135,6 +155,12 @@ static struct idevicerestore_device_t idevicerestore_devices[] = {
{ 5, "iPod3,1", "N18AP", 2, 8922 },
{ 6, "iPad1,1", "K48AP", 2, 8930 },
{ 7, "iPhone3,1", "N90AP", 0, 8930 },
+ { 8, "iPod4,1", "N81AP", 8, 8930 },
+ { 9, "AppleTV2,1", "K66AP", 10, 8930 },
+ { 10, "iPhone3,1", "N92AP", 6, 8930 },
+ { 11, "iPad2,1", "K93AP", 4, 8940 },
+ { 12, "iPad2,2", "K94AP", 6, 8940 },
+ { 13, "iPad2,3", "K95AP", 2, 8940 },
{ -1, NULL, NULL, -1, -1 }
};
diff --git a/src/idevicerestore.c b/src/idevicerestore.c
index f3fdbdc..3026551 100644
--- a/src/idevicerestore.c
+++ b/src/idevicerestore.c
@@ -36,12 +36,15 @@
#include "recovery.h"
#include "idevicerestore.h"
+int use_apple_server;
+
static struct option longopts[] = {
{ "uuid", required_argument, NULL, 'u' },
{ "debug", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "erase", no_argument, NULL, 'e' },
{ "custom", no_argument, NULL, 'c' },
+ { "cydia", no_argument, NULL, 's' },
{ "exclude", no_argument, NULL, 'x' },
{ NULL, 0, NULL, 0 }
};
@@ -55,6 +58,7 @@ void usage(int argc, char* argv[]) {
printf(" -h, --help\t\tprints usage information\n");
printf(" -e, --erase\t\tperform a full restore, erasing all data\n");
printf(" -c, --custom\t\trestore with a custom firmware\n");
+ printf(" -s, --cydia\t\tuse Cydia's signature service instead of Apple's\n");
printf(" -x, --exclude\t\texclude nor/baseband upgrade\n");
printf("\n");
}
@@ -65,6 +69,7 @@ int main(int argc, char* argv[]) {
char* ipsw = NULL;
char* uuid = NULL;
int tss_enabled = 0;
+ use_apple_server=1;
// create an instance of our context
struct idevicerestore_client_t* client = (struct idevicerestore_client_t*) malloc(sizeof(struct idevicerestore_client_t));
@@ -74,7 +79,7 @@ int main(int argc, char* argv[]) {
}
memset(client, '\0', sizeof(struct idevicerestore_client_t));
- while ((opt = getopt_long(argc, argv, "dhcexu:", longopts, &optindex)) > 0) {
+ while ((opt = getopt_long(argc, argv, "dhcesxu:", longopts, &optindex)) > 0) {
switch (opt) {
case 'h':
usage(argc, argv);
@@ -93,6 +98,9 @@ int main(int argc, char* argv[]) {
client->flags |= FLAG_CUSTOM;
break;
+ case 's':
+ use_apple_server=0;
+
case 'x':
client->flags |= FLAG_EXCLUDE;
break;
@@ -363,7 +371,63 @@ int check_device(struct idevicerestore_client_t* client) {
break;
case CPID_IPAD1G:
- device = DEVICE_IPAD1G;
+ // All the A4 devices are the same...BoardID'll solve that problem!
+ if (get_bdid(client, &bdid) < 0) {
+ error("ERROR: Unable to get device BDID\n");
+ break;
+ }
+
+ switch (bdid) {
+ case BDID_IPAD1G:
+ device = DEVICE_IPAD1G;
+ break;
+
+ case BDID_IPHONE4:
+ device = DEVICE_IPHONE4;
+ break;
+
+ case BDID_IPOD4G:
+ device = DEVICE_IPOD4G;
+ break;
+
+ case BDID_APPLETV2:
+ device = DEVICE_APPLETV2;
+ break;
+
+ case BDID_IPHONE42:
+ device = DEVICE_IPHONE42;
+ break;
+
+ default:
+ device = DEVICE_UNKNOWN;
+ break;
+ }
+ break;
+
+ case CPID_IPAD21:
+ // All the A5 devices are the same too...
+ if (get_bdid(client, &bdid) < 0) {
+ error("ERROR: Unable to get device BDID\n");
+ break;
+ }
+
+ switch (bdid) {
+ case BDID_IPAD21:
+ device = DEVICE_IPAD21;
+ break;
+
+ case BDID_IPAD22:
+ device = DEVICE_IPAD22;
+ break;
+
+ case BDID_IPAD23:
+ device = DEVICE_IPAD23;
+ break;
+
+ default:
+ device = DEVICE_UNKNOWN;
+ break;
+ }
break;
default:
@@ -657,7 +721,7 @@ void build_identity_print_information(plist_t build_identity) {
info("This restore will erase your device data.\n");
if (!strcmp(value, "Update"))
- info("This restore will update your device without loosing data.\n");
+ info("This restore will update your device without losing data.\n");
free(value);
diff --git a/src/libirecovery.c b/src/libirecovery.c
new file mode 100644
index 0000000..3e31028
--- /dev/null
+++ b/src/libirecovery.c
@@ -0,0 +1,1293 @@
+/**
+ * GreenPois0n iRecovery - libirecovery.c
+ * Copyright (C) 2010 Chronic-Dev Team
+ * Copyright (C) 2010 Joshua Hill
+ * Copyright (C) 2008-2011 Nicolas Haunold
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef WIN32
+#include <libusb-1.0/libusb.h>
+#else
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <setupapi.h>
+#endif
+
+#include "libirecovery.h"
+
+#define BUFFER_SIZE 0x1000
+#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__)
+
+static int libirecovery_debug = 1;
+#ifndef WIN32
+static libusb_context* libirecovery_context = NULL;
+#endif
+
+int irecv_write_file(const char* filename, const void* data, size_t size);
+int irecv_read_file(const char* filename, char** data, uint32_t* size);
+
+#ifdef WIN32
+static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
+static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
+
+typedef struct usb_control_request {
+ uint8_t bmRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+
+ char data[];
+} usb_control_request;
+
+irecv_error_t mobiledevice_openpipes(irecv_client_t client);
+void mobiledevice_closepipes(irecv_client_t client);
+
+irecv_error_t mobiledevice_connect(irecv_client_t* client) {
+ irecv_error_t ret;
+
+ SP_DEVICE_INTERFACE_DATA currentInterface;
+ HDEVINFO usbDevices;
+ DWORD i;
+ LPSTR path;
+ irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client));
+ memset(_client, 0, sizeof(struct irecv_client));
+
+ // Get DFU paths
+ usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if(!usbDevices) {
+ return IRECV_E_UNABLE_TO_CONNECT;
+ }
+ currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, &currentInterface); i++) {
+ DWORD requiredSize = 0;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA details;
+ SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
+ details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize);
+ details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+ if(!SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
+ irecv_close(_client);
+ free(details);
+ SetupDiDestroyDeviceInfoList(usbDevices);
+ return IRECV_E_UNABLE_TO_CONNECT;
+ } else {
+ LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD));
+ memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD));
+ free(details);
+ path = (LPSTR) malloc(requiredSize - sizeof(DWORD));
+ memcpy((void*) path, (void*) result, requiredSize - sizeof(DWORD));
+ TCHAR* pathEnd = strstr(path, "#{");
+ *pathEnd = '\0';
+ _client->DfuPath = result;
+ break;
+ }
+ }
+ SetupDiDestroyDeviceInfoList(usbDevices);
+ // Get iBoot path
+ usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if(!usbDevices) {
+ irecv_close(_client);
+ return IRECV_E_UNABLE_TO_CONNECT;
+ }
+ currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, &currentInterface); i++) {
+ DWORD requiredSize = 0;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA details;
+ SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, NULL, 0, &requiredSize, NULL);
+ details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize);
+ details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+ if(!SetupDiGetDeviceInterfaceDetail(usbDevices, &currentInterface, details, requiredSize, NULL, NULL)) {
+ irecv_close(_client);
+ free(details);
+ SetupDiDestroyDeviceInfoList(usbDevices);
+ return IRECV_E_UNABLE_TO_CONNECT;
+ } else {
+ LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD));
+ memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD));
+ free(details);
+
+ if(strstr(result, path) == NULL) {
+ free(result);
+ continue;
+ }
+
+ _client->iBootPath = result;
+ break;
+ }
+ }
+ SetupDiDestroyDeviceInfoList(usbDevices);
+ free(path);
+
+ ret = mobiledevice_openpipes(_client);
+ if (ret != IRECV_E_SUCCESS) return ret;
+
+ *client = _client;
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t mobiledevice_openpipes(irecv_client_t client) {
+ if (client->iBootPath && !(client->hIB = CreateFile(client->iBootPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) {
+ irecv_close(client);
+ return IRECV_E_UNABLE_TO_CONNECT;
+ }
+ if (client->DfuPath && !(client->hDFU = CreateFile(client->DfuPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) {
+ irecv_close(client);
+ return IRECV_E_UNABLE_TO_CONNECT;
+ }
+
+ if (client->iBootPath == NULL) {
+ client->mode = kDfuMode;
+ client->handle = client->hDFU;
+ } else {
+ client->mode = kRecoveryMode2;
+ client->handle = client->hIB;
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+void mobiledevice_closepipes(irecv_client_t client) {
+ if (client->hDFU!=NULL) {
+ CloseHandle(client->hDFU);
+ client->hDFU = NULL;
+ }
+ if (client->hIB!=NULL) {
+ CloseHandle(client->hIB);
+ client->hIB = NULL;
+ }
+}
+#endif
+
+int check_context(irecv_client_t client) {
+ if (client == NULL || client->handle == NULL) {
+ return IRECV_E_NO_DEVICE;
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+void irecv_init() {
+#ifndef WIN32
+ libusb_init(&libirecovery_context);
+#endif
+}
+
+void irecv_exit() {
+#ifndef WIN32
+ if (libirecovery_context != NULL) {
+ libusb_exit(libirecovery_context);
+ libirecovery_context = NULL;
+ }
+#endif
+}
+
+#ifdef __APPLE__
+ void dummy_callback() { }
+#endif
+
+int irecv_control_transfer( irecv_client_t client,
+ uint8_t bmRequestType,
+ uint8_t bRequest,
+ uint16_t wValue,
+ uint16_t wIndex,
+ unsigned char *data,
+ uint16_t wLength,
+ unsigned int timeout) {
+#ifndef WIN32
+ return libusb_control_transfer(client->handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
+#else
+ DWORD count = 0;
+ DWORD ret;
+ BOOL bRet;
+ OVERLAPPED overlapped;
+
+ if (data == NULL) wLength = 0;
+
+ usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + wLength);
+ packet->bmRequestType = bmRequestType;
+ packet->bRequest = bRequest;
+ packet->wValue = wValue;
+ packet->wIndex = wIndex;
+ packet->wLength = wLength;
+
+ if (bmRequestType < 0x80 && wLength > 0) {
+ memcpy(packet->data, data, wLength);
+ }
+
+ memset(&overlapped, 0, sizeof(overlapped));
+ overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + wLength, packet, sizeof(usb_control_request) + wLength, NULL, &overlapped);
+ ret = WaitForSingleObject(overlapped.hEvent, timeout);
+ bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE);
+ CloseHandle(overlapped.hEvent);
+ if (!bRet) {
+ CancelIo(client->handle);
+ free(packet);
+ return -1;
+ }
+
+ count -= sizeof(usb_control_request);
+ if (count > 0) {
+ if (bmRequestType >= 0x80) {
+ memcpy(data, packet->data, count);
+ }
+ }
+ free(packet);
+ return count;
+#endif
+}
+
+int irecv_bulk_transfer(irecv_client_t client,
+ unsigned char endpoint,
+ unsigned char *data,
+ int length,
+ int *transferred,
+ unsigned int timeout) {
+ int ret;
+
+#ifndef WIN32
+ ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout);
+ if (ret < 0) {
+ libusb_clear_halt(client->handle, endpoint);
+ }
+#else
+ if (endpoint==0x4) {
+ ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL);
+ } else {
+ ret = 0;
+ }
+ ret==0?-1:0;
+#endif
+
+ return ret;
+}
+
+int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) {
+#ifndef WIN32
+ return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size);
+#else
+ irecv_error_t ret;
+ unsigned short langid = 0;
+ unsigned char data[255];
+ int di, si;
+ memset(data, 0, sizeof(data));
+ memset(buffer, 0, size);
+
+ ret = irecv_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data), 1000);
+
+ if (ret < 0) return ret;
+ if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
+ if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR;
+
+ for (di = 0, si = 2; si < data[0]; si += 2) {
+ if (di >= (size - 1)) break;
+ if (data[si + 1]) {
+ /* high byte */
+ buffer[di++] = '?';
+ } else {
+ buffer[di++] = data[si];
+ }
+ }
+ buffer[di] = 0;
+
+ return di;
+#endif
+}
+
+irecv_error_t irecv_open(irecv_client_t* pclient) {
+#ifndef WIN32
+ int i = 0;
+ struct libusb_device* usb_device = NULL;
+ struct libusb_device** usb_device_list = NULL;
+ struct libusb_device_handle* usb_handle = NULL;
+ struct libusb_device_descriptor usb_descriptor;
+
+ *pclient = NULL;
+ if(libirecovery_debug) {
+ irecv_set_debug_level(libirecovery_debug);
+ }
+
+ irecv_error_t error = IRECV_E_SUCCESS;
+ int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list);
+ for (i = 0; i < usb_device_count; i++) {
+ usb_device = usb_device_list[i];
+ libusb_get_device_descriptor(usb_device, &usb_descriptor);
+ if (usb_descriptor.idVendor == APPLE_VENDOR_ID) {
+ /* verify this device is in a mode we understand */
+ if (usb_descriptor.idProduct == kRecoveryMode1 ||
+ usb_descriptor.idProduct == kRecoveryMode2 ||
+ usb_descriptor.idProduct == kRecoveryMode3 ||
+ usb_descriptor.idProduct == kRecoveryMode4 ||
+ usb_descriptor.idProduct == kDfuMode) {
+
+ debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct);
+
+ libusb_open(usb_device, &usb_handle);
+ if (usb_handle == NULL) {
+ libusb_free_device_list(usb_device_list, 1);
+ libusb_close(usb_handle);
+ libusb_exit(libirecovery_context);
+ return IRECV_E_UNABLE_TO_CONNECT;
+ }
+ libusb_free_device_list(usb_device_list, 1);
+
+ irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client));
+ if (client == NULL) {
+ libusb_close(usb_handle);
+ libusb_exit(libirecovery_context);
+ return IRECV_E_OUT_OF_MEMORY;
+ }
+
+ memset(client, '\0', sizeof(struct irecv_client));
+ client->interface = 0;
+ client->handle = usb_handle;
+ client->mode = usb_descriptor.idProduct;
+
+
+ error = irecv_set_configuration(client, 1);
+ if (error != IRECV_E_SUCCESS) {
+ return error;
+ }
+
+ if (client->mode != kDfuMode) {
+ error = irecv_set_interface(client, 0, 0);
+ error = irecv_set_interface(client, 1, 1);
+ } else {
+ error = irecv_set_interface(client, 0, 0);
+ }
+
+ if (error != IRECV_E_SUCCESS) {
+ return error;
+ }
+
+ /* cache usb serial */
+ irecv_get_string_descriptor_ascii(client, usb_descriptor.iSerialNumber, (unsigned char*) client->serial, 255);
+
+ *pclient = client;
+ return IRECV_E_SUCCESS;
+ }
+ }
+ }
+
+ return IRECV_E_UNABLE_TO_CONNECT;
+#else
+ int ret = mobiledevice_connect(pclient);
+ if (ret == IRECV_E_SUCCESS) {
+ irecv_get_string_descriptor_ascii(*pclient, 3, (unsigned char*) (*pclient)->serial, 255);
+ }
+ return ret;
+#endif
+}
+
+irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+#ifndef WIN32
+ debug("Setting to configuration %d\n", configuration);
+
+ int current = 0;
+ libusb_get_configuration(client->handle, &current);
+ if (current != configuration) {
+ if (libusb_set_configuration(client->handle, configuration) < 0) {
+ return IRECV_E_USB_CONFIGURATION;
+ }
+ }
+
+ client->config = configuration;
+#endif
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+#ifndef WIN32
+ // pod2g 2011-01-07: we may want to claim multiple interfaces
+ //libusb_release_interface(client->handle, client->interface);
+
+ debug("Setting to interface %d:%d\n", interface, alt_interface);
+ if (libusb_claim_interface(client->handle, interface) < 0) {
+ return IRECV_E_USB_INTERFACE;
+ }
+
+ if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) {
+ return IRECV_E_USB_INTERFACE;
+ }
+
+ client->interface = interface;
+ client->alt_interface = alt_interface;
+#endif
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_reset(irecv_client_t client) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+#ifndef WIN32
+ libusb_reset_device(client->handle);
+#else
+ int ret;
+ DWORD count;
+ ret = DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL);
+#endif
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_open_attempts(irecv_client_t* pclient, int attempts) {
+ int i;
+
+ for (i = 0; i < attempts; i++) {
+ if (irecv_open(pclient) != IRECV_E_SUCCESS) {
+ debug("Connection failed. Waiting 1 sec before retry.\n");
+ sleep(1);
+ } else {
+ return IRECV_E_SUCCESS;
+ }
+ }
+
+ return IRECV_E_UNABLE_TO_CONNECT;
+}
+
+irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) {
+ switch(type) {
+ case IRECV_RECEIVED:
+ client->received_callback = callback;
+ break;
+
+ case IRECV_PROGRESS:
+ client->progress_callback = callback;
+
+ case IRECV_CONNECTED:
+ client->connected_callback = callback;
+
+ case IRECV_PRECOMMAND:
+ client->precommand_callback = callback;
+ break;
+
+ case IRECV_POSTCOMMAND:
+ client->postcommand_callback = callback;
+ break;
+
+ case IRECV_DISCONNECTED:
+ client->disconnected_callback = callback;
+
+ default:
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) {
+ switch(type) {
+ case IRECV_RECEIVED:
+ client->received_callback = NULL;
+ break;
+
+ case IRECV_PROGRESS:
+ client->progress_callback = NULL;
+
+ case IRECV_CONNECTED:
+ client->connected_callback = NULL;
+
+ case IRECV_PRECOMMAND:
+ client->precommand_callback = NULL;
+ break;
+
+ case IRECV_POSTCOMMAND:
+ client->postcommand_callback = NULL;
+ break;
+
+ case IRECV_DISCONNECTED:
+ client->disconnected_callback = NULL;
+
+ default:
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_close(irecv_client_t client) {
+ if (client != NULL) {
+ if(client->disconnected_callback != NULL) {
+ irecv_event_t event;
+ event.size = 0;
+ event.data = NULL;
+ event.progress = 0;
+ event.type = IRECV_DISCONNECTED;
+ client->disconnected_callback(client, &event);
+ }
+#ifndef WIN32
+ if (client->handle != NULL) {
+ if (client->mode != kDfuMode) {
+ libusb_release_interface(client->handle, client->interface);
+ }
+ libusb_close(client->handle);
+ client->handle = NULL;
+ }
+#else
+ if (client->iBootPath!=NULL) {
+ free(client->iBootPath);
+ client->iBootPath = NULL;
+ }
+ if (client->DfuPath!=NULL) {
+ free(client->DfuPath);
+ client->DfuPath = NULL;
+ }
+ mobiledevice_closepipes(client);
+#endif
+ free(client);
+ client = NULL;
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+void irecv_set_debug_level(int level) {
+ libirecovery_debug = level;
+#ifndef WIN32
+ if(libirecovery_context) {
+ libusb_set_debug(libirecovery_context, libirecovery_debug);
+ }
+#endif
+}
+
+static irecv_error_t irecv_send_command_raw(irecv_client_t client, char* command) {
+ unsigned int length = strlen(command);
+ if (length >= 0x100) {
+ length = 0xFF;
+ }
+
+ if (length > 0) {
+ int ret = irecv_control_transfer(client, 0x40, 0, 0, 0, (unsigned char*) command, length + 1, 1000);
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_command(irecv_client_t client, char* command) {
+ irecv_error_t error = 0;
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ unsigned int length = strlen(command);
+ if (length >= 0x100) {
+ length = 0xFF;
+ }
+
+ irecv_event_t event;
+ if(client->precommand_callback != NULL) {
+ event.size = length;
+ event.data = command;
+ event.type = IRECV_PRECOMMAND;
+ if(client->precommand_callback(client, &event)) {
+ return IRECV_E_SUCCESS;
+ }
+ }
+
+ error = irecv_send_command_raw(client, command);
+ if (error != IRECV_E_SUCCESS) {
+ debug("Failed to send command %s\n", command);
+ if (error != IRECV_E_PIPE)
+ return error;
+ }
+
+ if(client->postcommand_callback != NULL) {
+ event.size = length;
+ event.data = command;
+ event.type = IRECV_POSTCOMMAND;
+ if(client->postcommand_callback(client, &event)) {
+ return IRECV_E_SUCCESS;
+ }
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ FILE* file = fopen(filename, "rb");
+ if (file == NULL) {
+ return IRECV_E_FILE_NOT_FOUND;
+ }
+
+ fseek(file, 0, SEEK_END);
+ long length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char* buffer = (char*) malloc(length);
+ if (buffer == NULL) {
+ fclose(file);
+ return IRECV_E_OUT_OF_MEMORY;
+ }
+
+ long bytes = fread(buffer, 1, length, file);
+ fclose(file);
+
+ if (bytes != length) {
+ free(buffer);
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+ irecv_error_t error = irecv_send_buffer(client, buffer, length, dfuNotifyFinished);
+ free(buffer);
+ return error;
+}
+
+irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) {
+ if (check_context(client) != IRECV_E_SUCCESS) {
+ *status = 0;
+ return IRECV_E_NO_DEVICE;
+ }
+
+ unsigned char buffer[6];
+ memset(buffer, '\0', 6);
+ if (irecv_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) {
+ *status = 0;
+ return IRECV_E_USB_STATUS;
+ }
+
+ *status = (unsigned int) buffer[4];
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished) {
+ irecv_error_t error = 0;
+ int recovery_mode = (client->mode != kDfuMode);
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ int packet_size = 0x800;
+ int last = length % packet_size;
+ int packets = length / packet_size;
+ if (last != 0) {
+ packets++;
+ } else {
+ last = packet_size;
+ }
+
+ /* initiate transfer */
+ if (recovery_mode) {
+ error = irecv_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, 1000);
+ } else {
+ error = irecv_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, 1000);
+ }
+ if (error != IRECV_E_SUCCESS) {
+ return error;
+ }
+
+ int i = 0;
+ double progress = 0;
+ unsigned long count = 0;
+ unsigned int status = 0;
+ int bytes = 0;
+ for (i = 0; i < packets; i++) {
+ int size = (i + 1) < packets ? packet_size : last;
+
+ /* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */
+ if (recovery_mode) {
+ error = irecv_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, 1000);
+ } else {
+ bytes = irecv_control_transfer(client, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000);
+ }
+
+ if (bytes != size) {
+ return IRECV_E_USB_UPLOAD;
+ }
+
+ if (!recovery_mode) {
+ error = irecv_get_status(client, &status);
+ }
+
+ if (error != IRECV_E_SUCCESS) {
+ return error;
+ }
+
+ if (!recovery_mode && status != 5) {
+ return IRECV_E_USB_UPLOAD;
+ }
+
+ count += size;
+ if(client->progress_callback != NULL) {
+ irecv_event_t event;
+ event.progress = ((double) count/ (double) length) * 100.0;
+ event.type = IRECV_PROGRESS;
+ event.data = "Uploading";
+ event.size = count;
+ client->progress_callback(client, &event);
+ } else {
+ debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
+ }
+ }
+
+ if (dfuNotifyFinished && !recovery_mode) {
+ irecv_control_transfer(client, 0x21, 1, 0, 0, (unsigned char*) buffer, 0, 1000);
+
+ for (i = 0; i < 3; i++) {
+ error = irecv_get_status(client, &status);
+ if (error != IRECV_E_SUCCESS) {
+ return error;
+ }
+ }
+ irecv_reset(client);
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_receive(irecv_client_t client) {
+ char buffer[BUFFER_SIZE];
+ memset(buffer, '\0', BUFFER_SIZE);
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ int bytes = 0;
+ while (irecv_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 500) == 0) {
+ if (bytes > 0) {
+ if (client->received_callback != NULL) {
+ irecv_event_t event;
+ event.size = bytes;
+ event.data = buffer;
+ event.type = IRECV_RECEIVED;
+ if (client->received_callback(client, &event) != 0) {
+ return IRECV_E_SUCCESS;
+ }
+ }
+ if (bytes < BUFFER_SIZE) break;
+ } else break;
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) {
+ int ret = 0;
+ char command[256];
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+ *value = NULL;
+
+ if(variable == NULL) {
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+ memset(command, '\0', sizeof(command));
+ snprintf(command, sizeof(command)-1, "getenv %s", variable);
+ irecv_error_t error = irecv_send_command_raw(client, command);
+ if(error == IRECV_E_PIPE) {
+ return IRECV_E_SUCCESS;
+ }
+ if(error != IRECV_E_SUCCESS) {
+ return error;
+ }
+
+ char* response = (char*) malloc(256);
+ if (response == NULL) {
+ return IRECV_E_OUT_OF_MEMORY;
+ }
+
+ memset(response, '\0', 256);
+ ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000);
+
+ *value = response;
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) {
+ int ret = 0;
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+ *value = 0;
+
+ char* response = (char*) malloc(256);
+ if (response == NULL) {
+ return IRECV_E_OUT_OF_MEMORY;
+ }
+
+ memset(response, '\0', 256);
+ ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000);
+
+ *value = (unsigned int) *response;
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ char* cpid_string = strstr(client->serial, "CPID:");
+ if (cpid_string == NULL) {
+ *cpid = 0;
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+ sscanf(cpid_string, "CPID:%d", cpid);
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ char* bdid_string = strstr(client->serial, "BDID:");
+ if (bdid_string == NULL) {
+ *bdid = 0;
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+ sscanf(bdid_string, "BDID:%d", bdid);
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ char* ecid_string = strstr(client->serial, "ECID:");
+ if (ecid_string == NULL) {
+ *ecid = 0;
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+ sscanf(ecid_string, "ECID:%qX", ecid);
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_srnm(irecv_client_t client, unsigned char* srnm) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ char* srnmp;
+ char* srnm_string = strstr(client->serial, "SRNM:[");
+ if(srnm_string == NULL) {
+ srnm = NULL;
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+ sscanf(srnm_string, "SRNM:[%s]", srnm);
+ srnmp = strrchr(srnm, ']');
+ if(srnmp != NULL) {
+ *srnmp = '\0';
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_imei(irecv_client_t client, unsigned char* imei) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ char* imeip;
+ char* imei_string = strstr(client->serial, "IMEI:[");
+ if (imei_string == NULL) {
+ *imei = 0;
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+
+ sscanf(imei_string, "IMEI:[%s]", imei);
+ imeip = strrchr(imei, ']');
+ if(imeip != NULL) {
+ *imeip = '\0';
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_exploit(irecv_client_t client) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+ irecv_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, 1000);
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) {
+ irecv_error_t error = IRECV_E_SUCCESS;
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ char* file_data = NULL;
+ unsigned int file_size = 0;
+ if(irecv_read_file(filename, &file_data, &file_size) < 0) {
+ return IRECV_E_FILE_NOT_FOUND;
+ }
+
+ char* line = strtok(file_data, "\n");
+ while(line != NULL) {
+ if(line[0] != '#') {
+ error = irecv_send_command(client, line);
+ if(error != IRECV_E_SUCCESS) {
+ return error;
+ }
+
+ error = irecv_receive(client);
+ if(error != IRECV_E_SUCCESS) {
+ return error;
+ }
+ }
+ line = strtok(NULL, "\n");
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_saveenv(irecv_client_t client) {
+ irecv_error_t error = irecv_send_command_raw(client, "saveenv");
+ if(error != IRECV_E_SUCCESS) {
+ return error;
+ }
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) {
+ char command[256];
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ if(variable == NULL || value == NULL) {
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+ memset(command, '\0', sizeof(command));
+ snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value);
+ irecv_error_t error = irecv_send_command_raw(client, command);
+ if(error != IRECV_E_SUCCESS) {
+ return error;
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+const char* irecv_strerror(irecv_error_t error) {
+ switch (error) {
+ case IRECV_E_SUCCESS:
+ return "Command completed successfully";
+
+ case IRECV_E_NO_DEVICE:
+ return "Unable to find device";
+
+ case IRECV_E_OUT_OF_MEMORY:
+ return "Out of memory";
+
+ case IRECV_E_UNABLE_TO_CONNECT:
+ return "Unable to connect to device";
+
+ case IRECV_E_INVALID_INPUT:
+ return "Invalid input";
+
+ case IRECV_E_FILE_NOT_FOUND:
+ return "File not found";
+
+ case IRECV_E_USB_UPLOAD:
+ return "Unable to upload data to device";
+
+ case IRECV_E_USB_STATUS:
+ return "Unable to get device status";
+
+ case IRECV_E_USB_INTERFACE:
+ return "Unable to set device interface";
+
+ case IRECV_E_USB_CONFIGURATION:
+ return "Unable to set device configuration";
+
+ case IRECV_E_PIPE:
+ return "Broken pipe";
+
+ case IRECV_E_TIMEOUT:
+ return "Timeout talking to device";
+
+ default:
+ return "Unknown error";
+ }
+
+ return NULL;
+}
+
+int irecv_write_file(const char* filename, const void* data, size_t size) {
+ size_t bytes = 0;
+ FILE* file = NULL;
+
+ debug("Writing data to %s\n", filename);
+ file = fopen(filename, "wb");
+ if (file == NULL) {
+ //error("read_file: Unable to open file %s\n", filename);
+ return -1;
+ }
+
+ bytes = fwrite(data, 1, size, file);
+ fclose(file);
+
+ if (bytes != size) {
+ //error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size);
+ return -1;
+ }
+
+ return size;
+}
+
+int irecv_read_file(const char* filename, char** data, uint32_t* size) {
+ size_t bytes = 0;
+ size_t length = 0;
+ FILE* file = NULL;
+ char* buffer = NULL;
+ debug("Reading data from %s\n", filename);
+
+ *size = 0;
+ *data = NULL;
+
+ file = fopen(filename, "rb");
+ if (file == NULL) {
+ //error("read_file: File %s not found\n", filename);
+ return -1;
+ }
+
+ fseek(file, 0, SEEK_END);
+ length = ftell(file);
+ rewind(file);
+
+ buffer = (char*) malloc(length);
+ if(buffer == NULL) {
+ //error("ERROR: Out of memory\n");
+ fclose(file);
+ return -1;
+ }
+ bytes = fread(buffer, 1, length, file);
+ fclose(file);
+
+ if(bytes != length) {
+ //error("ERROR: Unable to read entire file\n");
+ free(buffer);
+ return -1;
+ }
+
+ *size = length;
+ *data = buffer;
+ return 0;
+}
+
+irecv_error_t irecv_reset_counters(irecv_client_t client) {
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+ if (client->mode == kDfuMode) {
+ irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, 1000);
+ }
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length) {
+ irecv_error_t error = 0;
+ int recovery_mode = (client->mode != kDfuMode);
+
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ int packet_size = recovery_mode ? 0x2000: 0x800;
+ int last = length % packet_size;
+ int packets = length / packet_size;
+ if (last != 0) {
+ packets++;
+ } else {
+ last = packet_size;
+ }
+
+ int i = 0;
+ int bytes = 0;
+ double progress = 0;
+ unsigned long count = 0;
+ unsigned int status = 0;
+ for (i = 0; i < packets; i++) {
+ unsigned short size = (i+1) < packets ? packet_size : last;
+ bytes = irecv_control_transfer(client, 0xA1, 2, 0, 0, &buffer[i * packet_size], size, 1000);
+
+ if (bytes != size) {
+ return IRECV_E_USB_UPLOAD;
+ }
+
+ count += size;
+ if(client->progress_callback != NULL) {
+ irecv_event_t event;
+ event.progress = ((double) count/ (double) length) * 100.0;
+ event.type = IRECV_PROGRESS;
+ event.data = "Downloading";
+ event.size = count;
+ client->progress_callback(client, &event);
+ } else {
+ debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
+ }
+ }
+
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_finish_transfer(irecv_client_t client) {
+ int i = 0;
+ unsigned int status = 0;
+
+ if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+ irecv_control_transfer(client, 0x21, 1, 0, 0, 0, 0, 1000);
+
+ for(i = 0; i < 3; i++){
+ irecv_get_status(client, &status);
+ }
+ irecv_reset(client);
+ return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device) {
+ int device_id = DEVICE_UNKNOWN;
+ uint32_t bdid = 0;
+ uint32_t cpid = 0;
+
+ if (irecv_get_cpid(client, &cpid) < 0) {
+ return IRECV_E_UNKNOWN_ERROR;
+ }
+
+ switch (cpid) {
+ case CPID_IPHONE2G:
+ // iPhone1,1 iPhone1,2 and iPod1,1 all share the same ChipID
+ // so we need to check the BoardID
+ if (irecv_get_bdid(client, &bdid) < 0) {
+ break;
+ }
+
+ switch (bdid) {
+ case BDID_IPHONE2G:
+ device_id = DEVICE_IPHONE2G;
+ break;
+
+ case BDID_IPHONE3G:
+ device_id = DEVICE_IPHONE3G;
+ break;
+
+ case BDID_IPOD1G:
+ device_id = DEVICE_IPOD1G;
+ break;
+
+ default:
+ device_id = DEVICE_UNKNOWN;
+ break;
+ }
+ break;
+
+ case CPID_IPHONE3GS:
+ device_id = DEVICE_IPHONE3GS;
+ break;
+
+ case CPID_IPOD2G:
+ device_id = DEVICE_IPOD2G;
+ break;
+
+ case CPID_IPOD3G:
+ device_id = DEVICE_IPOD3G;
+ break;
+
+ case CPID_IPAD1G:
+ // iPhone3,1 iPhone3,3 iPad4,1 and iPad1,1 all share the same ChipID
+ // so we need to check the BoardID
+ if (irecv_get_bdid(client, &bdid) < 0) {
+ break;
+ }
+
+ switch (bdid) {
+ case BDID_IPAD1G:
+ device_id = DEVICE_IPAD1G;
+ break;
+
+ case BDID_IPHONE4:
+ device_id = DEVICE_IPHONE4;
+ break;
+
+ case BDID_IPOD4G:
+ device_id = DEVICE_IPOD4G;
+ break;
+
+ case BDID_APPLETV2:
+ device_id = DEVICE_APPLETV2;
+ break;
+
+ case BDID_IPHONE42:
+ device_id = DEVICE_IPHONE42;
+ break;
+
+ default:
+ device_id = DEVICE_UNKNOWN;
+ break;
+ }
+ break;
+
+ default:
+ device_id = DEVICE_UNKNOWN;
+ break;
+ }
+
+ *device = &irecv_devices[device_id];
+ return IRECV_E_SUCCESS;
+}
+
+irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) {
+ irecv_error_t error = 0;
+ irecv_client_t new_client = NULL;
+ irecv_event_cb_t progress_callback = client->progress_callback;
+
+ if (check_context(client) == IRECV_E_SUCCESS) {
+ irecv_close(client);
+ }
+
+ if (initial_pause > 0) {
+ debug("Waiting %d seconds for the device to pop up...\n", initial_pause);
+ sleep(initial_pause);
+ }
+
+ error = irecv_open_attempts(&new_client, 10);
+ if(error != IRECV_E_SUCCESS) {
+ return NULL;
+ }
+
+ new_client->progress_callback = progress_callback;
+ return new_client;
+}
+
+void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr) {
+ int i, j;
+ printf("0x%08x: ", addr);
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0 && i != 0) {
+ for (j=i-16; j < i; j++) {
+ unsigned char car = buf[j];
+ if (car < 0x20 || car > 0x7f) car = '.';
+ printf("%c", car);
+ }
+ printf("\n");
+ addr += 0x10;
+ printf("0x%08x: ", addr);
+ }
+ printf("%02x ", buf[i]);
+ }
+
+ int done = (i % 16);
+ int remains = 16 - done;
+ if (done > 0) {
+ for (j = 0; j < remains; j++) {
+ printf(" ");
+ }
+ }
+
+ if ((i - done) >= 0) {
+ if (done == 0 && i > 0) done = 16;
+ for (j = (i - done); j < i; j++) {
+ unsigned char car = buf[j];
+ if (car < 0x20 || car > 0x7f) car = '.';
+ printf("%c", car);
+ }
+ }
+ printf("\n");
+}
diff --git a/src/libirecovery.h b/src/libirecovery.h
new file mode 100644
index 0000000..12e6698
--- /dev/null
+++ b/src/libirecovery.h
@@ -0,0 +1,232 @@
+/**
+ * GreenPois0n iRecovery - libirecovery.h
+ * Copyright (C) 2010 Chronic-Dev Team
+ * Copyright (C) 2010 Joshua Hill
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef LIBIRECOVERY_H
+#define LIBIRECOVERY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef WIN32
+#include <libusb-1.0/libusb.h>
+#else
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define sleep(n) Sleep(1000 * n)
+#endif
+
+#define APPLE_VENDOR_ID 0x05AC
+
+#define CPID_UNKNOWN -1
+#define CPID_IPHONE2G 8900
+#define CPID_IPOD1G 8900
+#define CPID_IPHONE3G 8900
+#define CPID_IPOD2G 8720
+#define CPID_IPHONE3GS 8920
+#define CPID_IPOD3G 8922
+#define CPID_IPAD1G 8930
+#define CPID_IPHONE4 8930
+#define CPID_IPOD4G 8930
+#define CPID_APPLETV2 8930
+#define CPID_IPHONE42 8930
+#define CPID_IPAD21 8940
+#define CPID_IPAD22 8940
+#define CPID_IPAD23 8940
+
+#define BDID_UNKNOWN -1
+#define BDID_IPHONE2G 0
+#define BDID_IPOD1G 2
+#define BDID_IPHONE3G 4
+#define BDID_IPOD2G 0
+#define BDID_IPHONE3GS 0
+#define BDID_IPOD3G 2
+#define BDID_IPAD1G 2
+#define BDID_IPHONE4 0
+#define BDID_IPOD4G 8
+#define BDID_APPLETV2 10
+#define BDID_IPHONE42 6
+#define BDID_IPAD21 4
+#define BDID_IPAD22 6
+#define BDID_IPAD23 2
+
+#define DEVICE_UNKNOWN -1
+#define DEVICE_IPHONE2G 0
+#define DEVICE_IPOD1G 1
+#define DEVICE_IPHONE3G 2
+#define DEVICE_IPOD2G 3
+#define DEVICE_IPHONE3GS 4
+#define DEVICE_IPOD3G 5
+#define DEVICE_IPAD1G 6
+#define DEVICE_IPHONE4 7
+#define DEVICE_IPOD4G 8
+#define DEVICE_APPLETV2 9
+#define DEVICE_IPHONE42 10
+#define DEVICE_IPAD21 11
+#define DEVICE_IPAD22 12
+#define DEVICE_IPAD23 13
+
+enum {
+ kRecoveryMode1 = 0x1280,
+ kRecoveryMode2 = 0x1281,
+ kRecoveryMode3 = 0x1282,
+ kRecoveryMode4 = 0x1283,
+ kDfuMode = 0x1227
+};
+
+typedef enum {
+ IRECV_E_SUCCESS = 0,
+ IRECV_E_NO_DEVICE = -1,
+ IRECV_E_OUT_OF_MEMORY = -2,
+ IRECV_E_UNABLE_TO_CONNECT = -3,
+ IRECV_E_INVALID_INPUT = -4,
+ IRECV_E_FILE_NOT_FOUND = -5,
+ IRECV_E_USB_UPLOAD = -6,
+ IRECV_E_USB_STATUS = -7,
+ IRECV_E_USB_INTERFACE = -8,
+ IRECV_E_USB_CONFIGURATION = -9,
+ IRECV_E_PIPE = -10,
+ IRECV_E_TIMEOUT = -11,
+ IRECV_E_UNKNOWN_ERROR = -255
+} irecv_error_t;
+
+typedef enum {
+ IRECV_RECEIVED = 1,
+ IRECV_PRECOMMAND = 2,
+ IRECV_POSTCOMMAND = 3,
+ IRECV_CONNECTED = 4,
+ IRECV_DISCONNECTED = 5,
+ IRECV_PROGRESS = 6
+} irecv_event_type;
+
+typedef struct {
+ int size;
+ char* data;
+ double progress;
+ irecv_event_type type;
+} irecv_event_t;
+
+struct irecv_client;
+typedef struct irecv_client* irecv_client_t;
+typedef struct irecv_device* irecv_device_t;
+typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event);
+
+struct irecv_client {
+ int debug;
+ int config;
+ int interface;
+ int alt_interface;
+ unsigned short mode;
+ char serial[256];
+
+#ifndef WIN32
+ libusb_device_handle* handle;
+#else
+ HANDLE handle;
+ HANDLE hDFU;
+ HANDLE hIB;
+ LPSTR iBootPath;
+ LPSTR DfuPath;
+#endif
+
+ irecv_event_cb_t progress_callback;
+ irecv_event_cb_t received_callback;
+ irecv_event_cb_t connected_callback;
+ irecv_event_cb_t precommand_callback;
+ irecv_event_cb_t postcommand_callback;
+ irecv_event_cb_t disconnected_callback;
+};
+
+struct irecv_device {
+ int index;
+ const char* product;
+ const char* model;
+ unsigned int board_id;
+ unsigned int chip_id;
+ const char* url;
+};
+
+static struct irecv_device irecv_devices[] = {
+ { 0, "iPhone1,1", "m68ap", 0, 8900,
+ "http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-7481.20100202.4orot/iPhone1,1_3.1.3_7E18_Restore.ipsw" },
+ { 1, "iPod1,1", "n45ap", 2, 8900,
+ NULL },
+ { 2, "iPhone1,2", "n82ap", 4, 8900,
+ "http://appldnld.apple.com/iPhone4/061-7932.20100908.3fgt5/iPhone1,2_4.1_8B117_Restore.ipsw" },
+ { 3, "iPod2,1", "n72ap", 0, 8720,
+ "http://appldnld.apple.com/iPhone4/061-7937.20100908.ghj4f/iPod2,1_4.1_8B117_Restore.ipsw" },
+ { 4, "iPhone2,1", "n88ap", 0, 8920,
+ "http://appldnld.apple.com/iPhone4/061-7938.20100908.F3rCk/iPhone2,1_4.1_8B117_Restore.ipsw" },
+ { 5, "iPod3,1", "n18ap", 2, 8922,
+ "http://appldnld.apple.com/iPhone4/061-7941.20100908.sV9KE/iPod3,1_4.1_8B117_Restore.ipsw" },
+ { 6, "iPad1,1", "k48ap", 2, 8930,
+ "http://appldnld.apple.com/iPad/061-8801.20100811.CvfR5/iPad1,1_3.2.2_7B500_Restore.ipsw" },
+ { 7, "iPhone3,1", "n90ap", 0, 8930,
+ "http://appldnld.apple.com/iPhone4/061-7939.20100908.Lcyg3/iPhone3,1_4.1_8B117_Restore.ipsw" },
+ { 8, "iPod4,1", "n81ap", 8, 8930,
+ "http://appldnld.apple.com/iPhone4/061-8490.20100901.hyjtR/iPod4,1_4.1_8B117_Restore.ipsw" },
+ { 9, "AppleTV2,1", "k66ap", 10, 8930,
+ "http://appldnld.apple.com/AppleTV/061-8940.20100926.Tvtnz/AppleTV2,1_4.1_8M89_Restore.ipsw" },
+ { 10, "iPhone3,3", "n92ap", 6, 8930,
+ "http://appldnld.apple.com/iPhone4/041-0177.20110131.Pyvrz/iPhone3,3_4.2.6_8E200_Restore.ipsw" },
+ { -1, NULL, NULL, -1, -1,
+ NULL }
+};
+
+void irecv_set_debug_level(int level);
+const char* irecv_strerror(irecv_error_t error);
+irecv_error_t irecv_open_attempts(irecv_client_t* pclient, int attempts);
+irecv_error_t irecv_open(irecv_client_t* client);
+irecv_error_t irecv_reset(irecv_client_t client);
+irecv_error_t irecv_close(irecv_client_t client);
+irecv_error_t irecv_receive(irecv_client_t client);
+irecv_error_t irecv_send_exploit(irecv_client_t client);
+irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename);
+irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration);
+
+irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data);
+irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type);
+
+irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished);
+irecv_error_t irecv_send_command(irecv_client_t client, char* command);
+irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished);
+
+irecv_error_t irecv_saveenv(irecv_client_t client);
+irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value);
+irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value);
+irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value);
+irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface);
+irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid);
+irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid);
+irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid);
+void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr);
+
+void irecv_init();
+void irecv_exit();
+irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause);
+irecv_error_t irecv_reset_counters(irecv_client_t client);
+irecv_error_t irecv_finish_transfer(irecv_client_t client);
+irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length);
+irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/recovery.c b/src/recovery.c
index 40b207e..0642028 100644
--- a/src/recovery.c
+++ b/src/recovery.c
@@ -108,7 +108,9 @@ int recovery_check_mode() {
irecv_client_t recovery = NULL;
irecv_error_t recovery_error = IRECV_E_SUCCESS;
- recovery_error = irecv_open(&recovery);
+ irecv_init();
+ recovery_error=irecv_open(&recovery);
+
if (recovery_error != IRECV_E_SUCCESS) {
return -1;
}
@@ -120,13 +122,15 @@ int recovery_check_mode() {
irecv_close(recovery);
recovery = NULL;
+
return 0;
}
static int recovery_enable_autoboot(struct idevicerestore_client_t* client) {
irecv_error_t recovery_error = IRECV_E_SUCCESS;
- recovery_error = irecv_setenv(client->recovery->client, "auto-boot", "true");
+ //recovery_error = irecv_setenv(client->recovery->client, "auto-boot", "true");
+ recovery_error = irecv_send_command(client->recovery->client, "setenv auto-boot true");
if (recovery_error != IRECV_E_SUCCESS) {
error("ERROR: Unable to set auto-boot environmental variable\n");
return -1;
@@ -236,7 +240,8 @@ int recovery_send_component(struct idevicerestore_client_t* client, plist_t buil
info("Sending %s (%d bytes)...\n", component, size);
- error = irecv_send_buffer(client->recovery->client, data, size);
+ // FIXME: Did I do this right????
+ error = irecv_send_buffer(client->recovery->client, data, size, 0);
free(path);
if (error != IRECV_E_SUCCESS) {
error("ERROR: Unable to send %s component: %s\n", component, irecv_strerror(error));
diff --git a/src/recovery.h b/src/recovery.h
index b7cc0e4..278bd93 100644
--- a/src/recovery.h
+++ b/src/recovery.h
@@ -31,8 +31,10 @@ extern "C" {
#include "common.h"
-struct irecv_client;
-typedef struct irecv_client* irecv_client_t;
+#include "libirecovery.h"
+
+//struct irecv_client;
+//typedef struct irecv_client* irecv_client_t;
struct recovery_client_t {
irecv_client_t client;
const char* ipsw;
diff --git a/src/tss.c b/src/tss.c
index 0d4422c..eac3d32 100644
--- a/src/tss.c
+++ b/src/tss.c
@@ -171,8 +171,11 @@ plist_t tss_send_request(plist_t tss_request) {
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request);
curl_easy_setopt(handle, CURLOPT_USERAGENT, "InetURL/1.0");
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(request));
- curl_easy_setopt(handle, CURLOPT_URL, "http://cydia.saurik.com/TSS/controller?action=2");
- //curl_easy_setopt(handle, CURLOPT_URL, "http://gs.apple.com/TSS/controller?action=2");
+ if (use_apple_server==0) {
+ curl_easy_setopt(handle, CURLOPT_URL, "http://cydia.saurik.com/TSS/controller?action=2");
+ } else {
+ curl_easy_setopt(handle, CURLOPT_URL, "http://gs.apple.com/TSS/controller?action=2");
+ }
curl_easy_perform(handle);
curl_slist_free_all(header);