diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 12 | ||||
| -rw-r--r-- | src/asr.c | 6 | ||||
| -rw-r--r-- | src/common.h | 26 | ||||
| -rw-r--r-- | src/idevicerestore.c | 70 | ||||
| -rw-r--r-- | src/libirecovery.c | 1293 | ||||
| -rw-r--r-- | src/libirecovery.h | 232 | ||||
| -rw-r--r-- | src/recovery.c | 11 | ||||
| -rw-r--r-- | src/recovery.h | 6 | ||||
| -rw-r--r-- | src/tss.c | 7 | 
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 @@ -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, ¤tInterface); i++) { +		DWORD requiredSize = 0; +		PSP_DEVICE_INTERFACE_DETAIL_DATA details; +		SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); +		details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); +		details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); +		if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, 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, ¤tInterface); i++) { +		DWORD requiredSize = 0; +		PSP_DEVICE_INTERFACE_DETAIL_DATA details; +		SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL); +		details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize); +		details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); +		if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, 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, ¤t); +	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; @@ -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); | 
