summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/NotificationProxy.c404
-rw-r--r--src/NotificationProxy.h16
-rw-r--r--src/iphone.c5
-rw-r--r--src/iphone.h2
-rw-r--r--src/lockdown.c13
-rw-r--r--src/usbmux.c31
6 files changed, 359 insertions, 112 deletions
diff --git a/src/NotificationProxy.c b/src/NotificationProxy.c
index bf837bf..d8bcc34 100644
--- a/src/NotificationProxy.c
+++ b/src/NotificationProxy.c
@@ -21,10 +21,16 @@
#include <string.h>
#include <stdio.h>
+#include <arpa/inet.h>
#include <plist/plist.h>
#include "NotificationProxy.h"
#include "utils.h"
+struct np_thread {
+ iphone_np_client_t client;
+ iphone_np_notify_cb_t cbfunc;
+};
+
/** Locks an NP client, done for thread safety stuff.
*
* @param client The NP
@@ -45,6 +51,54 @@ static void np_unlock(iphone_np_client_t client)
g_mutex_unlock(client->mutex);
}
+/**
+ * Sends an xml plist to the device using the connection specified in client.
+ * This function is only used internally.
+ *
+ * @param client NP to send data to
+ * @param dict plist to send
+ *
+ * @return IPHONE_E_SUCCESS or an error code.
+ */
+static iphone_error_t np_plist_send(iphone_np_client_t client, plist_t dict)
+{
+ char *XML_content = NULL;
+ uint32_t length = 0;
+ uint32_t nlen = 0;
+ int bytes = 0;
+ iphone_error_t res = IPHONE_E_UNKNOWN_ERROR;
+
+ if (!client || !dict) {
+ return IPHONE_E_INVALID_ARG;
+ }
+
+ plist_to_xml(dict, &XML_content, &length);
+
+ if (!XML_content || length == 0) {
+ return IPHONE_E_PLIST_ERROR;
+ }
+
+ nlen = htonl(length);
+ iphone_mux_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes);
+ if (bytes == sizeof(nlen)) {
+ iphone_mux_send(client->connection, XML_content, length, (uint32_t*)&bytes);
+ if (bytes > 0) {
+ if ((uint32_t)bytes == length) {
+ res = IPHONE_E_SUCCESS;
+ } else {
+ log_debug_msg("%s: ERROR: Could not send all data (%d of %d)!\n", __func__, bytes, length);
+ }
+ }
+ }
+ if (bytes <= 0) {
+ log_debug_msg("%s: ERROR: sending to device failed.\n", __func__);
+ }
+
+ free(XML_content);
+
+ return res;
+}
+
/** Makes a connection to the NP service on the phone.
*
* @param phone The iPhone to connect on.
@@ -53,7 +107,7 @@ static void np_unlock(iphone_np_client_t client)
*
* @return A handle to the newly-connected client or NULL upon error.
*/
-iphone_error_t iphone_np_new_client(iphone_device_t device, int src_port, int dst_port, iphone_np_client_t * client)
+iphone_error_t iphone_np_new_client ( iphone_device_t device, int src_port, int dst_port, iphone_np_client_t *client )
{
int ret = IPHONE_E_SUCCESS;
@@ -75,6 +129,8 @@ iphone_error_t iphone_np_new_client(iphone_device_t device, int src_port, int ds
client_loc->mutex = g_mutex_new();
+ client_loc->notifier = NULL;
+
*client = client_loc;
return IPHONE_E_SUCCESS;
}
@@ -83,91 +139,97 @@ iphone_error_t iphone_np_new_client(iphone_device_t device, int src_port, int ds
*
* @param client The client to disconnect.
*/
-iphone_error_t iphone_np_free_client(iphone_np_client_t client)
+iphone_error_t iphone_np_free_client ( iphone_np_client_t client )
{
- if (!client || !client->connection)
+ if (!client)
return IPHONE_E_INVALID_ARG;
- iphone_mux_free_client(client->connection);
+ if (client->connection) {
+ iphone_mux_free_client(client->connection);
+ client->connection = NULL;
+ if (client->notifier) {
+ log_debug_msg("joining np callback\n");
+ g_thread_join(client->notifier);
+ }
+ }
+ if (client->mutex) {
+ g_mutex_free(client->mutex);
+ }
free(client);
+
return IPHONE_E_SUCCESS;
}
-/** Sends a notification to the NP client.
+/** Sends a notification to the device's Notification Proxy.
*
* notification messages seen so far:
* com.apple.itunes-mobdev.syncWillStart
* com.apple.itunes-mobdev.syncDidStart
*
* @param client The client to send to
- * @param notification The notification Message
+ * @param notification The notification message to send
*/
-iphone_error_t iphone_np_post_notification(iphone_np_client_t client, const char *notification)
+iphone_error_t iphone_np_post_notification( iphone_np_client_t client, const char *notification )
{
- char *XML_content = NULL;
- uint32_t length = 0;
- int bytes = 0;
- iphone_error_t ret;
- unsigned char sndbuf[4096];
- int sndlen = 0;
- int nlen = 0;
- plist_t dict = NULL;
-
if (!client || !notification) {
return IPHONE_E_INVALID_ARG;
}
np_lock(client);
- dict = plist_new_dict();
+ plist_t dict = plist_new_dict();
plist_add_sub_key_el(dict, "Command");
plist_add_sub_string_el(dict, "PostNotification");
plist_add_sub_key_el(dict, "Name");
plist_add_sub_string_el(dict, notification);
- plist_to_xml(dict, &XML_content, &length);
-
- nlen = htonl(length);
-
- memcpy(sndbuf + sndlen, &nlen, 4);
- sndlen += 4;
- memcpy(sndbuf + sndlen, XML_content, length);
- sndlen += length;
+ iphone_error_t res = np_plist_send(client, dict);
plist_free(dict);
- dict = NULL;
- free(XML_content);
- XML_content = NULL;
dict = plist_new_dict();
plist_add_sub_key_el(dict, "Command");
plist_add_sub_string_el(dict, "Shutdown");
- plist_to_xml(dict, &XML_content, &length);
- nlen = htonl(length);
+ res = np_plist_send(client, dict);
+ plist_free(dict);
- memcpy(sndbuf + sndlen, &nlen, 4);
- sndlen += 4;
+ if (res != IPHONE_E_SUCCESS) {
+ log_debug_msg("%s: Error sending XML plist to device!\n", __func__);
+ }
- memcpy(sndbuf + sndlen, XML_content, length);
- sndlen += length;
+ np_unlock(client);
+ return res;
+}
- plist_free(dict);
- dict = NULL;
- free(XML_content);
- XML_content = NULL;
+/** Notifies the iphone to send a notification on the specified event.
+ *
+ * @param client The client to send to
+ * @param notification The notifications that should be observed.
+ */
+iphone_error_t iphone_np_observe_notification( iphone_np_client_t client, const char *notification )
+{
+ if (!client || !notification) {
+ return IPHONE_E_INVALID_ARG;
+ }
+ np_lock(client);
- log_debug_buffer(sndbuf, sndlen);
+ plist_t dict = plist_new_dict();
+ plist_add_sub_key_el(dict, "Command");
+ plist_add_sub_string_el(dict, "ObserveNotification");
+ plist_add_sub_key_el(dict, "Name");
+ plist_add_sub_string_el(dict, notification);
- iphone_mux_send(client->connection, sndbuf, sndlen, &bytes);
- if (bytes <= 0) {
- np_unlock(client);
- return bytes;
+ iphone_error_t res = np_plist_send(client, dict);
+ if (res != IPHONE_E_SUCCESS) {
+ log_debug_msg("%s: Error sending XML plist to device!\n", __func__);
}
+ plist_free(dict);
np_unlock(client);
- return bytes;
+ return res;
}
-/** Notifies the iphone to send a notification on certain events.
+
+/** Notifies the iphone to send a notification on specified events.
*
* observation messages seen so far:
* com.apple.itunes-client.syncCancelRequest
@@ -181,85 +243,217 @@ iphone_error_t iphone_np_post_notification(iphone_np_client_t client, const char
* com.apple.mobile.application_uninstalled
*
* @param client The client to send to
+ * @param notification_spec Specification of the notifications that should be
+ * observed. This is expected to be an array of const char* that MUST have a
+ * terminating NULL entry. However this parameter can be NULL; in this case,
+ * the default set of notifications will be used.
*/
-iphone_error_t iphone_np_observe_notification(iphone_np_client_t client)
+iphone_error_t iphone_np_observe_notifications( iphone_np_client_t client, const char **notification_spec )
{
- plist_t dict = NULL;
- char *XML_content = NULL;
- uint32_t length = 0;
- int bytes = 0;
- iphone_error_t ret;
- unsigned char sndbuf[4096];
- int sndlen = 0;
- int nlen = 0;
int i = 0;
- const char *notifications[10] = {
- "com.apple.itunes-client.syncCancelRequest",
- "com.apple.itunes-client.syncSuspendRequest",
- "com.apple.itunes-client.syncResumeRequest",
- "com.apple.mobile.lockdown.phone_number_changed",
- "com.apple.mobile.lockdown.device_name_changed",
- "com.apple.springboard.attemptactivation",
- "com.apple.mobile.data_sync.domain_changed",
- "com.apple.mobile.application_installed",
- "com.apple.mobile.application_uninstalled",
- NULL
- };
-
- sndlen = 0;
+ iphone_error_t res = IPHONE_E_UNKNOWN_ERROR;
+ const char **notifications = notification_spec;
if (!client) {
return IPHONE_E_INVALID_ARG;
}
- np_lock(client);
+
+ if (!notifications) {
+ notifications = np_default_notifications;
+ }
while (notifications[i]) {
+ res = iphone_np_observe_notification(client, notifications[i]);
+ if (res != IPHONE_E_SUCCESS) {
+ break;
+ }
+ i++;
+ }
+
+ return res;
+}
- dict = plist_new_dict();
- plist_add_sub_key_el(dict, "Command");
- plist_add_sub_string_el(dict, "ObserveNotification");
- plist_add_sub_key_el(dict, "Name");
- plist_add_sub_string_el(dict, notifications[i++]);
- plist_to_xml(dict, &XML_content, &length);
-
- nlen = htonl(length);
- memcpy(sndbuf + sndlen, &nlen, 4);
- sndlen += 4;
- memcpy(sndbuf + sndlen, XML_content, length);
- sndlen += length;
-
- plist_free(dict);
- dict = NULL;
- free(XML_content);
- XML_content = NULL;
+/**
+ * Checks if a notification has been sent.
+ *
+ * @param client NP to get a notification from
+ * @param notification Pointer to a buffer that will be allocated and filled
+ * with the notification that has been received.
+ *
+ * @return IPHONE_E_SUCCESS if a notification has been received,
+ * IPHONE_E_TIMEOUT if nothing has been received,
+ * or an error value if an error occured.
+ *
+ * @note You probably want to check out iphone_np_set_notify_callback
+ * @see iphone_np_set_notify_callback
+ */
+iphone_error_t iphone_np_get_notification( iphone_np_client_t client, char **notification )
+{
+ uint32_t bytes = 0;
+ iphone_error_t res;
+ uint32_t pktlen = 0;
+ char *XML_content = NULL;
+ plist_t dict = NULL;
+
+ if (!client || !client->connection || *notification) {
+ return IPHONE_E_INVALID_ARG;
}
- dict = plist_new_dict();
- plist_add_sub_key_el(dict, "Command");
- plist_add_sub_string_el(dict, "Shutdown");
- plist_to_xml(dict, &XML_content, &length);
+ np_lock(client);
- nlen = htonl(length);
+ iphone_mux_recv_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, 500);
+ log_debug_msg("NotificationProxy: initial read=%i\n", bytes);
+ if (bytes < 4) {
+ log_debug_msg("NotificationProxy: no notification received!\n");
+ res = IPHONE_E_TIMEOUT;
+ } else {
+ if ((char)pktlen == 0) {
+ pktlen = ntohl(pktlen);
+ log_debug_msg("NotificationProxy: %d bytes following\n", pktlen);
+ XML_content = (char*)malloc(pktlen);
+ log_debug_msg("pointer %p\n", XML_content);
+
+ iphone_mux_recv_timeout(client->connection, XML_content, pktlen, &bytes, 1000);
+ if (bytes <= 0) {
+ res = IPHONE_E_UNKNOWN_ERROR;
+ } else {
+ log_debug_msg("NotificationProxy: received data:\n");
+ log_debug_buffer(XML_content, pktlen);
+
+ plist_from_xml(XML_content, bytes, &dict);
+ if (!dict) {
+ np_unlock(client);
+ return IPHONE_E_PLIST_ERROR;
+ }
+
+ plist_t cmd_key_node = plist_find_node_by_key(dict, "Command");
+ plist_t cmd_value_node = plist_get_next_sibling(cmd_key_node);
+ char *cmd_value = NULL;
+
+ 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, "RelayNotification")) {
+ plist_t name_key_node = plist_get_next_sibling(cmd_value_node);
+ plist_t name_value_node = plist_get_next_sibling(name_key_node);
+
+ char *name_key = NULL;
+ char *name_value = NULL;
+
+ if (plist_get_node_type(name_key_node) == PLIST_KEY) {
+ plist_get_key_val(name_key_node, &name_key);
+ }
+ if (plist_get_node_type(name_value_node) == PLIST_STRING) {
+ plist_get_string_val(name_value_node, &name_value);
+ }
+
+ res = IPHONE_E_PLIST_ERROR;
+ if (name_key && name_value && !strcmp(name_key, "Name")) {
+ *notification = name_value;
+ log_debug_msg("%s: got notification %s\n", __func__, name_value);
+ res = IPHONE_E_SUCCESS;
+ }
+ free(name_key);
+ } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
+ log_debug_msg("%s: ERROR: NotificationProxy died!\n", __func__);
+ res = IPHONE_E_UNKNOWN_ERROR;
+ } else if (cmd_value) {
+ log_debug_msg("%d: unknown NotificationProxy command '%s' received!\n", __func__);
+ res = IPHONE_E_UNKNOWN_ERROR;
+ } else {
+ res = IPHONE_E_PLIST_ERROR;
+ }
+ if (cmd_value) {
+ free(cmd_value);
+ }
+ plist_free(dict);
+ dict = NULL;
+ free(XML_content);
+ XML_content = NULL;
+ }
+ } else {
+ res = IPHONE_E_UNKNOWN_ERROR;
+ }
+ }
- memcpy(sndbuf + sndlen, &nlen, 4);
- sndlen += 4;
+ np_unlock(client);
- memcpy(sndbuf + sndlen, XML_content, length);
- sndlen += length;
+ return res;
+}
- plist_free(dict);
- dict = NULL;
- free(XML_content);
- XML_content = NULL;
+/**
+ * Internally used thread function.
+ */
+gpointer iphone_np_notifier( gpointer arg )
+{
+ char *notification = NULL;
+ struct np_thread *npt = (struct np_thread*)arg;
+
+ if (!npt) return NULL;
+
+ log_debug_msg("%s: starting callback.\n", __func__);
+ while (npt->client->connection) {
+ iphone_np_get_notification(npt->client, &notification);
+ if (notification) {
+ npt->cbfunc(notification);
+ free(notification);
+ notification = NULL;
+ }
+ sleep(1);
+ }
+ if (npt) {
+ free(npt);
+ }
- log_debug_buffer(sndbuf, sndlen);
+ return NULL;
+}
- iphone_mux_send(client->connection, sndbuf, sndlen, &bytes);
- if (bytes <= 0) {
- np_unlock(client);
- return bytes;
+/**
+ * This function allows an application to define a callback function that will
+ * be called when a notification has been received.
+ * It will start a thread that polls for notifications and calls the callback
+ * function if a notification has been received.
+ *
+ * @param client the NP client
+ * @param notify_cb pointer to a callback function or NULL to de-register a
+ * previously set callback function
+ *
+ * @return IPHONE_E_SUCCESS when the callback was successfully registered,
+ * or an error value when an error occured.
+ */
+iphone_error_t iphone_np_set_notify_callback( iphone_np_client_t client, iphone_np_notify_cb_t notify_cb )
+{
+ if (!client) {
+ return IPHONE_E_INVALID_ARG;
+ }
+ iphone_error_t res = IPHONE_E_UNKNOWN_ERROR;
+
+ np_lock(client);
+ if (client->notifier) {
+ log_debug_msg("%s: callback already set, removing\n");
+ iphone_umux_client_t conn = client->connection;
+ client->connection = NULL;
+ g_thread_join(client->notifier);
+ client->notifier = NULL;
+ client->connection = conn;
}
+ if (notify_cb) {
+ struct np_thread *npt = (struct np_thread*)malloc(sizeof(struct np_thread));
+ if (npt) {
+ npt->client = client;
+ npt->cbfunc = notify_cb;
+
+ client->notifier = g_thread_create(iphone_np_notifier, npt, TRUE, NULL);
+ if (client->notifier) {
+ res = IPHONE_E_SUCCESS;
+ }
+ }
+ } else {
+ log_debug_msg("%s: no callback set\n", __func__);
+ }
np_unlock(client);
- return bytes;
+
+ return res;
}
diff --git a/src/NotificationProxy.h b/src/NotificationProxy.h
index 7b4b48d..3552b79 100644
--- a/src/NotificationProxy.h
+++ b/src/NotificationProxy.h
@@ -27,4 +27,20 @@
struct iphone_np_client_int {
iphone_umux_client_t connection;
GMutex *mutex;
+ GThread *notifier;
};
+
+static const char *np_default_notifications[10] = {
+ NP_SYNC_SUSPEND_REQUEST,
+ NP_SYNC_RESUME_REQUEST,
+ NP_PHONE_NUMBER_CHANGED,
+ NP_SYNC_CANCEL_REQUEST,
+ NP_DEVICE_NAME_CHANGED,
+ NP_ATTEMPTACTIVATION,
+ NP_DS_DOMAIN_CHANGED,
+ NP_APP_INSTALLED,
+ NP_APP_UNINSTALLED,
+ NULL
+};
+
+gpointer iphone_np_notifier( gpointer arg );
diff --git a/src/iphone.c b/src/iphone.c
index 3c3034e..9dd3c07 100644
--- a/src/iphone.c
+++ b/src/iphone.c
@@ -288,10 +288,11 @@ int send_to_phone(iphone_device_t phone, char *data, int datalen)
* @param phone The iPhone to receive data from
* @param data Where to put data read
* @param datalen How much data to read in
+ * @param timeout How many milliseconds to wait for data
*
* @return How many bytes were read in, or -1 on error.
*/
-int recv_from_phone(iphone_device_t phone, char *data, int datalen)
+int recv_from_phone(iphone_device_t phone, char *data, int datalen, int timeout)
{
if (!phone)
return -1;
@@ -301,7 +302,7 @@ int recv_from_phone(iphone_device_t phone, char *data, int datalen)
return -1;
log_debug_msg("recv_from_phone(): attempting to receive %i bytes\n", datalen);
- bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, 3500);
+ bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, timeout);
if (bytes < 0) {
log_debug_msg("recv_from_phone(): libusb gave me the error %d: %s (%s)\n", bytes, usb_strerror(),
strerror(-bytes));
diff --git a/src/iphone.h b/src/iphone.h
index 222a1be..15515e3 100644
--- a/src/iphone.h
+++ b/src/iphone.h
@@ -41,5 +41,5 @@ struct iphone_device_int {
// Function definitions
int send_to_phone(iphone_device_t phone, char *data, int datalen);
-int recv_from_phone(iphone_device_t phone, char *data, int datalen);
+int recv_from_phone(iphone_device_t phone, char *data, int datalen, int timeout);
#endif
diff --git a/src/lockdown.c b/src/lockdown.c
index c017cdf..5ade79a 100644
--- a/src/lockdown.c
+++ b/src/lockdown.c
@@ -472,6 +472,19 @@ iphone_error_t lockdownd_get_device_public_key(iphone_lckd_client_t control, gnu
return lockdownd_generic_get_value(control, "Key", "DevicePublicKey", public_key);
}
+/** Askes for the device's name.
+ *
+ * @return IPHONE_E_SUCCESS on succes or an error value < 0 on failure.
+ */
+iphone_error_t lockdownd_get_device_name(iphone_lckd_client_t control, char **device_name)
+{
+ gnutls_datum_t temp = { NULL, 0 };
+ iphone_error_t res = lockdownd_generic_get_value(control, "Key", "DeviceName", &temp);
+ log_debug_msg("%s: %s\n", __func__, temp.data);
+ *device_name = (char*)temp.data;
+ return res;
+}
+
/** Completes the entire lockdownd handshake.
*
* @param phone The iPhone
diff --git a/src/usbmux.c b/src/usbmux.c
index 22ce588..7d74b4b 100644
--- a/src/usbmux.c
+++ b/src/usbmux.c
@@ -143,7 +143,7 @@ iphone_error_t iphone_mux_new_client(iphone_device_t device, uint16_t src_port,
if (send_to_phone(device, (char *) new_connection->header, sizeof(usbmux_tcp_header)) >= 0) {
usbmux_tcp_header *response;
response = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header));
- bytes = recv_from_phone(device, (char *) response, sizeof(*response));
+ bytes = recv_from_phone(device, (char *) response, sizeof(*response), 3500);
if (response->tcp_flags != 0x12) {
free(response);
return IPHONE_E_UNKNOWN_ERROR;
@@ -268,10 +268,13 @@ iphone_error_t iphone_mux_send(iphone_umux_client_t client, const char *data, ui
* @param connection The connection to receive data on.
* @param data Where to put the data we receive.
* @param datalen How much data to read.
+ * @param recv_bytes Pointer to a uint32_t that will be set
+ * to the number of bytes received.
+ * @param timeout How many milliseconds to wait for data.
*
- * @return How many bytes were read, or -1 if something bad happens.
+ * @return IPHONE_E_SUCCESS on success, or and error value.
*/
-iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes)
+iphone_error_t iphone_mux_recv_timeout(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes, int timeout)
{
if (!client || !data || datalen == 0 || !recv_bytes)
@@ -323,7 +326,7 @@ iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t
buffer = (char *) malloc(sizeof(char) * 131072); // make sure we get enough ;)
// See #3.
- bytes = recv_from_phone(client->phone, buffer, 131072);
+ bytes = recv_from_phone(client->phone, buffer, 131072, timeout);
if (bytes < 28) {
free(buffer);
log_debug_msg("mux_recv: Did not even get the header.\n");
@@ -385,3 +388,23 @@ iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t
log_debug_msg("mux_recv: Heisenbug: bytes and datalen not matching up\n");
return IPHONE_E_UNKNOWN_ERROR;
}
+
+/**
+ * This function is just like 'iphone_mux_recv_timeout' but you do not need
+ * to specify a timeout. It simply calls iphone_mux_recv_timeout with a
+ * timeout value of 3500 milliseconds.
+ *
+ * @param connection The connection to receive data on.
+ * @param data Where to put the data we receive.
+ * @param datalen How much data to read.
+ * @param recv_bytes Pointer to a uint32_t that will be set
+ * to the number of bytes received.
+ *
+ * @return The return value of iphone_mux_recv_timeout.
+ *
+ * @see iphone_mux_recv_timeout
+ */
+iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes)
+{
+ return iphone_mux_recv_timeout(client, data, datalen, recv_bytes, 3500);
+}