From f8066bbf5169b7d7e68771bce677355e33a595c1 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Tue, 26 Feb 2013 17:24:23 +0100 Subject: implement base service that all other services inherit from --- src/Makefile.am | 3 +- src/afc.c | 71 +++++------- src/afc.h | 5 +- src/file_relay.c | 2 +- src/house_arrest.c | 4 +- src/misagent.c | 2 +- src/mobilebackup2.c | 8 +- src/property_list_service.c | 51 ++++---- src/property_list_service.h | 3 +- src/service.c | 276 ++++++++++++++++++++++++++++++++++++++++++++ src/service.h | 59 ++++++++++ 11 files changed, 404 insertions(+), 80 deletions(-) create mode 100644 src/service.c create mode 100644 src/service.h diff --git a/src/Makefile.am b/src/Makefile.am index 136baf4..574075e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ libimobiledevice_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_SO_ libimobiledevice_la_SOURCES = idevice.c idevice.h \ debug.c debug.h\ userpref.c userpref.h\ + service.c service.h\ property_list_service.c property_list_service.h\ device_link_service.c device_link_service.h\ lockdown.c lockdown.h\ @@ -31,4 +32,4 @@ libimobiledevice_la_SOURCES = idevice.c idevice.h \ if WIN32 libimobiledevice_la_LIBADD += -lole32 -endif \ No newline at end of file +endif diff --git a/src/afc.c b/src/afc.c index b5228aa..83ee2ca 100644 --- a/src/afc.c +++ b/src/afc.c @@ -69,9 +69,7 @@ static void afc_unlock(afc_client_t client) * Makes a connection to the AFC service on the device using the given * connection. * - * @param connection An idevice_connection_t that must have been previously - * connected using idevice_connect(). Note that this connection will - * not be closed by calling afc_client_free(). + * @param serviceclient A connected service client * @param client Pointer that will be set to a newly allocated afc_client_t * upon successful return. * @@ -79,14 +77,14 @@ static void afc_unlock(afc_client_t client) * invalid, or AFC_E_NO_MEM if there is a memory allocation problem. */ -afc_error_t afc_client_new_from_connection(idevice_connection_t connection, afc_client_t *client) +afc_error_t afc_client_new_with_service_client(service_client_t serviceclient, afc_client_t *client) { - if (!connection) + if (!serviceclient) return AFC_E_INVALID_ARG; afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private)); - client_loc->connection = connection; - client_loc->own_connection = 0; + client_loc->parent = serviceclient; + client_loc->free_parent = 0; /* allocate a packet */ client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); @@ -113,10 +111,6 @@ afc_error_t afc_client_new_from_connection(idevice_connection_t connection, afc_ /** * Makes a connection to the AFC service on the device. - * This function calls afc_client_new_from_connection() after creating - * a connection to the specified device and port. - * - * @see afc_client_new_from_connection * * @param device The device to connect to. * @param service The service descriptor returned by lockdownd_start_service. @@ -132,21 +126,16 @@ afc_error_t afc_client_new(idevice_t device, lockdownd_service_descriptor_t serv if (!device || service->port == 0) return AFC_E_INVALID_ARG; - /* attempt connection */ - idevice_connection_t connection = NULL; - if (idevice_connect(device, service->port, &connection) != IDEVICE_E_SUCCESS) { + service_client_t parent = NULL; + if (service_client_new(device, service, &parent) != SERVICE_E_SUCCESS) { return AFC_E_MUX_ERROR; } - /* enable SSL if requested */ - if (service->ssl_enabled) - idevice_connection_enable_ssl(connection); - - afc_error_t err = afc_client_new_from_connection(connection, client); + afc_error_t err = afc_client_new_with_service_client(parent, client); if (err != AFC_E_SUCCESS) { - idevice_disconnect(connection); + service_client_free(parent); } else { - (*client)->own_connection = 1; + (*client)->free_parent = 1; } return err; } @@ -162,9 +151,9 @@ afc_error_t afc_client_free(afc_client_t client) if (!client || !client->afc_packet) return AFC_E_INVALID_ARG; - if (client->own_connection && client->connection) { - idevice_disconnect(client->connection); - client->connection = NULL; + if (client->free_parent && client->parent) { + service_client_free(client->parent); + client->parent = NULL; } free(client->afc_packet); #ifdef WIN32 @@ -196,7 +185,7 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, ui uint32_t offset = 0; uint32_t sent = 0; - if (!client || !client->connection || !client->afc_packet) + if (!client || !client->parent || !client->afc_packet) return AFC_E_INVALID_ARG; *bytes_sent = 0; @@ -229,7 +218,7 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, ui /* send AFC packet header */ AFCPacket_to_LE(client->afc_packet); sent = 0; - idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); + service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket), &sent); AFCPacket_from_LE(client->afc_packet); if (sent == 0) { /* FIXME: should this be handled as success?! */ @@ -239,7 +228,7 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, ui /* send AFC packet data */ sent = 0; - idevice_connection_send(client->connection, data, offset, &sent); + service_send(client->parent, data, offset, &sent); if (sent == 0) { return AFC_E_SUCCESS; } @@ -251,7 +240,7 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, ui debug_buffer(data + offset, length - offset); sent = 0; - idevice_connection_send(client->connection, data + offset, length - offset, &sent); + service_send(client->parent, data + offset, length - offset, &sent); *bytes_sent = sent; return AFC_E_SUCCESS; @@ -264,7 +253,7 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, ui /* send AFC packet header */ AFCPacket_to_LE(client->afc_packet); sent = 0; - idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); + service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket), &sent); AFCPacket_from_LE(client->afc_packet); if (sent == 0) { return AFC_E_SUCCESS; @@ -275,7 +264,7 @@ static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, ui debug_info("packet data follows"); debug_buffer(data, length); - idevice_connection_send(client->connection, data, length, &sent); + service_send(client->parent, data, length, &sent); *bytes_sent += sent; } return AFC_E_SUCCESS; @@ -303,7 +292,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 *bytes_recv = 0; /* first, read the AFC header */ - idevice_connection_receive(client->connection, (char*)&header, sizeof(AFCPacket), bytes_recv); + service_receive(client->parent, (char*)&header, sizeof(AFCPacket), bytes_recv); AFCPacket_from_LE(&header); if (*bytes_recv == 0) { debug_info("Just didn't get enough."); @@ -357,7 +346,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 *dump_here = (char*)malloc(entire_len); if (this_len > 0) { - idevice_connection_receive(client->connection, *dump_here, this_len, bytes_recv); + service_receive(client->parent, *dump_here, this_len, bytes_recv); if (*bytes_recv <= 0) { free(*dump_here); *dump_here = NULL; @@ -375,7 +364,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 if (entire_len > this_len) { while (current_count < entire_len) { - idevice_connection_receive(client->connection, (*dump_here)+current_count, entire_len - current_count, bytes_recv); + service_receive(client->parent, (*dump_here)+current_count, entire_len - current_count, bytes_recv); if (*bytes_recv <= 0) { debug_info("Error receiving data (recv returned %d)", *bytes_recv); break; @@ -625,7 +614,7 @@ afc_error_t afc_remove_path(afc_client_t client, const char *path) uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; - if (!client || !path || !client->afc_packet || !client->connection) + if (!client || !path || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; afc_lock(client); @@ -668,7 +657,7 @@ afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *t uint32_t bytes = 0; afc_error_t ret = AFC_E_UNKNOWN_ERROR; - if (!client || !from || !to || !client->afc_packet || !client->connection) + if (!client || !from || !to || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; afc_lock(client); @@ -800,7 +789,7 @@ afc_file_open(afc_client_t client, const char *filename, /* set handle to 0 so in case an error occurs, the handle is invalid */ *handle = 0; - if (!client || !client->connection || !client->afc_packet) + if (!client || !client->parent || !client->afc_packet) return AFC_E_INVALID_ARG; afc_lock(client); @@ -856,7 +845,7 @@ afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, const uint32_t MAXIMUM_READ_SIZE = 1 << 16; afc_error_t ret = AFC_E_SUCCESS; - if (!client || !client->afc_packet || !client->connection || handle == 0) + if (!client || !client->afc_packet || !client->parent || handle == 0) return AFC_E_INVALID_ARG; debug_info("called for length %i", length); @@ -933,7 +922,7 @@ afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t char *out_buffer = NULL; afc_error_t ret = AFC_E_SUCCESS; - if (!client || !client->afc_packet || !client->connection || !bytes_written || (handle == 0)) + if (!client || !client->afc_packet || !client->parent || !bytes_written || (handle == 0)) return AFC_E_INVALID_ARG; afc_lock(client); @@ -1258,7 +1247,7 @@ afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize uint64_t size_requested = htole64(newsize); afc_error_t ret = AFC_E_UNKNOWN_ERROR; - if (!client || !path || !client->afc_packet || !client->connection) + if (!client || !path || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; afc_lock(client); @@ -1302,7 +1291,7 @@ afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const c uint64_t type = htole64(linktype); afc_error_t ret = AFC_E_UNKNOWN_ERROR; - if (!client || !target || !linkname || !client->afc_packet || !client->connection) + if (!client || !target || !linkname || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; afc_lock(client); @@ -1350,7 +1339,7 @@ afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mt uint64_t mtime_loc = htole64(mtime); afc_error_t ret = AFC_E_UNKNOWN_ERROR; - if (!client || !path || !client->afc_packet || !client->connection) + if (!client || !path || !client->afc_packet || !client->parent) return AFC_E_INVALID_ARG; afc_lock(client); diff --git a/src/afc.h b/src/afc.h index 0c242f5..bdb1f16 100644 --- a/src/afc.h +++ b/src/afc.h @@ -30,6 +30,7 @@ #endif #include "libimobiledevice/afc.h" +#include "service.h" #include "endianness.h" #define AFC_MAGIC "CFA6LPAA" @@ -57,7 +58,7 @@ typedef struct { } AFCFilePacket; struct afc_client_private { - idevice_connection_t connection; + service_client_t parent; AFCPacket *afc_packet; int file_handle; int lock; @@ -66,7 +67,7 @@ struct afc_client_private { #else pthread_mutex_t mutex; #endif - int own_connection; + int free_parent; }; /* AFC Operations */ diff --git a/src/file_relay.c b/src/file_relay.c index e72be66..39382fb 100644 --- a/src/file_relay.c +++ b/src/file_relay.c @@ -188,7 +188,7 @@ file_relay_error_t file_relay_request_sources(file_relay_client_t client, const free(ack); err = FILE_RELAY_E_SUCCESS; - *connection = client->parent->connection; + *connection = client->parent->parent->connection; leave: if (dict) { diff --git a/src/house_arrest.c b/src/house_arrest.c index e0d7771..ec76346 100644 --- a/src/house_arrest.c +++ b/src/house_arrest.c @@ -105,7 +105,7 @@ house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) return HOUSE_ARREST_E_INVALID_ARG; house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS; - if (client->parent && client->parent->connection) { + if (client->parent && client->parent->parent->connection) { house_arrest_error(property_list_service_client_free(client->parent)); } client->parent = NULL; @@ -242,7 +242,7 @@ afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { return AFC_E_INVALID_ARG; } - afc_error_t err = afc_client_new_from_connection(client->parent->connection, afc_client); + afc_error_t err = afc_client_new_with_service_client(client->parent->parent, afc_client); if (err == AFC_E_SUCCESS) { client->mode = HOUSE_ARREST_CLIENT_MODE_AFC; } diff --git a/src/misagent.c b/src/misagent.c index c624603..cb84188 100644 --- a/src/misagent.c +++ b/src/misagent.c @@ -132,7 +132,7 @@ misagent_error_t misagent_client_free(misagent_client_t client) return MISAGENT_E_INVALID_ARG; misagent_error_t err = MISAGENT_E_SUCCESS; - if (client->parent && client->parent->connection) { + if (client->parent && client->parent->parent) { misagent_error(property_list_service_client_free(client->parent)); } client->parent = NULL; diff --git a/src/mobilebackup2.c b/src/mobilebackup2.c index bcf5944..15ba469 100644 --- a/src/mobilebackup2.c +++ b/src/mobilebackup2.c @@ -279,13 +279,13 @@ mobilebackup2_error_t mobilebackup2_send_raw(mobilebackup2_client_t client, cons *bytes = 0; - idevice_connection_t conn = client->parent->parent->connection; + service_client_t raw = client->parent->parent->parent; int bytes_loc = 0; uint32_t sent = 0; do { bytes_loc = 0; - idevice_connection_send(conn, data+sent, length-sent, (uint32_t*)&bytes_loc); + service_send(raw, data+sent, length-sent, (uint32_t*)&bytes_loc); if (bytes_loc <= 0) break; sent += bytes_loc; @@ -321,7 +321,7 @@ mobilebackup2_error_t mobilebackup2_receive_raw(mobilebackup2_client_t client, c if (!client || !client->parent || !data || (length == 0) || !bytes) return MOBILEBACKUP2_E_INVALID_ARG; - idevice_connection_t conn = client->parent->parent->connection; + service_client_t raw = client->parent->parent->parent; *bytes = 0; @@ -329,7 +329,7 @@ mobilebackup2_error_t mobilebackup2_receive_raw(mobilebackup2_client_t client, c uint32_t received = 0; do { bytes_loc = 0; - idevice_connection_receive(conn, data+received, length-received, (uint32_t*)&bytes_loc); + service_receive(raw, data+received, length-received, (uint32_t*)&bytes_loc); if (bytes_loc <= 0) break; received += bytes_loc; } while (received < length); diff --git a/src/property_list_service.c b/src/property_list_service.c index 15adbc8..025a9bc 100644 --- a/src/property_list_service.c +++ b/src/property_list_service.c @@ -25,27 +25,28 @@ #include #include "property_list_service.h" -#include "idevice.h" #include "debug.h" #include "endianness.h" /** - * Convert an idevice_error_t value to an property_list_service_error_t value. + * Convert a service_error_t value to a property_list_service_error_t value. * Used internally to get correct error codes. * - * @param err An idevice_error_t error code + * @param err A service_error_t error code * * @return A matching property_list_service_error_t error code, * PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR otherwise. */ -static property_list_service_error_t idevice_to_property_list_service_error(idevice_error_t err) +static property_list_service_error_t service_to_property_list_service_error(service_error_t err) { switch (err) { - case IDEVICE_E_SUCCESS: + case SERVICE_E_SUCCESS: return PROPERTY_LIST_SERVICE_E_SUCCESS; - case IDEVICE_E_INVALID_ARG: + case SERVICE_E_INVALID_ARG: return PROPERTY_LIST_SERVICE_E_INVALID_ARG; - case IDEVICE_E_SSL_ERROR: + case SERVICE_E_MUX_ERROR: + return PROPERTY_LIST_SERVICE_E_MUX_ERROR; + case SERVICE_E_SSL_ERROR: return PROPERTY_LIST_SERVICE_E_SSL_ERROR; default: break; @@ -70,19 +71,15 @@ property_list_service_error_t property_list_service_client_new(idevice_t device, if (!device || (service->port == 0) || !client || *client) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; - /* Attempt connection */ - idevice_connection_t connection = NULL; - if (idevice_connect(device, service->port, &connection) != IDEVICE_E_SUCCESS) { - return PROPERTY_LIST_SERVICE_E_MUX_ERROR; + service_client_t parent = NULL; + service_error_t rerr = service_client_new(device, service, &parent); + if (rerr != SERVICE_E_SUCCESS) { + return service_to_property_list_service_error(rerr); } /* create client object */ property_list_service_client_t client_loc = (property_list_service_client_t)malloc(sizeof(struct property_list_service_client_private)); - client_loc->connection = connection; - - /* enable SSL if requested */ - if (service->ssl_enabled == 1) - property_list_service_enable_ssl(client_loc); + client_loc->parent = parent; /* all done, return success */ *client = client_loc; @@ -103,7 +100,7 @@ property_list_service_error_t property_list_service_client_free(property_list_se if (!client) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; - property_list_service_error_t err = idevice_to_property_list_service_error(idevice_disconnect(client->connection)); + property_list_service_error_t err = service_to_property_list_service_error(service_client_free(client->parent)); free(client); return err; } @@ -130,7 +127,7 @@ static property_list_service_error_t internal_plist_send(property_list_service_c uint32_t nlen = 0; int bytes = 0; - if (!client || (client && !client->connection) || !plist) { + if (!client || (client && !client->parent) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; } @@ -146,9 +143,9 @@ static property_list_service_error_t internal_plist_send(property_list_service_c nlen = htobe32(length); debug_info("sending %d bytes", length); - idevice_connection_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes); + service_send(client->parent, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes); if (bytes == sizeof(nlen)) { - idevice_connection_send(client->connection, content, length, (uint32_t*)&bytes); + service_send(client->parent, content, length, (uint32_t*)&bytes); if (bytes > 0) { debug_info("sent %d bytes", bytes); debug_plist(plist); @@ -222,11 +219,11 @@ static property_list_service_error_t internal_plist_receive_timeout(property_lis uint32_t pktlen = 0; uint32_t bytes = 0; - if (!client || (client && !client->connection) || !plist) { + if (!client || (client && !client->parent) || !plist) { return PROPERTY_LIST_SERVICE_E_INVALID_ARG; } - idevice_connection_receive_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); + service_receive_with_timeout(client->parent, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); debug_info("initial read=%i", bytes); if (bytes < 4) { debug_info("initial read failed!"); @@ -240,7 +237,7 @@ static property_list_service_error_t internal_plist_receive_timeout(property_lis content = (char*)malloc(pktlen); while (curlen < pktlen) { - idevice_connection_receive(client->connection, content+curlen, pktlen-curlen, &bytes); + service_receive(client->parent, content+curlen, pktlen-curlen, &bytes); if (bytes <= 0) { res = PROPERTY_LIST_SERVICE_E_MUX_ERROR; break; @@ -332,9 +329,9 @@ property_list_service_error_t property_list_service_receive_plist(property_list_ */ property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client) { - if (!client || !client->connection) + if (!client || !client->parent) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; - return idevice_to_property_list_service_error(idevice_connection_enable_ssl(client->connection)); + return service_to_property_list_service_error(service_enable_ssl(client->parent)); } /** @@ -349,8 +346,8 @@ property_list_service_error_t property_list_service_enable_ssl(property_list_ser */ property_list_service_error_t property_list_service_disable_ssl(property_list_service_client_t client) { - if (!client || !client->connection) + if (!client || !client->parent) return PROPERTY_LIST_SERVICE_E_INVALID_ARG; - return idevice_to_property_list_service_error(idevice_connection_disable_ssl(client->connection)); + return service_to_property_list_service_error(service_disable_ssl(client->parent)); } diff --git a/src/property_list_service.h b/src/property_list_service.h index 8b15c09..c18fd76 100644 --- a/src/property_list_service.h +++ b/src/property_list_service.h @@ -23,6 +23,7 @@ #define __PROPERTY_LIST_SERVICE_H #include +#include "service.h" #include "idevice.h" /* Error Codes */ @@ -35,7 +36,7 @@ #define PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR -256 struct property_list_service_client_private { - idevice_connection_t connection; + service_client_t parent; }; typedef struct property_list_service_client_private *property_list_service_client_t; diff --git a/src/service.c b/src/service.c new file mode 100644 index 0000000..929beff --- /dev/null +++ b/src/service.c @@ -0,0 +1,276 @@ +/* + * service.c + * generic service implementation. + * + * Copyright (c) 2013 Nikias Bassen. 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 "service.h" +#include "idevice.h" +#include "debug.h" + +/** + * Convert an idevice_error_t value to an service_error_t value. + * Used internally to get correct error codes. + * + * @param err An idevice_error_t error code + * + * @return A matching service_error_t error code, + * SERVICE_E_UNKNOWN_ERROR otherwise. + */ +static service_error_t idevice_to_service_error(idevice_error_t err) +{ + switch (err) { + case IDEVICE_E_SUCCESS: + return SERVICE_E_SUCCESS; + case IDEVICE_E_INVALID_ARG: + return SERVICE_E_INVALID_ARG; + case IDEVICE_E_SSL_ERROR: + return SERVICE_E_SSL_ERROR; + default: + break; + } + return SERVICE_E_UNKNOWN_ERROR; +} + +/** + * Creates a new service for the specified service descriptor. + * + * @param device The device to connect to. + * @param service The service descriptor returned by lockdownd_start_service. + * @param client Pointer that will be set to a newly allocated + * service_client_t upon successful return. + * + * @return SERVICE_E_SUCCESS on success, + * SERVICE_E_INVALID_ARG when one of the arguments is invalid, + * or SERVICE_E_MUX_ERROR when connecting to the device failed. + */ +service_error_t service_client_new(idevice_t device, lockdownd_service_descriptor_t service, service_client_t *client) +{ + if (!device || (service->port == 0) || !client || *client) + return SERVICE_E_INVALID_ARG; + + /* Attempt connection */ + idevice_connection_t connection = NULL; + if (idevice_connect(device, service->port, &connection) != IDEVICE_E_SUCCESS) { + return SERVICE_E_MUX_ERROR; + } + + /* create client object */ + service_client_t client_loc = (service_client_t)malloc(sizeof(struct service_client_private)); + client_loc->connection = connection; + + /* enable SSL if requested */ + if (service->ssl_enabled == 1) + service_enable_ssl(client_loc); + + /* all done, return success */ + *client = client_loc; + return SERVICE_E_SUCCESS; +} + +/** + * Starts a new service on the specified device with given name and + * connects to it. + * + * @param device The device to connect to. + * @param service_name The name of the service to start. + * @param client Pointer that will point to a newly allocated service_client_t + * upon successful return. Must be freed using service_client_free() after + * use. + * + * @return SERVICE_E_SUCCESS on success, or a SERVICE_E_* error code + * otherwise. + */ +service_error_t service_start_service(idevice_t device, const char* service_name, service_client_t *client) +{ + *client = NULL; + + lockdownd_client_t lckd = NULL; + if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, NULL)) { + debug_info("Could not create a lockdown client."); + return SERVICE_E_START_SERVICE_ERROR; + } + + lockdownd_service_descriptor_t service = NULL; + lockdownd_start_service(lckd, service_name, &service); + lockdownd_client_free(lckd); + + if (service->port <= 0) { + debug_info("Could not start service %s!", service_name); + return SERVICE_E_START_SERVICE_ERROR; + } + + service_error_t res = service_client_new(device, service, client); + if (res != SERVICE_E_SUCCESS) { + debug_info("Could not connect to service %s! Port: %i, error: %i", service_name, service->port, res); + return res; + } + + if (service) { + lockdownd_service_descriptor_free(service); + service = NULL; + } + + return SERVICE_E_SUCCESS; +} + +/** + * Frees a service instance. + * + * @param client The service instance to free. + * + * @return SERVICE_E_SUCCESS on success, + * SERVICE_E_INVALID_ARG when client is invalid, or a + * SERVICE_E_UNKNOWN_ERROR when another error occured. + */ +service_error_t service_client_free(service_client_t client) +{ + if (!client) + return SERVICE_E_INVALID_ARG; + + service_error_t err = idevice_to_service_error(idevice_disconnect(client->connection)); + free(client); + return err; +} + +/** + * Sends data using the given service client. + * + * @param client The service client to use for sending. + * @param data Data to send + * @param size Size of the data to send + * @param sent Number of bytes sent (can be NULL to ignore) + * + * @return SERVICE_E_SUCCESS on success, + * SERVICE_E_INVALID_ARG when one or more parameters are + * invalid, or SERVICE_E_UNKNOWN_ERROR when an unspecified + * error occurs. + */ +service_error_t service_send(service_client_t client, const char* data, uint32_t size, uint32_t *sent) +{ + service_error_t res = SERVICE_E_UNKNOWN_ERROR; + int bytes = 0; + + if (!client || (client && !client->connection) || !data || (size == 0)) { + return SERVICE_E_INVALID_ARG; + } + + debug_info("sending %d bytes", size); + res = idevice_to_service_error(idevice_connection_send(client->connection, data, size, (uint32_t*)&bytes)); + if (bytes <= 0) { + debug_info("ERROR: sending to device failed."); + } + if (sent) { + *sent = (uint32_t)bytes; + } + + return res; +} + +/** + * Receives data using the given service client with specified timeout. + * + * @param client The service client to use for receiving + * @param data Buffer that will be filled with the data received + * @param size Number of bytes to receive + * @param received Number of bytes received (can be NULL to ignore) + * @param timeout Maximum time in milliseconds to wait for data. + * + * @return SERVICE_E_SUCCESS on success, + * SERVICE_E_INVALID_ARG when one or more parameters are + * invalid, SERVICE_E_MUX_ERROR when a communication error + * occurs, or SERVICE_E_UNKNOWN_ERROR when an unspecified + * error occurs. + */ +service_error_t service_receive_with_timeout(service_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) +{ + service_error_t res = SERVICE_E_UNKNOWN_ERROR; + int bytes = 0; + + if (!client || (client && !client->connection) || !data || (size == 0)) { + return SERVICE_E_INVALID_ARG; + } + + res = idevice_to_service_error(idevice_connection_receive_timeout(client->connection, data, size, (uint32_t*)&bytes, timeout)); + if (bytes <= 0) { + debug_info("could not read data"); + } + if (received) { + *received = (uint32_t)bytes; + } + + return res; +} + +/** + * Receives data using the given service client. + * + * @param client The service client to use for receiving + * @param data Buffer that will be filled with the data received + * @param size Number of bytes to receive + * @param received Number of bytes received (can be NULL to ignore) + * + * @return SERVICE_E_SUCCESS on success, + * SERVICE_E_INVALID_ARG when one or more parameters are + * invalid, SERVICE_E_MUX_ERROR when a communication error + * occurs, or SERVICE_E_UNKNOWN_ERROR when an unspecified + * error occurs. + */ +service_error_t service_receive(service_client_t client, char* data, uint32_t size, uint32_t *received) +{ + return service_receive_with_timeout(client, data, size, received, 10000); +} + +/** + * Enable SSL for the given service client. + * + * @param client The connected service client for that SSL should be enabled. + * + * @return SERVICE_E_SUCCESS on success, + * SERVICE_E_INVALID_ARG if client or client->connection is + * NULL, SERVICE_E_SSL_ERROR when SSL could not be enabled, + * or SERVICE_E_UNKNOWN_ERROR otherwise. + */ +service_error_t service_enable_ssl(service_client_t client) +{ + if (!client || !client->connection) + return SERVICE_E_INVALID_ARG; + return idevice_to_service_error(idevice_connection_enable_ssl(client->connection)); +} + +/** + * Disable SSL for the given service client. + * + * @param client The connected service client for that SSL should be disabled. + * + * @return SERVICE_E_SUCCESS on success, + * SERVICE_E_INVALID_ARG if client or client->connection is + * NULL, or SERVICE_E_UNKNOWN_ERROR otherwise. + */ +service_error_t service_disable_ssl(service_client_t client) +{ + if (!client || !client->connection) + return SERVICE_E_INVALID_ARG; + return idevice_to_service_error(idevice_connection_disable_ssl(client->connection)); +} + diff --git a/src/service.h b/src/service.h new file mode 100644 index 0000000..e498ed7 --- /dev/null +++ b/src/service.h @@ -0,0 +1,59 @@ +/* + * service.h + * Definitions for the generic service implementation + * + * Copyright (c) 2013 Nikias Bassen, 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 + */ +#ifndef SERVICE_H +#define SERVICE_H + +#include "libimobiledevice/lockdown.h" +#include "idevice.h" + +/* Error Codes */ +#define SERVICE_E_SUCCESS 0 +#define SERVICE_E_INVALID_ARG -1 +#define SERVICE_E_MUX_ERROR -3 +#define SERVICE_E_SSL_ERROR -4 +#define SERVICE_E_START_SERVICE_ERROR -5 +#define SERVICE_E_UNKNOWN_ERROR -256 + +struct service_client_private { + idevice_connection_t connection; +}; + +typedef struct service_client_private *service_client_t; + +typedef int16_t service_error_t; + +/* creation and destruction */ +service_error_t service_client_new(idevice_t device, lockdownd_service_descriptor_t service, service_client_t *client); +service_error_t service_start_service(idevice_t device, const char* service_name, service_client_t *client); +service_error_t service_client_free(service_client_t client); + +/* sending */ +service_error_t service_send(service_client_t client, const char *data, uint32_t size, uint32_t *sent); + +/* receiving */ +service_error_t service_receive_with_timeout(service_client_t client, char *data, uint32_t size, uint32_t *received, unsigned int timeout); +service_error_t service_receive(service_client_t client, char *data, uint32_t size, uint32_t *received); + +/* misc */ +service_error_t service_enable_ssl(service_client_t client); +service_error_t service_disable_ssl(service_client_t client); + +#endif -- cgit v1.1-32-gdbae