int asr_receive(asr_client_t asr, plist_t* data) { uint32_t size = 0; char* buffer = NULL; plist_t request = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; *data = NULL; buffer = (char*) malloc(ASR_BUFFER_SIZE); if (buffer == NULL) { error("ERROR: Unable to allocate memory for ASR receive buffer\n"); return -1; } memset(buffer, '\0', ASR_BUFFER_SIZE); device_error = idevice_connection_receive(asr->connection, buffer, ASR_BUFFER_SIZE, &size); if (device_error != IDEVICE_E_SUCCESS) { error("ERROR: Unable to receive data from ASR\n"); free(buffer); return -1; } plist_from_xml(buffer, size, &request); *data = request; debug("Received %d bytes:\n", size); if (idevicerestore_debug) debug_plist(request); free(buffer); return 0; }
/** * Hangs up the connection to the mobile_image_mounter service. * This functions has to be called before freeing up a mobile_image_mounter * instance. If not, errors appear in the device's syslog. * * @param client The client to hang up * * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is invalid, * or another error code otherwise. */ mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) { if (!client) { return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; } mobile_image_mounter_lock(client); plist_t dict = plist_new_dict(); plist_dict_insert_item(dict, "Command", plist_new_string("Hangup")); mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); plist_free(dict); if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { debug_info("%s: Error sending XML plist to device!", __func__); goto leave_unlock; } dict = NULL; res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &dict)); if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { debug_info("%s: Error receiving response from device!", __func__); } if (dict) { debug_plist(dict); plist_free(dict); } leave_unlock: mobile_image_mounter_unlock(client); return res; }
LIBIMOBILEDEVICE_API restored_error_t restored_query_type(restored_client_t client, char **type, uint64_t *version) { if (!client) return RESTORE_E_INVALID_ARG; restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_set_item(dict,"Request", plist_new_string("QueryType")); debug_info("called"); ret = restored_send(client, dict); plist_free(dict); dict = NULL; ret = restored_receive(client, &dict); if (RESTORE_E_SUCCESS != ret) return ret; ret = RESTORE_E_UNKNOWN_ERROR; plist_t type_node = plist_dict_get_item(dict, "Type"); if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) { char* typestr = NULL; /* save our device information info */ client->info = dict; plist_get_string_val(type_node, &typestr); debug_info("success with type %s", typestr); /* return the type if requested */ if (type) { *type = typestr; } else { free(typestr); } /* fetch the restore protocol version */ if (version) { plist_t version_node = plist_dict_get_item(dict, "RestoreProtocolVersion"); if (version_node && PLIST_UINT == plist_get_node_type(version_node)) { plist_get_uint_val(version_node, version); debug_info("restored protocol version %llu", *version); } else { return RESTORE_E_UNKNOWN_ERROR; } } ret = RESTORE_E_SUCCESS; } else { debug_info("hmm. QueryType response does not contain a type?!"); debug_plist(dict); plist_free(dict); } return ret; }
/** * Tells the device that the restore process is complete and waits for the * device to close the connection. After that, the device should reboot. * * @param client The connected MobileBackup client to use. * * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if * client is invalid, MOBILEBACKUP_E_PLIST_ERROR if the received disconnect * message plist is invalid, or MOBILEBACKUP_E_MUX_ERROR if a communication * error occurs. */ mobilebackup_error_t mobilebackup_send_restore_complete(mobilebackup_client_t client) { mobilebackup_error_t err = mobilebackup_send_message(client, "BackupMessageRestoreComplete", NULL); if (err != MOBILEBACKUP_E_SUCCESS) { return err; } plist_t dlmsg = NULL; err = mobilebackup_receive(client, &dlmsg); if ((err != MOBILEBACKUP_E_SUCCESS) || !dlmsg || (plist_get_node_type(dlmsg) != PLIST_ARRAY) || (plist_array_get_size(dlmsg) != 2)) { if (dlmsg) { debug_info("ERROR: Did not receive DLMessageDisconnect:"); debug_plist(dlmsg); plist_free(dlmsg); } if (err == MOBILEBACKUP_E_SUCCESS) { err = MOBILEBACKUP_E_PLIST_ERROR; } return err; } plist_t node = plist_array_get_item (dlmsg, 0); char *msg = NULL; if (node && (plist_get_node_type(node) == PLIST_STRING)) { plist_get_string_val(node, &msg); } if (msg && !strcmp(msg, "DLMessageDisconnect")) { err = MOBILEBACKUP_E_SUCCESS; /* we need to do this here, otherwise mobilebackup_client_free will fail */ device_link_service_client_free(client->parent); client->parent = NULL; } else { debug_info("ERROR: Malformed plist received:"); debug_plist(dlmsg); err = MOBILEBACKUP_E_PLIST_ERROR; } plist_free(dlmsg); if (msg) free(msg); return err; }
webinspector_error_t webinspector_send(webinspector_client_t client, plist_t plist) { webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR; uint32_t offset = 0; int is_final_message = 0; char *packet = NULL; uint32_t packet_length = 0; debug_info("Sending webinspector message..."); debug_plist(plist); /* convert plist to packet */ plist_to_bin(plist, &packet, &packet_length); if (!packet || packet_length == 0) { debug_info("Error converting plist to binary."); return res; } do { /* determine if we need to send partial messages */ if (packet_length < WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE) { is_final_message = 1; } else { /* send partial packet */ is_final_message = 0; } plist_t outplist = plist_new_dict(); if (!is_final_message) { /* split packet into partial chunks */ plist_dict_set_item(outplist, "WIRPartialMessageKey", plist_new_data(packet + offset, WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE)); offset += WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE; packet_length -= WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE; } else { /* send final chunk */ plist_dict_set_item(outplist, "WIRFinalMessageKey", plist_new_data(packet + offset, packet_length)); offset += packet_length; packet_length -= packet_length; } res = webinspector_error(property_list_service_send_binary_plist(client->parent, outplist)); plist_free(outplist); outplist = NULL; if (res != WEBINSPECTOR_E_SUCCESS) { debug_info("Sending plist failed with error %d", res); return res; } } while(packet_length > 0); free(packet); packet = NULL; return res; }
/** * Receives a plist using the given property list service client. * Internally used generic plist receive function. * * @param client The property list service client to use for receiving * @param plist pointer to a plist_t that will point to the received plist * upon successful return * @param timeout Maximum time in milliseconds to wait for data. * * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL, * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR * when an unspecified error occurs. */ static property_list_service_error_t internal_plist_receive_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) { property_list_service_error_t res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; uint32_t pktlen = 0; uint32_t bytes = 0; if (!client || (client && !client->connection) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; } idevice_connection_receive_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); debug_info("initial read=%i", bytes); if (bytes < 4) { debug_info("initial read failed!"); return PROPERTY_LIST_SERVICE_E_MUX_ERROR; } else { pktlen = be32toh(pktlen); if (pktlen < (1 << 24)) { /* prevent huge buffers */ uint32_t curlen = 0; char *content = NULL; debug_info("%d bytes following", pktlen); content = (char*)malloc(pktlen); while (curlen < pktlen) { idevice_connection_receive(client->connection, content+curlen, pktlen-curlen, &bytes); if (bytes <= 0) { res = PROPERTY_LIST_SERVICE_E_MUX_ERROR; break; } debug_info("received %d bytes", bytes); curlen += bytes; } if (!memcmp(content, "bplist00", 8)) { plist_from_bin(content, pktlen, plist); } else { /* iOS 4.3+ hack: plist data might contain invalid characters, thus we convert those to spaces */ for (bytes = 0; bytes < pktlen-1; bytes++) { if ((content[bytes] >= 0) && (content[bytes] < 0x20) && (content[bytes] != 0x09) && (content[bytes] != 0x0a) && (content[bytes] != 0x0d)) content[bytes] = 0x20; } plist_from_xml(content, pktlen, plist); } if (*plist) { debug_plist(*plist); res = PROPERTY_LIST_SERVICE_E_SUCCESS; } else { res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR; } free(content); content = NULL; } else { res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; } } return res; }
LIBIMOBILEDEVICE_API np_error_t np_client_free(np_client_t client) { plist_t dict; property_list_service_client_t parent; if (!client) return NP_E_INVALID_ARG; dict = plist_new_dict(); plist_dict_set_item(dict,"Command", plist_new_string("Shutdown")); property_list_service_send_xml_plist(client->parent, dict); plist_free(dict); parent = client->parent; /* notifies the client->notifier thread that it should terminate */ client->parent = NULL; if (client->notifier) { debug_info("joining np callback"); thread_join(client->notifier); thread_free(client->notifier); client->notifier = (thread_t)NULL; } else { dict = NULL; property_list_service_receive_plist(parent, &dict); if (dict) { #ifndef STRIP_DEBUG_CODE char *cmd_value = NULL; plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); if (plist_get_node_type(cmd_value_node) == PLIST_STRING) { plist_get_string_val(cmd_value_node, &cmd_value); } if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { // this is the expected answer } else { debug_info("Did not get ProxyDeath but:"); debug_plist(dict); } if (cmd_value) { free(cmd_value); } #endif plist_free(dict); } } property_list_service_client_free(parent); mutex_destroy(&client->mutex); free(client); return NP_E_SUCCESS; }
heartbeat_error_t heartbeat_send(heartbeat_client_t client, plist_t plist) { heartbeat_error_t res = HEARTBEAT_E_UNKNOWN_ERROR; res = heartbeat_error(property_list_service_send_binary_plist(client->parent, plist)); if (res != HEARTBEAT_E_SUCCESS) { debug_info("Sending plist failed with error %d", res); return res; } debug_plist(plist); return res; }
/** * Sends a notification to the device's notification_proxy. * * @param client The client to send to * @param notification The notification message to send * * @return NP_E_SUCCESS on success, or an error returned by np_plist_send */ np_error_t np_post_notification(np_client_t client, const char *notification) { if (!client || !notification) { return NP_E_INVALID_ARG; } np_lock(client); plist_t dict = plist_new_dict(); plist_dict_set_item(dict,"Command", plist_new_string("PostNotification")); plist_dict_set_item(dict,"Name", plist_new_string(notification)); np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); plist_free(dict); dict = plist_new_dict(); plist_dict_set_item(dict,"Command", plist_new_string("Shutdown")); res = np_error(property_list_service_send_xml_plist(client->parent, dict)); plist_free(dict); if (res != NP_E_SUCCESS) { debug_info("Error sending XML plist to device!"); } // try to read an answer, we just ignore errors here dict = NULL; property_list_service_receive_plist(client->parent, &dict); if (dict) { #ifndef STRIP_DEBUG_CODE char *cmd_value = NULL; plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); if (plist_get_node_type(cmd_value_node) == PLIST_STRING) { plist_get_string_val(cmd_value_node, &cmd_value); } if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { // this is the expected answer } else { debug_plist(dict); } if (cmd_value) { free(cmd_value); } #endif plist_free(dict); } np_unlock(client); return res; }
int isManifestBufSignedForDevice(char *buildManifestBuffer, char *device, uint64_t ecid, int checkBaseband){ int isSigned = 0; plist_t tssreq = NULL; plist_t apticket = NULL; tssrequest(&tssreq, buildManifestBuffer, device, ecid, checkBaseband); isSigned = ((apticket = tss_request_send(tssreq, NULL)) > 0); if (print_tss_response) debug_plist(apticket); error: if (tssreq) plist_free(tssreq); if (apticket) plist_free(apticket); return isSigned; }
int asr_send(idevice_connection_t asr, plist_t* data) { uint32_t size = 0; char* buffer = NULL; plist_to_xml(data, &buffer, &size); if (asr_send_buffer(asr, buffer, size) < 0) { error("ERROR: Unable to send plist to ASR\n"); free(buffer); return -1; } debug("Sent %d bytes:\n", size); debug_plist(data); free(buffer); return 0; }
/** * Sends a plist using the given property list service client. * Internally used generic plist send function. * * @param client The property list service client to use for sending. * @param plist plist to send * @param binary 1 = send binary plist, 0 = send xml plist * * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, * PROPERTY_LIST_SERVICE_E_INVALID_ARG when one or more parameters are * invalid, PROPERTY_LIST_SERVICE_E_PLIST_ERROR when dict is not a valid * plist, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when an unspecified * error occurs. */ static property_list_service_error_t internal_plist_send(property_list_service_client_t client, plist_t plist, int binary) { property_list_service_error_t res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; char *content = NULL; uint32_t length = 0; uint32_t nlen = 0; int bytes = 0; if (!client || (client && !client->parent) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; } if (binary) { plist_to_bin(plist, &content, &length); } else { plist_to_xml(plist, &content, &length); } if (!content || length == 0) { return PROPERTY_LIST_SERVICE_E_PLIST_ERROR; } nlen = htobe32(length); debug_info("sending %d bytes", length); service_send(client->parent, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes); if (bytes == sizeof(nlen)) { service_send(client->parent, content, length, (uint32_t*)&bytes); if (bytes > 0) { debug_info("sent %d bytes", bytes); debug_plist(plist); if ((uint32_t)bytes == length) { res = PROPERTY_LIST_SERVICE_E_SUCCESS; } else { debug_info("ERROR: Could not send all data (%d of %d)!", bytes, length); } } } if (bytes <= 0) { debug_info("ERROR: sending to device failed."); } free(content); return res; }
heartbeat_error_t heartbeat_receive_with_timeout(heartbeat_client_t client, plist_t * plist, uint32_t timeout_ms) { heartbeat_error_t res = HEARTBEAT_E_UNKNOWN_ERROR; plist_t outplist = NULL; res = heartbeat_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, timeout_ms)); if (res != HEARTBEAT_E_SUCCESS || !outplist) { debug_info("Could not receive plist, error %d", res); plist_free(outplist); return HEARTBEAT_E_MUX_ERROR; } *plist = outplist; debug_plist(*plist); return res; }
LIBIMOBILEDEVICE_API lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) { if (!client) return LOCKDOWN_E_INVALID_ARG; lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t dict = plist_new_dict(); plist_dict_add_label(dict, client->label); plist_dict_set_item(dict,"Request", plist_new_string("QueryType")); debug_info("called"); ret = lockdownd_send(client, dict); plist_free(dict); dict = NULL; ret = lockdownd_receive(client, &dict); if (LOCKDOWN_E_SUCCESS != ret) return ret; ret = LOCKDOWN_E_UNKNOWN_ERROR; plist_t type_node = plist_dict_get_item(dict, "Type"); if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) { char* typestr = NULL; plist_get_string_val(type_node, &typestr); debug_info("success with type %s", typestr); /* return the type if requested */ if (type != NULL) { *type = typestr; } else { free(typestr); } ret = LOCKDOWN_E_SUCCESS; } else { debug_info("hmm. QueryType response does not contain a type?!"); debug_plist(dict); } plist_free(dict); dict = NULL; return ret; }
int restore_handle_status_msg(restored_client_t client, plist_t msg) { uint64_t value = 0; info("Got status message\n"); debug_plist(msg); plist_t node = plist_dict_get_item(msg, "Status"); plist_get_uint_val(node, &value); switch(value) { case 0: info("Status: Restore Finished\n"); break; case 6: info("Status: Disk Failure\n"); break; case 14: info("Status: Fail\n"); break; default: info("Unknown status message.\n"); } return 0; }
/** * Receives a plist using the given property list service client. * Internally used generic plist receive function. * * @param client The property list service client to use for receiving * @param plist pointer to a plist_t that will point to the received plist * upon successful return * @param timeout Maximum time in milliseconds to wait for data. * * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL, * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR * when an unspecified error occurs. */ static property_list_service_error_t internal_plist_receive_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) { property_list_service_error_t res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; uint32_t pktlen = 0; uint32_t bytes = 0; if (!client || (client && !client->parent) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; } *plist = NULL; service_error_t serr = service_receive_with_timeout(client->parent, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); if ((serr == SERVICE_E_SUCCESS) && (bytes == 0)) { return PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT; } debug_info("initial read=%i", bytes); if (bytes < 4) { debug_info("initial read failed!"); return PROPERTY_LIST_SERVICE_E_MUX_ERROR; } else { uint32_t curlen = 0; char *content = NULL; pktlen = be32toh(pktlen); debug_info("%d bytes following", pktlen); content = (char*)malloc(pktlen); if (!content) { debug_info("out of memory when allocating %d bytes", pktlen); return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; } while (curlen < pktlen) { service_receive(client->parent, content+curlen, pktlen-curlen, &bytes); if (bytes <= 0) { res = PROPERTY_LIST_SERVICE_E_MUX_ERROR; break; } debug_info("received %d bytes", bytes); curlen += bytes; } if (curlen < pktlen) { debug_info("received incomplete packet (%d of %d bytes)", curlen, pktlen); if (curlen > 0) { debug_info("incomplete packet following:"); debug_buffer(content, curlen); } free(content); return res; } if ((pktlen > 8) && !memcmp(content, "bplist00", 8)) { plist_from_bin(content, pktlen, plist); } else if ((pktlen > 5) && !memcmp(content, "<?xml", 5)) { /* iOS 4.3+ hack: plist data might contain invalid characters, thus we convert those to spaces */ for (bytes = 0; bytes < pktlen-1; bytes++) { if ((content[bytes] >= 0) && (content[bytes] < 0x20) && (content[bytes] != 0x09) && (content[bytes] != 0x0a) && (content[bytes] != 0x0d)) content[bytes] = 0x20; } plist_from_xml(content, pktlen, plist); } else { debug_info("WARNING: received unexpected non-plist content"); debug_buffer(content, pktlen); } if (*plist) { debug_plist(*plist); res = PROPERTY_LIST_SERVICE_E_SUCCESS; } else { res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR; } free(content); content = NULL; } return res; }
plist_t tss_create_request(plist_t build_identity, uint64_t ecid, unsigned char* nonce, int nonce_size) { uint64_t unique_build_size = 0; char* unique_build_data = NULL; plist_t unique_build_node = plist_dict_get_item(build_identity, "UniqueBuildID"); if (!unique_build_node || plist_get_node_type(unique_build_node) != PLIST_DATA) { error("ERROR: Unable to find UniqueBuildID node\n"); return NULL; } plist_get_data_val(unique_build_node, &unique_build_data, &unique_build_size); int chip_id = 0; char* chip_id_string = NULL; plist_t chip_id_node = plist_dict_get_item(build_identity, "ApChipID"); if (!chip_id_node || plist_get_node_type(chip_id_node) != PLIST_STRING) { error("ERROR: Unable to find ApChipID node\n"); return NULL; } plist_get_string_val(chip_id_node, &chip_id_string); sscanf(chip_id_string, "%x", &chip_id); int board_id = 0; char* board_id_string = NULL; plist_t board_id_node = plist_dict_get_item(build_identity, "ApBoardID"); if (!board_id_node || plist_get_node_type(board_id_node) != PLIST_STRING) { error("ERROR: Unable to find ApBoardID node\n"); return NULL; } plist_get_string_val(board_id_node, &board_id_string); sscanf(board_id_string, "%x", &board_id); int security_domain = 0; char* security_domain_string = NULL; plist_t security_domain_node = plist_dict_get_item(build_identity, "ApSecurityDomain"); if (!security_domain_node || plist_get_node_type(security_domain_node) != PLIST_STRING) { error("ERROR: Unable to find ApSecurityDomain node\n"); return NULL; } plist_get_string_val(security_domain_node, &security_domain_string); sscanf(security_domain_string, "%x", &security_domain); char ecid_string[ECID_STRSIZE]; memset(ecid_string, '\0', ECID_STRSIZE); if (ecid == 0) { error("ERROR: Unable to get ECID\n"); return NULL; } snprintf(ecid_string, ECID_STRSIZE, FMT_qu, (long long unsigned int)ecid); // Add build information to TSS request plist_t tss_request = plist_new_dict(); plist_dict_insert_item(tss_request, "@APTicket", plist_new_bool(1)); plist_dict_insert_item(tss_request, "@BBTicket", plist_new_bool(1)); plist_dict_insert_item(tss_request, "@HostIpAddress", plist_new_string("192.168.0.1")); plist_dict_insert_item(tss_request, "@HostPlatformInfo", plist_new_string("mac")); plist_dict_insert_item(tss_request, "@Locality", plist_new_string("en_US")); char* guid = generate_guid(); if (guid) { plist_dict_insert_item(tss_request, "@UUID", plist_new_string(guid)); free(guid); } plist_dict_insert_item(tss_request, "@VersionInfo", plist_new_string("libauthinstall-107.3")); plist_dict_insert_item(tss_request, "ApBoardID", plist_new_uint(board_id)); plist_dict_insert_item(tss_request, "ApChipID", plist_new_uint(chip_id)); plist_dict_insert_item(tss_request, "ApECID", plist_new_string(ecid_string)); if (nonce && (nonce_size > 0)) { plist_dict_insert_item(tss_request, "ApNonce", plist_new_data(nonce, nonce_size)); } plist_dict_insert_item(tss_request, "ApProductionMode", plist_new_bool(1)); plist_dict_insert_item(tss_request, "ApSecurityDomain", plist_new_uint(security_domain)); plist_dict_insert_item(tss_request, "UniqueBuildID", plist_new_data(unique_build_data, unique_build_size)); free(unique_build_data); // Add all firmware files to TSS request plist_t manifest_node = plist_dict_get_item(build_identity, "Manifest"); if (!manifest_node || plist_get_node_type(manifest_node) != PLIST_DICT) { error("ERROR: Unable to find restore manifest\n"); plist_free(tss_request); return NULL; } char* key = NULL; plist_t manifest_entry = NULL; plist_dict_iter iter = NULL; plist_dict_new_iter(manifest_node, &iter); while (1) { plist_dict_next_item(manifest_node, iter, &key, &manifest_entry); if (key == NULL) break; if (!manifest_entry || plist_get_node_type(manifest_entry) != PLIST_DICT) { error("ERROR: Unable to fetch BuildManifest entry\n"); free(tss_request); return NULL; } if (strcmp(key, "BasebandFirmware") == 0) { free(key); continue; } plist_t tss_entry = plist_copy(manifest_entry); plist_dict_insert_item(tss_request, key, tss_entry); free(key); } if (idevicerestore_debug) { debug_plist(tss_request); } return tss_request; }
plist_t tss_send_request(plist_t tss_request) { curl_global_init(CURL_GLOBAL_ALL); int status_code = -1; char* request = NULL; int retry = 0; int max_retries = 15; unsigned int size = 0; plist_to_xml(tss_request, &request, &size); tss_response* response = NULL; while (retry++ < max_retries) { response = NULL; CURL* handle = curl_easy_init(); if (handle == NULL) { break; } struct curl_slist* header = NULL; header = curl_slist_append(header, "Cache-Control: no-cache"); header = curl_slist_append(header, "Content-type: text/xml; charset=\"utf-8\""); header = curl_slist_append(header, "Expect:"); response = malloc(sizeof(tss_response)); if (response == NULL) { fprintf(stderr, "Unable to allocate sufficent memory\n"); return NULL; } response->length = 0; response->content = malloc(1); response->content[0] = '\0'; curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&tss_write_callback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request); curl_easy_setopt(handle, CURLOPT_USERAGENT, "InetURL/1.0"); curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(request)); 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); curl_easy_cleanup(handle); if (strstr(response->content, "MESSAGE=SUCCESS")) { status_code = 0; break; } if (response->length > 0) { error("TSS server returned: %s\n", response->content); } char* status = strstr(response->content, "STATUS="); if (status) { sscanf(status+7, "%d&%*s", &status_code); } if (status_code == -1) { // no status code in response. retry free(response->content); free(response); sleep(2); continue; } else if (status_code == 94) { // This device isn't eligible for the requested build. break; } else if (status_code == 100) { // server error, most likely the request was malformed break; } else { error("ERROR: tss_send_request: Unhandled status code %d\n", status_code); } } if (status_code != 0) { error("ERROR: TSS request failed (status=%d)\n", status_code); free(response->content); free(response); free(request); return NULL; } char* tss_data = strstr(response->content, "<?xml"); if (tss_data == NULL) { error("ERROR: Incorrectly formatted TSS response\n"); free(response->content); free(response); free(request); return NULL; } uint32_t tss_size = 0; plist_t tss_response = NULL; tss_size = response->length - (tss_data - response->content); plist_from_xml(tss_data, tss_size, &tss_response); free(response->content); free(response); if (idevicerestore_debug) { debug_plist(tss_response); } free(request); curl_global_cleanup(); return tss_response; }
webinspector_error_t webinspector_receive_with_timeout(webinspector_client_t client, plist_t * plist, uint32_t timeout_ms) { webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR; plist_t message = NULL; plist_t key = NULL; int is_final_message = 1; char* buffer = NULL; uint64_t length = 0; char* packet = NULL; char* newpacket = NULL; uint64_t packet_length = 0; debug_info("Receiving webinspector message..."); do { /* receive message */ res = webinspector_error(property_list_service_receive_plist_with_timeout(client->parent, &message, timeout_ms)); if (res != WEBINSPECTOR_E_SUCCESS || !message) { debug_info("Could not receive message, error %d", res); plist_free(message); return WEBINSPECTOR_E_MUX_ERROR; } /* get message key */ key = plist_dict_get_item(message, "WIRFinalMessageKey"); if (!key) { key = plist_dict_get_item(message, "WIRPartialMessageKey"); if (!key) { debug_info("ERROR: Unable to read message key."); plist_free(message); return WEBINSPECTOR_E_PLIST_ERROR; } is_final_message = 0; } else { is_final_message = 1; } /* read partial data */ plist_get_data_val(key, &buffer, &length); if (!buffer || length == 0 || length > 0xFFFFFFFF) { debug_info("ERROR: Unable to get the inner plist binary data."); free(packet); free(buffer); return WEBINSPECTOR_E_PLIST_ERROR; } /* (re)allocate packet data */ if (!packet) { packet = (char*)malloc(length * sizeof(char)); } else { newpacket = (char*)realloc(packet, (packet_length + length) * sizeof(char)); packet = newpacket; } /* copy partial data into final packet data */ memcpy(packet + packet_length, buffer, length); /* cleanup buffer */ free(buffer); buffer = NULL; if (message) { plist_free(message); message = NULL; } /* adjust packet length */ packet_length += length; length = 0; } while(!is_final_message); /* read final message */ if (packet_length) { plist_from_bin(packet, (uint32_t)packet_length, plist); if (!*plist) { debug_info("Error restoring the final plist."); free(packet); return WEBINSPECTOR_E_PLIST_ERROR; } debug_plist(*plist); } if (packet) { free(packet); } return res; }
int restore_send_nor(restored_client_t client, const char* ipsw, plist_t tss) { char* llb_path = NULL; if (tss_get_entry_path(tss, "LLB", &llb_path) < 0) { error("ERROR: Unable to get LLB info from TSS response\n"); return -1; } char* llb_filename = strstr(llb_path, "LLB"); if (llb_filename == NULL) { error("ERROR: Unable to extract firmware path from LLB filename\n"); free(llb_path); return -1; } char firmware_path[256]; memset(firmware_path, '\0', sizeof(firmware_path)); memcpy(firmware_path, llb_path, (llb_filename - 1) - llb_path); info("Found firmware path %s\n", firmware_path); char manifest_file[256]; memset(manifest_file, '\0', sizeof(manifest_file)); snprintf(manifest_file, sizeof(manifest_file), "%s/manifest", firmware_path); info("Getting firmware manifest %s\n", manifest_file); int manifest_size = 0; char* manifest_data = NULL; if (ipsw_extract_to_memory(ipsw, manifest_file, &manifest_data, &manifest_size) < 0) { error("ERROR: Unable to extract firmware manifest from ipsw\n"); free(llb_path); return -1; } char firmware_filename[256]; memset(firmware_filename, '\0', sizeof(firmware_filename)); int llb_size = 0; char* llb_data = NULL; plist_t dict = plist_new_dict(); char* filename = strtok(manifest_data, "\n"); if (filename != NULL) { memset(firmware_filename, '\0', sizeof(firmware_filename)); snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); if (get_signed_component(ipsw, tss, firmware_filename, &llb_data, &llb_size) < 0) { error("ERROR: Unable to get signed LLB\n"); return -1; } plist_dict_insert_item(dict, "LlbImageData", plist_new_data(llb_data, (uint64_t) llb_size)); } int nor_size = 0; char* nor_data = NULL; filename = strtok(NULL, "\n"); plist_t norimage_array = plist_new_array(); while (filename != NULL) { memset(firmware_filename, '\0', sizeof(firmware_filename)); snprintf(firmware_filename, sizeof(firmware_filename), "%s/%s", firmware_path, filename); if (get_signed_component(ipsw, tss, firmware_filename, &nor_data, &nor_size) < 0) { error("ERROR: Unable to get signed firmware %s\n", firmware_filename); break; } plist_array_append_item(norimage_array, plist_new_data(nor_data, (uint64_t) nor_size)); free(nor_data); nor_data = NULL; nor_size = 0; filename = strtok(NULL, "\n"); } plist_dict_insert_item(dict, "NorImageData", norimage_array); debug_plist(dict); restored_error_t ret = restored_send(client, dict); if (ret != RESTORE_E_SUCCESS) { error("ERROR: Unable to send kernelcache data\n"); plist_free(dict); return -1; } plist_free(dict); return 0; }
/** * Request data for the given sources. * * @param client The connected file_relay client. * @param sources A NULL-terminated list of sources to retrieve. * Valid sources are: * - AppleSupport * - Network * - VPN * - WiFi * - UserDatabases * - CrashReporter * - tmp * - SystemConfiguration * @param connection The connection that has to be used for receiving the * data using idevice_connection_receive(). The connection will be closed * automatically by the device, but use file_relay_client_free() to clean * up properly. * * @note WARNING: Don't call this function without reading the data afterwards. * A directory mobile_file_relay.XXXX used for creating the archive will * remain in the /tmp directory otherwise. * * @return FILE_RELAY_E_SUCCESS on succes, FILE_RELAY_E_INVALID_ARG when one or * more parameters are invalid, FILE_RELAY_E_MUX_ERROR if a communication * error occurs, FILE_RELAY_E_PLIST_ERROR when the received result is NULL * or is not a valid plist, FILE_RELAY_E_INVALID_SOURCE if one or more * sources are invalid, FILE_RELAY_E_STAGING_EMPTY if no data is available * for the given sources, or FILE_RELAY_E_UNKNOWN_ERROR otherwise. */ file_relay_error_t file_relay_request_sources(file_relay_client_t client, const char **sources, idevice_connection_t *connection) { if (!client || !client->parent || !sources || !sources[0]) { return FILE_RELAY_E_INVALID_ARG; } *connection = NULL; file_relay_error_t err = FILE_RELAY_E_UNKNOWN_ERROR; /* set up request plist */ plist_t array = plist_new_array(); int i = 0; while (sources[i]) { plist_array_append_item(array, plist_new_string(sources[i])); i++; } plist_t dict = plist_new_dict(); plist_dict_insert_item(dict, "Sources", array); if (property_list_service_send_xml_plist(client->parent, dict) != PROPERTY_LIST_SERVICE_E_SUCCESS) { debug_info("ERROR: Could not send request to device!"); err = FILE_RELAY_E_MUX_ERROR; goto leave; } plist_free(dict); dict = NULL; if (property_list_service_receive_plist_with_timeout(client->parent, &dict, 60000) != PROPERTY_LIST_SERVICE_E_SUCCESS) { debug_info("ERROR: Could not receive answer from device!"); err = FILE_RELAY_E_MUX_ERROR; goto leave; } if (!dict) { debug_info("ERROR: Did not receive any plist!"); err = FILE_RELAY_E_PLIST_ERROR; goto leave; } plist_t error = plist_dict_get_item(dict, "Error"); if (error) { char *errmsg = NULL; plist_get_string_val(error, &errmsg); if (errmsg) { if (!strcmp(errmsg, "InvalidSource")) { debug_info("ERROR: One or more given sources are invalid!"); err = FILE_RELAY_E_INVALID_SOURCE; } else if (!strcmp(errmsg, "StagingEmpty")) { debug_info("ERROR: StagingEmpty - No data available!"); err = FILE_RELAY_E_STAGING_EMPTY; } else { debug_info("ERROR: Unknown error '%s'", errmsg); } free(errmsg); } else { debug_info("ERROR: Could not get error message!"); } goto leave; } plist_t status = plist_dict_get_item(dict, "Status"); if (!status) { debug_info("ERROR: Unexpected plist received!"); debug_plist(dict); err = FILE_RELAY_E_PLIST_ERROR; goto leave; } char *ack = NULL; plist_get_string_val(status, &ack); if (!ack) { debug_info("ERROR: Could not get 'Acknowledged' string!"); goto leave; } if (strcmp(ack, "Acknowledged")) { debug_info("ERROR: Did not receive 'Acknowledged' but '%s'", ack); goto leave; } free(ack); err = FILE_RELAY_E_SUCCESS; *connection = client->parent->connection; leave: if (dict) { plist_free(dict); } return err; }
plist_t tss_request_send(plist_t tss_request, const char* server_url_string) { if (idevicerestore_debug) { debug_plist(tss_request); } char* request = NULL; int status_code = -1; int retry = 0; int max_retries = 15; unsigned int size = 0; char curl_error_message[CURL_ERROR_SIZE]; const char* urls[6] = { "https://gs.apple.com/TSS/controller?action=2", "https://17.171.36.30/TSS/controller?action=2", "https://17.151.36.30/TSS/controller?action=2", "http://gs.apple.com/TSS/controller?action=2", "http://17.171.36.30/TSS/controller?action=2", "http://17.151.36.30/TSS/controller?action=2" }; plist_to_xml(tss_request, &request, &size); tss_response* response = NULL; memset(curl_error_message, '\0', CURL_ERROR_SIZE); while (retry++ < max_retries) { response = NULL; CURL* handle = curl_easy_init(); if (handle == NULL) { break; } struct curl_slist* header = NULL; header = curl_slist_append(header, "Cache-Control: no-cache"); header = curl_slist_append(header, "Content-type: text/xml; charset=\"utf-8\""); header = curl_slist_append(header, "Expect:"); response = malloc(sizeof(tss_response)); if (response == NULL) { fprintf(stderr, "Unable to allocate sufficent memory\n"); return NULL; } response->length = 0; response->content = malloc(1); response->content[0] = '\0'; /* disable SSL verification to allow download from untrusted https locations */ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_message); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, (curl_write_callback)&tss_write_callback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request); curl_easy_setopt(handle, CURLOPT_USERAGENT, "InetURL/1.0"); curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(request)); if (server_url_string) { curl_easy_setopt(handle, CURLOPT_URL, server_url_string); } else { int url_index = (retry - 1) % 6; curl_easy_setopt(handle, CURLOPT_URL, urls[url_index]); info("Request URL set to %s\n", urls[url_index]); } info("Sending TSS request attempt %d... ", retry); curl_easy_perform(handle); curl_slist_free_all(header); curl_easy_cleanup(handle); if (strstr(response->content, "MESSAGE=SUCCESS")) { status_code = 0; info("response successfully received\n"); break; } if (response->length > 0) { error("TSS server returned: %s\n", response->content); } char* status = strstr(response->content, "STATUS="); if (status) { sscanf(status+7, "%d&%*s", &status_code); } if (status_code == -1) { error("%s\n", curl_error_message); // no status code in response. retry free(response->content); free(response); sleep(2); continue; } else if (status_code == 8) { // server error (invalid bb request?) break; } else if (status_code == 49) { // server error (invalid bb data, e.g. BbSNUM?) break; } else if (status_code == 69 || status_code == 94) { // This device isn't eligible for the requested build. break; } else if (status_code == 100) { // server error, most likely the request was malformed break; } else if (status_code == 126) { // An internal error occured, most likely the request was malformed break; } else { error("ERROR: tss_send_request: Unhandled status code %d\n", status_code); } } if (status_code != 0) { if (strstr(response->content, "MESSAGE=") != NULL) { char* message = strstr(response->content, "MESSAGE=") + strlen("MESSAGE="); error("ERROR: TSS request failed (status=%d, message=%s)\n", status_code, message); } else { error("ERROR: TSS request failed: %s (status=%d)\n", curl_error_message, status_code); } free(request); free(response->content); free(response); return NULL; } char* tss_data = strstr(response->content, "<?xml"); if (tss_data == NULL) { error("ERROR: Incorrectly formatted TSS response\n"); free(request); free(response->content); free(response); return NULL; } uint32_t tss_size = 0; plist_t tss_response = NULL; tss_size = response->length - (tss_data - response->content); plist_from_xml(tss_data, tss_size, &tss_response); free(response->content); free(response); if (idevicerestore_debug) { debug_plist(tss_response); } free(request); return tss_response; }
int restore_device(const char* uuid, const char* ipsw, plist_t tss, const char* filesystem) { int error = 0; char* type = NULL; 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 the restore process restore_error = restored_start_restore(restore); if (restore_error != RESTORE_E_SUCCESS) { error("ERROR: Unable to start the restore process\n"); restore_close(device, restore); return -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; } 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; } restore_close(device, restore); return 0; }
int asr_open_with_timeout(idevice_t device, asr_client_t* asr) { int i = 0; int attempts = 10; idevice_connection_t connection = NULL; idevice_error_t device_error = IDEVICE_E_SUCCESS; *asr = NULL; if (device == NULL) { return -1; } debug("Connecting to ASR\n"); for (i = 1; i <= attempts; i++) { device_error = idevice_connect(device, ASR_PORT, &connection); if (device_error == IDEVICE_E_SUCCESS) { break; } if (i >= attempts) { error("ERROR: Unable to connect to ASR client\n"); return -1; } sleep(2); debug("Retrying connection...\n"); } asr_client_t asr_loc = (asr_client_t)malloc(sizeof(struct asr_client)); memset(asr_loc, '\0', sizeof(struct asr_client)); asr_loc->connection = connection; /* receive Initiate command message */ plist_t data = NULL; asr_loc->checksum_chunks = 0; if (asr_receive(asr_loc, &data) < 0) { error("ERROR: Unable to receive data from ASR\n"); asr_free(asr_loc); plist_free(data); return -1; } plist_t node; node = plist_dict_get_item(data, "Command"); if (node && (plist_get_node_type(node) == PLIST_STRING)) { char* strval = NULL; plist_get_string_val(node, &strval); if (strval && (strcmp(strval, "Initiate") != 0)) { error("ERROR: unexpected ASR plist received:\n"); debug_plist(data); plist_free(data); asr_free(asr_loc); return -1; } } node = plist_dict_get_item(data, "Checksum Chunks"); if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { plist_get_bool_val(node, &(asr_loc->checksum_chunks)); } plist_free(data); *asr = asr_loc; return 0; }