diff options
Diffstat (limited to 'src/restore.c')
-rw-r--r-- | src/restore.c | 464 |
1 files changed, 195 insertions, 269 deletions
diff --git a/src/restore.c b/src/restore.c index b5eea66..fc22b75 100644 --- a/src/restore.c +++ b/src/restore.c @@ -24,12 +24,11 @@ #include <string.h> #include <libimobiledevice/restore.h> +#include "asr.h" #include "tss.h" #include "restore.h" #include "idevicerestore.h" -#define ASR_PORT 12345 - #define CREATE_PARTITION_MAP 12 #define CREATE_FILESYSTEM 13 #define RESTORE_IMAGE 14 @@ -110,7 +109,7 @@ int restore_check_device(const char* uuid) { } restore_error = restored_get_value(restore, "HardwareModel", &node); - if(restore_error != RESTORE_E_SUCCESS) { + if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to get HardwareModel from restored\n"); restored_client_free(restore); idevice_free(device); @@ -124,13 +123,14 @@ int restore_check_device(const char* uuid) { if (!node || plist_get_node_type(node) != PLIST_STRING) { error("ERROR: Unable to get HardwareModel information\n"); - if(node) plist_free(node); + if (node) + plist_free(node); return -1; } plist_get_string_val(node, &model); - for(i = 0; idevicerestore_devices[i].model != NULL; i++) { - if(!strcasecmp(model, idevicerestore_devices[i].model)) { + for (i = 0; idevicerestore_devices[i].model != NULL; i++) { + if (!strcasecmp(model, idevicerestore_devices[i].model)) { break; } } @@ -147,11 +147,31 @@ void restore_device_callback(const idevice_event_t* event, void* user_data) { } } +int restore_reboot(const char* uuid) { + idevice_t device = NULL; + restored_client_t restore = NULL; + restored_error_t restore_error = RESTORE_E_SUCCESS; + if (restore_open_with_timeout(uuid, &device, &restore) < 0) { + error("ERROR: Unable to open device in restore mode\n"); + return -1; + } + + restore_error = restored_reboot(restore); + if (restore_error != RESTORE_E_SUCCESS) { + error("ERROR: Unable to reboot the device from restore mode\n"); + restore_close(device, restore); + return -1; + } + + restore_close(device, restore); + restore = NULL; + device = NULL; + return 0; +} + int restore_open_with_timeout(const char* uuid, idevice_t* device, restored_client_t* restore) { int i = 0; - int attempt = 10; - char* type = NULL; - uint64_t version = 0; + int attempts = 10; idevice_t context = NULL; restored_client_t client = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; @@ -166,12 +186,12 @@ int restore_open_with_timeout(const char* uuid, idevice_t* device, restored_clie return -1; } - for (i = 1; i <= attempt; i++) { + for (i = 1; i <= attempts; i++) { if (restore_device_connected == 1) { break; } - if (i == attempt) { + if (i == attempts) { error("ERROR: Unable to connect to device in restore mode\n"); } @@ -190,7 +210,7 @@ int restore_open_with_timeout(const char* uuid, idevice_t* device, restored_clie return -1; } - restore_error = restored_query_type(client, &type, &version); + restore_error = restored_query_type(client, NULL, NULL); if (restore_error != RESTORE_E_SUCCESS) { restored_client_free(client); idevice_event_unsubscribe(); @@ -208,7 +228,6 @@ void restore_close(idevice_t device, restored_client_t restore) { restored_client_free(restore); if (device) idevice_free(device); - //idevice_event_unsubscribe(); } const char* restore_progress_string(unsigned int operation) { @@ -262,202 +281,83 @@ const char* restore_progress_string(unsigned int operation) { int restore_handle_progress_msg(restored_client_t client, plist_t msg) { plist_t node = NULL; - uint64_t operation = 0; - uint64_t uprogress = 0; uint64_t progress = 0; + uint64_t operation = 0; node = plist_dict_get_item(msg, "Operation"); - if (node && PLIST_UINT == plist_get_node_type(node)) { - plist_get_uint_val(node, &operation); - } else { + if (!node || plist_get_node_type(node) != PLIST_UINT) { debug("Failed to parse operation from ProgressMsg plist\n"); - return 0; + return -1; } + plist_get_uint_val(node, &operation); node = plist_dict_get_item(msg, "Progress"); - if (node && PLIST_UINT == plist_get_node_type(node)) { - plist_get_uint_val(node, &uprogress); - progress = uprogress; - } else { + if (!node || plist_get_node_type(node) != PLIST_UINT) { debug("Failed to parse progress from ProgressMsg plist \n"); - return 0; + return -1; } + plist_get_uint_val(node, &progress); + + if ((progress > 0) && (progress < 100)) { + print_progress_bar(restore_progress_string(operation), (double) progress); - if ((progress > 0) && (progress < 100)) - info("%s - Progress: %llu%%\n", restore_progress_string(operation), progress); - else + } else { info("%s\n", restore_progress_string(operation)); + } return 0; } int restore_handle_status_msg(restored_client_t client, plist_t msg) { info("Got status message\n"); + debug_plist(msg); return 0; } -int restore_send_filesystem(idevice_t device, restored_client_t client, const char* filesystem) { +int restore_send_filesystem(idevice_t device, const char* filesystem) { int i = 0; - char buffer[0x1000]; - uint32_t recv_bytes = 0; - memset(buffer, '\0', 0x1000); - idevice_connection_t connection = NULL; - idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; - - for (i = 0; i < 5; i++) { - ret = idevice_connect(device, ASR_PORT, &connection); - if (ret == IDEVICE_E_SUCCESS) - break; + FILE* file = NULL; + plist_t data = NULL; + idevice_connection_t asr = NULL; + idevice_error_t device_error = IDEVICE_E_UNKNOWN_ERROR; - else - sleep(1); - } - - if (ret != IDEVICE_E_SUCCESS) - return ret; - - memset(buffer, '\0', 0x1000); - ret = idevice_connection_receive(connection, buffer, 0x1000, &recv_bytes); - if (ret != IDEVICE_E_SUCCESS) { - idevice_disconnect(connection); - return ret; - } - info("Received %d bytes\n", recv_bytes); - info("%s", buffer); - - FILE* fd = fopen(filesystem, "rb"); - if (fd == NULL) { - idevice_disconnect(connection); - return ret; + if (asr_open_with_timeout(device, &asr) < 0) { + error("ERROR: Unable to connect to ASR\n"); + return -1; } - - fseek(fd, 0, SEEK_END); - uint64_t len = ftell(fd); - fseek(fd, 0, SEEK_SET); - info("Connected to ASR\n"); - plist_t dict = plist_new_dict(); - plist_dict_insert_item(dict, "FEC Slice Stride", plist_new_uint(40)); - plist_dict_insert_item(dict, "Packet Payload Size", plist_new_uint(1450)); - plist_dict_insert_item(dict, "Packets Per FEC", plist_new_uint(25)); - - plist_t payload = plist_new_dict(); - plist_dict_insert_item(payload, "Port", plist_new_uint(1)); - plist_dict_insert_item(payload, "Size", plist_new_uint(len)); - plist_dict_insert_item(dict, "Payload", payload); - - plist_dict_insert_item(dict, "Stream ID", plist_new_uint(1)); - plist_dict_insert_item(dict, "Version", plist_new_uint(1)); - char* xml = NULL; - unsigned int dict_size = 0; - unsigned int sent_bytes = 0; - plist_to_xml(dict, &xml, &dict_size); - - ret = idevice_connection_send(connection, xml, dict_size, &sent_bytes); - if (ret != IDEVICE_E_SUCCESS) { - idevice_disconnect(connection); - return ret; + // we don't really need to do anything with this, + // we're just clearing the output buffer + if (asr_receive(asr, &data) < 0) { + error("ERROR: Unable to receive data from ASR\n"); + asr_close(asr); + return -1; } + plist_free(data); - info("Sent %d bytes\n", sent_bytes); - info("%s", xml); - plist_free(dict); - free(xml); - - char* command = NULL; - do { - memset(buffer, '\0', 0x1000); - ret = idevice_connection_receive(connection, buffer, 0x1000, &recv_bytes); - if (ret != IDEVICE_E_SUCCESS) { - idevice_disconnect(connection); - return ret; - } - info("Received %d bytes\n", recv_bytes); - info("%s", buffer); - - plist_t request = NULL; - plist_from_xml(buffer, recv_bytes, &request); - plist_t command_node = plist_dict_get_item(request, "Command"); - if (command_node && PLIST_STRING == plist_get_node_type(command_node)) { - plist_get_string_val(command_node, &command); - if (!strcmp(command, "OOBData")) { - plist_t oob_length_node = plist_dict_get_item(request, "OOB Length"); - if (!oob_length_node || PLIST_UINT != plist_get_node_type(oob_length_node)) { - error("Error fetching OOB Length\n"); - idevice_disconnect(connection); - return IDEVICE_E_UNKNOWN_ERROR; - } - uint64_t oob_length = 0; - plist_get_uint_val(oob_length_node, &oob_length); - - plist_t oob_offset_node = plist_dict_get_item(request, "OOB Offset"); - if (!oob_offset_node || PLIST_UINT != plist_get_node_type(oob_offset_node)) { - error("Error fetching OOB Offset\n"); - idevice_disconnect(connection); - return IDEVICE_E_UNKNOWN_ERROR; - } - uint64_t oob_offset = 0; - plist_get_uint_val(oob_offset_node, &oob_offset); - - char* oob_data = (char*) malloc(oob_length); - if (oob_data == NULL) { - error("Out of memory\n"); - idevice_disconnect(connection); - return IDEVICE_E_UNKNOWN_ERROR; - } - - fseek(fd, oob_offset, SEEK_SET); - if (fread(oob_data, 1, oob_length, fd) != oob_length) { - error("Unable to read filesystem offset\n"); - idevice_disconnect(connection); - free(oob_data); - return ret; - } - - ret = idevice_connection_send(connection, oob_data, oob_length, &sent_bytes); - if (sent_bytes != oob_length || ret != IDEVICE_E_SUCCESS) { - error("Unable to send %d bytes to asr\n", sent_bytes); - idevice_disconnect(connection); - free(oob_data); - return ret; - } - plist_free(request); - free(oob_data); - } - } - - } while (strcmp(command, "Payload")); - - fseek(fd, 0, SEEK_SET); - char data[1450]; - for (i = len; i > 0; i -= 1450) { - int size = 1450; - if (i < 1450) { - size = i; - } - - if (fread(data, 1, size, fd) != (unsigned int) size) { - fclose(fd); - idevice_disconnect(connection); - error("Error reading filesystem\n"); - return IDEVICE_E_UNKNOWN_ERROR; - } - - ret = idevice_connection_send(connection, data, size, &sent_bytes); - if (ret != IDEVICE_E_SUCCESS) { - fclose(fd); - } + // this step sends requested chunks of data from various offsets to asr so + // it can validate the filesystem before installing it + debug("Preparing to validate the filesystem\n"); + if (asr_perform_validation(asr, filesystem) < 0) { + error("ERROR: ASR was unable to validate the filesystem\n"); + asr_close(asr); + return -1; + } + info("Filesystem validated\n"); - if (i % (1450 * 1000) == 0) { - info("."); - } + // once the target filesystem has been validated, ASR then requests the + // entire filesystem to be sent. + debug("Preparing to send filesystem\n"); + if (asr_send_payload(asr, filesystem) < 0) { + error("ERROR: Unable to send payload to ASR\n"); + asr_close(asr); + return -1; } + info("Filesystem finished\n"); - info("Done sending filesystem\n"); - fclose(fd); - ret = idevice_disconnect(connection); - return ret; + asr_close(asr); + return 0; } int restore_send_kernelcache(restored_client_t client, char* kernel_data, int len) { @@ -550,11 +450,7 @@ int restore_send_nor(restored_client_t client, const char* ipsw, plist_t tss) { } plist_dict_insert_item(dict, "NorImageData", norimage_array); - int sz = 0; - char* xml = NULL; - plist_to_xml(dict, &xml, &sz); - debug("%s", xml); - free(xml); + debug_plist(dict); restored_error_t ret = restored_send(client, dict); if (ret != RESTORE_E_SUCCESS) { @@ -567,105 +463,135 @@ int restore_send_nor(restored_client_t client, const char* ipsw, plist_t tss) { return 0; } -int restore_device(const char* uuid, const char* ipsw, plist_t tss, const char* filesystem) { - idevice_t device = NULL; - idevice_error_t device_error = idevice_new(&device, uuid); - if (device_error != IDEVICE_E_SUCCESS) { - error("ERROR: Unable to open device\n"); - plist_free(tss); - return -1; - } +int restore_handle_data_request_msg(idevice_t device, restored_client_t restore, plist_t message, plist_t tss, const char* ipsw, const char* filesystem) { + char* type = NULL; + plist_t node = NULL; - restored_client_t restore = NULL; - restored_error_t restore_error = restored_client_new(device, &restore, "idevicerestore"); - if (restore_error != RESTORE_E_SUCCESS) { - error("ERROR: Unable to start restored client\n"); - plist_free(tss); - idevice_free(device); - return -1; + // checks and see what kind of data restored is requests and pass + // the request to its own handler + node = plist_dict_get_item(message, "DataType"); + if (node && PLIST_STRING == plist_get_node_type(node)) { + plist_get_string_val(node, &type); + + // this request is sent when restored is ready to receive the filesystem + if (!strcmp(type, "SystemImageData")) { + restore_send_filesystem(device, filesystem); + + } + + else if (!strcmp(type, "KernelCache")) { + int kernelcache_size = 0; + char* kernelcache_data = NULL; + char* kernelcache_path = NULL; + if (tss_get_entry_path(tss, "KernelCache", &kernelcache_path) < 0) { + error("ERROR: Unable to find kernelcache path\n"); + return -1; + } + + if (get_signed_component(ipsw, tss, kernelcache_path, &kernelcache_data, &kernelcache_size) < 0) { + error("ERROR: Unable to get kernelcache file\n"); + return -1; + } + restore_send_kernelcache(restore, kernelcache_data, kernelcache_size); + free(kernelcache_data); + + } + + else if (!strcmp(type, "NORData")) { + restore_send_nor(restore, ipsw, tss); + + } else { + // Unknown DataType!! + debug("Unknown data request received\n"); + } } + return 0; +} +int restore_device(const char* uuid, const char* ipsw, plist_t tss, const char* filesystem) { + int error = 0; char* type = NULL; - uint64_t version = 0; - if (restored_query_type(restore, &type, &version) != RESTORE_E_SUCCESS) { - error("ERROR: Device is not in restore mode. QueryType returned \"%s\"\n", type); - plist_free(tss); - restored_client_free(restore); - idevice_free(device); + char* kernel = NULL; + plist_t node = NULL; + plist_t message = NULL; + idevice_t device = NULL; + restored_client_t restore = NULL; + idevice_error_t device_error = IDEVICE_E_SUCCESS; + restored_error_t restore_error = RESTORE_E_SUCCESS; + + // open our connection to the device and verify we're in restore mode + if (restore_open_with_timeout(uuid, &device, &restore) < 0) { + error("ERROR: Unable to open device in restore mode\n"); return -1; } info("Device has successfully entered restore mode\n"); - /* start restore process */ - char* kernelcache = NULL; - info("Restore protocol version is %llu.\n", version); + // start the restore process restore_error = restored_start_restore(restore); - if (restore_error == RESTORE_E_SUCCESS) { - while (!idevicerestore_quit) { - plist_t message = NULL; - restore_error = restored_receive(restore, &message); - plist_t msgtype_node = plist_dict_get_item(message, "MsgType"); - if (msgtype_node && PLIST_STRING == plist_get_node_type(msgtype_node)) { - char *msgtype = NULL; - plist_get_string_val(msgtype_node, &msgtype); - if (!strcmp(msgtype, "ProgressMsg")) { - restore_error = restore_handle_progress_msg(restore, message); - - } else if (!strcmp(msgtype, "DataRequestMsg")) { - // device is requesting data to be sent - plist_t datatype_node = plist_dict_get_item(message, "DataType"); - if (datatype_node && PLIST_STRING == plist_get_node_type(datatype_node)) { - char *datatype = NULL; - plist_get_string_val(datatype_node, &datatype); - if (!strcmp(datatype, "SystemImageData")) { - restore_send_filesystem(device, restore, filesystem); - - } else if (!strcmp(datatype, "KernelCache")) { - int kernelcache_size = 0; - char* kernelcache_data = NULL; - char* kernelcache_path = NULL; - if (tss_get_entry_path(tss, "KernelCache", &kernelcache_path) < 0) { - error("ERROR: Unable to find kernelcache path\n"); - return -1; - } - - if (get_signed_component(ipsw, tss, kernelcache_path, &kernelcache_data, &kernelcache_size) < 0) { - error("ERROR: Unable to get kernelcache file\n"); - return -1; - } - restore_send_kernelcache(restore, kernelcache_data, kernelcache_size); - free(kernelcache_data); - - } else if (!strcmp(datatype, "NORData")) { - restore_send_nor(restore, ipsw, tss); - - } else { - // Unknown DataType!! - error("Unknown DataType\n"); - return -1; - } - } - - } else if (!strcmp(msgtype, "StatusMsg")) { - restore_error = restore_handle_status_msg(restore, message); - - } else { - info("Received unknown message type: %s\n", msgtype); - } - } + if (restore_error != RESTORE_E_SUCCESS) { + error("ERROR: Unable to start the restore process\n"); + restore_close(device, restore); + return -1; + } - if (RESTORE_E_SUCCESS != restore_error) { - error("Invalid return status %d\n", restore_error); - //idevicerestore_quit = 1; - } + // this is the restore process loop, it reads each message in from + // restored and passes that data on to it's specific handler + while (!idevicerestore_quit) { + restore_error = restored_receive(restore, &message); + if (restore_error != RESTORE_E_SUCCESS) { + debug("No data to read\n"); + message = NULL; + continue; + } + // discover what kind of message has been received + node = plist_dict_get_item(message, "MsgType"); + if (!node || plist_get_node_type(node) != PLIST_STRING) { + debug("Unknown message received\n"); + debug_plist(message); plist_free(message); + message = NULL; + continue; } - } else { - error("ERROR: Could not start restore. %d\n", restore_error); + plist_get_string_val(node, &type); + + // data request messages are sent by restored whenever it requires + // files sent to the server by the client. these data requests include + // SystemImageData, KernelCache, and NORData requests + if (!strcmp(type, "DataRequestMsg")) { + error = restore_handle_data_request_msg(device, restore, message, tss, ipsw, filesystem); + } + + // progress notification messages sent by the restored inform the client + // of it's current operation and sometimes percent of progress is complete + else if (!strcmp(type, "ProgressMsg")) { + error = restore_handle_progress_msg(restore, message); + } + + // status messages usually indicate the current state of the restored + // process or often to signal an error has been encountered + else if (!strcmp(type, "StatusMsg")) { + error = restore_handle_status_msg(restore, message); + } + + // there might be some other message types i'm not aware of, but I think + // at least the "previous error logs" messages usually end up here + else { + debug("Unknown message type received\n"); + debug_plist(message); + } + + // finally, if any of these message handlers returned -1 then we encountered + // an unrecoverable error, so we need to bail. + if (error < 0) { + error("ERROR: Unable to successfully restore device\n"); + idevicerestore_quit = 1; + } + + plist_free(message); + message = NULL; } - restored_client_free(restore); - idevice_free(device); + restore_close(device, restore); return 0; } |