/* * webinspector.c * com.apple.webinspector service implementation. * * Copyright (c) 2013 Yury Melnichek All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "webinspector.h" #include "lockdown.h" #include "common/debug.h" /** * Convert a property_list_service_error_t value to a webinspector_error_t value. * Used internally to get correct error codes. * * @param err An property_list_service_error_t error code * * @return A matching webinspector_error_t error code, * WEBINSPECTOR_E_UNKNOWN_ERROR otherwise. */ static webinspector_error_t webinspector_error(property_list_service_error_t err) { switch (err) { case PROPERTY_LIST_SERVICE_E_SUCCESS: return WEBINSPECTOR_E_SUCCESS; case PROPERTY_LIST_SERVICE_E_INVALID_ARG: return WEBINSPECTOR_E_INVALID_ARG; case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: return WEBINSPECTOR_E_PLIST_ERROR; case PROPERTY_LIST_SERVICE_E_MUX_ERROR: return WEBINSPECTOR_E_MUX_ERROR; case PROPERTY_LIST_SERVICE_E_SSL_ERROR: return WEBINSPECTOR_E_SSL_ERROR; case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: return WEBINSPECTOR_E_RECEIVE_TIMEOUT; case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA: return WEBINSPECTOR_E_NOT_ENOUGH_DATA; default: break; } return WEBINSPECTOR_E_UNKNOWN_ERROR; } LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_new(idevice_t device, lockdownd_service_descriptor_t service, webinspector_client_t * client) { *client = NULL; if (!device || !service || service->port == 0 || !client || *client) { debug_info("Incorrect parameter passed to webinspector_client_new."); return WEBINSPECTOR_E_INVALID_ARG; } debug_info("Creating webinspector_client, port = %d.", service->port); property_list_service_client_t plclient = NULL; webinspector_error_t ret = webinspector_error(property_list_service_client_new(device, service, &plclient)); if (ret != WEBINSPECTOR_E_SUCCESS) { debug_info("Creating a property list client failed. Error: %i", ret); return ret; } webinspector_client_t client_loc = (webinspector_client_t) malloc(sizeof(struct webinspector_client_private)); client_loc->parent = plclient; *client = client_loc; debug_info("webinspector_client successfully created."); return 0; } LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_start_service(idevice_t device, webinspector_client_t * client, const char* label) { webinspector_error_t err = WEBINSPECTOR_E_UNKNOWN_ERROR; service_client_factory_start_service(device, WEBINSPECTOR_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(webinspector_client_new), &err); return err; } LIBIMOBILEDEVICE_API webinspector_error_t webinspector_client_free(webinspector_client_t client) { if (!client) return WEBINSPECTOR_E_INVALID_ARG; webinspector_error_t err = webinspector_error(property_list_service_client_free(client->parent)); free(client); return err; } LIBIMOBILEDEVICE_API 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; } LIBIMOBILEDEVICE_API webinspector_error_t webinspector_receive(webinspector_client_t client, plist_t * plist) { return webinspector_receive_with_timeout(client, plist, 5000); } LIBIMOBILEDEVICE_API 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; }