summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am89
-rw-r--r--src/afc.c1161
-rw-r--r--src/afc.h122
-rw-r--r--src/bt_packet_logger.c231
-rw-r--r--src/bt_packet_logger.h37
-rw-r--r--src/companion_proxy.c380
-rw-r--r--src/companion_proxy.h35
-rw-r--r--src/debug.c164
-rw-r--r--src/debug.h52
-rw-r--r--src/debugserver.c657
-rw-r--r--src/debugserver.h44
-rw-r--r--src/device_link_service.c129
-rw-r--r--src/device_link_service.h42
-rw-r--r--src/diagnostics_relay.c467
-rw-r--r--src/diagnostics_relay.h33
-rw-r--r--src/file_relay.c103
-rw-r--r--src/file_relay.h25
-rw-r--r--src/heartbeat.c147
-rw-r--r--src/heartbeat.h33
-rw-r--r--src/house_arrest.c179
-rw-r--r--src/house_arrest.h12
-rw-r--r--src/idevice.c1434
-rw-r--r--src/idevice.h74
-rw-r--r--src/installation_proxy.c1232
-rw-r--r--src/installation_proxy.h20
-rw-r--r--src/libimobiledevice-1.0.pc.in12
-rw-r--r--src/lockdown-cu.c1193
-rw-r--r--src/lockdown.c1440
-rw-r--r--src/lockdown.h20
-rw-r--r--src/misagent.c290
-rw-r--r--src/misagent.h34
-rw-r--r--src/mobile_image_mounter.c514
-rw-r--r--src/mobile_image_mounter.h15
-rw-r--r--src/mobileactivation.c314
-rw-r--r--src/mobileactivation.h33
-rw-r--r--src/mobilebackup.c227
-rw-r--r--src/mobilebackup.h17
-rw-r--r--src/mobilebackup2.c386
-rw-r--r--src/mobilebackup2.h33
-rw-r--r--src/mobilesync.c286
-rw-r--r--src/mobilesync.h16
-rw-r--r--src/notification_proxy.c253
-rw-r--r--src/notification_proxy.h19
-rw-r--r--src/preboard.c256
-rw-r--r--src/preboard.h35
-rw-r--r--src/property_list_service.c301
-rw-r--r--src/property_list_service.h48
-rw-r--r--src/restore.c290
-rw-r--r--src/restore.h7
-rw-r--r--src/reverse_proxy.c810
-rw-r--r--src/reverse_proxy.h51
-rw-r--r--src/sbservices.c219
-rw-r--r--src/sbservices.h15
-rw-r--r--src/screenshotr.c80
-rw-r--r--src/screenshotr.h14
-rw-r--r--src/service.c207
-rw-r--r--src/service.h32
-rw-r--r--src/syslog_relay.c248
-rw-r--r--src/syslog_relay.h37
-rw-r--r--src/userpref.c605
-rw-r--r--src/userpref.h46
-rw-r--r--src/webinspector.c263
-rw-r--r--src/webinspector.h35
63 files changed, 10923 insertions, 4680 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 70dc895..58cf07c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,24 +1,69 @@
1AM_CPPFLAGS = -I$(top_srcdir)/include 1AM_CPPFLAGS = \
2 -I$(top_srcdir)/include \
3 -I$(top_srcdir)/3rd_party/libsrp6a-sha512 \
4 -I$(top_srcdir)/3rd_party/ed25519 \
5 -I$(top_srcdir)
2 6
3AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusbmuxd_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(libplist_CFLAGS) $(LFS_CFLAGS) 7AM_CFLAGS = \
4AM_LDFLAGS = $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS) $(libplist_LIBS) $(libusbmuxd_LIBS) $(libgcrypt_LIBS) 8 $(GLOBAL_CFLAGS) \
9 $(ssl_lib_CFLAGS) \
10 $(LFS_CFLAGS) \
11 $(PTHREAD_CFLAGS) \
12 $(libusbmuxd_CFLAGS) \
13 $(libplist_CFLAGS) \
14 $(limd_glue_CFLAGS)
5 15
6lib_LTLIBRARIES = libimobiledevice.la 16AM_LDFLAGS = \
7libimobiledevice_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_SO_VERSION) -no-undefined 17 $(ssl_lib_LIBS) \
8libimobiledevice_la_SOURCES = idevice.c idevice.h \ 18 $(PTHREAD_LIBS) \
9 debug.c debug.h\ 19 $(libusbmuxd_LIBS) \
10 userpref.c userpref.h\ 20 $(libplist_LIBS) \
11 property_list_service.c property_list_service.h\ 21 $(limd_glue_LIBS)
12 device_link_service.c device_link_service.h\ 22
13 lockdown.c lockdown.h\ 23lib_LTLIBRARIES = libimobiledevice-1.0.la
14 afc.c afc.h\ 24libimobiledevice_1_0_la_LIBADD = $(top_builddir)/common/libinternalcommon.la
15 file_relay.c file_relay.h\ 25if HAVE_WIRELESS_PAIRING
16 notification_proxy.c notification_proxy.h\ 26libimobiledevice_1_0_la_LIBADD += $(top_builddir)/3rd_party/ed25519/libed25519.la $(top_builddir)/3rd_party/libsrp6a-sha512/libsrp6a-sha512.la
17 installation_proxy.c installation_proxy.h\ 27endif
18 sbservices.c sbservices.h\ 28libimobiledevice_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_SO_VERSION) -no-undefined
19 mobile_image_mounter.c mobile_image_mounter.h\ 29if DARWIN
20 screenshotr.c screenshotr.h\ 30libimobiledevice_1_0_la_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration
21 mobilesync.c mobilesync.h\ 31endif
22 mobilebackup.c mobilebackup.h\ 32libimobiledevice_1_0_la_SOURCES = \
23 house_arrest.c house_arrest.h\ 33 idevice.c idevice.h \
24 restore.c restore.h 34 service.c service.h \
35 property_list_service.c property_list_service.h \
36 device_link_service.c device_link_service.h \
37 lockdown.c lockdown.h \
38 lockdown-cu.c \
39 afc.c afc.h \
40 file_relay.c file_relay.h \
41 notification_proxy.c notification_proxy.h \
42 installation_proxy.c installation_proxy.h \
43 sbservices.c sbservices.h \
44 mobile_image_mounter.c mobile_image_mounter.h \
45 screenshotr.c screenshotr.h \
46 mobilesync.c mobilesync.h \
47 mobilebackup.c mobilebackup.h \
48 house_arrest.c house_arrest.h \
49 mobilebackup2.c mobilebackup2.h \
50 misagent.c misagent.h \
51 restore.c restore.h \
52 diagnostics_relay.c diagnostics_relay.h \
53 heartbeat.c heartbeat.h \
54 debugserver.c debugserver.h \
55 webinspector.c webinspector.h \
56 mobileactivation.c mobileactivation.h \
57 preboard.c preboard.h \
58 companion_proxy.c companion_proxy.h \
59 reverse_proxy.c reverse_proxy.h \
60 syslog_relay.c syslog_relay.h \
61 bt_packet_logger.c bt_packet_logger.h
62
63if WIN32
64libimobiledevice_1_0_la_LDFLAGS += -avoid-version -static-libgcc
65libimobiledevice_1_0_la_LIBADD += -lole32 -lws2_32 -lgdi32
66endif
67
68pkgconfigdir = $(libdir)/pkgconfig
69pkgconfig_DATA = libimobiledevice-1.0.pc
diff --git a/src/afc.c b/src/afc.c
index 989d0ec..1b4070b 100644
--- a/src/afc.c
+++ b/src/afc.c
@@ -1,308 +1,243 @@
1/* 1/*
2 * afc.c 2 * afc.c
3 * Contains functions for the built-in AFC client. 3 * Contains functions for the built-in AFC client.
4 * 4 *
5 * Copyright (c) 2014 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2009-2014 Nikias Bassen. All Rights Reserved.
5 * Copyright (c) 2008 Zach C. All Rights Reserved. 7 * Copyright (c) 2008 Zach C. All Rights Reserved.
6 * 8 *
7 * This library is free software; you can redistribute it and/or 9 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 10 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 11 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 12 * version 2.1 of the License, or (at your option) any later version.
11 * 13 *
12 * This library is distributed in the hope that it will be useful, 14 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 17 * Lesser General Public License for more details.
16 * 18 *
17 * You should have received a copy of the GNU Lesser General Public 19 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 20 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 22 */
21 23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
22#include <stdio.h> 27#include <stdio.h>
23#include <stdlib.h> 28#include <stdlib.h>
24#include <unistd.h> 29#include <unistd.h>
25#include <string.h> 30#include <string.h>
26 31
27#include "afc.h"
28#include "idevice.h" 32#include "idevice.h"
29#include "debug.h" 33#include "afc.h"
30 34#include "common/debug.h"
31/** The maximum size an AFC data packet can be */ 35#include "endianness.h"
32static const int MAXIMUM_PACKET_SIZE = (2 << 15);
33 36
34/** 37/**
35 * Locks an AFC client, done for thread safety stuff 38 * Locks an AFC client, done for thread safety stuff
36 * 39 *
37 * @param client The AFC client connection to lock 40 * @param client The AFC client connection to lock
38 */ 41 */
39static void afc_lock(afc_client_t client) 42static void afc_lock(afc_client_t client)
40{ 43{
41 debug_info("Locked"); 44 debug_info("Locked");
42 g_mutex_lock(client->mutex); 45 mutex_lock(&client->mutex);
43} 46}
44 47
45/** 48/**
46 * Unlocks an AFC client, done for thread safety stuff. 49 * Unlocks an AFC client, done for thread safety stuff.
47 * 50 *
48 * @param client The AFC 51 * @param client The AFC
49 */ 52 */
50static void afc_unlock(afc_client_t client) 53static void afc_unlock(afc_client_t client)
51{ 54{
52 debug_info("Unlocked"); 55 debug_info("Unlocked");
53 g_mutex_unlock(client->mutex); 56 mutex_unlock(&client->mutex);
54} 57}
55 58
56/** 59/**
57 * Makes a connection to the AFC service on the device using the given 60 * Makes a connection to the AFC service on the device using the given
58 * connection. 61 * connection.
59 * 62 *
60 * @param connection An idevice_connection_t that must have been previously 63 * @param service_client A connected service client
61 * connected using idevice_connect(). Note that this connection will
62 * not be closed by calling afc_client_free().
63 * @param client Pointer that will be set to a newly allocated afc_client_t 64 * @param client Pointer that will be set to a newly allocated afc_client_t
64 * upon successful return. 65 * upon successful return.
65 * 66 *
66 * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if connection is 67 * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if connection is
67 * invalid, or AFC_E_NO_MEM if there is a memory allocation problem. 68 * invalid, or AFC_E_NO_MEM if there is a memory allocation problem.
68 */ 69 */
69 70
70afc_error_t afc_client_new_from_connection(idevice_connection_t connection, afc_client_t *client) 71afc_error_t afc_client_new_with_service_client(service_client_t service_client, afc_client_t *client)
71{ 72{
72 /* makes sure thread environment is available */ 73 if (!service_client)
73 if (!g_thread_supported())
74 g_thread_init(NULL);
75
76 if (!connection)
77 return AFC_E_INVALID_ARG; 74 return AFC_E_INVALID_ARG;
78 75
79 afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private)); 76 afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private));
80 client_loc->connection = connection; 77 client_loc->parent = service_client;
81 client_loc->own_connection = 0; 78 client_loc->free_parent = 0;
82 79
83 /* allocate a packet */ 80 /* allocate a packet */
84 client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); 81 client_loc->packet_extra = 1024;
82 client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket) + client_loc->packet_extra);
85 if (!client_loc->afc_packet) { 83 if (!client_loc->afc_packet) {
86 free(client_loc); 84 free(client_loc);
87 return AFC_E_NO_MEM; 85 return AFC_E_NO_MEM;
88 } 86 }
89
90 client_loc->afc_packet->packet_num = 0; 87 client_loc->afc_packet->packet_num = 0;
91 client_loc->afc_packet->entire_length = 0; 88 client_loc->afc_packet->entire_length = 0;
92 client_loc->afc_packet->this_length = 0; 89 client_loc->afc_packet->this_length = 0;
93 memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN); 90 memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN);
94 client_loc->file_handle = 0; 91 mutex_init(&client_loc->mutex);
95 client_loc->lock = 0;
96 client_loc->mutex = g_mutex_new();
97 92
98 *client = client_loc; 93 *client = client_loc;
99 return AFC_E_SUCCESS; 94 return AFC_E_SUCCESS;
100} 95}
101 96
102/** 97afc_error_t afc_client_new(idevice_t device, lockdownd_service_descriptor_t service, afc_client_t * client)
103 * Makes a connection to the AFC service on the device.
104 * This function calls afc_client_new_from_connection() after creating
105 * a connection to the specified device and port.
106 *
107 * @see afc_client_new_from_connection
108 *
109 * @param device The device to connect to.
110 * @param port The destination port.
111 * @param client Pointer that will be set to a newly allocated afc_client_t
112 * upon successful return.
113 *
114 * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if device or port is
115 * invalid, AFC_E_MUX_ERROR if the connection cannot be established,
116 * or AFC_E_NO_MEM if there is a memory allocation problem.
117 */
118afc_error_t afc_client_new(idevice_t device, uint16_t port, afc_client_t * client)
119{ 98{
120 /* makes sure thread environment is available */ 99 if (!device || !service || service->port == 0)
121 if (!g_thread_supported())
122 g_thread_init(NULL);
123
124 if (!device || port==0)
125 return AFC_E_INVALID_ARG; 100 return AFC_E_INVALID_ARG;
126 101
127 /* attempt connection */ 102 service_client_t parent = NULL;
128 idevice_connection_t connection = NULL; 103 if (service_client_new(device, service, &parent) != SERVICE_E_SUCCESS) {
129 if (idevice_connect(device, port, &connection) != IDEVICE_E_SUCCESS) {
130 return AFC_E_MUX_ERROR; 104 return AFC_E_MUX_ERROR;
131 } 105 }
132 106
133 afc_error_t err = afc_client_new_from_connection(connection, client); 107 afc_error_t err = afc_client_new_with_service_client(parent, client);
134 if (err != AFC_E_SUCCESS) { 108 if (err != AFC_E_SUCCESS) {
135 idevice_disconnect(connection); 109 service_client_free(parent);
136 } else { 110 } else {
137 (*client)->own_connection = 1; 111 (*client)->free_parent = 1;
138 } 112 }
139 return err; 113 return err;
140} 114}
141 115
142/** 116afc_error_t afc_client_start_service(idevice_t device, afc_client_t * client, const char* label)
143 * Frees up an AFC client. If the connection was created by the 117{
144 * client itself, the connection will be closed. 118 afc_error_t err = AFC_E_UNKNOWN_ERROR;
145 * 119 service_client_factory_start_service(device, AFC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(afc_client_new), &err);
146 * @param client The client to free. 120 return err;
147 */ 121}
122
148afc_error_t afc_client_free(afc_client_t client) 123afc_error_t afc_client_free(afc_client_t client)
149{ 124{
150 if (!client || !client->afc_packet) 125 if (!client || !client->afc_packet)
151 return AFC_E_INVALID_ARG; 126 return AFC_E_INVALID_ARG;
152 127
153 if (client->own_connection && client->connection) { 128 if (client->free_parent && client->parent) {
154 idevice_disconnect(client->connection); 129 service_client_free(client->parent);
155 client->connection = NULL; 130 client->parent = NULL;
156 } 131 }
157 free(client->afc_packet); 132 free(client->afc_packet);
158 if (client->mutex) { 133 mutex_destroy(&client->mutex);
159 g_mutex_free(client->mutex);
160 }
161 free(client); 134 free(client);
162 return AFC_E_SUCCESS; 135 return AFC_E_SUCCESS;
163} 136}
164 137
165/** 138/**
166 * Dispatches an AFC packet over a client. 139 * Dispatches an AFC packet over a client.
167 * 140 *
168 * @param client The client to send data through. 141 * @param client The client to send data through.
169 * @param data The data to send. 142 * @param operation The operation to perform.
170 * @param length The length to send. 143 * @param data The data to send together with the header.
171 * @param bytes_sent The number of bytes actually sent. 144 * @param data_length The length of the data to send with the header.
145 * @param payload The data to send after the header has been sent.
146 * @param payload_length The length of data to send after the header.
147 * @param bytes_sent The total number of bytes actually sent.
172 * 148 *
173 * @return AFC_E_SUCCESS on success or an AFC_E_* error value. 149 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
174 *
175 * @warning set client->afc_packet->this_length and
176 * client->afc_packet->entire_length to 0 before calling this. The
177 * reason is that if you set them to different values, it indicates
178 * you want to send the data as two packets.
179 */ 150 */
180static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, uint32_t length, uint32_t *bytes_sent) 151static afc_error_t afc_dispatch_packet(afc_client_t client, uint64_t operation, uint32_t data_length, const char* payload, uint32_t payload_length, uint32_t *bytes_sent)
181{ 152{
182 uint32_t offset = 0;
183 uint32_t sent = 0; 153 uint32_t sent = 0;
184 154
185 if (!client || !client->connection || !client->afc_packet) 155 if (!client || !client->parent || !client->afc_packet)
186 return AFC_E_INVALID_ARG; 156 return AFC_E_INVALID_ARG;
187 157
188 *bytes_sent = 0; 158 *bytes_sent = 0;
189 159
190 if (!data || !length) 160 if (!payload || !payload_length)
191 length = 0; 161 payload_length = 0;
192 162
193 client->afc_packet->packet_num++; 163 client->afc_packet->packet_num++;
194 if (!client->afc_packet->entire_length) { 164 client->afc_packet->operation = operation;
195 client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length : sizeof(AFCPacket); 165 client->afc_packet->entire_length = sizeof(AFCPacket) + data_length + payload_length;
196 client->afc_packet->this_length = client->afc_packet->entire_length; 166 client->afc_packet->this_length = sizeof(AFCPacket) + data_length;
197 } 167
198 if (!client->afc_packet->this_length) { 168 debug_info("packet length = %i", client->afc_packet->this_length);
199 client->afc_packet->this_length = sizeof(AFCPacket); 169
200 } 170 /* send AFC packet header and data */
201 /* We want to send two segments; buffer+sizeof(AFCPacket) to this_length 171 AFCPacket_to_LE(client->afc_packet);
202 is the parameters and everything beyond that is the next packet. 172 debug_buffer((char*)client->afc_packet, sizeof(AFCPacket) + data_length);
203 (for writing) */ 173 sent = 0;
204 if (client->afc_packet->this_length != client->afc_packet->entire_length) { 174 service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket) + data_length, &sent);
205 offset = client->afc_packet->this_length - sizeof(AFCPacket); 175 AFCPacket_from_LE(client->afc_packet);
206 176 *bytes_sent += sent;
207 debug_info("Offset: %i", offset); 177 if (sent < sizeof(AFCPacket) + data_length) {
208 if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) {
209 debug_info("Length did not resemble what it was supposed to based on packet");
210 debug_info("length minus offset: %i", length - offset);
211 debug_info("rest of packet: %i\n", client->afc_packet->entire_length - client->afc_packet->this_length);
212 return AFC_E_INTERNAL_ERROR;
213 }
214
215 /* send AFC packet header */
216 AFCPacket_to_LE(client->afc_packet);
217 sent = 0;
218 idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent);
219 AFCPacket_from_LE(client->afc_packet);
220 if (sent == 0) {
221 /* FIXME: should this be handled as success?! */
222 return AFC_E_SUCCESS;
223 }
224 *bytes_sent += sent;
225
226 /* send AFC packet data */
227 sent = 0;
228 idevice_connection_send(client->connection, data, offset, &sent);
229 if (sent == 0) {
230 return AFC_E_SUCCESS;
231 }
232 *bytes_sent += sent;
233
234 debug_info("sent the first now go with the second");
235 debug_info("Length: %i", length - offset);
236 debug_info("Buffer: ");
237 debug_buffer(data + offset, length - offset);
238
239 sent = 0;
240 idevice_connection_send(client->connection, data + offset, length - offset, &sent);
241
242 *bytes_sent = sent;
243 return AFC_E_SUCCESS; 178 return AFC_E_SUCCESS;
244 } else { 179 }
245 debug_info("doin things the old way");
246 debug_info("packet length = %i", client->afc_packet->this_length);
247
248 debug_buffer((char*)client->afc_packet, sizeof(AFCPacket));
249 180
250 /* send AFC packet header */ 181 sent = 0;
251 AFCPacket_to_LE(client->afc_packet); 182 if (payload_length > 0) {
252 sent = 0; 183 if (payload_length > 256) {
253 idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); 184 debug_info("packet payload follows (256/%u)", payload_length);
254 AFCPacket_from_LE(client->afc_packet); 185 debug_buffer(payload, 256);
255 if (sent == 0) { 186 } else {
256 return AFC_E_SUCCESS; 187 debug_info("packet payload follows");
257 } 188 debug_buffer(payload, payload_length);
258 *bytes_sent += sent;
259 /* send AFC packet data (if there's data to send) */
260 if (length > 0) {
261 debug_info("packet data follows");
262
263 debug_buffer(data, length);
264 idevice_connection_send(client->connection, data, length, &sent);
265 *bytes_sent += sent;
266 } 189 }
190 service_send(client->parent, payload, payload_length, &sent);
191 }
192 *bytes_sent += sent;
193 if (sent < payload_length) {
267 return AFC_E_SUCCESS; 194 return AFC_E_SUCCESS;
268 } 195 }
269 return AFC_E_INTERNAL_ERROR; 196
197 return AFC_E_SUCCESS;
270} 198}
271 199
272/** 200/**
273 * Receives data through an AFC client and sets a variable to the received data. 201 * Receives data through an AFC client and sets a variable to the received data.
274 * 202 *
275 * @param client The client to receive data on. 203 * @param client The client to receive data on.
276 * @param dump_here The char* to point to the newly-received data. 204 * @param bytes The char* to point to the newly-received data.
277 * @param bytes_recv How much data was received. 205 * @param bytes_recv How much data was received.
278 * 206 *
279 * @return AFC_E_SUCCESS on success or an AFC_E_* error value. 207 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
280 */ 208 */
281static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint32_t *bytes_recv) 209static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t *bytes_recv)
282{ 210{
283 AFCPacket header; 211 AFCPacket header;
284 uint32_t entire_len = 0; 212 uint32_t entire_len = 0;
285 uint32_t this_len = 0; 213 uint32_t this_len = 0;
286 uint32_t current_count = 0; 214 uint32_t current_count = 0;
287 uint64_t param1 = -1; 215 uint64_t param1 = -1;
216 char *buf = NULL;
217 uint32_t recv_len = 0;
288 218
289 *bytes_recv = 0; 219 if (bytes_recv) {
220 *bytes_recv = 0;
221 }
222 if (bytes) {
223 *bytes = NULL;
224 }
290 225
291 /* first, read the AFC header */ 226 /* first, read the AFC header */
292 idevice_connection_receive(client->connection, (char*)&header, sizeof(AFCPacket), bytes_recv); 227 service_receive(client->parent, (char*)&header, sizeof(AFCPacket), &recv_len);
293 AFCPacket_from_LE(&header); 228 AFCPacket_from_LE(&header);
294 if (*bytes_recv == 0) { 229 if (recv_len == 0) {
295 debug_info("Just didn't get enough."); 230 debug_info("Just didn't get enough.");
296 *dump_here = NULL;
297 return AFC_E_MUX_ERROR; 231 return AFC_E_MUX_ERROR;
298 } else if (*bytes_recv < sizeof(AFCPacket)) { 232 }
233
234 if (recv_len < sizeof(AFCPacket)) {
299 debug_info("Did not even get the AFCPacket header"); 235 debug_info("Did not even get the AFCPacket header");
300 *dump_here = NULL;
301 return AFC_E_MUX_ERROR; 236 return AFC_E_MUX_ERROR;
302 } 237 }
303 238
304 /* check if it's a valid AFC header */ 239 /* check if it's a valid AFC header */
305 if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN)) { 240 if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN) != 0) {
306 debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!"); 241 debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!");
307 } 242 }
308 243
@@ -310,25 +245,21 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
310 if (header.packet_num != client->afc_packet->packet_num) { 245 if (header.packet_num != client->afc_packet->packet_num) {
311 /* otherwise print a warning but do not abort */ 246 /* otherwise print a warning but do not abort */
312 debug_info("ERROR: Unexpected packet number (%lld != %lld) aborting.", header.packet_num, client->afc_packet->packet_num); 247 debug_info("ERROR: Unexpected packet number (%lld != %lld) aborting.", header.packet_num, client->afc_packet->packet_num);
313 *dump_here = NULL;
314 return AFC_E_OP_HEADER_INVALID; 248 return AFC_E_OP_HEADER_INVALID;
315 } 249 }
316 250
317 /* then, read the attached packet */ 251 /* then, read the attached packet */
318 if (header.this_length < sizeof(AFCPacket)) { 252 if (header.this_length < sizeof(AFCPacket)) {
319 debug_info("Invalid AFCPacket header received!"); 253 debug_info("Invalid AFCPacket header received!");
320 *dump_here = NULL;
321 return AFC_E_OP_HEADER_INVALID; 254 return AFC_E_OP_HEADER_INVALID;
322 } else if ((header.this_length == header.entire_length) 255 }
323 && header.entire_length == sizeof(AFCPacket)) { 256 if ((header.this_length == header.entire_length)
257 && header.entire_length == sizeof(AFCPacket)) {
324 debug_info("Empty AFCPacket received!"); 258 debug_info("Empty AFCPacket received!");
325 *dump_here = NULL;
326 *bytes_recv = 0;
327 if (header.operation == AFC_OP_DATA) { 259 if (header.operation == AFC_OP_DATA) {
328 return AFC_E_SUCCESS; 260 return AFC_E_SUCCESS;
329 } else {
330 return AFC_E_IO_ERROR;
331 } 261 }
262 return AFC_E_IO_ERROR;
332 } 263 }
333 264
334 debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation); 265 debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation);
@@ -336,22 +267,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
336 entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); 267 entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket);
337 this_len = (uint32_t)header.this_length - sizeof(AFCPacket); 268 this_len = (uint32_t)header.this_length - sizeof(AFCPacket);
338 269
339 /* this is here as a check (perhaps a different upper limit is good?) */ 270 buf = (char*)malloc(entire_len);
340 if (entire_len > (uint32_t)MAXIMUM_PACKET_SIZE) {
341 fprintf(stderr, "%s: entire_len is larger than MAXIMUM_PACKET_SIZE, (%d > %d)!", __func__, entire_len, MAXIMUM_PACKET_SIZE);
342 }
343
344 *dump_here = (char*)malloc(entire_len);
345 if (this_len > 0) { 271 if (this_len > 0) {
346 idevice_connection_receive(client->connection, *dump_here, this_len, bytes_recv); 272 recv_len = 0;
347 if (*bytes_recv <= 0) { 273 service_receive(client->parent, buf, this_len, &recv_len);
348 free(*dump_here); 274 if (recv_len <= 0) {
349 *dump_here = NULL; 275 free(buf);
350 debug_info("Did not get packet contents!"); 276 debug_info("Did not get packet contents!");
351 return AFC_E_NOT_ENOUGH_DATA; 277 return AFC_E_NOT_ENOUGH_DATA;
352 } else if (*bytes_recv < this_len) { 278 }
353 free(*dump_here); 279 if (recv_len < this_len) {
354 *dump_here = NULL; 280 free(buf);
355 debug_info("Could not receive this_len=%d bytes", this_len); 281 debug_info("Could not receive this_len=%d bytes", this_len);
356 return AFC_E_NOT_ENOUGH_DATA; 282 return AFC_E_NOT_ENOUGH_DATA;
357 } 283 }
@@ -361,12 +287,13 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
361 287
362 if (entire_len > this_len) { 288 if (entire_len > this_len) {
363 while (current_count < entire_len) { 289 while (current_count < entire_len) {
364 idevice_connection_receive(client->connection, (*dump_here)+current_count, entire_len - current_count, bytes_recv); 290 recv_len = 0;
365 if (*bytes_recv <= 0) { 291 service_receive(client->parent, buf+current_count, entire_len - current_count, &recv_len);
366 debug_info("Error receiving data (recv returned %d)", *bytes_recv); 292 if (recv_len <= 0) {
293 debug_info("Error receiving data (recv returned %d)", recv_len);
367 break; 294 break;
368 } 295 }
369 current_count += *bytes_recv; 296 current_count += recv_len;
370 } 297 }
371 if (current_count < entire_len) { 298 if (current_count < entire_len) {
372 debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len); 299 debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len);
@@ -374,12 +301,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
374 } 301 }
375 302
376 if (current_count >= sizeof(uint64_t)) { 303 if (current_count >= sizeof(uint64_t)) {
377 param1 = GUINT64_FROM_LE(*(uint64_t*)(*dump_here)); 304 param1 = le64toh(*(uint64_t*)(buf));
378 } 305 }
379 306
380 debug_info("packet data size = %i", current_count); 307 debug_info("packet data size = %i", current_count);
381 debug_info("packet data follows"); 308 if (current_count > 256) {
382 debug_buffer(*dump_here, current_count); 309 debug_info("packet data follows (256/%u)", current_count);
310 debug_buffer(buf, 256);
311 } else {
312 debug_info("packet data follows");
313 debug_buffer(buf, current_count);
314 }
383 315
384 /* check operation types */ 316 /* check operation types */
385 if (header.operation == AFC_OP_STATUS) { 317 if (header.operation == AFC_OP_STATUS) {
@@ -389,8 +321,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
389 if (param1 != AFC_E_SUCCESS) { 321 if (param1 != AFC_E_SUCCESS) {
390 /* error status */ 322 /* error status */
391 /* free buffer */ 323 /* free buffer */
392 free(*dump_here); 324 free(buf);
393 *dump_here = NULL;
394 return (afc_error_t)param1; 325 return (afc_error_t)param1;
395 } 326 }
396 } else if (header.operation == AFC_OP_DATA) { 327 } else if (header.operation == AFC_OP_DATA) {
@@ -404,16 +335,22 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
404 debug_info("got a tell response, position=%lld", param1); 335 debug_info("got a tell response, position=%lld", param1);
405 } else { 336 } else {
406 /* unknown operation code received */ 337 /* unknown operation code received */
407 free(*dump_here); 338 free(buf);
408 *dump_here = NULL;
409 *bytes_recv = 0;
410 339
411 debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); 340 debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1);
341#ifndef WIN32
412 fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1); 342 fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1);
343#endif
413 344
414 return AFC_E_OP_NOT_SUPPORTED; 345 return AFC_E_OP_NOT_SUPPORTED;
415 } 346 }
416 347
348 if (bytes) {
349 *bytes = buf;
350 } else {
351 free(buf);
352 }
353
417 *bytes_recv = current_count; 354 *bytes_recv = current_count;
418 return AFC_E_SUCCESS; 355 return AFC_E_SUCCESS;
419} 356}
@@ -421,7 +358,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
421/** 358/**
422 * Returns counts of null characters within a string. 359 * Returns counts of null characters within a string.
423 */ 360 */
424static uint32_t count_nullspaces(char *string, uint32_t number) 361static uint32_t count_nullspaces(const char *string, uint32_t number)
425{ 362{
426 uint32_t i = 0, nulls = 0; 363 uint32_t i = 0, nulls = 0;
427 364
@@ -462,32 +399,42 @@ static char **make_strings_list(char *tokens, uint32_t length)
462 return list; 399 return list;
463} 400}
464 401
465/** 402static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len)
466 * Gets a directory listing of the directory requested. 403{
467 * 404 if (data_len > client->packet_extra) {
468 * @param client The client to get a directory listing from. 405 client->packet_extra = (data_len & ~8) + 8;
469 * @param dir The directory to list. (must be a fully-qualified path) 406 AFCPacket* newpkt = (AFCPacket*)realloc(client->afc_packet, sizeof(AFCPacket) + client->packet_extra);
470 * @param list A char list of files in that directory, terminated by an empty 407 if (!newpkt) {
471 * string or NULL if there was an error. 408 return -1;
472 * 409 }
473 * @return AFC_E_SUCCESS on success or an AFC_E_* error value. 410 client->afc_packet = newpkt;
474 */ 411 }
475afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***list) 412 return 0;
413}
414
415#define AFC_PACKET_DATA_PTR ((char*)client->afc_packet + sizeof(AFCPacket))
416
417afc_error_t afc_read_directory(afc_client_t client, const char *path, char ***directory_information)
476{ 418{
477 uint32_t bytes = 0; 419 uint32_t bytes = 0;
478 char *data = NULL, **list_loc = NULL; 420 char *data = NULL, **list_loc = NULL;
479 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 421 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
480 422
481 if (!client || !dir || !list || (list && *list)) 423 if (!client || !path || !directory_information || (directory_information && *directory_information))
482 return AFC_E_INVALID_ARG; 424 return AFC_E_INVALID_ARG;
483 425
484 afc_lock(client); 426 afc_lock(client);
485 427
428 uint32_t data_len = (uint32_t)strlen(path)+1;
429 if (_afc_check_packet_buffer(client, data_len) < 0) {
430 afc_unlock(client);
431 debug_info("Failed to realloc packet buffer");
432 return AFC_E_NO_MEM;
433 }
434
486 /* Send the command */ 435 /* Send the command */
487 client->afc_packet->operation = AFC_OP_READ_DIR; 436 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
488 client->afc_packet->entire_length = 0; 437 ret = afc_dispatch_packet(client, AFC_OP_READ_DIR, data_len, NULL, 0, &bytes);
489 client->afc_packet->this_length = 0;
490 ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes);
491 if (ret != AFC_E_SUCCESS) { 438 if (ret != AFC_E_SUCCESS) {
492 afc_unlock(client); 439 afc_unlock(client);
493 return AFC_E_NOT_ENOUGH_DATA; 440 return AFC_E_NOT_ENOUGH_DATA;
@@ -495,6 +442,8 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis
495 /* Receive the data */ 442 /* Receive the data */
496 ret = afc_receive_data(client, &data, &bytes); 443 ret = afc_receive_data(client, &data, &bytes);
497 if (ret != AFC_E_SUCCESS) { 444 if (ret != AFC_E_SUCCESS) {
445 if (data)
446 free(data);
498 afc_unlock(client); 447 afc_unlock(client);
499 return ret; 448 return ret;
500 } 449 }
@@ -504,37 +453,24 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis
504 free(data); 453 free(data);
505 454
506 afc_unlock(client); 455 afc_unlock(client);
507 *list = list_loc; 456 *directory_information = list_loc;
508 457
509 return ret; 458 return ret;
510} 459}
511 460
512/** 461afc_error_t afc_get_device_info(afc_client_t client, char ***device_information)
513 * Get device info for a client connection to phone. The device information
514 * returned is the device model as well as the free space, the total capacity
515 * and blocksize on the accessed disk partition.
516 *
517 * @param client The client to get device info for.
518 * @param infos A char ** list of parameters as given by AFC or NULL if there
519 * was an error.
520 *
521 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
522 */
523afc_error_t afc_get_device_info(afc_client_t client, char ***infos)
524{ 462{
525 uint32_t bytes = 0; 463 uint32_t bytes = 0;
526 char *data = NULL, **list = NULL; 464 char *data = NULL, **list = NULL;
527 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 465 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
528 466
529 if (!client || !infos) 467 if (!client || !device_information)
530 return AFC_E_INVALID_ARG; 468 return AFC_E_INVALID_ARG;
531 469
532 afc_lock(client); 470 afc_lock(client);
533 471
534 /* Send the command */ 472 /* Send the command */
535 client->afc_packet->operation = AFC_OP_GET_DEVINFO; 473 ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes);
536 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
537 ret = afc_dispatch_packet(client, NULL, 0, &bytes);
538 if (ret != AFC_E_SUCCESS) { 474 if (ret != AFC_E_SUCCESS) {
539 afc_unlock(client); 475 afc_unlock(client);
540 return AFC_E_NOT_ENOUGH_DATA; 476 return AFC_E_NOT_ENOUGH_DATA;
@@ -542,6 +478,8 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos)
542 /* Receive the data */ 478 /* Receive the data */
543 ret = afc_receive_data(client, &data, &bytes); 479 ret = afc_receive_data(client, &data, &bytes);
544 if (ret != AFC_E_SUCCESS) { 480 if (ret != AFC_E_SUCCESS) {
481 if (data)
482 free(data);
545 afc_unlock(client); 483 afc_unlock(client);
546 return ret; 484 return ret;
547 } 485 }
@@ -552,22 +490,11 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos)
552 490
553 afc_unlock(client); 491 afc_unlock(client);
554 492
555 *infos = list; 493 *device_information = list;
556 494
557 return ret; 495 return ret;
558} 496}
559 497
560/**
561 * Get a specific key of the device info list for a client connection.
562 * Known key values are: Model, FSTotalBytes, FSFreeBytes and FSBlockSize.
563 * This is a helper function for afc_get_device_info().
564 *
565 * @param client The client to get device info for.
566 * @param key The key to get the value of.
567 * @param value The value for the key if successful or NULL otherwise.
568 *
569 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
570 */
571afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) 498afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value)
572{ 499{
573 afc_error_t ret = AFC_E_INTERNAL_ERROR; 500 afc_error_t ret = AFC_E_INTERNAL_ERROR;
@@ -587,43 +514,40 @@ afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char *
587 break; 514 break;
588 } 515 }
589 } 516 }
590 517 for (ptr = kvps; *ptr; ptr++) {
591 g_strfreev(kvps); 518 free(*ptr);
519 }
520 free(kvps);
592 521
593 return ret; 522 return ret;
594} 523}
595 524
596/**
597 * Deletes a file or directory.
598 *
599 * @param client The client to use.
600 * @param path The path to delete. (must be a fully-qualified path)
601 *
602 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
603 */
604afc_error_t afc_remove_path(afc_client_t client, const char *path) 525afc_error_t afc_remove_path(afc_client_t client, const char *path)
605{ 526{
606 char *response = NULL;
607 uint32_t bytes = 0; 527 uint32_t bytes = 0;
608 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 528 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
609 529
610 if (!client || !path || !client->afc_packet || !client->connection) 530 if (!client || !path || !client->afc_packet || !client->parent)
611 return AFC_E_INVALID_ARG; 531 return AFC_E_INVALID_ARG;
612 532
613 afc_lock(client); 533 afc_lock(client);
614 534
535 uint32_t data_len = (uint32_t)strlen(path)+1;
536 if (_afc_check_packet_buffer(client, data_len) < 0) {
537 afc_unlock(client);
538 debug_info("Failed to realloc packet buffer");
539 return AFC_E_NO_MEM;
540 }
541
615 /* Send command */ 542 /* Send command */
616 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 543 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
617 client->afc_packet->operation = AFC_OP_REMOVE_PATH; 544 ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH, data_len, NULL, 0, &bytes);
618 ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes);
619 if (ret != AFC_E_SUCCESS) { 545 if (ret != AFC_E_SUCCESS) {
620 afc_unlock(client); 546 afc_unlock(client);
621 return AFC_E_NOT_ENOUGH_DATA; 547 return AFC_E_NOT_ENOUGH_DATA;
622 } 548 }
623 /* Receive response */ 549 /* Receive response */
624 ret = afc_receive_data(client, &response, &bytes); 550 ret = afc_receive_data(client, NULL, &bytes);
625 if (response)
626 free(response);
627 551
628 /* special case; unknown error actually means directory not empty */ 552 /* special case; unknown error actually means directory not empty */
629 if (ret == AFC_E_UNKNOWN_ERROR) 553 if (ret == AFC_E_UNKNOWN_ERROR)
@@ -634,61 +558,45 @@ afc_error_t afc_remove_path(afc_client_t client, const char *path)
634 return ret; 558 return ret;
635} 559}
636 560
637/**
638 * Renames a file or directory on the phone.
639 *
640 * @param client The client to have rename.
641 * @param from The name to rename from. (must be a fully-qualified path)
642 * @param to The new name. (must also be a fully-qualified path)
643 *
644 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
645 */
646afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to) 561afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to)
647{ 562{
648 char *response = NULL; 563 if (!client || !from || !to || !client->afc_packet || !client->parent)
649 char *send = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t))); 564 return AFC_E_INVALID_ARG;
565
650 uint32_t bytes = 0; 566 uint32_t bytes = 0;
651 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 567 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
652 568
653 if (!client || !from || !to || !client->afc_packet || !client->connection) 569 size_t from_len = strlen(from);
654 return AFC_E_INVALID_ARG; 570 size_t to_len = strlen(to);
655 571
656 afc_lock(client); 572 afc_lock(client);
657 573
574 uint32_t data_len = (uint32_t)(from_len+1 + to_len+1);
575 if (_afc_check_packet_buffer(client, data_len) < 0) {
576 afc_unlock(client);
577 debug_info("Failed to realloc packet buffer");
578 return AFC_E_NO_MEM;
579 }
580
658 /* Send command */ 581 /* Send command */
659 memcpy(send, from, strlen(from) + 1); 582 memcpy(AFC_PACKET_DATA_PTR, from, from_len+1);
660 memcpy(send + strlen(from) + 1, to, strlen(to) + 1); 583 memcpy(AFC_PACKET_DATA_PTR + from_len+1, to, to_len+1);
661 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 584 ret = afc_dispatch_packet(client, AFC_OP_RENAME_PATH, data_len, NULL, 0, &bytes);
662 client->afc_packet->operation = AFC_OP_RENAME_PATH;
663 ret = afc_dispatch_packet(client, send, strlen(to)+1 + strlen(from)+1, &bytes);
664 free(send);
665 if (ret != AFC_E_SUCCESS) { 585 if (ret != AFC_E_SUCCESS) {
666 afc_unlock(client); 586 afc_unlock(client);
667 return AFC_E_NOT_ENOUGH_DATA; 587 return AFC_E_NOT_ENOUGH_DATA;
668 } 588 }
669 /* Receive response */ 589 /* Receive response */
670 ret = afc_receive_data(client, &response, &bytes); 590 ret = afc_receive_data(client, NULL, &bytes);
671 if (response)
672 free(response);
673 591
674 afc_unlock(client); 592 afc_unlock(client);
675 593
676 return ret; 594 return ret;
677} 595}
678 596
679/** 597afc_error_t afc_make_directory(afc_client_t client, const char *path)
680 * Creates a directory on the phone.
681 *
682 * @param client The client to use to make a directory.
683 * @param dir The directory's path. (must be a fully-qualified path, I assume
684 * all other mkdir restrictions apply as well)
685 *
686 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
687 */
688afc_error_t afc_make_directory(afc_client_t client, const char *dir)
689{ 598{
690 uint32_t bytes = 0; 599 uint32_t bytes = 0;
691 char *response = NULL;
692 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 600 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
693 601
694 if (!client) 602 if (!client)
@@ -696,50 +604,51 @@ afc_error_t afc_make_directory(afc_client_t client, const char *dir)
696 604
697 afc_lock(client); 605 afc_lock(client);
698 606
607 uint32_t data_len = (uint32_t)strlen(path)+1;
608 if (_afc_check_packet_buffer(client, data_len) < 0) {
609 afc_unlock(client);
610 debug_info("Failed to realloc packet buffer");
611 return AFC_E_NO_MEM;
612 }
613
699 /* Send command */ 614 /* Send command */
700 client->afc_packet->operation = AFC_OP_MAKE_DIR; 615 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
701 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 616 ret = afc_dispatch_packet(client, AFC_OP_MAKE_DIR, data_len, NULL, 0, &bytes);
702 ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes);
703 if (ret != AFC_E_SUCCESS) { 617 if (ret != AFC_E_SUCCESS) {
704 afc_unlock(client); 618 afc_unlock(client);
705 return AFC_E_NOT_ENOUGH_DATA; 619 return AFC_E_NOT_ENOUGH_DATA;
706 } 620 }
707 /* Receive response */ 621 /* Receive response */
708 ret = afc_receive_data(client, &response, &bytes); 622 ret = afc_receive_data(client, NULL, &bytes);
709 if (response)
710 free(response);
711 623
712 afc_unlock(client); 624 afc_unlock(client);
713 625
714 return ret; 626 return ret;
715} 627}
716 628
717/** 629afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***file_information)
718 * Gets information about a specific file.
719 *
720 * @param client The client to use to get the information of the file.
721 * @param path The fully-qualified path to the file.
722 * @param infolist Pointer to a buffer that will be filled with a NULL-terminated
723 * list of strings with the file information.
724 * Set to NULL before calling this function.
725 *
726 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
727 */
728afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***infolist)
729{ 630{
730 char *received = NULL; 631 char *received = NULL;
731 uint32_t bytes = 0; 632 uint32_t bytes = 0;
732 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 633 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
733 634
734 if (!client || !path || !infolist) 635 if (!client || !path || !file_information)
735 return AFC_E_INVALID_ARG; 636 return AFC_E_INVALID_ARG;
736 637
737 afc_lock(client); 638 afc_lock(client);
738 639
640 uint32_t data_len = (uint32_t)strlen(path)+1;
641 if (_afc_check_packet_buffer(client, data_len) < 0) {
642 afc_unlock(client);
643 debug_info("Failed to realloc packet buffer");
644 return AFC_E_NO_MEM;
645 }
646
647 debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR);
648
739 /* Send command */ 649 /* Send command */
740 client->afc_packet->operation = AFC_OP_GET_FILE_INFO; 650 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
741 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 651 ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes);
742 ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes);
743 if (ret != AFC_E_SUCCESS) { 652 if (ret != AFC_E_SUCCESS) {
744 afc_unlock(client); 653 afc_unlock(client);
745 return AFC_E_NOT_ENOUGH_DATA; 654 return AFC_E_NOT_ENOUGH_DATA;
@@ -748,7 +657,7 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf
748 /* Receive data */ 657 /* Receive data */
749 ret = afc_receive_data(client, &received, &bytes); 658 ret = afc_receive_data(client, &received, &bytes);
750 if (received) { 659 if (received) {
751 *infolist = make_strings_list(received, bytes); 660 *file_information = make_strings_list(received, bytes);
752 free(received); 661 free(received);
753 } 662 }
754 663
@@ -757,51 +666,39 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf
757 return ret; 666 return ret;
758} 667}
759 668
760/** 669afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle)
761 * Opens a file on the phone.
762 *
763 * @param client The client to use to open the file.
764 * @param filename The file to open. (must be a fully-qualified path)
765 * @param file_mode The mode to use to open the file. Can be AFC_FILE_READ or
766 * AFC_FILE_WRITE; the former lets you read and write,
767 * however, and the second one will *create* the file,
768 * destroying anything previously there.
769 * @param handle Pointer to a uint64_t that will hold the handle of the file
770 *
771 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
772 */
773idevice_error_t
774afc_file_open(afc_client_t client, const char *filename,
775 afc_file_mode_t file_mode, uint64_t *handle)
776{ 670{
777 uint64_t file_mode_loc = GUINT64_TO_LE(file_mode); 671 if (!client || !client->parent || !client->afc_packet)
672 return AFC_E_INVALID_ARG;
673
674 //uint64_t file_mode_loc = htole64(file_mode);
778 uint32_t bytes = 0; 675 uint32_t bytes = 0;
779 char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1));
780 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 676 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
781 677
782 /* set handle to 0 so in case an error occurs, the handle is invalid */ 678 /* set handle to 0 so in case an error occurs, the handle is invalid */
783 *handle = 0; 679 *handle = 0;
784 680
785 if (!client || !client->connection || !client->afc_packet)
786 return AFC_E_INVALID_ARG;
787
788 afc_lock(client); 681 afc_lock(client);
789 682
790 /* Send command */ 683 uint32_t data_len = (uint32_t)(strlen(filename)+1 + 8);
791 memcpy(data, &file_mode_loc, 8); 684 if (_afc_check_packet_buffer(client, data_len) < 0) {
792 memcpy(data + 8, filename, strlen(filename)); 685 afc_unlock(client);
793 data[8 + strlen(filename)] = '\0'; 686 debug_info("Failed to realloc packet buffer");
794 client->afc_packet->operation = AFC_OP_FILE_OPEN; 687 return AFC_E_NO_MEM;
795 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 688 }
796 ret = afc_dispatch_packet(client, data, 8 + strlen(filename) + 1, &bytes);
797 free(data);
798 689
690 /* Send command */
691 //memcpy(AFC_PACKET_DATA_PTR, &file_mode_loc, 8);
692 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(file_mode);
693 memcpy(AFC_PACKET_DATA_PTR + 8, filename, data_len-8);
694 ret = afc_dispatch_packet(client, AFC_OP_FILE_OPEN, data_len, NULL, 0, &bytes);
799 if (ret != AFC_E_SUCCESS) { 695 if (ret != AFC_E_SUCCESS) {
800 debug_info("Didn't receive a response to the command"); 696 debug_info("Didn't receive a response to the command");
801 afc_unlock(client); 697 afc_unlock(client);
802 return AFC_E_NOT_ENOUGH_DATA; 698 return AFC_E_NOT_ENOUGH_DATA;
803 } 699 }
804 /* Receive the data */ 700 /* Receive the data */
701 char* data = NULL;
805 ret = afc_receive_data(client, &data, &bytes); 702 ret = afc_receive_data(client, &data, &bytes);
806 if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { 703 if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) {
807 afc_unlock(client); 704 afc_unlock(client);
@@ -811,6 +708,8 @@ afc_file_open(afc_client_t client, const char *filename,
811 free(data); 708 free(data);
812 return ret; 709 return ret;
813 } 710 }
711 /* in case memory was allocated but no data received or an error occurred */
712 free(data);
814 713
815 debug_info("Didn't get any further data"); 714 debug_info("Didn't get any further data");
816 715
@@ -819,156 +718,81 @@ afc_file_open(afc_client_t client, const char *filename,
819 return ret; 718 return ret;
820} 719}
821 720
822/** 721afc_error_t afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read)
823 * Attempts to the read the given number of bytes from the given file.
824 *
825 * @param client The relevant AFC client
826 * @param handle File handle of a previously opened file
827 * @param data The pointer to the memory region to store the read data
828 * @param length The number of bytes to read
829 * @param bytes_read The number of bytes actually read.
830 *
831 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
832 */
833idevice_error_t
834afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read)
835{ 722{
836 char *input = NULL; 723 char *input = NULL;
837 uint32_t current_count = 0, bytes_loc = 0; 724 uint32_t current_count = 0, bytes_loc = 0;
838 const uint32_t MAXIMUM_READ_SIZE = 1 << 16; 725 struct readinfo {
726 uint64_t handle;
727 uint64_t size;
728 };
839 afc_error_t ret = AFC_E_SUCCESS; 729 afc_error_t ret = AFC_E_SUCCESS;
840 730
841 if (!client || !client->afc_packet || !client->connection || handle == 0) 731 if (!client || !client->afc_packet || !client->parent || handle == 0)
842 return AFC_E_INVALID_ARG; 732 return AFC_E_INVALID_ARG;
843 debug_info("called for length %i", length); 733 debug_info("called for length %i", length);
844 734
735 //uint32_t data_len = 8 + 8;
736
845 afc_lock(client); 737 afc_lock(client);
846 738
847 /* Looping here to get around the maximum amount of data that 739 /* Send the read command */
848 afc_receive_data can handle */ 740 struct readinfo* readinfo = (struct readinfo*)(AFC_PACKET_DATA_PTR);
849 while (current_count < length) { 741 readinfo->handle = handle;
850 debug_info("current count is %i but length is %i", current_count, length); 742 readinfo->size = htole64(length);
851 743 ret = afc_dispatch_packet(client, AFC_OP_FILE_READ, sizeof(struct readinfo), NULL, 0, &bytes_loc);
852 /* Send the read command */ 744 if (ret != AFC_E_SUCCESS) {
853 AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); 745 afc_unlock(client);
854 packet->filehandle = handle; 746 return AFC_E_NOT_ENOUGH_DATA;
855 packet->size = GUINT64_TO_LE(((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE); 747 }
856 client->afc_packet->operation = AFC_OP_READ; 748 /* Receive the data */
857 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 749 ret = afc_receive_data(client, &input, &bytes_loc);
858 ret = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket), &bytes_loc); 750 debug_info("afc_receive_data returned error: %d", ret);
859 free(packet); 751 debug_info("bytes returned: %i", bytes_loc);
860 752 if (ret != AFC_E_SUCCESS) {
861 if (ret != AFC_E_SUCCESS) { 753 afc_unlock(client);
862 afc_unlock(client); 754 return ret;
863 return AFC_E_NOT_ENOUGH_DATA; 755 }
864 } 756 if (bytes_loc == 0) {
865 /* Receive the data */ 757 if (input)
866 ret = afc_receive_data(client, &input, &bytes_loc); 758 free(input);
867 debug_info("afc_receive_data returned error: %d", ret); 759 afc_unlock(client);
868 debug_info("bytes returned: %i", bytes_loc); 760 *bytes_read = current_count;
869 if (ret != AFC_E_SUCCESS) { 761 /* FIXME: check that's actually a success */
870 afc_unlock(client); 762 return ret;
871 return ret; 763 }
872 } else if (bytes_loc == 0) { 764 if (input) {
873 if (input) 765 debug_info("%d", bytes_loc);
874 free(input); 766 memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc);
875 afc_unlock(client); 767 free(input);
876 *bytes_read = current_count; 768 input = NULL;
877 /* FIXME: check that's actually a success */ 769 current_count += (bytes_loc > length) ? length : bytes_loc;
878 return ret;
879 } else {
880 if (input) {
881 debug_info("%d", bytes_loc);
882 memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc);
883 free(input);
884 input = NULL;
885 current_count += (bytes_loc > length) ? length : bytes_loc;
886 }
887 }
888 } 770 }
889 debug_info("returning current_count as %i", current_count);
890 771
891 afc_unlock(client); 772 afc_unlock(client);
892 *bytes_read = current_count; 773 *bytes_read = current_count;
893 return ret; 774 return ret;
894} 775}
895 776
896/** 777afc_error_t afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written)
897 * Writes a given number of bytes to a file.
898 *
899 * @param client The client to use to write to the file.
900 * @param handle File handle of previously opened file.
901 * @param data The data to write to the file.
902 * @param length How much data to write.
903 * @param bytes_written The number of bytes actually written to the file.
904 *
905 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
906 */
907idevice_error_t
908afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written)
909{ 778{
910 char *acknowledgement = NULL; 779 uint32_t current_count = 0;
911 const uint32_t MAXIMUM_WRITE_SIZE = 1 << 15;
912 uint32_t current_count = 0, i = 0;
913 uint32_t segments = (length / MAXIMUM_WRITE_SIZE);
914 uint32_t bytes_loc = 0; 780 uint32_t bytes_loc = 0;
915 char *out_buffer = NULL;
916 afc_error_t ret = AFC_E_SUCCESS; 781 afc_error_t ret = AFC_E_SUCCESS;
917 782
918 if (!client || !client->afc_packet || !client->connection || !bytes_written || (handle == 0)) 783 if (!client || !client->afc_packet || !client->parent || !bytes_written || (handle == 0))
919 return AFC_E_INVALID_ARG; 784 return AFC_E_INVALID_ARG;
920 785
786 uint32_t data_len = 8;
787
921 afc_lock(client); 788 afc_lock(client);
922 789
923 debug_info("Write length: %i", length); 790 debug_info("Write length: %i", length);
924 791
925 /* Divide the file into segments. */ 792 *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle;
926 for (i = 0; i < segments; i++) { 793 ret = afc_dispatch_packet(client, AFC_OP_FILE_WRITE, data_len, data, length, &bytes_loc);
927 /* Send the segment */
928 client->afc_packet->this_length = sizeof(AFCPacket) + 8;
929 client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE;
930 client->afc_packet->operation = AFC_OP_WRITE;
931 out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket));
932 memcpy(out_buffer, (char *)&handle, sizeof(uint64_t));
933 memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE);
934 ret = afc_dispatch_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8, &bytes_loc);
935 if (ret != AFC_E_SUCCESS) {
936 afc_unlock(client);
937 return AFC_E_NOT_ENOUGH_DATA;
938 }
939 free(out_buffer);
940 out_buffer = NULL;
941
942 current_count += bytes_loc;
943 ret = afc_receive_data(client, &acknowledgement, &bytes_loc);
944 if (ret != AFC_E_SUCCESS) {
945 afc_unlock(client);
946 return ret;
947 } else {
948 free(acknowledgement);
949 }
950 }
951
952 /* By this point, we should be at the end. i.e. the last segment that didn't
953 get sent in the for loop. This length is fine because it's always
954 sizeof(AFCPacket) + 8, but to be sure we do it again */
955 if (current_count == length) {
956 afc_unlock(client);
957 *bytes_written = current_count;
958 return ret;
959 }
960
961 client->afc_packet->this_length = sizeof(AFCPacket) + 8;
962 client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count);
963 client->afc_packet->operation = AFC_OP_WRITE;
964 out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket));
965 memcpy(out_buffer, (char *) &handle, sizeof(uint64_t));
966 memcpy(out_buffer + 8, data + current_count, (length - current_count));
967 ret = afc_dispatch_packet(client, out_buffer, (length - current_count) + 8, &bytes_loc);
968 free(out_buffer);
969 out_buffer = NULL;
970 794
971 current_count += bytes_loc; 795 current_count += bytes_loc - (sizeof(AFCPacket) + 8);
972 796
973 if (ret != AFC_E_SUCCESS) { 797 if (ret != AFC_E_SUCCESS) {
974 afc_unlock(client); 798 afc_unlock(client);
@@ -976,43 +800,32 @@ afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t
976 return AFC_E_SUCCESS; 800 return AFC_E_SUCCESS;
977 } 801 }
978 802
979 ret = afc_receive_data(client, &acknowledgement, &bytes_loc); 803 ret = afc_receive_data(client, NULL, &bytes_loc);
980 afc_unlock(client); 804 afc_unlock(client);
981 if (ret != AFC_E_SUCCESS) { 805 if (ret != AFC_E_SUCCESS) {
982 debug_info("uh oh?"); 806 debug_info("Failed to receive reply (%d)", ret);
983 } else {
984 free(acknowledgement);
985 } 807 }
986 *bytes_written = current_count; 808 *bytes_written = current_count;
987 return ret; 809 return ret;
988} 810}
989 811
990/**
991 * Closes a file on the phone.
992 *
993 * @param client The client to close the file with.
994 * @param handle File handle of a previously opened file.
995 */
996afc_error_t afc_file_close(afc_client_t client, uint64_t handle) 812afc_error_t afc_file_close(afc_client_t client, uint64_t handle)
997{ 813{
998 char *buffer = malloc(sizeof(char) * 8);
999 uint32_t bytes = 0; 814 uint32_t bytes = 0;
1000 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 815 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1001 816
1002 if (!client || (handle == 0)) 817 if (!client || (handle == 0))
1003 return AFC_E_INVALID_ARG; 818 return AFC_E_INVALID_ARG;
1004 819
820 uint32_t data_len = 8;
821
1005 afc_lock(client); 822 afc_lock(client);
1006 823
1007 debug_info("File handle %i", handle); 824 debug_info("File handle %i", handle);
1008 825
1009 /* Send command */ 826 /* Send command */
1010 memcpy(buffer, &handle, sizeof(uint64_t)); 827 *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle;
1011 client->afc_packet->operation = AFC_OP_FILE_CLOSE; 828 ret = afc_dispatch_packet(client, AFC_OP_FILE_CLOSE, data_len, NULL, 0, &bytes);
1012 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
1013 ret = afc_dispatch_packet(client, buffer, 8, &bytes);
1014 free(buffer);
1015 buffer = NULL;
1016 829
1017 if (ret != AFC_E_SUCCESS) { 830 if (ret != AFC_E_SUCCESS) {
1018 afc_unlock(client); 831 afc_unlock(client);
@@ -1020,32 +833,20 @@ afc_error_t afc_file_close(afc_client_t client, uint64_t handle)
1020 } 833 }
1021 834
1022 /* Receive the response */ 835 /* Receive the response */
1023 ret = afc_receive_data(client, &buffer, &bytes); 836 ret = afc_receive_data(client, NULL, &bytes);
1024 if (buffer)
1025 free(buffer);
1026 837
1027 afc_unlock(client); 838 afc_unlock(client);
1028 839
1029 return ret; 840 return ret;
1030} 841}
1031 842
1032/**
1033 * Locks or unlocks a file on the phone.
1034 *
1035 * makes use of flock on the device, see
1036 * http://developer.apple.com/documentation/Darwin/Reference/ManPages/man2/flock.2.html
1037 *
1038 * @param client The client to lock the file with.
1039 * @param handle File handle of a previously opened file.
1040 * @param operation the lock or unlock operation to perform, this is one of
1041 * AFC_LOCK_SH (shared lock), AFC_LOCK_EX (exclusive lock),
1042 * or AFC_LOCK_UN (unlock).
1043 */
1044afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) 843afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation)
1045{ 844{
1046 char *buffer = malloc(16);
1047 uint32_t bytes = 0; 845 uint32_t bytes = 0;
1048 uint64_t op = GUINT64_TO_LE(operation); 846 struct lockinfo {
847 uint64_t handle;
848 uint64_t op;
849 };
1049 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 850 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1050 851
1051 if (!client || (handle == 0)) 852 if (!client || (handle == 0))
@@ -1056,47 +857,31 @@ afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t op
1056 debug_info("file handle %i", handle); 857 debug_info("file handle %i", handle);
1057 858
1058 /* Send command */ 859 /* Send command */
1059 memcpy(buffer, &handle, sizeof(uint64_t)); 860 struct lockinfo* lockinfo = (struct lockinfo*)(AFC_PACKET_DATA_PTR);
1060 memcpy(buffer + 8, &op, 8); 861 lockinfo->handle = handle;
1061 862 lockinfo->op = htole64(operation);
1062 client->afc_packet->operation = AFC_OP_FILE_LOCK; 863 ret = afc_dispatch_packet(client, AFC_OP_FILE_LOCK, sizeof(struct lockinfo), NULL, 0, &bytes);
1063 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
1064 ret = afc_dispatch_packet(client, buffer, 16, &bytes);
1065 free(buffer);
1066 buffer = NULL;
1067
1068 if (ret != AFC_E_SUCCESS) { 864 if (ret != AFC_E_SUCCESS) {
1069 afc_unlock(client); 865 afc_unlock(client);
1070 debug_info("could not send lock command"); 866 debug_info("could not send lock command");
1071 return AFC_E_UNKNOWN_ERROR; 867 return AFC_E_UNKNOWN_ERROR;
1072 } 868 }
1073 /* Receive the response */ 869 /* Receive the response */
1074 ret = afc_receive_data(client, &buffer, &bytes); 870 ret = afc_receive_data(client, NULL, &bytes);
1075 if (buffer) { 871
1076 debug_buffer(buffer, bytes);
1077 free(buffer);
1078 }
1079 afc_unlock(client); 872 afc_unlock(client);
1080 873
1081 return ret; 874 return ret;
1082} 875}
1083 876
1084/**
1085 * Seeks to a given position of a pre-opened file on the phone.
1086 *
1087 * @param client The client to use to seek to the position.
1088 * @param handle File handle of a previously opened.
1089 * @param offset Seek offset.
1090 * @param whence Seeking direction, one of SEEK_SET, SEEK_CUR, or SEEK_END.
1091 *
1092 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
1093 */
1094afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) 877afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence)
1095{ 878{
1096 char *buffer = (char *) malloc(sizeof(char) * 24);
1097 int64_t offset_loc = (int64_t)GUINT64_TO_LE(offset);
1098 uint64_t whence_loc = GUINT64_TO_LE(whence);
1099 uint32_t bytes = 0; 879 uint32_t bytes = 0;
880 struct seekinfo {
881 uint64_t handle;
882 uint64_t whence;
883 int64_t offset;
884 };
1100 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 885 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1101 886
1102 if (!client || (handle == 0)) 887 if (!client || (handle == 0))
@@ -1105,57 +890,40 @@ afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset,
1105 afc_lock(client); 890 afc_lock(client);
1106 891
1107 /* Send the command */ 892 /* Send the command */
1108 memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ 893 struct seekinfo* seekinfo = (struct seekinfo*)(AFC_PACKET_DATA_PTR);
1109 memcpy(buffer + 8, &whence_loc, sizeof(uint64_t)); /* fromwhere */ 894 seekinfo->handle = handle;
1110 memcpy(buffer + 16, &offset_loc, sizeof(uint64_t)); /* offset */ 895 seekinfo->whence = htole64(whence);
1111 client->afc_packet->operation = AFC_OP_FILE_SEEK; 896 seekinfo->offset = (int64_t)htole64(offset);
1112 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 897 ret = afc_dispatch_packet(client, AFC_OP_FILE_SEEK, sizeof(struct seekinfo), NULL, 0, &bytes);
1113 ret = afc_dispatch_packet(client, buffer, 24, &bytes);
1114 free(buffer);
1115 buffer = NULL;
1116 898
1117 if (ret != AFC_E_SUCCESS) { 899 if (ret != AFC_E_SUCCESS) {
1118 afc_unlock(client); 900 afc_unlock(client);
1119 return AFC_E_NOT_ENOUGH_DATA; 901 return AFC_E_NOT_ENOUGH_DATA;
1120 } 902 }
1121 /* Receive response */ 903 /* Receive response */
1122 ret = afc_receive_data(client, &buffer, &bytes); 904 ret = afc_receive_data(client, NULL, &bytes);
1123 if (buffer)
1124 free(buffer);
1125 905
1126 afc_unlock(client); 906 afc_unlock(client);
1127 907
1128 return ret; 908 return ret;
1129} 909}
1130 910
1131/**
1132 * Returns current position in a pre-opened file on the phone.
1133 *
1134 * @param client The client to use.
1135 * @param handle File handle of a previously opened file.
1136 * @param position Position in bytes of indicator
1137 *
1138 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
1139 */
1140afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position) 911afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position)
1141{ 912{
1142 char *buffer = (char *) malloc(sizeof(char) * 8); 913 char *buffer = NULL;
1143 uint32_t bytes = 0; 914 uint32_t bytes = 0;
1144 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 915 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1145 916
1146 if (!client || (handle == 0)) 917 if (!client || (handle == 0))
1147 return AFC_E_INVALID_ARG; 918 return AFC_E_INVALID_ARG;
1148 919
920 uint32_t data_len = 8;
921
1149 afc_lock(client); 922 afc_lock(client);
1150 923
1151 /* Send the command */ 924 /* Send the command */
1152 memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ 925 *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle;
1153 client->afc_packet->operation = AFC_OP_FILE_TELL; 926 ret = afc_dispatch_packet(client, AFC_OP_FILE_TELL, data_len, NULL, 0, &bytes);
1154 client->afc_packet->this_length = client->afc_packet->entire_length = 0;
1155 ret = afc_dispatch_packet(client, buffer, 8, &bytes);
1156 free(buffer);
1157 buffer = NULL;
1158
1159 if (ret != AFC_E_SUCCESS) { 927 if (ret != AFC_E_SUCCESS) {
1160 afc_unlock(client); 928 afc_unlock(client);
1161 return AFC_E_NOT_ENOUGH_DATA; 929 return AFC_E_NOT_ENOUGH_DATA;
@@ -1166,33 +934,22 @@ afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *positi
1166 if (bytes > 0 && buffer) { 934 if (bytes > 0 && buffer) {
1167 /* Get the position */ 935 /* Get the position */
1168 memcpy(position, buffer, sizeof(uint64_t)); 936 memcpy(position, buffer, sizeof(uint64_t));
1169 *position = GUINT64_FROM_LE(*position); 937 *position = le64toh(*position);
1170 } 938 }
1171 if (buffer) 939 free(buffer);
1172 free(buffer);
1173 940
1174 afc_unlock(client); 941 afc_unlock(client);
1175 942
1176 return ret; 943 return ret;
1177} 944}
1178 945
1179/**
1180 * Sets the size of a file on the phone.
1181 *
1182 * @param client The client to use to set the file size.
1183 * @param handle File handle of a previously opened file.
1184 * @param newsize The size to set the file to.
1185 *
1186 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
1187 *
1188 * @note This function is more akin to ftruncate than truncate, and truncate
1189 * calls would have to open the file before calling this, sadly.
1190 */
1191afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize) 946afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize)
1192{ 947{
1193 char *buffer = (char *) malloc(sizeof(char) * 16);
1194 uint32_t bytes = 0; 948 uint32_t bytes = 0;
1195 uint64_t newsize_loc = GUINT64_TO_LE(newsize); 949 struct truncinfo {
950 uint64_t handle;
951 uint64_t newsize;
952 };
1196 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 953 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1197 954
1198 if (!client || (handle == 0)) 955 if (!client || (handle == 0))
@@ -1201,160 +958,240 @@ afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t new
1201 afc_lock(client); 958 afc_lock(client);
1202 959
1203 /* Send command */ 960 /* Send command */
1204 memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ 961 struct truncinfo* truncinfo = (struct truncinfo*)(AFC_PACKET_DATA_PTR);
1205 memcpy(buffer + 8, &newsize_loc, sizeof(uint64_t)); /* newsize */ 962 truncinfo->handle = handle;
1206 client->afc_packet->operation = AFC_OP_FILE_SET_SIZE; 963 truncinfo->newsize = htole64(newsize);
1207 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 964 ret = afc_dispatch_packet(client, AFC_OP_FILE_SET_SIZE, sizeof(struct truncinfo), NULL, 0, &bytes);
1208 ret = afc_dispatch_packet(client, buffer, 16, &bytes);
1209 free(buffer);
1210 buffer = NULL;
1211 965
1212 if (ret != AFC_E_SUCCESS) { 966 if (ret != AFC_E_SUCCESS) {
1213 afc_unlock(client); 967 afc_unlock(client);
1214 return AFC_E_NOT_ENOUGH_DATA; 968 return AFC_E_NOT_ENOUGH_DATA;
1215 } 969 }
1216 /* Receive response */ 970 /* Receive response */
1217 ret = afc_receive_data(client, &buffer, &bytes); 971 ret = afc_receive_data(client, NULL, &bytes);
1218 if (buffer)
1219 free(buffer);
1220 972
1221 afc_unlock(client); 973 afc_unlock(client);
1222 974
1223 return ret; 975 return ret;
1224} 976}
1225 977
1226/**
1227 * Sets the size of a file on the phone without prior opening it.
1228 *
1229 * @param client The client to use to set the file size.
1230 * @param path The path of the file to be truncated.
1231 * @param newsize The size to set the file to.
1232 *
1233 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
1234 */
1235afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize) 978afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize)
1236{ 979{
1237 char *response = NULL; 980 if (!client || !path || !client->afc_packet || !client->parent)
1238 char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); 981 return AFC_E_INVALID_ARG;
982
1239 uint32_t bytes = 0; 983 uint32_t bytes = 0;
1240 uint64_t size_requested = GUINT64_TO_LE(newsize);
1241 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 984 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1242 985
1243 if (!client || !path || !client->afc_packet || !client->connection)
1244 return AFC_E_INVALID_ARG;
1245
1246 afc_lock(client); 986 afc_lock(client);
1247 987
988 uint32_t data_len = 8 + (uint32_t)(strlen(path)+1);
989 if (_afc_check_packet_buffer(client, data_len) < 0) {
990 afc_unlock(client);
991 debug_info("Failed to realloc packet buffer");
992 return AFC_E_NO_MEM;
993 }
994
1248 /* Send command */ 995 /* Send command */
1249 memcpy(send, &size_requested, 8); 996 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(newsize);
1250 memcpy(send + 8, path, strlen(path) + 1); 997 memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8);
1251 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 998 ret = afc_dispatch_packet(client, AFC_OP_TRUNCATE, data_len, NULL, 0, &bytes);
1252 client->afc_packet->operation = AFC_OP_TRUNCATE;
1253 ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes);
1254 free(send);
1255 if (ret != AFC_E_SUCCESS) { 999 if (ret != AFC_E_SUCCESS) {
1256 afc_unlock(client); 1000 afc_unlock(client);
1257 return AFC_E_NOT_ENOUGH_DATA; 1001 return AFC_E_NOT_ENOUGH_DATA;
1258 } 1002 }
1259 /* Receive response */ 1003 /* Receive response */
1260 ret = afc_receive_data(client, &response, &bytes); 1004 ret = afc_receive_data(client, NULL, &bytes);
1261 if (response)
1262 free(response);
1263 1005
1264 afc_unlock(client); 1006 afc_unlock(client);
1265 1007
1266 return ret; 1008 return ret;
1267} 1009}
1268 1010
1269/**
1270 * Creates a hard link or symbolic link on the device.
1271 *
1272 * @param client The client to use for making a link
1273 * @param linktype 1 = hard link, 2 = symlink
1274 * @param target The file to be linked.
1275 * @param linkname The name of link.
1276 *
1277 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
1278 */
1279afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) 1011afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname)
1280{ 1012{
1281 char *response = NULL; 1013 if (!client || !target || !linkname || !client->afc_packet || !client->parent)
1282 char *send = (char *) malloc(sizeof(char) * (strlen(target)+1 + strlen(linkname)+1 + 8)); 1014 return AFC_E_INVALID_ARG;
1015
1283 uint32_t bytes = 0; 1016 uint32_t bytes = 0;
1284 uint64_t type = GUINT64_TO_LE(linktype);
1285 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1017 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1286 1018
1287 if (!client || !target || !linkname || !client->afc_packet || !client->connection) 1019 size_t target_len = strlen(target);
1288 return AFC_E_INVALID_ARG; 1020 size_t link_len = strlen(linkname);
1289 1021
1290 afc_lock(client); 1022 afc_lock(client);
1291 1023
1292 debug_info("link type: %lld", type); 1024 uint32_t data_len = 8 + target_len + 1 + link_len + 1;
1293 debug_info("target: %s, length:%d", target, strlen(target)); 1025 if (_afc_check_packet_buffer(client, data_len) < 0) {
1294 debug_info("linkname: %s, length:%d", linkname, strlen(linkname)); 1026 afc_unlock(client);
1027 debug_info("Failed to realloc packet buffer");
1028 return AFC_E_NO_MEM;
1029 }
1030
1031 debug_info("link type: %lld", htole64(linktype));
1032 debug_info("target: %s, length:%d", target, target_len);
1033 debug_info("linkname: %s, length:%d", linkname, link_len);
1295 1034
1296 /* Send command */ 1035 /* Send command */
1297 memcpy(send, &type, 8); 1036 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(linktype);
1298 memcpy(send + 8, target, strlen(target) + 1); 1037 memcpy(AFC_PACKET_DATA_PTR + 8, target, target_len + 1);
1299 memcpy(send + 8 + strlen(target) + 1, linkname, strlen(linkname) + 1); 1038 memcpy(AFC_PACKET_DATA_PTR + 8 + target_len + 1, linkname, link_len + 1);
1300 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 1039 ret = afc_dispatch_packet(client, AFC_OP_MAKE_LINK, data_len, NULL, 0, &bytes);
1301 client->afc_packet->operation = AFC_OP_MAKE_LINK;
1302 ret = afc_dispatch_packet(client, send, 8 + strlen(linkname) + 1 + strlen(target) + 1, &bytes);
1303 free(send);
1304 if (ret != AFC_E_SUCCESS) { 1040 if (ret != AFC_E_SUCCESS) {
1305 afc_unlock(client); 1041 afc_unlock(client);
1306 return AFC_E_NOT_ENOUGH_DATA; 1042 return AFC_E_NOT_ENOUGH_DATA;
1307 } 1043 }
1308 /* Receive response */ 1044 /* Receive response */
1309 ret = afc_receive_data(client, &response, &bytes); 1045 ret = afc_receive_data(client, NULL, &bytes);
1310 if (response)
1311 free(response);
1312 1046
1313 afc_unlock(client); 1047 afc_unlock(client);
1314 1048
1315 return ret; 1049 return ret;
1316} 1050}
1317 1051
1318/**
1319 * Sets the modification time of a file on the phone.
1320 *
1321 * @param client The client to use to set the file size.
1322 * @param path Path of the file for which the modification time should be set.
1323 * @param mtime The modification time to set in nanoseconds since epoch.
1324 *
1325 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
1326 */
1327afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime) 1052afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime)
1328{ 1053{
1329 char *response = NULL; 1054 if (!client || !path || !client->afc_packet || !client->parent)
1330 char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); 1055 return AFC_E_INVALID_ARG;
1056
1331 uint32_t bytes = 0; 1057 uint32_t bytes = 0;
1332 uint64_t mtime_loc = GUINT64_TO_LE(mtime);
1333 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1058 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1334 1059
1335 if (!client || !path || !client->afc_packet || !client->connection) 1060 afc_lock(client);
1061
1062 uint32_t data_len = 8 + strlen(path) + 1;
1063 if (_afc_check_packet_buffer(client, data_len) < 0) {
1064 afc_unlock(client);
1065 debug_info("Failed to realloc packet buffer");
1066 return AFC_E_NO_MEM;
1067 }
1068
1069 /* Send command */
1070 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(mtime);
1071 memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8);
1072 ret = afc_dispatch_packet(client, AFC_OP_SET_FILE_MOD_TIME, data_len, NULL, 0, &bytes);
1073 if (ret != AFC_E_SUCCESS) {
1074 afc_unlock(client);
1075 return AFC_E_NOT_ENOUGH_DATA;
1076 }
1077 /* Receive response */
1078 ret = afc_receive_data(client, NULL, &bytes);
1079
1080 afc_unlock(client);
1081
1082 return ret;
1083}
1084
1085afc_error_t afc_remove_path_and_contents(afc_client_t client, const char *path)
1086{
1087 uint32_t bytes = 0;
1088 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1089
1090 if (!client || !path || !client->afc_packet || !client->parent)
1336 return AFC_E_INVALID_ARG; 1091 return AFC_E_INVALID_ARG;
1337 1092
1338 afc_lock(client); 1093 afc_lock(client);
1339 1094
1095 uint32_t data_len = strlen(path) + 1;
1096 if (_afc_check_packet_buffer(client, data_len) < 0) {
1097 afc_unlock(client);
1098 debug_info("Failed to realloc packet buffer");
1099 return AFC_E_NO_MEM;
1100 }
1101
1340 /* Send command */ 1102 /* Send command */
1341 memcpy(send, &mtime_loc, 8); 1103 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
1342 memcpy(send + 8, path, strlen(path) + 1); 1104 ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH_AND_CONTENTS, data_len, NULL, 0, &bytes);
1343 client->afc_packet->entire_length = client->afc_packet->this_length = 0;
1344 client->afc_packet->operation = AFC_OP_SET_FILE_TIME;
1345 ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes);
1346 free(send);
1347 if (ret != AFC_E_SUCCESS) { 1105 if (ret != AFC_E_SUCCESS) {
1348 afc_unlock(client); 1106 afc_unlock(client);
1349 return AFC_E_NOT_ENOUGH_DATA; 1107 return AFC_E_NOT_ENOUGH_DATA;
1350 } 1108 }
1351 /* Receive response */ 1109 /* Receive response */
1352 ret = afc_receive_data(client, &response, &bytes); 1110 ret = afc_receive_data(client, NULL, &bytes);
1353 if (response)
1354 free(response);
1355 1111
1356 afc_unlock(client); 1112 afc_unlock(client);
1357 1113
1358 return ret; 1114 return ret;
1359} 1115}
1360 1116
1117afc_error_t afc_dictionary_free(char **dictionary)
1118{
1119 int i = 0;
1120
1121 if (!dictionary)
1122 return AFC_E_INVALID_ARG;
1123
1124 for (i = 0; dictionary[i]; i++) {
1125 free(dictionary[i]);
1126 }
1127 free(dictionary);
1128
1129 return AFC_E_SUCCESS;
1130}
1131
1132const char* afc_strerror(afc_error_t err)
1133{
1134 switch (err) {
1135 case AFC_E_SUCCESS:
1136 return "Success";
1137 case AFC_E_UNKNOWN_ERROR:
1138 return "Unknown Error";
1139 case AFC_E_OP_HEADER_INVALID:
1140 return "Operation header invalid";
1141 case AFC_E_NO_RESOURCES:
1142 return "No resources";
1143 case AFC_E_READ_ERROR:
1144 return "Read error";
1145 case AFC_E_WRITE_ERROR:
1146 return "Write error";
1147 case AFC_E_UNKNOWN_PACKET_TYPE:
1148 return "Unknown packet type";
1149 case AFC_E_INVALID_ARG:
1150 return "Invalid argument";
1151 case AFC_E_OBJECT_NOT_FOUND:
1152 return "Not found";
1153 case AFC_E_OBJECT_IS_DIR:
1154 return "Object is a directory";
1155 case AFC_E_PERM_DENIED:
1156 return "Permission denied";
1157 case AFC_E_SERVICE_NOT_CONNECTED:
1158 return "Service not connected";
1159 case AFC_E_OP_TIMEOUT:
1160 return "Timeout";
1161 case AFC_E_TOO_MUCH_DATA:
1162 return "Too much data";
1163 case AFC_E_END_OF_DATA:
1164 return "End of data";
1165 case AFC_E_OP_NOT_SUPPORTED:
1166 return "Operation not supported";
1167 case AFC_E_OBJECT_EXISTS:
1168 return "Object exists";
1169 case AFC_E_OBJECT_BUSY:
1170 return "Object busy";
1171 case AFC_E_NO_SPACE_LEFT:
1172 return "No space left on device";
1173 case AFC_E_OP_WOULD_BLOCK:
1174 return "Operation would block";
1175 case AFC_E_IO_ERROR:
1176 return "I/O error";
1177 case AFC_E_OP_INTERRUPTED:
1178 return "Operation interrupted";
1179 case AFC_E_OP_IN_PROGRESS:
1180 return "Operation on progress";
1181 case AFC_E_INTERNAL_ERROR:
1182 return "Internal error";
1183 case AFC_E_MUX_ERROR:
1184 return "MUX error";
1185 case AFC_E_NO_MEM:
1186 return "Out of memory";
1187 case AFC_E_NOT_ENOUGH_DATA:
1188 return "Not enough data";
1189 case AFC_E_DIR_NOT_EMPTY:
1190 return "Directory not empty";
1191 case AFC_E_FORCE_SIGNED_TYPE:
1192 return "Force signed type";
1193 default:
1194 break;
1195 }
1196 return "Unknown Error";
1197}
diff --git a/src/afc.h b/src/afc.h
index 9c9f12d..6bfdf56 100644
--- a/src/afc.h
+++ b/src/afc.h
@@ -1,28 +1,34 @@
1/* 1/*
2 * afc.h 2 * afc.h
3 * Defines and structs and the like for the built-in AFC client 3 * Defines and structs and the like for the built-in AFC client
4 * 4 *
5 * Copyright (c) 2014 Martin Szulecki All Rights Reserved.
5 * Copyright (c) 2008 Zach C. All Rights Reserved. 6 * Copyright (c) 2008 Zach C. All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21 22
22#include <glib.h> 23#ifndef __AFC_H
24#define __AFC_H
25
23#include <stdint.h> 26#include <stdint.h>
24 27
25#include "libimobiledevice/afc.h" 28#include "libimobiledevice/afc.h"
29#include "service.h"
30#include "endianness.h"
31#include <libimobiledevice-glue/thread.h>
26 32
27#define AFC_MAGIC "CFA6LPAA" 33#define AFC_MAGIC "CFA6LPAA"
28#define AFC_MAGIC_LEN (8) 34#define AFC_MAGIC_LEN (8)
@@ -33,60 +39,72 @@ typedef struct {
33} AFCPacket; 39} AFCPacket;
34 40
35#define AFCPacket_to_LE(x) \ 41#define AFCPacket_to_LE(x) \
36 (x)->entire_length = GUINT64_TO_LE((x)->entire_length); \ 42 (x)->entire_length = htole64((x)->entire_length); \
37 (x)->this_length = GUINT64_TO_LE((x)->this_length); \ 43 (x)->this_length = htole64((x)->this_length); \
38 (x)->packet_num = GUINT64_TO_LE((x)->packet_num); \ 44 (x)->packet_num = htole64((x)->packet_num); \
39 (x)->operation = GUINT64_TO_LE((x)->operation); 45 (x)->operation = htole64((x)->operation);
40 46
41#define AFCPacket_from_LE(x) \ 47#define AFCPacket_from_LE(x) \
42 (x)->entire_length = GUINT64_FROM_LE((x)->entire_length); \ 48 (x)->entire_length = le64toh((x)->entire_length); \
43 (x)->this_length = GUINT64_FROM_LE((x)->this_length); \ 49 (x)->this_length = le64toh((x)->this_length); \
44 (x)->packet_num = GUINT64_FROM_LE((x)->packet_num); \ 50 (x)->packet_num = le64toh((x)->packet_num); \
45 (x)->operation = GUINT64_FROM_LE((x)->operation); 51 (x)->operation = le64toh((x)->operation);
46
47typedef struct {
48 uint64_t filehandle, size;
49} AFCFilePacket;
50 52
51struct afc_client_private { 53struct afc_client_private {
52 idevice_connection_t connection; 54 service_client_t parent;
53 AFCPacket *afc_packet; 55 AFCPacket *afc_packet;
54 int file_handle; 56 uint32_t packet_extra;
55 int lock; 57 mutex_t mutex;
56 GMutex *mutex; 58 int free_parent;
57 int own_connection;
58}; 59};
59 60
60/* AFC Operations */ 61/* AFC Operations */
61enum { 62enum {
62 AFC_OP_STATUS = 0x00000001, /* Status */ 63 AFC_OP_INVALID = 0x00000000, /* Invalid */
63 AFC_OP_DATA = 0x00000002, /* Data */ 64 AFC_OP_STATUS = 0x00000001, /* Status */
64 AFC_OP_READ_DIR = 0x00000003, /* ReadDir */ 65 AFC_OP_DATA = 0x00000002, /* Data */
65 AFC_OP_READ_FILE = 0x00000004, /* ReadFile */ 66 AFC_OP_READ_DIR = 0x00000003, /* ReadDir */
66 AFC_OP_WRITE_FILE = 0x00000005, /* WriteFile */ 67 AFC_OP_READ_FILE = 0x00000004, /* ReadFile */
67 AFC_OP_WRITE_PART = 0x00000006, /* WritePart */ 68 AFC_OP_WRITE_FILE = 0x00000005, /* WriteFile */
68 AFC_OP_TRUNCATE = 0x00000007, /* TruncateFile */ 69 AFC_OP_WRITE_PART = 0x00000006, /* WritePart */
69 AFC_OP_REMOVE_PATH = 0x00000008, /* RemovePath */ 70 AFC_OP_TRUNCATE = 0x00000007, /* TruncateFile */
70 AFC_OP_MAKE_DIR = 0x00000009, /* MakeDir */ 71 AFC_OP_REMOVE_PATH = 0x00000008, /* RemovePath */
71 AFC_OP_GET_FILE_INFO = 0x0000000a, /* GetFileInfo */ 72 AFC_OP_MAKE_DIR = 0x00000009, /* MakeDir */
72 AFC_OP_GET_DEVINFO = 0x0000000b, /* GetDeviceInfo */ 73 AFC_OP_GET_FILE_INFO = 0x0000000A, /* GetFileInfo */
73 AFC_OP_WRITE_FILE_ATOM = 0x0000000c, /* WriteFileAtomic (tmp file+rename) */ 74 AFC_OP_GET_DEVINFO = 0x0000000B, /* GetDeviceInfo */
74 AFC_OP_FILE_OPEN = 0x0000000d, /* FileRefOpen */ 75 AFC_OP_WRITE_FILE_ATOM = 0x0000000C, /* WriteFileAtomic (tmp file+rename) */
75 AFC_OP_FILE_OPEN_RES = 0x0000000e, /* FileRefOpenResult */ 76 AFC_OP_FILE_OPEN = 0x0000000D, /* FileRefOpen */
76 AFC_OP_READ = 0x0000000f, /* FileRefRead */ 77 AFC_OP_FILE_OPEN_RES = 0x0000000E, /* FileRefOpenResult */
77 AFC_OP_WRITE = 0x00000010, /* FileRefWrite */ 78 AFC_OP_FILE_READ = 0x0000000F, /* FileRefRead */
78 AFC_OP_FILE_SEEK = 0x00000011, /* FileRefSeek */ 79 AFC_OP_FILE_WRITE = 0x00000010, /* FileRefWrite */
79 AFC_OP_FILE_TELL = 0x00000012, /* FileRefTell */ 80 AFC_OP_FILE_SEEK = 0x00000011, /* FileRefSeek */
80 AFC_OP_FILE_TELL_RES = 0x00000013, /* FileRefTellResult */ 81 AFC_OP_FILE_TELL = 0x00000012, /* FileRefTell */
81 AFC_OP_FILE_CLOSE = 0x00000014, /* FileRefClose */ 82 AFC_OP_FILE_TELL_RES = 0x00000013, /* FileRefTellResult */
82 AFC_OP_FILE_SET_SIZE = 0x00000015, /* FileRefSetFileSize (ftruncate) */ 83 AFC_OP_FILE_CLOSE = 0x00000014, /* FileRefClose */
83 AFC_OP_GET_CON_INFO = 0x00000016, /* GetConnectionInfo */ 84 AFC_OP_FILE_SET_SIZE = 0x00000015, /* FileRefSetFileSize (ftruncate) */
84 AFC_OP_SET_CON_OPTIONS = 0x00000017, /* SetConnectionOptions */ 85 AFC_OP_GET_CON_INFO = 0x00000016, /* GetConnectionInfo */
85 AFC_OP_RENAME_PATH = 0x00000018, /* RenamePath */ 86 AFC_OP_SET_CON_OPTIONS = 0x00000017, /* SetConnectionOptions */
86 AFC_OP_SET_FS_BS = 0x00000019, /* SetFSBlockSize (0x800000) */ 87 AFC_OP_RENAME_PATH = 0x00000018, /* RenamePath */
87 AFC_OP_SET_SOCKET_BS = 0x0000001A, /* SetSocketBlockSize (0x800000) */ 88 AFC_OP_SET_FS_BS = 0x00000019, /* SetFSBlockSize (0x800000) */
88 AFC_OP_FILE_LOCK = 0x0000001B, /* FileRefLock */ 89 AFC_OP_SET_SOCKET_BS = 0x0000001A, /* SetSocketBlockSize (0x800000) */
89 AFC_OP_MAKE_LINK = 0x0000001C, /* MakeLink */ 90 AFC_OP_FILE_LOCK = 0x0000001B, /* FileRefLock */
90 AFC_OP_SET_FILE_TIME = 0x0000001E /* set st_mtime */ 91 AFC_OP_MAKE_LINK = 0x0000001C, /* MakeLink */
92 AFC_OP_GET_FILE_HASH = 0x0000001D, /* GetFileHash */
93 AFC_OP_SET_FILE_MOD_TIME = 0x0000001E, /* SetModTime */
94 AFC_OP_GET_FILE_HASH_RANGE = 0x0000001F, /* GetFileHashWithRange */
95 /* iOS 6+ */
96 AFC_OP_FILE_SET_IMMUTABLE_HINT = 0x00000020, /* FileRefSetImmutableHint */
97 AFC_OP_GET_SIZE_OF_PATH_CONTENTS = 0x00000021, /* GetSizeOfPathContents */
98 AFC_OP_REMOVE_PATH_AND_CONTENTS = 0x00000022, /* RemovePathAndContents */
99 AFC_OP_DIR_OPEN = 0x00000023, /* DirectoryEnumeratorRefOpen */
100 AFC_OP_DIR_OPEN_RESULT = 0x00000024, /* DirectoryEnumeratorRefOpenResult */
101 AFC_OP_DIR_READ = 0x00000025, /* DirectoryEnumeratorRefRead */
102 AFC_OP_DIR_CLOSE = 0x00000026, /* DirectoryEnumeratorRefClose */
103 /* iOS 7+ */
104 AFC_OP_FILE_READ_OFFSET = 0x00000027, /* FileRefReadWithOffset */
105 AFC_OP_FILE_WRITE_OFFSET = 0x00000028 /* FileRefWriteWithOffset */
91}; 106};
92 107
108afc_error_t afc_client_new_with_service_client(service_client_t service_client, afc_client_t *client);
109
110#endif
diff --git a/src/bt_packet_logger.c b/src/bt_packet_logger.c
new file mode 100644
index 0000000..937747c
--- /dev/null
+++ b/src/bt_packet_logger.c
@@ -0,0 +1,231 @@
1/*
2 * bt_packet_logger.c
3 * com.apple.bluetooth.BTPacketLogger service implementation.
4 *
5 * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27
28#include "bt_packet_logger.h"
29#include "lockdown.h"
30#include "common/debug.h"
31
32struct bt_packet_logger_worker_thread {
33 bt_packet_logger_client_t client;
34 bt_packet_logger_receive_cb_t cbfunc;
35 void *user_data;
36 uint8_t rxbuff[BT_MAX_PACKET_SIZE];
37};
38
39#define SZ_READ_TIMEOUT 100
40#define PAYLOAD_READ_TIMEOUT 500
41
42/**
43 * Convert a service_error_t value to a bt_packet_logger_error_t value.
44 * Used internally to get correct error codes.
45 *
46 * @param err An service_error_t error code
47 *
48 * @return A matching bt_packet_logger_error_t error code,
49 * BT_PACKET_LOGGER_E_UNKNOWN_ERROR otherwise.
50 */
51static bt_packet_logger_error_t bt_packet_logger_error(service_error_t err)
52{
53 switch (err) {
54 case SERVICE_E_SUCCESS:
55 return BT_PACKET_LOGGER_E_SUCCESS;
56 case SERVICE_E_INVALID_ARG:
57 return BT_PACKET_LOGGER_E_INVALID_ARG;
58 case SERVICE_E_MUX_ERROR:
59 return BT_PACKET_LOGGER_E_MUX_ERROR;
60 case SERVICE_E_SSL_ERROR:
61 return BT_PACKET_LOGGER_E_SSL_ERROR;
62 case SERVICE_E_NOT_ENOUGH_DATA:
63 return BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA;
64 case SERVICE_E_TIMEOUT:
65 return BT_PACKET_LOGGER_E_TIMEOUT;
66 default:
67 break;
68 }
69 return BT_PACKET_LOGGER_E_UNKNOWN_ERROR;
70}
71
72bt_packet_logger_error_t bt_packet_logger_client_new(idevice_t device, lockdownd_service_descriptor_t service, bt_packet_logger_client_t * client)
73{
74 if (!device || !service || service->port == 0 || !client || *client) {
75 debug_info("Incorrect parameter passed to bt_packet_logger_client_new.");
76 return BT_PACKET_LOGGER_E_INVALID_ARG;
77 }
78
79 debug_info("Creating bt_packet_logger_client, port = %d.", service->port);
80
81 service_client_t parent = NULL;
82 bt_packet_logger_error_t ret = bt_packet_logger_error(service_client_new(device, service, &parent));
83 if (ret != BT_PACKET_LOGGER_E_SUCCESS) {
84 debug_info("Creating base service client failed. Error: %i", ret);
85 return ret;
86 }
87
88 bt_packet_logger_client_t client_loc = (bt_packet_logger_client_t) malloc(sizeof(struct bt_packet_logger_client_private));
89 client_loc->parent = parent;
90 client_loc->worker = THREAD_T_NULL;
91
92 *client = client_loc;
93
94 debug_info("bt_packet_logger_client successfully created.");
95 return 0;
96}
97
98bt_packet_logger_error_t bt_packet_logger_client_start_service(idevice_t device, bt_packet_logger_client_t * client, const char* label)
99{
100 bt_packet_logger_error_t err = BT_PACKET_LOGGER_E_UNKNOWN_ERROR;
101 service_client_factory_start_service(device, BT_PACKETLOGGER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(bt_packet_logger_client_new), &err);
102 return err;
103}
104
105bt_packet_logger_error_t bt_packet_logger_client_free(bt_packet_logger_client_t client)
106{
107 if (!client)
108 return BT_PACKET_LOGGER_E_INVALID_ARG;
109 bt_packet_logger_stop_capture(client);
110 bt_packet_logger_error_t err = bt_packet_logger_error(service_client_free(client->parent));
111 free(client);
112
113 return err;
114}
115
116bt_packet_logger_error_t bt_packet_logger_receive_with_timeout(bt_packet_logger_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
117{
118 bt_packet_logger_error_t res = BT_PACKET_LOGGER_E_UNKNOWN_ERROR;
119 int bytes = 0;
120
121 if (!client || !data || (size == 0)) {
122 return BT_PACKET_LOGGER_E_INVALID_ARG;
123 }
124
125 res = bt_packet_logger_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
126 if (res != BT_PACKET_LOGGER_E_SUCCESS && res != BT_PACKET_LOGGER_E_TIMEOUT && res != BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA) {
127 debug_info("Could not read data, error %d", res);
128 }
129 if (received) {
130 *received = (uint32_t)bytes;
131 }
132
133 return res;
134}
135
136void *bt_packet_logger_worker(void *arg)
137{
138 bt_packet_logger_error_t ret = BT_PACKET_LOGGER_E_UNKNOWN_ERROR;
139 struct bt_packet_logger_worker_thread *btwt = (struct bt_packet_logger_worker_thread*)arg;
140
141 if (!btwt) {
142 return NULL;
143 }
144
145 debug_info("Running");
146
147 while (btwt->client->parent) {
148 uint32_t bytes = 0;
149 uint16_t len;
150
151 ret = bt_packet_logger_receive_with_timeout(btwt->client, (char*)&len, 2, &bytes, SZ_READ_TIMEOUT);
152
153 if (ret == BT_PACKET_LOGGER_E_TIMEOUT || ret == BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA || ((bytes == 0) && (ret == BT_PACKET_LOGGER_E_SUCCESS))) {
154 continue;
155 } else if (ret < 0) {
156 debug_info("Connection to bt packet logger interrupted");
157 break;
158 }
159
160 // sanity check received length
161 if(bytes > 0 && len > sizeof(bt_packet_logger_header_t)) {
162 debug_info("Reading %u bytes\n", len);
163 ret = bt_packet_logger_receive_with_timeout(btwt->client, (char *)btwt->rxbuff, len, &bytes, PAYLOAD_READ_TIMEOUT);
164
165 if(len != bytes) {
166 debug_info("Failed Read Expected %u, Received %u\n", len, bytes);
167 continue;
168 }
169
170 if (ret == BT_PACKET_LOGGER_E_TIMEOUT || ret == BT_PACKET_LOGGER_E_NOT_ENOUGH_DATA || ((bytes == 0) && (ret == BT_PACKET_LOGGER_E_SUCCESS))) {
171 continue;
172 } else if (ret < 0) {
173 debug_info("Connection to bt packet logger interrupted");
174 break;
175 }
176
177 btwt->cbfunc(btwt->rxbuff, len, btwt->user_data);
178 }
179 }
180
181 // null check performed above
182 free(btwt);
183
184 debug_info("Exiting");
185
186 return NULL;
187}
188
189bt_packet_logger_error_t bt_packet_logger_start_capture(bt_packet_logger_client_t client, bt_packet_logger_receive_cb_t callback, void* user_data)
190{
191 if (!client || !callback)
192 return BT_PACKET_LOGGER_E_INVALID_ARG;
193
194 bt_packet_logger_error_t res = BT_PACKET_LOGGER_E_UNKNOWN_ERROR;
195
196 if (client->worker) {
197 debug_info("Another syslog capture thread appears to be running already.");
198 return res;
199 }
200
201 /* start worker thread */
202 struct bt_packet_logger_worker_thread *btwt = (struct bt_packet_logger_worker_thread*)malloc(sizeof(struct bt_packet_logger_worker_thread));
203 if (btwt) {
204 btwt->client = client;
205 btwt->cbfunc = callback;
206 btwt->user_data = user_data;
207
208 if (thread_new(&client->worker, bt_packet_logger_worker, btwt) == 0) {
209 res = BT_PACKET_LOGGER_E_SUCCESS;
210 }
211 }
212
213 return res;
214}
215
216
217bt_packet_logger_error_t bt_packet_logger_stop_capture(bt_packet_logger_client_t client)
218{
219 if (client->worker) {
220 /* notify thread to finish */
221 service_client_t parent = client->parent;
222 client->parent = NULL;
223 /* join thread to make it exit */
224 thread_join(client->worker);
225 thread_free(client->worker);
226 client->worker = THREAD_T_NULL;
227 client->parent = parent;
228 }
229
230 return BT_PACKET_LOGGER_E_SUCCESS;
231}
diff --git a/src/bt_packet_logger.h b/src/bt_packet_logger.h
new file mode 100644
index 0000000..620555e
--- /dev/null
+++ b/src/bt_packet_logger.h
@@ -0,0 +1,37 @@
1/*
2 * bt_packet_logger.h
3 * com.apple.bluetooth.BTPacketLogger service header file.
4 *
5 * Copyright (c) 2021 Geoffrey Kruse, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef _BR_PACKET_LOGGER_H
23#define _BR_PACKET_LOGGER_H
24
25#include "idevice.h"
26#include "libimobiledevice/bt_packet_logger.h"
27#include "service.h"
28#include <libimobiledevice-glue/thread.h>
29
30struct bt_packet_logger_client_private {
31 service_client_t parent;
32 THREAD_T worker;
33};
34
35void *bt_packet_logger_worker(void *arg);
36
37#endif
diff --git a/src/companion_proxy.c b/src/companion_proxy.c
new file mode 100644
index 0000000..421fa9a
--- /dev/null
+++ b/src/companion_proxy.c
@@ -0,0 +1,380 @@
1/*
2 * companion_proxy.c
3 * com.apple.companion_proxy service implementation.
4 *
5 * Copyright (c) 2019-2020 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include <plist/plist.h>
28
29#include "companion_proxy.h"
30#include "lockdown.h"
31#include "common/debug.h"
32
33/**
34 * Convert a property_list_service_error_t value to a companion_proxy_error_t value.
35 * Used internally to get correct error codes.
36 *
37 * @param err An property_list_service_error_t error code
38 *
39 * @return A matching companion_proxy_error_t error code,
40 * COMPANION_PROXY_E_UNKNOWN_ERROR otherwise.
41 */
42static companion_proxy_error_t companion_proxy_error(property_list_service_error_t err)
43{
44 switch (err) {
45 case PROPERTY_LIST_SERVICE_E_SUCCESS:
46 return COMPANION_PROXY_E_SUCCESS;
47 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
48 return COMPANION_PROXY_E_INVALID_ARG;
49 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
50 return COMPANION_PROXY_E_PLIST_ERROR;
51 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
52 return COMPANION_PROXY_E_MUX_ERROR;
53 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
54 return COMPANION_PROXY_E_SSL_ERROR;
55 case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA:
56 return COMPANION_PROXY_E_NOT_ENOUGH_DATA;
57 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
58 return COMPANION_PROXY_E_TIMEOUT;
59 default:
60 break;
61 }
62 return COMPANION_PROXY_E_UNKNOWN_ERROR;
63}
64
65companion_proxy_error_t companion_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, companion_proxy_client_t * client)
66{
67 *client = NULL;
68
69 if (!device || !service || service->port == 0 || !client || *client) {
70 debug_info("Incorrect parameter passed to companion_proxy_client_new.");
71 return COMPANION_PROXY_E_INVALID_ARG;
72 }
73
74 debug_info("Creating companion_proxy_client, port = %d.", service->port);
75
76 property_list_service_client_t plclient = NULL;
77 companion_proxy_error_t ret = companion_proxy_error(property_list_service_client_new(device, service, &plclient));
78 if (ret != COMPANION_PROXY_E_SUCCESS) {
79 debug_info("Creating a property list client failed. Error: %i", ret);
80 return ret;
81 }
82
83 companion_proxy_client_t client_loc = (companion_proxy_client_t) malloc(sizeof(struct companion_proxy_client_private));
84 client_loc->parent = plclient;
85 client_loc->event_thread = THREAD_T_NULL;
86
87 *client = client_loc;
88
89 debug_info("Created companion_proxy_client successfully.");
90 return COMPANION_PROXY_E_SUCCESS;
91}
92
93companion_proxy_error_t companion_proxy_client_start_service(idevice_t device, companion_proxy_client_t * client, const char* label)
94{
95 companion_proxy_error_t err = COMPANION_PROXY_E_UNKNOWN_ERROR;
96 service_client_factory_start_service(device, COMPANION_PROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(companion_proxy_client_new), &err);
97 return err;
98}
99
100companion_proxy_error_t companion_proxy_client_free(companion_proxy_client_t client)
101{
102 if (!client)
103 return COMPANION_PROXY_E_INVALID_ARG;
104
105 property_list_service_client_t parent = client->parent;
106 client->parent = NULL;
107 if (client->event_thread) {
108 debug_info("joining event thread");
109 thread_join(client->event_thread);
110 thread_free(client->event_thread);
111 client->event_thread = THREAD_T_NULL;
112 }
113 companion_proxy_error_t err = companion_proxy_error(property_list_service_client_free(parent));
114 free(client);
115
116 return err;
117}
118
119companion_proxy_error_t companion_proxy_send(companion_proxy_client_t client, plist_t plist)
120{
121 companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR;
122
123 res = companion_proxy_error(property_list_service_send_binary_plist(client->parent, plist));
124 if (res != COMPANION_PROXY_E_SUCCESS) {
125 debug_info("Sending plist failed with error %d", res);
126 return res;
127 }
128
129 return res;
130}
131
132companion_proxy_error_t companion_proxy_receive(companion_proxy_client_t client, plist_t * plist)
133{
134 companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR;
135 plist_t outplist = NULL;
136 res = companion_proxy_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, 10000));
137 if (res != COMPANION_PROXY_E_SUCCESS && res != COMPANION_PROXY_E_TIMEOUT) {
138 debug_info("Could not receive plist, error %d", res);
139 plist_free(outplist);
140 } else if (res == COMPANION_PROXY_E_SUCCESS) {
141 *plist = outplist;
142 }
143 return res;
144}
145
146companion_proxy_error_t companion_proxy_get_device_registry(companion_proxy_client_t client, plist_t* paired_devices)
147{
148 if (!client || !paired_devices) {
149 return COMPANION_PROXY_E_INVALID_ARG;
150 }
151
152 plist_t dict = plist_new_dict();
153 plist_dict_set_item(dict, "Command", plist_new_string("GetDeviceRegistry"));
154
155 companion_proxy_error_t res = companion_proxy_send(client, dict);
156 plist_free(dict);
157 dict = NULL;
158 if (res != COMPANION_PROXY_E_SUCCESS) {
159 return res;
160 }
161
162 res = companion_proxy_receive(client, &dict);
163 if (res != COMPANION_PROXY_E_SUCCESS) {
164 return res;
165 }
166 if (!dict || !PLIST_IS_DICT(dict)) {
167 return COMPANION_PROXY_E_PLIST_ERROR;
168 }
169 plist_t val = plist_dict_get_item(dict, "PairedDevicesArray");
170 if (val) {
171 *paired_devices = plist_copy(val);
172 res = COMPANION_PROXY_E_SUCCESS;
173 } else {
174 res = COMPANION_PROXY_E_UNKNOWN_ERROR;
175 val = plist_dict_get_item(dict, "Error");
176 if (val) {
177 if (plist_string_val_compare(val, "NoPairedWatches")) {
178 res = COMPANION_PROXY_E_NO_DEVICES;
179 }
180 }
181 }
182 plist_free(dict);
183 return res;
184}
185
186struct companion_proxy_cb_data {
187 companion_proxy_client_t client;
188 companion_proxy_device_event_cb_t cbfunc;
189 void* user_data;
190};
191
192static void* companion_proxy_event_thread(void* arg)
193{
194 struct companion_proxy_cb_data* data = (struct companion_proxy_cb_data*)arg;
195 companion_proxy_client_t client = data->client;
196 companion_proxy_error_t res;
197
198 plist_t command = plist_new_dict();
199 plist_dict_set_item(command, "Command", plist_new_string("StartListeningForDevices"));
200 res = companion_proxy_send(client, command);
201 plist_free(command);
202
203 if (res != COMPANION_PROXY_E_SUCCESS) {
204 free(data);
205 client->event_thread = THREAD_T_NULL;
206 return NULL;
207 }
208
209 while (client && client->parent) {
210 plist_t node = NULL;
211 res = companion_proxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000));
212 if (res != COMPANION_PROXY_E_SUCCESS && res != COMPANION_PROXY_E_TIMEOUT) {
213 debug_info("could not receive plist, error %d", res);
214 break;
215 }
216
217 if (node) {
218 data->cbfunc(node, data->user_data);
219 }
220 plist_free(node);
221 }
222
223 client->event_thread = THREAD_T_NULL;
224 free(data);
225
226 return NULL;
227}
228
229companion_proxy_error_t companion_proxy_start_listening_for_devices(companion_proxy_client_t client, companion_proxy_device_event_cb_t callback, void* userdata)
230{
231 if (!client || !client->parent || !callback) {
232 return COMPANION_PROXY_E_INVALID_ARG;
233 }
234
235 if (client->event_thread) {
236 return COMPANION_PROXY_E_OP_IN_PROGRESS;
237 }
238
239 companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR;
240 struct companion_proxy_cb_data *data = (struct companion_proxy_cb_data*)malloc(sizeof(struct companion_proxy_cb_data));
241 if (data) {
242 data->client = client;
243 data->cbfunc = callback;
244 data->user_data = userdata;
245
246 if (thread_new(&client->event_thread, companion_proxy_event_thread, data) == 0) {
247 res = COMPANION_PROXY_E_SUCCESS;
248 } else {
249 free(data);
250 }
251 }
252 return res;
253}
254
255companion_proxy_error_t companion_proxy_stop_listening_for_devices(companion_proxy_client_t client)
256{
257 property_list_service_client_t parent = client->parent;
258 client->parent = NULL;
259 if (client->event_thread) {
260 debug_info("joining event thread");
261 thread_join(client->event_thread);
262 thread_free(client->event_thread);
263 client->event_thread = THREAD_T_NULL;
264 }
265 client->parent = parent;
266 return COMPANION_PROXY_E_SUCCESS;
267}
268
269companion_proxy_error_t companion_proxy_get_value_from_registry(companion_proxy_client_t client, const char* companion_udid, const char* key, plist_t* value)
270{
271 if (!client || !companion_udid || !key || !value) {
272 return COMPANION_PROXY_E_INVALID_ARG;
273 }
274
275 plist_t dict = plist_new_dict();
276 plist_dict_set_item(dict, "Command", plist_new_string("GetValueFromRegistry"));
277 plist_dict_set_item(dict, "GetValueGizmoUDIDKey", plist_new_string(companion_udid));
278 plist_dict_set_item(dict, "GetValueKeyKey", plist_new_string(key));
279
280 companion_proxy_error_t res = companion_proxy_send(client, dict);
281 plist_free(dict);
282 dict = NULL;
283 if (res != COMPANION_PROXY_E_SUCCESS) {
284 return res;
285 }
286
287 res = companion_proxy_receive(client, &dict);
288 if (res != COMPANION_PROXY_E_SUCCESS) {
289 return res;
290 }
291 if (!dict || !PLIST_IS_DICT(dict)) {
292 return COMPANION_PROXY_E_PLIST_ERROR;
293 }
294 plist_t val = plist_dict_get_item(dict, "RetrievedValueDictionary");
295 if (val) {
296 *value = plist_copy(val);
297 res = COMPANION_PROXY_E_SUCCESS;
298 } else {
299 res = COMPANION_PROXY_E_UNKNOWN_ERROR;
300 val = plist_dict_get_item(dict, "Error");
301 if (val) {
302 if (!plist_string_val_compare(val, "UnsupportedWatchKey")) {
303 res = COMPANION_PROXY_E_UNSUPPORTED_KEY;
304 } else if (plist_string_val_compare(val, "TimeoutReply")) {
305 res = COMPANION_PROXY_E_TIMEOUT_REPLY;
306 }
307 }
308 }
309 plist_free(dict);
310 return res;
311}
312
313companion_proxy_error_t companion_proxy_start_forwarding_service_port(companion_proxy_client_t client, uint16_t remote_port, const char* service_name, uint16_t* forward_port, plist_t options)
314{
315 if (!client) {
316 return COMPANION_PROXY_E_INVALID_ARG;
317 }
318
319 plist_t dict = plist_new_dict();
320 plist_dict_set_item(dict, "Command", plist_new_string("StartForwardingServicePort"));
321 plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port));
322 if (service_name) {
323 plist_dict_set_item(dict, "ForwardedServiceName", plist_new_string(service_name));
324 }
325 plist_dict_set_item(dict, "IsServiceLowPriority", plist_new_bool(0));
326 plist_dict_set_item(dict, "PreferWifi", plist_new_bool(0));
327 if (options) {
328 plist_dict_merge(&dict, options);
329 }
330
331 companion_proxy_error_t res = companion_proxy_send(client, dict);
332 plist_free(dict);
333 dict = NULL;
334 if (res != COMPANION_PROXY_E_SUCCESS) {
335 return res;
336 }
337
338 res = companion_proxy_receive(client, &dict);
339 if (res != COMPANION_PROXY_E_SUCCESS) {
340 return res;
341 }
342 plist_t val = plist_dict_get_item(dict, "CompanionProxyServicePort");
343 if (val) {
344 uint64_t u64val = 0;
345 plist_get_uint_val(val, &u64val);
346 *forward_port = (uint16_t)u64val;
347 res = COMPANION_PROXY_E_SUCCESS;
348 } else {
349 res = COMPANION_PROXY_E_UNKNOWN_ERROR;
350 }
351 plist_free(dict);
352
353 return res;
354}
355
356companion_proxy_error_t companion_proxy_stop_forwarding_service_port(companion_proxy_client_t client, uint16_t remote_port)
357{
358 if (!client) {
359 return COMPANION_PROXY_E_INVALID_ARG;
360 }
361
362 plist_t dict = plist_new_dict();
363 plist_dict_set_item(dict, "Command", plist_new_string("StopForwardingServicePort"));
364 plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port));
365
366 companion_proxy_error_t res = companion_proxy_send(client, dict);
367 plist_free(dict);
368 dict = NULL;
369 if (res != COMPANION_PROXY_E_SUCCESS) {
370 return res;
371 }
372
373 res = companion_proxy_receive(client, &dict);
374 if (res != COMPANION_PROXY_E_SUCCESS) {
375 return res;
376 }
377 plist_free(dict);
378
379 return res;
380}
diff --git a/src/companion_proxy.h b/src/companion_proxy.h
new file mode 100644
index 0000000..e36932a
--- /dev/null
+++ b/src/companion_proxy.h
@@ -0,0 +1,35 @@
1/*
2 * companion_proxy.h
3 * com.apple.companion_proxy service header file.
4 *
5 * Copyright (c) 2019-2020 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __COMPANION_PROXY_H
23#define __COMPANION_PROXY_H
24
25#include "idevice.h"
26#include "libimobiledevice/companion_proxy.h"
27#include "property_list_service.h"
28#include <libimobiledevice-glue/thread.h>
29
30struct companion_proxy_client_private {
31 property_list_service_client_t parent;
32 THREAD_T event_thread;
33};
34
35#endif
diff --git a/src/debug.c b/src/debug.c
deleted file mode 100644
index 26a9678..0000000
--- a/src/debug.c
+++ /dev/null
@@ -1,164 +0,0 @@
1/*
2 * debug.c
3 * contains utilitary functions for debugging
4 *
5 * Copyright (c) 2008 Jonathan Beck All Rights Reserved.
6 * Copyright (c) 2010 Martin S. All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26#include <stdarg.h>
27#define _GNU_SOURCE 1
28#define __USE_GNU 1
29#include <stdio.h>
30#include <stdint.h>
31#include <stdlib.h>
32
33#include "debug.h"
34#include "libimobiledevice/libimobiledevice.h"
35
36int debug_level = 0;
37
38/**
39 * Sets the level of debugging. Currently the only acceptable values are 0 and
40 * 1.
41 *
42 * @param level Set to 0 for no debugging or 1 for debugging.
43 */
44void idevice_set_debug_level(int level)
45{
46 debug_level = level;
47}
48
49#ifndef STRIP_DEBUG_CODE
50static void debug_print_line(const char *func, const char *file, int line, const char *buffer)
51{
52 char *str_time = NULL;
53 char *header = NULL;
54 time_t the_time;
55
56 time(&the_time);
57 str_time = g_new0 (gchar, 255);
58 strftime(str_time, 254, "%H:%M:%S", localtime (&the_time));
59
60 /* generate header text */
61 (void)asprintf(&header, "%s %s:%d %s()", str_time, file, line, func);
62 free (str_time);
63
64 /* trim ending newlines */
65
66 /* print header */
67 printf ("%s: ", header);
68
69 /* print actual debug content */
70 printf ("%s\n", buffer);
71
72 /* flush this output, as we need to debug */
73 fflush (stdout);
74
75 free (header);
76}
77#endif
78
79inline void debug_info_real(const char *func, const char *file, int line, const char *format, ...)
80{
81#ifndef STRIP_DEBUG_CODE
82 va_list args;
83 char *buffer = NULL;
84
85 if (!debug_level)
86 return;
87
88 /* run the real fprintf */
89 va_start(args, format);
90 (void)vasprintf(&buffer, format, args);
91 va_end(args);
92
93 debug_print_line(func, file, line, buffer);
94
95 free(buffer);
96#endif
97}
98
99inline void debug_buffer(const char *data, const int length)
100{
101#ifndef STRIP_DEBUG_CODE
102 int i;
103 int j;
104 unsigned char c;
105
106 if (debug_level) {
107 for (i = 0; i < length; i += 16) {
108 fprintf(stderr, "%04x: ", i);
109 for (j = 0; j < 16; j++) {
110 if (i + j >= length) {
111 fprintf(stderr, " ");
112 continue;
113 }
114 fprintf(stderr, "%02hhx ", *(data + i + j));
115 }
116 fprintf(stderr, " | ");
117 for (j = 0; j < 16; j++) {
118 if (i + j >= length)
119 break;
120 c = *(data + i + j);
121 if ((c < 32) || (c > 127)) {
122 fprintf(stderr, ".");
123 continue;
124 }
125 fprintf(stderr, "%c", c);
126 }
127 fprintf(stderr, "\n");
128 }
129 fprintf(stderr, "\n");
130 }
131#endif
132}
133
134inline void debug_buffer_to_file(const char *file, const char *data, const int length)
135{
136#ifndef STRIP_DEBUG_CODE
137 if (debug_level) {
138 FILE *f = fopen(file, "w+");
139 fwrite(data, 1, length, f);
140 fflush(f);
141 fclose(f);
142 }
143#endif
144}
145
146inline void debug_plist_real(const char *func, const char *file, int line, plist_t plist)
147{
148#ifndef STRIP_DEBUG_CODE
149 if (!plist)
150 return;
151
152 char *buffer = NULL;
153 uint32_t length = 0;
154 plist_to_xml(plist, &buffer, &length);
155
156 /* get rid of ending newline as one is already added in the debug line */
157 if (buffer[length-1] == '\n')
158 buffer[length-1] = '\0';
159
160 debug_info_real(func, file, line, "printing %i bytes plist:\n%s", length, buffer);
161 free(buffer);
162#endif
163}
164
diff --git a/src/debug.h b/src/debug.h
deleted file mode 100644
index 2fd0960..0000000
--- a/src/debug.h
+++ /dev/null
@@ -1,52 +0,0 @@
1/*
2 * debug.h
3 * contains utilitary functions for debugging
4 *
5 * Copyright (c) 2008 Jonathan Beck All Rights Reserved.
6 * Copyright (c) 2010 Martin S. All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifndef DEBUG_H
24#define DEBUG_H
25
26#include <plist/plist.h>
27#include <glib.h>
28
29#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && !defined(STRIP_DEBUG_CODE)
30#define debug_info(...) debug_info_real (__func__, __FILE__, __LINE__, __VA_ARGS__)
31#define debug_plist(a) debug_plist_real (__func__, __FILE__, __LINE__, a)
32#elif defined(__GNUC__) && __GNUC__ >= 3 && !defined(STRIP_DEBUG_CODE)
33#define debug_info(...) debug_info_real (__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
34#define debug_plist(a) debug_plist_real (__FUNCTION__, __FILE__, __LINE__, a)
35#else
36#define debug_info(...)
37#define debug_plist(a)
38#endif
39
40G_GNUC_INTERNAL inline void debug_info_real(const char *func,
41 const char *file,
42 int line,
43 const char *format, ...);
44
45G_GNUC_INTERNAL inline void debug_buffer(const char *data, const int length);
46G_GNUC_INTERNAL inline void debug_buffer_to_file(const char *file, const char *data, const int length);
47G_GNUC_INTERNAL inline void debug_plist_real(const char *func,
48 const char *file,
49 int line,
50 plist_t plist);
51
52#endif
diff --git a/src/debugserver.c b/src/debugserver.c
new file mode 100644
index 0000000..74ade8a
--- /dev/null
+++ b/src/debugserver.c
@@ -0,0 +1,657 @@
1/*
2 * debugserver.c
3 * com.apple.debugserver service implementation.
4 *
5 * Copyright (c) 2019 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2014-2015 Martin Szulecki All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26#include <string.h>
27#include <stdlib.h>
28#define _GNU_SOURCE 1
29#define __USE_GNU 1
30#include <stdio.h>
31
32#include <libimobiledevice-glue/utils.h>
33
34#include "debugserver.h"
35#include "lockdown.h"
36#include "common/debug.h"
37#include "asprintf.h"
38
39/**
40 * Convert a service_error_t value to a debugserver_error_t value.
41 * Used internally to get correct error codes.
42 *
43 * @param err An service_error_t error code
44 *
45 * @return A matching debugserver_error_t error code,
46 * DEBUGSERVER_E_UNKNOWN_ERROR otherwise.
47 */
48static debugserver_error_t debugserver_error(service_error_t err)
49{
50 switch (err) {
51 case SERVICE_E_SUCCESS:
52 return DEBUGSERVER_E_SUCCESS;
53 case SERVICE_E_INVALID_ARG:
54 return DEBUGSERVER_E_INVALID_ARG;
55 case SERVICE_E_MUX_ERROR:
56 return DEBUGSERVER_E_MUX_ERROR;
57 case SERVICE_E_SSL_ERROR:
58 return DEBUGSERVER_E_SSL_ERROR;
59 case SERVICE_E_TIMEOUT:
60 return DEBUGSERVER_E_TIMEOUT;
61 default:
62 break;
63 }
64 return DEBUGSERVER_E_UNKNOWN_ERROR;
65}
66
67debugserver_error_t debugserver_client_new(idevice_t device, lockdownd_service_descriptor_t service, debugserver_client_t* client)
68{
69 *client = NULL;
70
71 if (!device || !service || service->port == 0 || !client || *client) {
72 debug_info("Incorrect parameter passed to debugserver_client_new.");
73 return DEBUGSERVER_E_INVALID_ARG;
74 }
75
76 debug_info("Creating debugserver_client, port = %d.", service->port);
77
78 service_client_t parent = NULL;
79 debugserver_error_t ret = debugserver_error(service_client_new(device, service, &parent));
80 if (ret != DEBUGSERVER_E_SUCCESS) {
81 debug_info("Creating base service client failed. Error: %i", ret);
82 return ret;
83 }
84
85 if (service->identifier && (strcmp(service->identifier, DEBUGSERVER_SECURE_SERVICE_NAME) != 0)) {
86 service_disable_bypass_ssl(parent, 1);
87 }
88
89 debugserver_client_t client_loc = (debugserver_client_t) malloc(sizeof(struct debugserver_client_private));
90 client_loc->parent = parent;
91 client_loc->noack_mode = 0;
92 client_loc->cancel_receive = NULL;
93 client_loc->receive_loop_timeout = 1000;
94
95 *client = client_loc;
96
97 debug_info("debugserver_client successfully created.");
98 return DEBUGSERVER_E_SUCCESS;
99}
100
101debugserver_error_t debugserver_client_start_service(idevice_t device, debugserver_client_t * client, const char* label)
102{
103 debugserver_error_t err = DEBUGSERVER_E_UNKNOWN_ERROR;
104 service_client_factory_start_service(device, DEBUGSERVER_SECURE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err);
105 if (err != DEBUGSERVER_E_SUCCESS) {
106 err = DEBUGSERVER_E_UNKNOWN_ERROR;
107 service_client_factory_start_service(device, DEBUGSERVER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err);
108 }
109 return err;
110}
111
112debugserver_error_t debugserver_client_free(debugserver_client_t client)
113{
114 if (!client)
115 return DEBUGSERVER_E_INVALID_ARG;
116
117 debugserver_error_t err = debugserver_error(service_client_free(client->parent));
118 client->parent = NULL;
119 free(client);
120
121 return err;
122}
123
124debugserver_error_t debugserver_client_send(debugserver_client_t client, const char* data, uint32_t size, uint32_t *sent)
125{
126 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
127 int bytes = 0;
128
129 if (!client || !data || (size == 0)) {
130 return DEBUGSERVER_E_INVALID_ARG;
131 }
132
133 debug_info("sending %d bytes", size);
134 res = debugserver_error(service_send(client->parent, data, size, (uint32_t*)&bytes));
135 if (bytes <= 0) {
136 debug_info("ERROR: sending to device failed.");
137 }
138 if (sent) {
139 *sent = (uint32_t)bytes;
140 }
141
142 return res;
143}
144
145debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
146{
147 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
148 int bytes = 0;
149
150 if (!client || !data || (size == 0)) {
151 return DEBUGSERVER_E_INVALID_ARG;
152 }
153
154 res = debugserver_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
155 if (bytes <= 0 && res != DEBUGSERVER_E_TIMEOUT) {
156 debug_info("Could not read data, error %d", res);
157 }
158 if (received) {
159 *received = (uint32_t)bytes;
160 }
161
162 return (bytes > 0) ? DEBUGSERVER_E_SUCCESS : res;
163}
164
165debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received)
166{
167 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
168 do {
169 /* Is this allowed to return DEBUGSERVER_E_TIMEOUT and also set data and received? */
170 res = debugserver_client_receive_with_timeout(client, data, size, received, client->receive_loop_timeout);
171 } while (res == DEBUGSERVER_E_TIMEOUT && client->cancel_receive != NULL && !client->cancel_receive());
172 return res;
173}
174
175debugserver_error_t debugserver_command_new(const char* name, int argc, char* argv[], debugserver_command_t* command)
176{
177 int i;
178 debugserver_command_t tmp = (debugserver_command_t) malloc(sizeof(struct debugserver_command_private));
179
180 /* copy name */
181 tmp->name = strdup(name);
182
183 /* copy arguments */
184 tmp->argc = argc;
185 tmp->argv = NULL;
186 if (argc > 0) {
187 tmp->argv = malloc(sizeof(char*) * (argc + 2));
188 for (i = 0; i < argc; i++) {
189 tmp->argv[i] = strdup(argv[i]);
190 }
191 tmp->argv[i+1] = NULL;
192 }
193
194 /* return */
195 *command = tmp;
196
197 return DEBUGSERVER_E_SUCCESS;
198}
199
200debugserver_error_t debugserver_command_free(debugserver_command_t command)
201{
202 int i;
203 debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
204
205 if (!command)
206 return DEBUGSERVER_E_INVALID_ARG;
207
208 if (command) {
209 if (command->name)
210 free(command->name);
211 if (command->argv && command->argc) {
212 for (i = 0; i < command->argc; i++) {
213 free(command->argv[i]);
214 }
215 free(command->argv);
216 }
217 free(command);
218 res = DEBUGSERVER_E_SUCCESS;
219 }
220
221 return res;
222}
223
224static int debugserver_hex2int(char c)
225{
226 if (c >= '0' && c <= '9')
227 return c - '0';
228 else if (c >= 'a' && c <= 'f')
229 return 10 + c - 'a';
230 else if (c >= 'A' && c <= 'F')
231 return 10 + c - 'A';
232 else
233 return c;
234}
235
236static char debugserver_int2hex(int x)
237{
238 const char *hexchars = "0123456789ABCDEF";
239 return hexchars[x];
240}
241
242#define DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(byte) debugserver_int2hex(((byte) >> 0x4) & 0xf)
243#define DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(byte) debugserver_int2hex((byte) & 0xf)
244#define DEBUGSERVER_HEX_DECODE_FIRST_BYTE(byte) (((byte) >> 0x4) & 0xf)
245#define DEBUGSERVER_HEX_DECODE_SECOND_BYTE(byte) ((byte) & 0xf)
246
247static uint32_t debugserver_get_checksum_for_buffer(const char* buffer, uint32_t size)
248{
249 uint32_t checksum = 0;
250 uint32_t i;
251
252 for (i = 0; i < size; i++) {
253 checksum += buffer[i];
254 }
255
256 return checksum;
257}
258
259static int debugserver_response_is_checksum_valid(const char* response, uint32_t size)
260{
261 uint32_t checksum = 0;
262 if ((size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1) > 0)
263 checksum = debugserver_get_checksum_for_buffer(&response[1], size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1);
264
265 debug_info("checksum: 0x%x", checksum);
266
267 if ((unsigned)debugserver_hex2int(response[size - 2]) != DEBUGSERVER_HEX_DECODE_FIRST_BYTE(checksum))
268 return 0;
269
270 if ((unsigned)debugserver_hex2int(response[size - 1]) != DEBUGSERVER_HEX_DECODE_SECOND_BYTE(checksum))
271 return 0;
272
273 debug_info("valid checksum");
274
275 return 1;
276}
277
278void debugserver_encode_string(const char* buffer, char** encoded_buffer, uint32_t* encoded_length)
279{
280 uint32_t position;
281 uint32_t index;
282 uint32_t length = strlen(buffer);
283 *encoded_length = (2 * length) + DEBUGSERVER_CHECKSUM_HASH_LENGTH + 1;
284
285 *encoded_buffer = malloc(sizeof(char) * (*encoded_length));
286 memset(*encoded_buffer, '\0', *encoded_length);
287 for (position = 0, index = 0; index < length; index++) {
288 position = (index * (2 * sizeof(char)));
289 (*encoded_buffer)[position] = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(buffer[index]);
290 (*encoded_buffer)[position + 1] = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(buffer[index]);
291 }
292}
293
294void debugserver_decode_string(const char *encoded_buffer, size_t encoded_length, char** buffer)
295{
296 *buffer = malloc(sizeof(char) * ((encoded_length / 2)+1));
297 char* t = *buffer;
298 const char *f = encoded_buffer;
299 const char *fend = f + encoded_length;
300 while (f < fend) {
301 *t++ = debugserver_hex2int(*f) << 4 | debugserver_hex2int(f[1]);
302 f += 2;
303 }
304 *t = '\0';
305}
306
307static void debugserver_format_command(const char* prefix, const char* command, const char* arguments, int calculate_checksum, char** buffer, uint32_t* size)
308{
309 char checksum_hash[DEBUGSERVER_CHECKSUM_HASH_LENGTH + 1] = {'#', '0', '0', '\0'};
310 char* encoded = NULL;
311 uint32_t encoded_length = 0;
312
313 if (arguments) {
314 /* arguments must be hex encoded */
315 debugserver_encode_string(arguments, &encoded, &encoded_length);
316 } else {
317 encoded = NULL;
318 }
319
320 char* encoded_command = string_concat(command, encoded, NULL);
321 encoded_length = strlen(encoded_command);
322
323 if (calculate_checksum) {
324 uint32_t checksum = debugserver_get_checksum_for_buffer(encoded_command, encoded_length);
325 checksum_hash[1] = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(checksum);
326 checksum_hash[2] = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(checksum);
327 }
328
329 *buffer = string_concat(prefix, encoded_command, checksum_hash, NULL);
330 *size = strlen(prefix) + strlen(encoded_command) + DEBUGSERVER_CHECKSUM_HASH_LENGTH;
331
332 debug_info("formatted command: %s size: %d checksum: 0x%s", *buffer, *size, checksum_hash);
333
334 if (encoded_command)
335 free(encoded_command);
336
337 if (encoded)
338 free(encoded);
339}
340
341static debugserver_error_t debugserver_client_send_ack(debugserver_client_t client)
342{
343 debug_info("sending ACK");
344 return debugserver_client_send(client, "+", sizeof(char), NULL);
345}
346
347static debugserver_error_t debugserver_client_send_noack(debugserver_client_t client)
348{
349 debug_info("sending !ACK");
350 return debugserver_client_send(client, "-", sizeof(char), NULL);
351}
352
353debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled)
354{
355 if (!client)
356 return DEBUGSERVER_E_INVALID_ARG;
357
358 client->noack_mode = (enabled == 0)? 1: 0;
359
360 debug_info("ack mode: %s", client->noack_mode == 0 ? "on": "off");
361
362 return DEBUGSERVER_E_SUCCESS;
363}
364
365debugserver_error_t debugserver_client_set_receive_params(debugserver_client_t client, int (*cancel_receive)(), int receive_loop_timeout)
366{
367 if (!client)
368 return DEBUGSERVER_E_INVALID_ARG;
369
370 client->cancel_receive = cancel_receive;
371 client->receive_loop_timeout = receive_loop_timeout;
372
373 debug_info("receive params: cancel_receive %s, receive_loop_timeout %dms", (client->cancel_receive == NULL ? "unset": "set"), client->receive_loop_timeout);
374
375 return DEBUGSERVER_E_SUCCESS;
376}
377
378static debugserver_error_t debugserver_client_receive_internal_char(debugserver_client_t client, char* received_char)
379{
380 debugserver_error_t res = DEBUGSERVER_E_SUCCESS;
381 uint32_t bytes = 0;
382
383 /* we loop here as we expect an answer */
384 res = debugserver_client_receive(client, received_char, sizeof(char), &bytes);
385 if (res != DEBUGSERVER_E_SUCCESS) {
386 return res;
387 }
388 if (bytes != 1) {
389 debug_info("received %d bytes when asking for %d!", bytes, sizeof(char));
390 return DEBUGSERVER_E_UNKNOWN_ERROR;
391 }
392 return res;
393}
394
395debugserver_error_t debugserver_client_receive_response(debugserver_client_t client, char** response, size_t* response_size)
396{
397 debugserver_error_t res = DEBUGSERVER_E_SUCCESS;
398
399 char data = '\0';
400 int skip_prefix = 0;
401
402 char* buffer = malloc(1024);
403 uint32_t buffer_size = 0;
404 uint32_t buffer_capacity = 1024;
405
406 if (response)
407 *response = NULL;
408
409 if (!client->noack_mode) {
410 debug_info("attempting to receive ACK (+)");
411 res = debugserver_client_receive_internal_char(client, &data);
412 if (res != DEBUGSERVER_E_SUCCESS) {
413 goto cleanup;
414 }
415 if (data == '+') {
416 debug_info("received ACK (+)");
417 } else if (data == '$') {
418 debug_info("received prefix ($)");
419 buffer[0] = '$';
420 buffer_size = 1;
421 skip_prefix = 1;
422 } else {
423 debug_info("unrecognized response when looking for ACK: %c", data);
424 goto cleanup;
425 }
426 }
427
428 debug_info("skip_prefix: %d", skip_prefix);
429
430 if (!skip_prefix) {
431 debug_info("attempting to receive prefix ($)");
432 res = debugserver_client_receive_internal_char(client, &data);
433 if (res != DEBUGSERVER_E_SUCCESS) {
434 goto cleanup;
435 }
436 if (data == '$') {
437 debug_info("received prefix ($)");
438 buffer[0] = '$';
439 buffer_size = 1;
440 } else {
441 debug_info("unrecognized response when looking for prefix: %c", data);
442 goto cleanup;
443 }
444 }
445
446 uint32_t checksum_length = DEBUGSERVER_CHECKSUM_HASH_LENGTH;
447 int receiving_checksum_response = 0;
448 debug_info("attempting to read up response until checksum");
449
450 while ((checksum_length > 0)) {
451 res = debugserver_client_receive_internal_char(client, &data);
452 if (res != DEBUGSERVER_E_SUCCESS) {
453 goto cleanup;
454 }
455 if (data == '#') {
456 receiving_checksum_response = 1;
457 }
458 if (receiving_checksum_response) {
459 checksum_length--;
460 }
461 if (buffer_size + 1 >= buffer_capacity) {
462 char* newbuffer = realloc(buffer, buffer_capacity+1024);
463 if (!newbuffer) {
464 return DEBUGSERVER_E_UNKNOWN_ERROR;
465 }
466 buffer = newbuffer;
467 buffer_capacity += 1024;
468 }
469 buffer[buffer_size] = data;
470 buffer_size += sizeof(char);
471 }
472 debug_info("validating response checksum...");
473 if (client->noack_mode || debugserver_response_is_checksum_valid(buffer, buffer_size)) {
474 if (response) {
475 /* assemble response string */
476 uint32_t resp_size = sizeof(char) * (buffer_size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1);
477 *response = (char*)malloc(resp_size + 1);
478 memcpy(*response, buffer + 1, resp_size);
479 (*response)[resp_size] = '\0';
480 if (response_size) *response_size = resp_size;
481 }
482 if (!client->noack_mode) {
483 /* confirm valid command */
484 debugserver_client_send_ack(client);
485 }
486 } else {
487 /* response was invalid */
488 res = DEBUGSERVER_E_RESPONSE_ERROR;
489 if (!client->noack_mode) {
490 /* report invalid command */
491 debugserver_client_send_noack(client);
492 }
493 }
494
495cleanup:
496 if (response) {
497 debug_info("response: %s", *response);
498 }
499
500 if (buffer)
501 free(buffer);
502
503 return res;
504}
505
506debugserver_error_t debugserver_client_send_command(debugserver_client_t client, debugserver_command_t command, char** response, size_t* response_size)
507{
508 debugserver_error_t res = DEBUGSERVER_E_SUCCESS;
509 int i;
510 uint32_t bytes = 0;
511
512 char* send_buffer = NULL;
513 uint32_t send_buffer_size = 0;
514
515 char* command_arguments = NULL;
516
517 /* concat all arguments */
518 for (i = 0; i < command->argc; i++) {
519 debug_info("argv[%d]: %s", i, command->argv[i]);
520 command_arguments = string_append(command_arguments, command->argv[i], NULL);
521 }
522
523 debug_info("command_arguments(%d): %s", command->argc, command_arguments);
524
525 /* encode command arguments, add checksum if required and assemble entire command */
526 debugserver_format_command("$", command->name, command_arguments, 1, &send_buffer, &send_buffer_size);
527
528 debug_info("sending encoded command: %s", send_buffer);
529
530 res = debugserver_client_send(client, send_buffer, send_buffer_size, &bytes);
531 debug_info("command result: %d", res);
532 if (res != DEBUGSERVER_E_SUCCESS) {
533 goto cleanup;
534 }
535
536 /* receive response */
537 res = debugserver_client_receive_response(client, response, response_size);
538 debug_info("response result: %d", res);
539 if (res != DEBUGSERVER_E_SUCCESS) {
540 goto cleanup;
541 }
542
543 if (response) {
544 debug_info("received response: %s", *response);
545 }
546
547 /* disable sending ack on the client */
548 if (!strncmp(command->name, "QStartNoAckMode", 16)) {
549 debugserver_client_set_ack_mode(client, 0);
550 }
551
552cleanup:
553 if (command_arguments)
554 free(command_arguments);
555
556 if (send_buffer)
557 free(send_buffer);
558
559 return res;
560}
561
562debugserver_error_t debugserver_client_set_environment_hex_encoded(debugserver_client_t client, const char* env, char** response)
563{
564 if (!client || !env)
565 return DEBUGSERVER_E_INVALID_ARG;
566
567 debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR;
568 char* env_tmp = strdup(env);
569 char* env_arg[2] = { env_tmp, NULL };
570
571 debugserver_command_t command = NULL;
572 debugserver_command_new("QEnvironmentHexEncoded:", 1, env_arg, &command);
573 result = debugserver_client_send_command(client, command, response, NULL);
574 debugserver_command_free(command);
575
576 free(env_tmp);
577
578 return result;
579}
580
581debugserver_error_t debugserver_client_set_argv(debugserver_client_t client, int argc, char* argv[], char** response)
582{
583 if (!client || !argc)
584 return DEBUGSERVER_E_INVALID_ARG;
585
586 debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR;
587 char *pkt = NULL;
588 size_t pkt_len = 0;
589 int i = 0;
590
591 /* calculate total length */
592 while (i < argc && argv && argv[i]) {
593 char *prefix = NULL;
594 int ret = asprintf(&prefix, ",%zu,%d,", strlen(argv[i]) * 2, i);
595 if (ret < 0 || prefix == NULL) {
596 debug_info("asprintf failed, out of memory?");
597 return DEBUGSERVER_E_UNKNOWN_ERROR;
598 }
599 pkt_len += strlen(prefix) + strlen(argv[i]) * 2;
600 free(prefix);
601 i++;
602 }
603
604 /* allocate packet and initialize it */
605 pkt = (char *) malloc(pkt_len + 1);
606 memset(pkt, 0, pkt_len + 1);
607
608 char *pktp = pkt;
609
610 i = 0;
611 while (i < argc && argv && argv[i]) {
612 debug_info("argv[%d] = \"%s\"", i, argv[i]);
613
614 char *prefix = NULL;
615 char *m = NULL;
616 size_t arg_len = strlen(argv[i]);
617 size_t arg_hexlen = arg_len * 2;
618
619 int ret = asprintf(&prefix, ",%zu,%d,", arg_hexlen, i);
620 if (ret < 0 || prefix == NULL) {
621 debug_info("asprintf failed, out of memory?");
622 return DEBUGSERVER_E_UNKNOWN_ERROR;
623 }
624
625 m = (char *) malloc(arg_hexlen);
626 char *p = m;
627 char *q = (char*)argv[i];
628 while (*q) {
629 *p++ = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(*q);
630 *p++ = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(*q);
631 q++;
632 }
633
634 memcpy(pktp, prefix, strlen(prefix));
635 pktp += strlen(prefix);
636
637 memcpy(pktp, m, arg_hexlen);
638 pktp += arg_hexlen;
639
640 free(prefix);
641 free(m);
642
643 i++;
644 }
645
646 pkt[0] = 'A';
647
648 debugserver_command_t command = NULL;
649 debugserver_command_new(pkt, 0, NULL, &command);
650 result = debugserver_client_send_command(client, command, response, NULL);
651 debugserver_command_free(command);
652
653 if (pkt)
654 free(pkt);
655
656 return result;
657}
diff --git a/src/debugserver.h b/src/debugserver.h
new file mode 100644
index 0000000..ce9c255
--- /dev/null
+++ b/src/debugserver.h
@@ -0,0 +1,44 @@
1/*
2 * debugserver.h
3 * com.apple.debugserver service header file.
4 *
5 * Copyright (c) 2014 Martin Szulecki All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef _DEBUGSERVER_H
23#define _DEBUGSERVER_H
24
25#include "idevice.h"
26#include "libimobiledevice/debugserver.h"
27#include "service.h"
28
29#define DEBUGSERVER_CHECKSUM_HASH_LENGTH 0x3
30
31struct debugserver_client_private {
32 service_client_t parent;
33 int noack_mode;
34 int (*cancel_receive)();
35 int receive_loop_timeout;
36};
37
38struct debugserver_command_private {
39 char* name;
40 int argc;
41 char** argv;
42};
43
44#endif
diff --git a/src/device_link_service.c b/src/device_link_service.c
index 6083d80..66c2461 100644
--- a/src/device_link_service.c
+++ b/src/device_link_service.c
@@ -1,28 +1,53 @@
1 /* 1/*
2 * device_link_service.c 2 * device_link_service.c
3 * DeviceLink service implementation. 3 * DeviceLink service implementation.
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
21#include <string.h> 25#include <string.h>
22#include <stdlib.h> 26#include <stdlib.h>
23#include "device_link_service.h" 27#include "device_link_service.h"
24#include "property_list_service.h" 28#include "property_list_service.h"
25#include "debug.h" 29#include "common/debug.h"
30
31static device_link_service_error_t device_link_error(property_list_service_error_t err)
32{
33 switch (err) {
34 case PROPERTY_LIST_SERVICE_E_SUCCESS:
35 return DEVICE_LINK_SERVICE_E_SUCCESS;
36 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
37 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
38 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
39 return DEVICE_LINK_SERVICE_E_PLIST_ERROR;
40 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
41 return DEVICE_LINK_SERVICE_E_MUX_ERROR;
42 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
43 return DEVICE_LINK_SERVICE_E_SSL_ERROR;
44 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
45 return DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT;
46 default:
47 break;
48 }
49 return DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
50}
26 51
27/** 52/**
28 * Internally used function to extract the message string from a DL* message 53 * Internally used function to extract the message string from a DL* message
@@ -58,7 +83,7 @@ static int device_link_service_get_message(plist_t dl_msg, char **message)
58 return 0; 83 return 0;
59 } 84 }
60 85
61 if ((strlen(cmd_str) < 9) || (strncmp(cmd_str, "DL", 2))) { 86 if ((strlen(cmd_str) < 9) || (strncmp(cmd_str, "DL", 2) != 0)) {
62 free(cmd_str); 87 free(cmd_str);
63 return 0; 88 return 0;
64 } 89 }
@@ -74,7 +99,7 @@ static int device_link_service_get_message(plist_t dl_msg, char **message)
74 * Creates a new device link service client. 99 * Creates a new device link service client.
75 * 100 *
76 * @param device The device to connect to. 101 * @param device The device to connect to.
77 * @param port Port on device to connect to. 102 * @param service The service descriptor returned by lockdownd_start_service.
78 * @param client Reference that will point to a newly allocated 103 * @param client Reference that will point to a newly allocated
79 * device_link_service_client_t upon successful return. 104 * device_link_service_client_t upon successful return.
80 * 105 *
@@ -82,15 +107,16 @@ static int device_link_service_get_message(plist_t dl_msg, char **message)
82 * DEVICE_LINK_SERVICE_E_INVALID_ARG when one of the parameters is invalid, 107 * DEVICE_LINK_SERVICE_E_INVALID_ARG when one of the parameters is invalid,
83 * or DEVICE_LINK_SERVICE_E_MUX_ERROR when the connection failed. 108 * or DEVICE_LINK_SERVICE_E_MUX_ERROR when the connection failed.
84 */ 109 */
85device_link_service_error_t device_link_service_client_new(idevice_t device, uint16_t port, device_link_service_client_t *client) 110device_link_service_error_t device_link_service_client_new(idevice_t device, lockdownd_service_descriptor_t service, device_link_service_client_t *client)
86{ 111{
87 if (!device || port == 0 || !client || *client) { 112 if (!device || !service || service->port == 0 || !client || *client) {
88 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 113 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
89 } 114 }
90 115
91 property_list_service_client_t plistclient = NULL; 116 property_list_service_client_t plistclient = NULL;
92 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 117 device_link_service_error_t err = device_link_error(property_list_service_client_new(device, service, &plistclient));
93 return DEVICE_LINK_SERVICE_E_MUX_ERROR; 118 if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
119 return err;
94 } 120 }
95 121
96 /* create client object */ 122 /* create client object */
@@ -117,11 +143,10 @@ device_link_service_error_t device_link_service_client_free(device_link_service_
117 if (!client) 143 if (!client)
118 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 144 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
119 145
120 if (property_list_service_client_free(client->parent) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 146 device_link_service_error_t err = device_link_error(property_list_service_client_free(client->parent));
121 return DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
122 }
123 free(client); 147 free(client);
124 return DEVICE_LINK_SERVICE_E_SUCCESS; 148
149 return err;
125} 150}
126 151
127/** 152/**
@@ -145,7 +170,7 @@ device_link_service_error_t device_link_service_version_exchange(device_link_ser
145{ 170{
146 if (!client) 171 if (!client)
147 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 172 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
148 173
149 device_link_service_error_t err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR; 174 device_link_service_error_t err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
150 175
151 /* perform version exchange */ 176 /* perform version exchange */
@@ -153,13 +178,13 @@ device_link_service_error_t device_link_service_version_exchange(device_link_ser
153 char *msg = NULL; 178 char *msg = NULL;
154 179
155 /* receive DLMessageVersionExchange from device */ 180 /* receive DLMessageVersionExchange from device */
156 if (property_list_service_receive_plist(client->parent, &array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 181 err = device_link_error(property_list_service_receive_plist(client->parent, &array));
182 if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
157 debug_info("Did not receive initial message from device!"); 183 debug_info("Did not receive initial message from device!");
158 err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
159 goto leave; 184 goto leave;
160 } 185 }
161 device_link_service_get_message(array, &msg); 186 device_link_service_get_message(array, &msg);
162 if (!msg || strcmp(msg, "DLMessageVersionExchange")) { 187 if (!msg || strcmp(msg, "DLMessageVersionExchange") != 0) {
163 debug_info("Did not receive DLMessageVersionExchange from device!"); 188 debug_info("Did not receive DLMessageVersionExchange from device!");
164 err = DEVICE_LINK_SERVICE_E_PLIST_ERROR; 189 err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
165 goto leave; 190 goto leave;
@@ -199,22 +224,22 @@ device_link_service_error_t device_link_service_version_exchange(device_link_ser
199 plist_array_append_item(array, plist_new_string("DLMessageVersionExchange")); 224 plist_array_append_item(array, plist_new_string("DLMessageVersionExchange"));
200 plist_array_append_item(array, plist_new_string("DLVersionsOk")); 225 plist_array_append_item(array, plist_new_string("DLVersionsOk"));
201 plist_array_append_item(array, plist_new_uint(version_major)); 226 plist_array_append_item(array, plist_new_uint(version_major));
202 if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 227 err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
228 if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
203 debug_info("Error when sending DLVersionsOk"); 229 debug_info("Error when sending DLVersionsOk");
204 err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
205 goto leave; 230 goto leave;
206 } 231 }
207 plist_free(array); 232 plist_free(array);
208 233
209 /* receive DeviceReady message */ 234 /* receive DeviceReady message */
210 array = NULL; 235 array = NULL;
211 if (property_list_service_receive_plist(client->parent, &array) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 236 err = device_link_error(property_list_service_receive_plist(client->parent, &array));
237 if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
212 debug_info("Error when receiving DLMessageDeviceReady!"); 238 debug_info("Error when receiving DLMessageDeviceReady!");
213 err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
214 goto leave; 239 goto leave;
215 } 240 }
216 device_link_service_get_message(array, &msg); 241 device_link_service_get_message(array, &msg);
217 if (!msg || strcmp(msg, "DLMessageDeviceReady")) { 242 if (!msg || strcmp(msg, "DLMessageDeviceReady") != 0) {
218 debug_info("Did not get DLMessageDeviceReady!"); 243 debug_info("Did not get DLMessageDeviceReady!");
219 err = DEVICE_LINK_SERVICE_E_PLIST_ERROR; 244 err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
220 goto leave; 245 goto leave;
@@ -235,26 +260,28 @@ leave:
235 * Performs a disconnect with the connected device link service client. 260 * Performs a disconnect with the connected device link service client.
236 * 261 *
237 * @param client The device link service client to disconnect. 262 * @param client The device link service client to disconnect.
238 * 263 * @param message Optional message to send send to the device or NULL.
264 *
239 * @return DEVICE_LINK_SERVICE_E_SUCCESS on success, 265 * @return DEVICE_LINK_SERVICE_E_SUCCESS on success,
240 * DEVICE_LINK_SERVICE_E_INVALID_ARG if client is NULL, 266 * DEVICE_LINK_SERVICE_E_INVALID_ARG if client is NULL,
241 * or DEVICE_LINK_SERVICE_E_MUX_ERROR when there's an error when sending 267 * or DEVICE_LINK_SERVICE_E_MUX_ERROR when there's an error when sending
242 * the the disconnect message. 268 * the the disconnect message.
243 */ 269 */
244device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client) 270device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client, const char *message)
245{ 271{
246 if (!client) 272 if (!client)
247 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 273 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
248 274
249 plist_t array = plist_new_array(); 275 plist_t array = plist_new_array();
250 plist_array_append_item(array, plist_new_string("DLMessageDisconnect")); 276 plist_array_append_item(array, plist_new_string("DLMessageDisconnect"));
251 plist_array_append_item(array, plist_new_string("All done, thanks for the memories")); 277 if (message)
278 plist_array_append_item(array, plist_new_string(message));
279 else
280 plist_array_append_item(array, plist_new_string("___EmptyParameterString___"));
252 281
253 device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS; 282 device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
254 if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
255 err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
256 }
257 plist_free(array); 283 plist_free(array);
284
258 return err; 285 return err;
259} 286}
260 287
@@ -278,11 +305,9 @@ device_link_service_error_t device_link_service_send_ping(device_link_service_cl
278 plist_array_append_item(array, plist_new_string("DLMessagePing")); 305 plist_array_append_item(array, plist_new_string("DLMessagePing"));
279 plist_array_append_item(array, plist_new_string(message)); 306 plist_array_append_item(array, plist_new_string(message));
280 307
281 device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS; 308 device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
282 if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
283 err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
284 }
285 plist_free(array); 309 plist_free(array);
310
286 return err; 311 return err;
287} 312}
288 313
@@ -309,11 +334,9 @@ device_link_service_error_t device_link_service_send_process_message(device_link
309 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage")); 334 plist_array_append_item(array, plist_new_string("DLMessageProcessMessage"));
310 plist_array_append_item(array, plist_copy(message)); 335 plist_array_append_item(array, plist_copy(message));
311 336
312 device_link_service_error_t err = DEVICE_LINK_SERVICE_E_SUCCESS; 337 device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
313 if (property_list_service_send_binary_plist(client->parent, array) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
314 err = DEVICE_LINK_SERVICE_E_MUX_ERROR;
315 }
316 plist_free(array); 338 plist_free(array);
339
317 return err; 340 return err;
318} 341}
319 342
@@ -340,8 +363,9 @@ device_link_service_error_t device_link_service_receive_message(device_link_serv
340 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 363 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
341 364
342 *msg_plist = NULL; 365 *msg_plist = NULL;
343 if (property_list_service_receive_plist(client->parent, msg_plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 366 device_link_service_error_t err = device_link_error(property_list_service_receive_plist(client->parent, msg_plist));
344 return DEVICE_LINK_SERVICE_E_MUX_ERROR; 367 if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
368 return err;
345 } 369 }
346 370
347 if (!device_link_service_get_message(*msg_plist, dlmessage)) { 371 if (!device_link_service_get_message(*msg_plist, dlmessage)) {
@@ -370,15 +394,16 @@ device_link_service_error_t device_link_service_receive_process_message(device_l
370 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 394 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
371 395
372 plist_t pmsg = NULL; 396 plist_t pmsg = NULL;
373 if (property_list_service_receive_plist(client->parent, &pmsg) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 397 device_link_service_error_t err = device_link_error(property_list_service_receive_plist(client->parent, &pmsg));
374 return DEVICE_LINK_SERVICE_E_MUX_ERROR; 398 if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
399 return err;
375 } 400 }
376 401
377 device_link_service_error_t err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR; 402 err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
378 403
379 char *msg = NULL; 404 char *msg = NULL;
380 device_link_service_get_message(pmsg, &msg); 405 device_link_service_get_message(pmsg, &msg);
381 if (!msg || strcmp(msg, "DLMessageProcessMessage")) { 406 if (!msg || strcmp(msg, "DLMessageProcessMessage") != 0) {
382 debug_info("Did not receive DLMessageProcessMessage as expected!"); 407 debug_info("Did not receive DLMessageProcessMessage as expected!");
383 err = DEVICE_LINK_SERVICE_E_PLIST_ERROR; 408 err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
384 goto leave; 409 goto leave;
@@ -424,10 +449,7 @@ device_link_service_error_t device_link_service_send(device_link_service_client_
424 if (!client || !plist) { 449 if (!client || !plist) {
425 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 450 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
426 } 451 }
427 if (property_list_service_send_binary_plist(client->parent, plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 452 return device_link_error(property_list_service_send_binary_plist(client->parent, plist));
428 return DEVICE_LINK_SERVICE_E_MUX_ERROR;
429 }
430 return DEVICE_LINK_SERVICE_E_SUCCESS;
431} 453}
432 454
433/* Generic device link service receive function. 455/* Generic device link service receive function.
@@ -447,9 +469,6 @@ device_link_service_error_t device_link_service_receive(device_link_service_clie
447 return DEVICE_LINK_SERVICE_E_INVALID_ARG; 469 return DEVICE_LINK_SERVICE_E_INVALID_ARG;
448 } 470 }
449 471
450 if (property_list_service_receive_plist(client->parent, plist) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 472 return device_link_error(property_list_service_receive_plist(client->parent, plist));
451 return DEVICE_LINK_SERVICE_E_MUX_ERROR;
452 }
453 return DEVICE_LINK_SERVICE_E_SUCCESS;
454} 473}
455 474
diff --git a/src/device_link_service.h b/src/device_link_service.h
index 9953f77..0255b21 100644
--- a/src/device_link_service.h
+++ b/src/device_link_service.h
@@ -1,39 +1,41 @@
1 /* 1/*
2 * device_link_service.h 2 * device_link_service.h
3 * Definitions for the DeviceLink service 3 * Definitions for the DeviceLink service
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef DEVICE_LINK_SERVICE_H
22#define DEVICE_LINK_SERVICE_H
23 21
22#ifndef __DEVICE_LINK_SERVICE_H
23#define __DEVICE_LINK_SERVICE_H
24
25#include "idevice.h"
24#include "property_list_service.h" 26#include "property_list_service.h"
25 27
26/* Error Codes */ 28/* Error Codes */
27#define DEVICE_LINK_SERVICE_E_SUCCESS 0 29typedef enum {
28#define DEVICE_LINK_SERVICE_E_INVALID_ARG -1 30 DEVICE_LINK_SERVICE_E_SUCCESS = 0,
29#define DEVICE_LINK_SERVICE_E_PLIST_ERROR -2 31 DEVICE_LINK_SERVICE_E_INVALID_ARG = -1,
30#define DEVICE_LINK_SERVICE_E_MUX_ERROR -3 32 DEVICE_LINK_SERVICE_E_PLIST_ERROR = -2,
31#define DEVICE_LINK_SERVICE_E_BAD_VERSION -4 33 DEVICE_LINK_SERVICE_E_MUX_ERROR = -3,
32 34 DEVICE_LINK_SERVICE_E_SSL_ERROR = -4,
33#define DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR -256 35 DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT = -5,
34 36 DEVICE_LINK_SERVICE_E_BAD_VERSION = -6,
35/** Represents an error code. */ 37 DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR = -256
36typedef int16_t device_link_service_error_t; 38} device_link_service_error_t;
37 39
38struct device_link_service_client_private { 40struct device_link_service_client_private {
39 property_list_service_client_t parent; 41 property_list_service_client_t parent;
@@ -41,14 +43,14 @@ struct device_link_service_client_private {
41 43
42typedef struct device_link_service_client_private *device_link_service_client_t; 44typedef struct device_link_service_client_private *device_link_service_client_t;
43 45
44device_link_service_error_t device_link_service_client_new(idevice_t device, uint16_t port, device_link_service_client_t *client); 46device_link_service_error_t device_link_service_client_new(idevice_t device, lockdownd_service_descriptor_t service, device_link_service_client_t *client);
45device_link_service_error_t device_link_service_client_free(device_link_service_client_t client); 47device_link_service_error_t device_link_service_client_free(device_link_service_client_t client);
46device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor); 48device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor);
47device_link_service_error_t device_link_service_send_ping(device_link_service_client_t client, const char *message); 49device_link_service_error_t device_link_service_send_ping(device_link_service_client_t client, const char *message);
48device_link_service_error_t device_link_service_receive_message(device_link_service_client_t client, plist_t *msg_plist, char **dlmessage); 50device_link_service_error_t device_link_service_receive_message(device_link_service_client_t client, plist_t *msg_plist, char **dlmessage);
49device_link_service_error_t device_link_service_send_process_message(device_link_service_client_t client, plist_t message); 51device_link_service_error_t device_link_service_send_process_message(device_link_service_client_t client, plist_t message);
50device_link_service_error_t device_link_service_receive_process_message(device_link_service_client_t client, plist_t *message); 52device_link_service_error_t device_link_service_receive_process_message(device_link_service_client_t client, plist_t *message);
51device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client); 53device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client, const char *message);
52device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist); 54device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist);
53device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist); 55device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist);
54 56
diff --git a/src/diagnostics_relay.c b/src/diagnostics_relay.c
new file mode 100644
index 0000000..6ee3150
--- /dev/null
+++ b/src/diagnostics_relay.c
@@ -0,0 +1,467 @@
1/*
2 * diagnostics_relay.c
3 * com.apple.mobile.diagnostics_relay service implementation.
4 *
5 * Copyright (c) 2012 Martin Szulecki, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include "diagnostics_relay.h"
28#include "property_list_service.h"
29#include "common/debug.h"
30
31#define RESULT_SUCCESS 0
32#define RESULT_FAILURE 1
33#define RESULT_UNKNOWN_REQUEST 2
34
35/**
36 * Internally used function for checking the result from a service response
37 * plist to a previously sent request.
38 *
39 * @param dict The plist to evaluate.
40 *
41 * @return RESULT_SUCCESS when the result is 'Success',
42 * RESULT_FAILURE when the result is 'Failure',
43 * or a negative value if an error occurred during evaluation.
44 */
45static int diagnostics_relay_check_result(plist_t dict)
46{
47 int ret = -1;
48
49 plist_t result_node = plist_dict_get_item(dict, "Status");
50 if (!result_node)
51 return ret;
52
53 plist_type result_type = plist_get_node_type(result_node);
54 if (result_type == PLIST_STRING) {
55 char *result_value = NULL;
56
57 plist_get_string_val(result_node, &result_value);
58
59 if (result_value) {
60 if (!strcmp(result_value, "Success")) {
61 ret = RESULT_SUCCESS;
62 } else if (!strcmp(result_value, "Failure")) {
63 ret = RESULT_FAILURE;
64 } else if (!strcmp(result_value, "UnknownRequest")) {
65 ret = RESULT_UNKNOWN_REQUEST;
66 } else {
67 debug_info("ERROR: unknown result value '%s'", result_value);
68 }
69 }
70 if (result_value)
71 free(result_value);
72 }
73 return ret;
74}
75
76diagnostics_relay_error_t diagnostics_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, diagnostics_relay_client_t *client)
77{
78 if (!device || !service || service->port == 0 || !client || *client) {
79 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
80 }
81
82 property_list_service_client_t plistclient = NULL;
83 if (property_list_service_client_new(device, service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
84 return DIAGNOSTICS_RELAY_E_MUX_ERROR;
85 }
86
87 /* create client object */
88 diagnostics_relay_client_t client_loc = (diagnostics_relay_client_t) malloc(sizeof(struct diagnostics_relay_client_private));
89 client_loc->parent = plistclient;
90
91 /* all done, return success */
92 *client = client_loc;
93 return DIAGNOSTICS_RELAY_E_SUCCESS;
94}
95
96diagnostics_relay_error_t diagnostics_relay_client_start_service(idevice_t device, diagnostics_relay_client_t * client, const char* label)
97{
98 diagnostics_relay_error_t err = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
99 service_client_factory_start_service(device, DIAGNOSTICS_RELAY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(diagnostics_relay_client_new), &err);
100 return err;
101}
102
103diagnostics_relay_error_t diagnostics_relay_client_free(diagnostics_relay_client_t client)
104{
105 if (!client)
106 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
107
108 if (property_list_service_client_free(client->parent) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
109 return DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
110 }
111 free(client);
112 return DIAGNOSTICS_RELAY_E_SUCCESS;
113}
114
115/**
116 * Receives a plist from the service.
117 *
118 * @param client The diagnostics_relay client
119 * @param plist The plist to store the received data
120 *
121 * @return DIAGNOSTICS_RELAY_E_SUCCESS on success,
122 * DIAGNOSTICS_RELAY_E_INVALID_ARG when client or plist is NULL
123 */
124static diagnostics_relay_error_t diagnostics_relay_receive(diagnostics_relay_client_t client, plist_t *plist)
125{
126 if (!client || !plist || (plist && *plist))
127 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
128
129 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_SUCCESS;
130 property_list_service_error_t err;
131
132 err = property_list_service_receive_plist(client->parent, plist);
133 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
134 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
135 }
136
137 if (!*plist)
138 ret = DIAGNOSTICS_RELAY_E_PLIST_ERROR;
139
140 return ret;
141}
142
143/**
144 * Sends a plist to the service.
145 *
146 * @note This function is low-level and should only be used if you need to send
147 * a new type of message.
148 *
149 * @param client The diagnostics_relay client
150 * @param plist The plist to send
151 *
152 * @return DIAGNOSTICS_RELAY_E_SUCCESS on success,
153 * DIAGNOSTICS_RELAY_E_INVALID_ARG when client or plist is NULL
154 */
155static diagnostics_relay_error_t diagnostics_relay_send(diagnostics_relay_client_t client, plist_t plist)
156{
157 if (!client || !plist)
158 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
159
160 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_SUCCESS;
161 property_list_service_error_t err;
162
163 err = property_list_service_send_xml_plist(client->parent, plist);
164 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
165 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
166 }
167 return ret;
168}
169
170diagnostics_relay_error_t diagnostics_relay_goodbye(diagnostics_relay_client_t client)
171{
172 if (!client)
173 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
174
175 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
176
177 plist_t dict = plist_new_dict();
178 plist_dict_set_item(dict, "Request", plist_new_string("Goodbye"));
179
180 ret = diagnostics_relay_send(client, dict);
181 plist_free(dict);
182 dict = NULL;
183
184 ret = diagnostics_relay_receive(client, &dict);
185 if (!dict) {
186 debug_info("did not get goodbye response back");
187 return DIAGNOSTICS_RELAY_E_PLIST_ERROR;
188 }
189
190 int check = diagnostics_relay_check_result(dict);
191 if (check == RESULT_SUCCESS) {
192 ret = DIAGNOSTICS_RELAY_E_SUCCESS;
193 } else if (check == RESULT_UNKNOWN_REQUEST) {
194 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_REQUEST;
195 } else {
196 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
197 }
198
199 plist_free(dict);
200 dict = NULL;
201 return ret;
202}
203
204diagnostics_relay_error_t diagnostics_relay_sleep(diagnostics_relay_client_t client)
205{
206 if (!client)
207 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
208
209 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
210
211 plist_t dict = plist_new_dict();
212
213 plist_dict_set_item(dict,"Request", plist_new_string("Sleep"));
214 ret = diagnostics_relay_send(client, dict);
215 plist_free(dict);
216 dict = NULL;
217
218 ret = diagnostics_relay_receive(client, &dict);
219 if (!dict) {
220 return DIAGNOSTICS_RELAY_E_PLIST_ERROR;
221 }
222
223 int check = diagnostics_relay_check_result(dict);
224 if (check == RESULT_SUCCESS) {
225 ret = DIAGNOSTICS_RELAY_E_SUCCESS;
226 } else if (check == RESULT_UNKNOWN_REQUEST) {
227 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_REQUEST;
228 } else {
229 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
230 }
231
232 plist_free(dict);
233 return ret;
234}
235
236static diagnostics_relay_error_t internal_diagnostics_relay_action(diagnostics_relay_client_t client, const char* name, diagnostics_relay_action_t flags)
237{
238 if (!client)
239 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
240
241 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
242
243 plist_t dict = plist_new_dict();
244 plist_dict_set_item(dict,"Request", plist_new_string(name));
245
246 if (flags & DIAGNOSTICS_RELAY_ACTION_FLAG_WAIT_FOR_DISCONNECT) {
247 plist_dict_set_item(dict, "WaitForDisconnect", plist_new_bool(1));
248 }
249
250 if (flags & DIAGNOSTICS_RELAY_ACTION_FLAG_DISPLAY_PASS) {
251 plist_dict_set_item(dict, "DisplayPass", plist_new_bool(1));
252 }
253
254 if (flags & DIAGNOSTICS_RELAY_ACTION_FLAG_DISPLAY_FAIL) {
255 plist_dict_set_item(dict, "DisplayFail", plist_new_bool(1));
256 }
257
258 ret = diagnostics_relay_send(client, dict);
259 plist_free(dict);
260 dict = NULL;
261
262 ret = diagnostics_relay_receive(client, &dict);
263 if (!dict) {
264 return DIAGNOSTICS_RELAY_E_PLIST_ERROR;
265 }
266
267 int check = diagnostics_relay_check_result(dict);
268 if (check == RESULT_SUCCESS) {
269 ret = DIAGNOSTICS_RELAY_E_SUCCESS;
270 } else if (check == RESULT_UNKNOWN_REQUEST) {
271 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_REQUEST;
272 } else {
273 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
274 }
275
276 plist_free(dict);
277 return ret;
278}
279
280diagnostics_relay_error_t diagnostics_relay_restart(diagnostics_relay_client_t client, diagnostics_relay_action_t flags)
281{
282 return internal_diagnostics_relay_action(client, "Restart", flags);
283}
284
285diagnostics_relay_error_t diagnostics_relay_shutdown(diagnostics_relay_client_t client, diagnostics_relay_action_t flags)
286{
287 return internal_diagnostics_relay_action(client, "Shutdown", flags);
288}
289
290diagnostics_relay_error_t diagnostics_relay_request_diagnostics(diagnostics_relay_client_t client, const char* type, plist_t* diagnostics)
291{
292 if (!client || diagnostics == NULL)
293 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
294
295 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
296
297 plist_t dict = plist_new_dict();
298 plist_dict_set_item(dict,"Request", plist_new_string(type));
299 ret = diagnostics_relay_send(client, dict);
300 plist_free(dict);
301 dict = NULL;
302 if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) {
303 return ret;
304 }
305
306 ret = diagnostics_relay_receive(client, &dict);
307 if (!dict) {
308 return DIAGNOSTICS_RELAY_E_PLIST_ERROR;
309 }
310
311 int check = diagnostics_relay_check_result(dict);
312 if (check == RESULT_SUCCESS) {
313 ret = DIAGNOSTICS_RELAY_E_SUCCESS;
314 } else if (check == RESULT_UNKNOWN_REQUEST) {
315 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_REQUEST;
316 } else {
317 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
318 }
319
320 if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) {
321 plist_free(dict);
322 return ret;
323 }
324
325 plist_t value_node = plist_dict_get_item(dict, "Diagnostics");
326 if (value_node) {
327 *diagnostics = plist_copy(value_node);
328 }
329
330 plist_free(dict);
331 return ret;
332}
333
334diagnostics_relay_error_t diagnostics_relay_query_mobilegestalt(diagnostics_relay_client_t client, plist_t keys, plist_t* result)
335{
336 if (!client || plist_get_node_type(keys) != PLIST_ARRAY || result == NULL)
337 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
338
339 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
340
341 plist_t dict = plist_new_dict();
342 plist_dict_set_item(dict,"MobileGestaltKeys", plist_copy(keys));
343 plist_dict_set_item(dict,"Request", plist_new_string("MobileGestalt"));
344 ret = diagnostics_relay_send(client, dict);
345 plist_free(dict);
346 dict = NULL;
347 if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) {
348 return ret;
349 }
350
351 ret = diagnostics_relay_receive(client, &dict);
352 if (!dict) {
353 return DIAGNOSTICS_RELAY_E_PLIST_ERROR;
354 }
355
356 int check = diagnostics_relay_check_result(dict);
357 if (check == RESULT_SUCCESS) {
358 ret = DIAGNOSTICS_RELAY_E_SUCCESS;
359 } else if (check == RESULT_UNKNOWN_REQUEST) {
360 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_REQUEST;
361 } else {
362 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
363 }
364
365 if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) {
366 plist_free(dict);
367 return ret;
368 }
369
370 plist_t value_node = plist_dict_get_item(dict, "Diagnostics");
371 if (value_node) {
372 *result = plist_copy(value_node);
373 }
374
375 plist_free(dict);
376 return ret;
377}
378
379diagnostics_relay_error_t diagnostics_relay_query_ioregistry_entry(diagnostics_relay_client_t client, const char* entry_name, const char* entry_class, plist_t* result)
380{
381 if (!client || (entry_name == NULL && entry_class == NULL) || result == NULL)
382 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
383
384 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
385
386 plist_t dict = plist_new_dict();
387 if (entry_name)
388 plist_dict_set_item(dict,"EntryName", plist_new_string(entry_name));
389 if (entry_class)
390 plist_dict_set_item(dict,"EntryClass", plist_new_string(entry_class));
391 plist_dict_set_item(dict,"Request", plist_new_string("IORegistry"));
392 ret = diagnostics_relay_send(client, dict);
393 plist_free(dict);
394 dict = NULL;
395 if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) {
396 return ret;
397 }
398
399 ret = diagnostics_relay_receive(client, &dict);
400 if (!dict) {
401 return DIAGNOSTICS_RELAY_E_PLIST_ERROR;
402 }
403
404 int check = diagnostics_relay_check_result(dict);
405 if (check == RESULT_SUCCESS) {
406 ret = DIAGNOSTICS_RELAY_E_SUCCESS;
407 } else if (check == RESULT_UNKNOWN_REQUEST) {
408 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_REQUEST;
409 } else {
410 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
411 }
412
413 if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) {
414 plist_free(dict);
415 return ret;
416 }
417
418 plist_t value_node = plist_dict_get_item(dict, "Diagnostics");
419 if (value_node) {
420 *result = plist_copy(value_node);
421 }
422
423 plist_free(dict);
424 return ret;
425}
426
427diagnostics_relay_error_t diagnostics_relay_query_ioregistry_plane(diagnostics_relay_client_t client, const char* plane, plist_t* result)
428{
429 if (!client || plane == NULL || result == NULL)
430 return DIAGNOSTICS_RELAY_E_INVALID_ARG;
431
432 diagnostics_relay_error_t ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
433
434 plist_t dict = plist_new_dict();
435 plist_dict_set_item(dict,"CurrentPlane", plist_new_string(plane));
436 plist_dict_set_item(dict,"Request", plist_new_string("IORegistry"));
437 ret = diagnostics_relay_send(client, dict);
438 plist_free(dict);
439 dict = NULL;
440
441 ret = diagnostics_relay_receive(client, &dict);
442 if (!dict) {
443 return DIAGNOSTICS_RELAY_E_PLIST_ERROR;
444 }
445
446 int check = diagnostics_relay_check_result(dict);
447 if (check == RESULT_SUCCESS) {
448 ret = DIAGNOSTICS_RELAY_E_SUCCESS;
449 } else if (check == RESULT_UNKNOWN_REQUEST) {
450 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_REQUEST;
451 } else {
452 ret = DIAGNOSTICS_RELAY_E_UNKNOWN_ERROR;
453 }
454
455 if (ret != DIAGNOSTICS_RELAY_E_SUCCESS) {
456 plist_free(dict);
457 return ret;
458 }
459
460 plist_t value_node = plist_dict_get_item(dict, "Diagnostics");
461 if (value_node) {
462 *result = plist_copy(value_node);
463 }
464
465 plist_free(dict);
466 return ret;
467}
diff --git a/src/diagnostics_relay.h b/src/diagnostics_relay.h
new file mode 100644
index 0000000..3bb543a
--- /dev/null
+++ b/src/diagnostics_relay.h
@@ -0,0 +1,33 @@
1/*
2 * diagnostics_relay.h
3 * com.apple.mobile.diagnostics_relay service header file.
4 *
5 * Copyright (c) 2012 Martin Szulecki, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __DIAGNOSTICS_RELAY_H
23#define __DIAGNOSTICS_RELAY_H
24
25#include "idevice.h"
26#include "libimobiledevice/diagnostics_relay.h"
27#include "property_list_service.h"
28
29struct diagnostics_relay_client_private {
30 property_list_service_client_t parent;
31};
32
33#endif
diff --git a/src/file_relay.c b/src/file_relay.c
index 680e28d..fbe7cbf 100644
--- a/src/file_relay.c
+++ b/src/file_relay.c
@@ -1,49 +1,41 @@
1 /* 1/*
2 * file_relay.c 2 * file_relay.c
3 * com.apple.mobile.file_relay service implementation. 3 * com.apple.mobile.file_relay service implementation.
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
21#include <string.h> 25#include <string.h>
22#include <stdlib.h> 26#include <stdlib.h>
23#include "file_relay.h" 27#include "file_relay.h"
24#include "property_list_service.h" 28#include "property_list_service.h"
25#include "debug.h" 29#include "common/debug.h"
26 30
27/** 31file_relay_error_t file_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, file_relay_client_t *client)
28 * Connects to the file_relay service on the specified device.
29 *
30 * @param device The device to connect to.
31 * @param port Destination port (usually given by lockdownd_start_service).
32 * @param client Reference that will point to a newly allocated
33 * file_relay_client_t upon successful return.
34 *
35 * @return FILE_RELAY_E_SUCCESS on success,
36 * FILE_RELAY_E_INVALID_ARG when one of the parameters is invalid,
37 * or FILE_RELAY_E_MUX_ERROR when the connection failed.
38 */
39file_relay_error_t file_relay_client_new(idevice_t device, uint16_t port, file_relay_client_t *client)
40{ 32{
41 if (!device || port == 0 || !client || *client) { 33 if (!device || !service || service->port == 0 || !client || *client) {
42 return FILE_RELAY_E_INVALID_ARG; 34 return FILE_RELAY_E_INVALID_ARG;
43 } 35 }
44 36
45 property_list_service_client_t plistclient = NULL; 37 property_list_service_client_t plistclient = NULL;
46 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 38 if (property_list_service_client_new(device, service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
47 return FILE_RELAY_E_MUX_ERROR; 39 return FILE_RELAY_E_MUX_ERROR;
48 } 40 }
49 41
@@ -56,17 +48,13 @@ file_relay_error_t file_relay_client_new(idevice_t device, uint16_t port, file_r
56 return FILE_RELAY_E_SUCCESS; 48 return FILE_RELAY_E_SUCCESS;
57} 49}
58 50
59/** 51file_relay_error_t file_relay_client_start_service(idevice_t device, file_relay_client_t * client, const char* label)
60 * Disconnects a file_relay client from the device and frees up the file_relay 52{
61 * client data. 53 file_relay_error_t err = FILE_RELAY_E_UNKNOWN_ERROR;
62 * 54 service_client_factory_start_service(device, FILE_RELAY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(file_relay_client_new), &err);
63 * @param client The file_relay client to disconnect and free. 55 return err;
64 * 56}
65 * @return FILE_RELAY_E_SUCCESS on success, 57
66 * FILE_RELAY_E_INVALID_ARG when one of client or client->parent
67 * is invalid, or FILE_RELAY_E_UNKNOWN_ERROR when the was an error
68 * freeing the parent property_list_service client.
69 */
70file_relay_error_t file_relay_client_free(file_relay_client_t client) 58file_relay_error_t file_relay_client_free(file_relay_client_t client)
71{ 59{
72 if (!client) 60 if (!client)
@@ -75,40 +63,11 @@ file_relay_error_t file_relay_client_free(file_relay_client_t client)
75 if (property_list_service_client_free(client->parent) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 63 if (property_list_service_client_free(client->parent) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
76 return FILE_RELAY_E_UNKNOWN_ERROR; 64 return FILE_RELAY_E_UNKNOWN_ERROR;
77 } 65 }
66 free(client);
78 return FILE_RELAY_E_SUCCESS; 67 return FILE_RELAY_E_SUCCESS;
79} 68}
80 69
81/** 70file_relay_error_t file_relay_request_sources_timeout(file_relay_client_t client, const char **sources, idevice_connection_t *connection, unsigned int timeout)
82 * Request data for the given sources.
83 *
84 * @param client The connected file_relay client.
85 * @param sources A NULL-terminated list of sources to retrieve.
86 * Valid sources are:
87 * - AppleSupport
88 * - Network
89 * - VPN
90 * - WiFi
91 * - UserDatabases
92 * - CrashReporter
93 * - tmp
94 * - SystemConfiguration
95 * @param connection The connection that has to be used for receiving the
96 * data using idevice_connection_receive(). The connection will be closed
97 * automatically by the device, but use file_relay_client_free() to clean
98 * up properly.
99 *
100 * @note WARNING: Don't call this function without reading the data afterwards.
101 * A directory mobile_file_relay.XXXX used for creating the archive will
102 * remain in the /tmp directory otherwise.
103 *
104 * @return FILE_RELAY_E_SUCCESS on succes, FILE_RELAY_E_INVALID_ARG when one or
105 * more parameters are invalid, FILE_RELAY_E_MUX_ERROR if a communication
106 * error occurs, FILE_RELAY_E_PLIST_ERROR when the received result is NULL
107 * or is not a valid plist, FILE_RELAY_E_INVALID_SOURCE if one or more
108 * sources are invalid, FILE_RELAY_E_STAGING_EMPTY if no data is available
109 * for the given sources, or FILE_RELAY_E_UNKNOWN_ERROR otherwise.
110 */
111file_relay_error_t file_relay_request_sources(file_relay_client_t client, const char **sources, idevice_connection_t *connection)
112{ 71{
113 if (!client || !client->parent || !sources || !sources[0]) { 72 if (!client || !client->parent || !sources || !sources[0]) {
114 return FILE_RELAY_E_INVALID_ARG; 73 return FILE_RELAY_E_INVALID_ARG;
@@ -121,9 +80,9 @@ file_relay_error_t file_relay_request_sources(file_relay_client_t client, const
121 while (sources[i]) { 80 while (sources[i]) {
122 plist_array_append_item(array, plist_new_string(sources[i])); 81 plist_array_append_item(array, plist_new_string(sources[i]));
123 i++; 82 i++;
124 } 83 }
125 plist_t dict = plist_new_dict(); 84 plist_t dict = plist_new_dict();
126 plist_dict_insert_item(dict, "Sources", array); 85 plist_dict_set_item(dict, "Sources", array);
127 86
128 if (property_list_service_send_xml_plist(client->parent, dict) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 87 if (property_list_service_send_xml_plist(client->parent, dict) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
129 debug_info("ERROR: Could not send request to device!"); 88 debug_info("ERROR: Could not send request to device!");
@@ -133,7 +92,7 @@ file_relay_error_t file_relay_request_sources(file_relay_client_t client, const
133 plist_free(dict); 92 plist_free(dict);
134 93
135 dict = NULL; 94 dict = NULL;
136 if (property_list_service_receive_plist_with_timeout(client->parent, &dict, 60000) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 95 if (property_list_service_receive_plist_with_timeout(client->parent, &dict, timeout) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
137 debug_info("ERROR: Could not receive answer from device!"); 96 debug_info("ERROR: Could not receive answer from device!");
138 err = FILE_RELAY_E_MUX_ERROR; 97 err = FILE_RELAY_E_MUX_ERROR;
139 goto leave; 98 goto leave;
@@ -156,6 +115,9 @@ file_relay_error_t file_relay_request_sources(file_relay_client_t client, const
156 } else if (!strcmp(errmsg, "StagingEmpty")) { 115 } else if (!strcmp(errmsg, "StagingEmpty")) {
157 debug_info("ERROR: StagingEmpty - No data available!"); 116 debug_info("ERROR: StagingEmpty - No data available!");
158 err = FILE_RELAY_E_STAGING_EMPTY; 117 err = FILE_RELAY_E_STAGING_EMPTY;
118 } else if (!strcmp(errmsg, "PermissionDenied")) {
119 debug_info("ERROR: Permission denied.");
120 err = FILE_RELAY_E_PERMISSION_DENIED;
159 } else { 121 } else {
160 debug_info("ERROR: Unknown error '%s'", errmsg); 122 debug_info("ERROR: Unknown error '%s'", errmsg);
161 } 123 }
@@ -181,14 +143,14 @@ file_relay_error_t file_relay_request_sources(file_relay_client_t client, const
181 goto leave; 143 goto leave;
182 } 144 }
183 145
184 if (strcmp(ack, "Acknowledged")) { 146 if (strcmp(ack, "Acknowledged") != 0) {
185 debug_info("ERROR: Did not receive 'Acknowledged' but '%s'", ack); 147 debug_info("ERROR: Did not receive 'Acknowledged' but '%s'", ack);
186 goto leave; 148 goto leave;
187 } 149 }
188 free(ack); 150 free(ack);
189 err = FILE_RELAY_E_SUCCESS; 151 err = FILE_RELAY_E_SUCCESS;
190 152
191 *connection = client->parent->connection; 153 *connection = client->parent->parent->connection;
192 154
193leave: 155leave:
194 if (dict) { 156 if (dict) {
@@ -196,3 +158,8 @@ leave:
196 } 158 }
197 return err; 159 return err;
198} 160}
161
162file_relay_error_t file_relay_request_sources(file_relay_client_t client, const char **sources, idevice_connection_t *connection)
163{
164 return file_relay_request_sources_timeout(client, sources, connection, 60000);
165}
diff --git a/src/file_relay.h b/src/file_relay.h
index 60cc32f..65bf460 100644
--- a/src/file_relay.h
+++ b/src/file_relay.h
@@ -1,38 +1,31 @@
1 /* 1/*
2 * file_relay.h 2 * file_relay.h
3 * com.apple.mobile.file_relay service header file. 3 * com.apple.mobile.file_relay service header file.
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef FILE_RELAY_H
22#define FILE_RELAY_H
23 21
22#ifndef __FILE_RELAY_H
23#define __FILE_RELAY_H
24
25#include "idevice.h"
24#include "libimobiledevice/file_relay.h" 26#include "libimobiledevice/file_relay.h"
25#include "property_list_service.h" 27#include "property_list_service.h"
26 28
27/* Error Codes */
28#define FILE_RELAY_E_SUCCESS 0
29#define FILE_RELAY_E_INVALID_ARG -1
30#define FILE_RELAY_E_PLIST_ERROR -2
31#define FILE_RELAY_E_MUX_ERROR -3
32
33#define FILE_RELAY_E_UNKNOWN_ERROR -256
34
35
36struct file_relay_client_private { 29struct file_relay_client_private {
37 property_list_service_client_t parent; 30 property_list_service_client_t parent;
38}; 31};
diff --git a/src/heartbeat.c b/src/heartbeat.c
new file mode 100644
index 0000000..3945d73
--- /dev/null
+++ b/src/heartbeat.c
@@ -0,0 +1,147 @@
1/*
2 * heartbeat.c
3 * com.apple.mobile.heartbeat service implementation.
4 *
5 * Copyright (c) 2013 Martin Szulecki All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include <plist/plist.h>
28
29#include "heartbeat.h"
30#include "lockdown.h"
31#include "common/debug.h"
32
33/**
34 * Convert a property_list_service_error_t value to a heartbeat_error_t value.
35 * Used internally to get correct error codes.
36 *
37 * @param err An property_list_service_error_t error code
38 *
39 * @return A matching heartbeat_error_t error code,
40 * HEARTBEAT_E_UNKNOWN_ERROR otherwise.
41 */
42static heartbeat_error_t heartbeat_error(property_list_service_error_t err)
43{
44 switch (err) {
45 case PROPERTY_LIST_SERVICE_E_SUCCESS:
46 return HEARTBEAT_E_SUCCESS;
47 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
48 return HEARTBEAT_E_INVALID_ARG;
49 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
50 return HEARTBEAT_E_PLIST_ERROR;
51 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
52 return HEARTBEAT_E_MUX_ERROR;
53 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
54 return HEARTBEAT_E_SSL_ERROR;
55 case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA:
56 return HEARTBEAT_E_NOT_ENOUGH_DATA;
57 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
58 return HEARTBEAT_E_TIMEOUT;
59 default:
60 break;
61 }
62 return HEARTBEAT_E_UNKNOWN_ERROR;
63}
64
65heartbeat_error_t heartbeat_client_new(idevice_t device, lockdownd_service_descriptor_t service, heartbeat_client_t * client)
66{
67 *client = NULL;
68
69 if (!device || !service || service->port == 0 || !client || *client) {
70 debug_info("Incorrect parameter passed to heartbeat_client_new.");
71 return HEARTBEAT_E_INVALID_ARG;
72 }
73
74 debug_info("Creating heartbeat_client, port = %d.", service->port);
75
76 property_list_service_client_t plclient = NULL;
77 heartbeat_error_t ret = heartbeat_error(property_list_service_client_new(device, service, &plclient));
78 if (ret != HEARTBEAT_E_SUCCESS) {
79 debug_info("Creating a property list client failed. Error: %i", ret);
80 return ret;
81 }
82
83 heartbeat_client_t client_loc = (heartbeat_client_t) malloc(sizeof(struct heartbeat_client_private));
84 client_loc->parent = plclient;
85
86 *client = client_loc;
87
88 debug_info("heartbeat_client successfully created.");
89 return 0;
90}
91
92heartbeat_error_t heartbeat_client_start_service(idevice_t device, heartbeat_client_t * client, const char* label)
93{
94 heartbeat_error_t err = HEARTBEAT_E_UNKNOWN_ERROR;
95 service_client_factory_start_service(device, HEARTBEAT_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(heartbeat_client_new), &err);
96 return err;
97}
98
99heartbeat_error_t heartbeat_client_free(heartbeat_client_t client)
100{
101 if (!client)
102 return HEARTBEAT_E_INVALID_ARG;
103
104 heartbeat_error_t err = heartbeat_error(property_list_service_client_free(client->parent));
105 free(client);
106
107 return err;
108}
109
110heartbeat_error_t heartbeat_send(heartbeat_client_t client, plist_t plist)
111{
112 heartbeat_error_t res = HEARTBEAT_E_UNKNOWN_ERROR;
113
114 res = heartbeat_error(property_list_service_send_binary_plist(client->parent, plist));
115 if (res != HEARTBEAT_E_SUCCESS) {
116 debug_info("Sending plist failed with error %d", res);
117 return res;
118 }
119
120 debug_plist(plist);
121
122 return res;
123}
124
125heartbeat_error_t heartbeat_receive(heartbeat_client_t client, plist_t * plist)
126{
127 return heartbeat_receive_with_timeout(client, plist, 1000);
128}
129
130heartbeat_error_t heartbeat_receive_with_timeout(heartbeat_client_t client, plist_t * plist, uint32_t timeout_ms)
131{
132 heartbeat_error_t res = HEARTBEAT_E_UNKNOWN_ERROR;
133 plist_t outplist = NULL;
134
135 res = heartbeat_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, timeout_ms));
136 if (res != HEARTBEAT_E_SUCCESS || !outplist) {
137 debug_info("Could not receive plist, error %d", res);
138 plist_free(outplist);
139 return HEARTBEAT_E_MUX_ERROR;
140 }
141
142 *plist = outplist;
143
144 debug_plist(*plist);
145
146 return res;
147}
diff --git a/src/heartbeat.h b/src/heartbeat.h
new file mode 100644
index 0000000..379ecc1
--- /dev/null
+++ b/src/heartbeat.h
@@ -0,0 +1,33 @@
1/*
2 * heartbeat.h
3 * com.apple.mobile.heartbeat service header file.
4 *
5 * Copyright (c) 2013 Martin Szulecki All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __HEARTBEAT_H
23#define __HEARTBEAT_H
24
25#include "idevice.h"
26#include "libimobiledevice/heartbeat.h"
27#include "property_list_service.h"
28
29struct heartbeat_client_private {
30 property_list_service_client_t parent;
31};
32
33#endif
diff --git a/src/house_arrest.c b/src/house_arrest.c
index 5baa76e..caad731 100644
--- a/src/house_arrest.c
+++ b/src/house_arrest.c
@@ -8,17 +8,20 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
22#include <string.h> 25#include <string.h>
23#include <stdlib.h> 26#include <stdlib.h>
24#include <unistd.h> 27#include <unistd.h>
@@ -27,7 +30,7 @@
27#include "house_arrest.h" 30#include "house_arrest.h"
28#include "property_list_service.h" 31#include "property_list_service.h"
29#include "afc.h" 32#include "afc.h"
30#include "debug.h" 33#include "common/debug.h"
31 34
32/** 35/**
33 * Convert a property_list_service_error_t value to a house_arrest_error_t 36 * Convert a property_list_service_error_t value to a house_arrest_error_t
@@ -40,39 +43,25 @@
40 */ 43 */
41static house_arrest_error_t house_arrest_error(property_list_service_error_t err) 44static house_arrest_error_t house_arrest_error(property_list_service_error_t err)
42{ 45{
43 switch (err) { 46 switch (err) {
44 case PROPERTY_LIST_SERVICE_E_SUCCESS: 47 case PROPERTY_LIST_SERVICE_E_SUCCESS:
45 return HOUSE_ARREST_E_SUCCESS; 48 return HOUSE_ARREST_E_SUCCESS;
46 case PROPERTY_LIST_SERVICE_E_INVALID_ARG: 49 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
47 return HOUSE_ARREST_E_INVALID_ARG; 50 return HOUSE_ARREST_E_INVALID_ARG;
48 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: 51 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
49 return HOUSE_ARREST_E_PLIST_ERROR; 52 return HOUSE_ARREST_E_PLIST_ERROR;
50 case PROPERTY_LIST_SERVICE_E_MUX_ERROR: 53 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
51 return HOUSE_ARREST_E_CONN_FAILED; 54 return HOUSE_ARREST_E_CONN_FAILED;
52 default: 55 default:
53 break; 56 break;
54 } 57 }
55 return HOUSE_ARREST_E_UNKNOWN_ERROR; 58 return HOUSE_ARREST_E_UNKNOWN_ERROR;
56} 59}
57 60
58/** 61house_arrest_error_t house_arrest_client_new(idevice_t device, lockdownd_service_descriptor_t service, house_arrest_client_t *client)
59 * Connects to the house_arrest service on the specified device.
60 *
61 * @param device The device to connect to.
62 * @param port Destination port (usually given by lockdownd_start_service).
63 * @param client Pointer that will point to a newly allocated
64 * housearrest_client_t upon successful return.
65 *
66 * @return HOUSE_ARREST_E_SUCCESS on success, HOUSE_ARREST_E_INVALID_ARG when
67 * client is NULL, or an HOUSE_ARREST_E_* error code otherwise.
68 */
69house_arrest_error_t house_arrest_client_new(idevice_t device, uint16_t port, house_arrest_client_t *client)
70{ 62{
71 if (!device)
72 return HOUSE_ARREST_E_INVALID_ARG;
73
74 property_list_service_client_t plistclient = NULL; 63 property_list_service_client_t plistclient = NULL;
75 house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, port, &plistclient)); 64 house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, service, &plistclient));
76 if (err != HOUSE_ARREST_E_SUCCESS) { 65 if (err != HOUSE_ARREST_E_SUCCESS) {
77 return err; 66 return err;
78 } 67 }
@@ -85,27 +74,20 @@ house_arrest_error_t house_arrest_client_new(idevice_t device, uint16_t port, ho
85 return HOUSE_ARREST_E_SUCCESS; 74 return HOUSE_ARREST_E_SUCCESS;
86} 75}
87 76
88/** 77house_arrest_error_t house_arrest_client_start_service(idevice_t device, house_arrest_client_t * client, const char* label)
89 * Disconnects an house_arrest client from the device and frees up the 78{
90 * house_arrest client data. 79 house_arrest_error_t err = HOUSE_ARREST_E_UNKNOWN_ERROR;
91 * 80 service_client_factory_start_service(device, HOUSE_ARREST_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(house_arrest_client_new), &err);
92 * @note After using afc_client_new_from_house_arrest_client(), make sure 81 return err;
93 * you call afc_client_free() before calling this function to ensure 82}
94 * a proper cleanup. Do not call this function if you still need to 83
95 * perform AFC operations since it will close the connection.
96 *
97 * @param client The house_arrest client to disconnect and free.
98 *
99 * @return HOUSE_ARREST_E_SUCCESS on success, HOUSE_ARREST_E_INVALID_ARG when
100 * client is NULL, or an HOUSE_ARREST_E_* error code otherwise.
101 */
102house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) 84house_arrest_error_t house_arrest_client_free(house_arrest_client_t client)
103{ 85{
104 if (!client) 86 if (!client)
105 return HOUSE_ARREST_E_INVALID_ARG; 87 return HOUSE_ARREST_E_INVALID_ARG;
106 88
107 house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS; 89 house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS;
108 if (client->parent && client->parent->connection) { 90 if (client->parent && client->parent->parent->connection) {
109 house_arrest_error(property_list_service_client_free(client->parent)); 91 house_arrest_error(property_list_service_client_free(client->parent));
110 } 92 }
111 client->parent = NULL; 93 client->parent = NULL;
@@ -114,70 +96,34 @@ house_arrest_error_t house_arrest_client_free(house_arrest_client_t client)
114 return err; 96 return err;
115} 97}
116 98
117/**
118 * Sends a generic request to the connected house_arrest service.
119 *
120 * @param client The house_arrest client to use.
121 * @param dict The request to send as a plist of type PLIST_DICT.
122 *
123 * @note If this function returns HOUSE_ARREST_E_SUCCESS it does not mean
124 * that the request was successful. To check for success or failure you
125 * need to call house_arrest_get_result().
126 * @see house_arrest_get_result
127 *
128 * @return HOUSE_ARREST_E_SUCCESS if the request was successfully sent,
129 * HOUSE_ARREST_E_INVALID_ARG if client or dict is invalid,
130 * HOUSE_ARREST_E_PLIST_ERROR if dict is not a plist of type PLIST_DICT,
131 * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode,
132 * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured.
133 */
134house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict) 99house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict)
135{ 100{
136 if (!client || !client->parent || !dict) 101 if (!client || !client->parent || !dict)
137 return HOUSE_ARREST_E_INVALID_ARG; 102 return HOUSE_ARREST_E_INVALID_ARG;
138 if (plist_get_node_type(dict) != PLIST_DICT) 103 if (plist_get_node_type(dict) != PLIST_DICT)
139 return HOUSE_ARREST_E_PLIST_ERROR; 104 return HOUSE_ARREST_E_PLIST_ERROR;
140 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) 105 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL)
141 return HOUSE_ARREST_E_INVALID_MODE; 106 return HOUSE_ARREST_E_INVALID_MODE;
142 107
143 house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict)); 108 house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict));
144 if (res != HOUSE_ARREST_E_SUCCESS) { 109 if (res != HOUSE_ARREST_E_SUCCESS) {
145 debug_info("could not send plist, error %d", res); 110 debug_info("could not send plist, error %d", res);
146 } 111 }
147 return res; 112 return res;
148} 113}
149 114
150/**
151 * Send a command to the connected house_arrest service.
152 * Calls house_arrest_send_request() internally.
153 *
154 * @param client The house_arrest client to use.
155 * @param command The command to send. Currently, only VendContainer and
156 * VendDocuments are known.
157 * @param appid The application identifier to pass along with the .
158 *
159 * @note If this function returns HOUSE_ARREST_E_SUCCESS it does not mean
160 * that the command was successful. To check for success or failure you
161 * need to call house_arrest_get_result().
162 * @see house_arrest_get_result
163 *
164 * @return HOUSE_ARREST_E_SUCCESS if the command was successfully sent,
165 * HOUSE_ARREST_E_INVALID_ARG if client, command, or appid is invalid,
166 * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode,
167 * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured.
168 */
169house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid) 115house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid)
170{ 116{
171 if (!client || !client->parent || !command || !appid) 117 if (!client || !client->parent || !command || !appid)
172 return HOUSE_ARREST_E_INVALID_ARG; 118 return HOUSE_ARREST_E_INVALID_ARG;
173 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) 119 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL)
174 return HOUSE_ARREST_E_INVALID_MODE; 120 return HOUSE_ARREST_E_INVALID_MODE;
175 121
176 house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR; 122 house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR;
177 123
178 plist_t dict = plist_new_dict(); 124 plist_t dict = plist_new_dict();
179 plist_dict_insert_item(dict, "Command", plist_new_string(command)); 125 plist_dict_set_item(dict, "Command", plist_new_string(command));
180 plist_dict_insert_item(dict, "Identifier", plist_new_string(appid)); 126 plist_dict_set_item(dict, "Identifier", plist_new_string(appid));
181 127
182 res = house_arrest_send_request(client, dict); 128 res = house_arrest_send_request(client, dict);
183 129
@@ -186,63 +132,30 @@ house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, con
186 return res; 132 return res;
187} 133}
188 134
189/**
190 * Retrieves the result of a previously sent house_arrest_request_* request.
191 *
192 * @param client The house_arrest client to use
193 * @param dict Pointer that will be set to a plist containing the result to
194 * the last performed operation. It holds a key 'Status' with the value
195 * 'Complete' on success or a key 'Error' with an error description as
196 * value. The caller is responsible for freeing the returned plist.
197 *
198 * @return HOUSE_ARREST_E_SUCCESS if a result plist was retrieved,
199 * HOUSE_ARREST_E_INVALID_ARG if client is invalid,
200 * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode,
201 * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured.
202 */
203house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict) 135house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict)
204{ 136{
205 if (!client || !client->parent) 137 if (!client || !client->parent)
206 return HOUSE_ARREST_E_INVALID_ARG; 138 return HOUSE_ARREST_E_INVALID_ARG;
207 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) 139 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL)
208 return HOUSE_ARREST_E_INVALID_MODE; 140 return HOUSE_ARREST_E_INVALID_MODE;
209 141
210 house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict)); 142 house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict));
211 if (res != HOUSE_ARREST_E_SUCCESS) { 143 if (res != HOUSE_ARREST_E_SUCCESS) {
212 debug_info("could not get result, error %d", res); 144 debug_info("could not get result, error %d", res);
213 if (*dict) { 145 if (*dict) {
214 plist_free(*dict); 146 plist_free(*dict);
215 *dict = NULL; 147 *dict = NULL;
216 } 148 }
217 } 149 }
218 return res; 150 return res;
219} 151}
220 152
221/**
222 * Creates an AFC client using the given house_arrest client's connection
223 * allowing file access to a specific application directory requested by
224 * functions like house_arrest_request_vendor_documents().
225 *
226 * @param client The house_arrest client to use.
227 * @param afc_client Pointer that will be set to a newly allocated afc_client_t
228 * upon successful return.
229 *
230 * @note After calling this function the house_arrest client will go in an
231 * AFC mode that will only allow calling house_arrest_client_free().
232 * Only call house_arrest_client_free() if all AFC operations have
233 * completed since it will close the connection.
234 *
235 * @return AFC_E_SUCCESS if the afc client was successfully created,
236 * AFC_E_INVALID_ARG if client is invalid or was already used to create
237 * an afc client, or an AFC_E_* error code returned by
238 * afc_client_new_from_connection().
239 */
240afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client) 153afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client)
241{ 154{
242 if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { 155 if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) {
243 return AFC_E_INVALID_ARG; 156 return AFC_E_INVALID_ARG;
244 } 157 }
245 afc_error_t err = afc_client_new_from_connection(client->parent->connection, afc_client); 158 afc_error_t err = afc_client_new_with_service_client(client->parent->parent, afc_client);
246 if (err == AFC_E_SUCCESS) { 159 if (err == AFC_E_SUCCESS) {
247 client->mode = HOUSE_ARREST_CLIENT_MODE_AFC; 160 client->mode = HOUSE_ARREST_CLIENT_MODE_AFC;
248 } 161 }
diff --git a/src/house_arrest.h b/src/house_arrest.h
index 6d13a88..5612a29 100644
--- a/src/house_arrest.h
+++ b/src/house_arrest.h
@@ -8,21 +8,21 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef IHOUSE_ARREST_H
22#define IHOUSE_ARREST_H
23 21
24#include <glib.h> 22#ifndef __HOUSE_ARREST_H
23#define __HOUSE_ARREST_H
25 24
25#include "idevice.h"
26#include "libimobiledevice/house_arrest.h" 26#include "libimobiledevice/house_arrest.h"
27#include "property_list_service.h" 27#include "property_list_service.h"
28 28
diff --git a/src/idevice.c b/src/idevice.c
index 5a9d49b..b9bbb1f 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -1,98 +1,387 @@
1/* 1/*
2 * idevice.c 2 * idevice.c
3 * Device discovery and communication interface. 3 * Device discovery and communication interface.
4 * 4 *
5 * Copyright (c) 2009-2021 Nikias Bassen. All Rights Reserved.
6 * Copyright (c) 2014 Martin Szulecki All Rights Reserved.
5 * Copyright (c) 2008 Zach C. All Rights Reserved. 7 * Copyright (c) 2008 Zach C. All Rights Reserved.
6 * Copyright (c) 2009 Nikias Bassen. All Rights Reserved.
7 * 8 *
8 * This library is free software; you can redistribute it and/or 9 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public 10 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either 11 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version. 12 * version 2.1 of the License, or (at your option) any later version.
12 * 13 *
13 * This library is distributed in the hope that it will be useful, 14 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details. 17 * Lesser General Public License for more details.
17 * 18 *
18 * You should have received a copy of the GNU Lesser General Public 19 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software 20 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */ 22 */
22 23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
23#include <stdlib.h> 28#include <stdlib.h>
24#include <string.h> 29#include <string.h>
25#include <errno.h> 30#include <errno.h>
31#include <time.h>
32
33#ifdef WIN32
34#include <winsock2.h>
35#include <ws2tcpip.h>
36#include <windows.h>
37#else
38#include <sys/socket.h>
39#include <netinet/in.h>
40#endif
26 41
27#include <usbmuxd.h> 42#include <usbmuxd.h>
43
44#if defined(HAVE_OPENSSL)
45#include <openssl/err.h>
46#include <openssl/rsa.h>
47#include <openssl/ssl.h>
48#elif defined(HAVE_GNUTLS)
28#include <gnutls/gnutls.h> 49#include <gnutls/gnutls.h>
50#elif defined(HAVE_MBEDTLS)
51#include <mbedtls/rsa.h>
52#include <mbedtls/ssl.h>
53#include <mbedtls/entropy.h>
54#include <mbedtls/ctr_drbg.h>
55#include <mbedtls/debug.h>
56#else
57#error No supported TLS/SSL library enabled
58#endif
59
60#include <libimobiledevice-glue/socket.h>
61#include <libimobiledevice-glue/thread.h>
62
29#include "idevice.h" 63#include "idevice.h"
30#include "userpref.h" 64#include "lockdown.h"
31#include "debug.h" 65#include "common/userpref.h"
66#include "common/debug.h"
67
68#ifndef ECONNREFUSED
69#define ECONNREFUSED 107
70#endif
71#ifndef ETIMEDOUT
72#define ETIMEDOUT 138
73#endif
74
75
76#ifdef HAVE_OPENSSL
77
78#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
79 (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20020000L))
80#define TLS_method TLSv1_method
81#endif
82
83#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
84static void SSL_COMP_free_compression_methods(void)
85{
86 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
87}
88#endif
89
90static void openssl_remove_thread_state(void)
91{
92/* ERR_remove_thread_state() is available since OpenSSL 1.0.0-beta1, but
93 * deprecated in OpenSSL 1.1.0 */
94#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
95#if OPENSSL_VERSION_NUMBER >= 0x10000001L
96 ERR_remove_thread_state(NULL);
97#else
98 ERR_remove_state(0);
99#endif
100#endif
101}
102
103#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
104static mutex_t *mutex_buf = NULL;
105static void locking_function(int mode, int n, const char* file, int line)
106{
107 if (mode & CRYPTO_LOCK)
108 mutex_lock(&mutex_buf[n]);
109 else
110 mutex_unlock(&mutex_buf[n]);
111}
112
113#if OPENSSL_VERSION_NUMBER < 0x10000000L
114static unsigned long id_function(void)
115{
116 return ((unsigned long)THREAD_ID);
117}
118#else
119static void id_function(CRYPTO_THREADID *thread)
120{
121 CRYPTO_THREADID_set_numeric(thread, (unsigned long)THREAD_ID);
122}
123#endif
124#endif
125#endif /* HAVE_OPENSSL */
126
127static void internal_idevice_init(void)
128{
129#if defined(HAVE_OPENSSL)
130#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
131 int i;
132 SSL_library_init();
133
134 mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t));
135 if (!mutex_buf)
136 return;
137 for (i = 0; i < CRYPTO_num_locks(); i++)
138 mutex_init(&mutex_buf[i]);
139
140#if OPENSSL_VERSION_NUMBER < 0x10000000L
141 CRYPTO_set_id_callback(id_function);
142#else
143 CRYPTO_THREADID_set_callback(id_function);
144#endif
145 CRYPTO_set_locking_callback(locking_function);
146#endif
147#elif defined(HAVE_GNUTLS)
148 gnutls_global_init();
149#elif defined(HAVE_MBEDTLS)
150 // NO-OP
151#endif
152}
153
154static void internal_idevice_deinit(void)
155{
156#if defined(HAVE_OPENSSL)
157#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
158 int i;
159 if (mutex_buf) {
160#if OPENSSL_VERSION_NUMBER < 0x10000000L
161 CRYPTO_set_id_callback(NULL);
162#else
163 CRYPTO_THREADID_set_callback(NULL);
164#endif
165 CRYPTO_set_locking_callback(NULL);
166 for (i = 0; i < CRYPTO_num_locks(); i++)
167 mutex_destroy(&mutex_buf[i]);
168 free(mutex_buf);
169 mutex_buf = NULL;
170 }
171
172 EVP_cleanup();
173 CRYPTO_cleanup_all_ex_data();
174 SSL_COMP_free_compression_methods();
175 openssl_remove_thread_state();
176#endif
177#elif defined(HAVE_GNUTLS)
178 gnutls_global_deinit();
179#elif defined(HAVE_MBEDTLS)
180 // NO-OP
181#endif
182}
183
184static thread_once_t init_once = THREAD_ONCE_INIT;
185static thread_once_t deinit_once = THREAD_ONCE_INIT;
186
187#ifndef HAVE_ATTRIBUTE_CONSTRUCTOR
188 #if defined(__llvm__) || defined(__GNUC__)
189 #define HAVE_ATTRIBUTE_CONSTRUCTOR
190 #endif
191#endif
192
193#ifdef HAVE_ATTRIBUTE_CONSTRUCTOR
194static void __attribute__((constructor)) libimobiledevice_initialize(void)
195{
196 thread_once(&init_once, internal_idevice_init);
197}
198
199static void __attribute__((destructor)) libimobiledevice_deinitialize(void)
200{
201 thread_once(&deinit_once, internal_idevice_deinit);
202}
203#elif defined(WIN32)
204BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
205{
206 switch (dwReason) {
207 case DLL_PROCESS_ATTACH:
208 thread_once(&init_once, internal_idevice_init);
209 break;
210 case DLL_PROCESS_DETACH:
211 thread_once(&deinit_once, internal_idevice_deinit);
212 break;
213 default:
214 break;
215 }
216 return 1;
217}
218#else
219#warning No compiler support for constructor/destructor attributes, some features might not be available.
220#endif
221
222const char* libimobiledevice_version()
223{
224#ifndef PACKAGE_VERSION
225#error PACKAGE_VERSION is not defined!
226#endif
227 return PACKAGE_VERSION;
228}
229
230struct idevice_subscription_context {
231 idevice_event_cb_t callback;
232 void *user_data;
233 usbmuxd_subscription_context_t ctx;
234};
32 235
33static idevice_event_cb_t event_cb = NULL; 236static idevice_subscription_context_t event_ctx = NULL;
34 237
35static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) 238static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data)
36{ 239{
240 idevice_subscription_context_t context = (idevice_subscription_context_t)user_data;
37 idevice_event_t ev; 241 idevice_event_t ev;
38 242
39 ev.event = event->event; 243 ev.event = event->event;
40 ev.uuid = event->device.uuid; 244 ev.udid = event->device.udid;
41 ev.conn_type = CONNECTION_USBMUXD; 245 ev.conn_type = 0;
246 if (event->device.conn_type == CONNECTION_TYPE_USB) {
247 ev.conn_type = CONNECTION_USBMUXD;
248 } else if (event->device.conn_type == CONNECTION_TYPE_NETWORK) {
249 ev.conn_type = CONNECTION_NETWORK;
250 } else {
251 debug_info("Unknown connection type %d", event->device.conn_type);
252 }
42 253
43 if (event_cb) { 254 if (context->callback) {
44 event_cb(&ev, user_data); 255 context->callback(&ev, context->user_data);
45 } 256 }
46} 257}
47 258
48/** 259idevice_error_t idevice_events_subscribe(idevice_subscription_context_t *context, idevice_event_cb_t callback, void *user_data)
49 * Register a callback function that will be called when device add/remove
50 * events occur.
51 *
52 * @param callback Callback function to call.
53 * @param user_data Application-specific data passed as parameter
54 * to the registered callback function.
55 *
56 * @return IDEVICE_E_SUCCESS on success or an error value when an error occured.
57 */
58idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data)
59{ 260{
60 event_cb = callback; 261 if (!context || !callback) {
61 int res = usbmuxd_subscribe(usbmux_event_cb, user_data); 262 return IDEVICE_E_INVALID_ARG;
62 if (res != 0) { 263 }
63 event_cb = NULL; 264 *context = malloc(sizeof(struct idevice_subscription_context));
64 debug_info("Error %d when subscribing usbmux event callback!", res); 265 if (!*context) {
266 debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__);
267 return IDEVICE_E_UNKNOWN_ERROR;
268 }
269 (*context)->callback = callback;
270 (*context)->user_data = user_data;
271 int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context);
272 if (res != 0) {
273 free(*context);
274 *context = NULL;
275 debug_info("ERROR: usbmuxd_subscribe() returned %d!", res);
65 return IDEVICE_E_UNKNOWN_ERROR; 276 return IDEVICE_E_UNKNOWN_ERROR;
66 } 277 }
67 return IDEVICE_E_SUCCESS; 278 return IDEVICE_E_SUCCESS;
68} 279}
69 280
70/** 281idevice_error_t idevice_events_unsubscribe(idevice_subscription_context_t context)
71 * Release the event callback function that has been registered with
72 * idevice_event_subscribe().
73 *
74 * @return IDEVICE_E_SUCCESS on success or an error value when an error occured.
75 */
76idevice_error_t idevice_event_unsubscribe()
77{ 282{
78 event_cb = NULL; 283 if (!context) {
79 int res = usbmuxd_unsubscribe(); 284 return IDEVICE_E_INVALID_ARG;
285 }
286 int res = usbmuxd_events_unsubscribe(context->ctx);
80 if (res != 0) { 287 if (res != 0) {
81 debug_info("Error %d when unsubscribing usbmux event callback!", res); 288 debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res);
82 return IDEVICE_E_UNKNOWN_ERROR; 289 return IDEVICE_E_UNKNOWN_ERROR;
83 } 290 }
291 if (context == event_ctx) {
292 event_ctx = NULL;
293 }
294 free(context);
295 return IDEVICE_E_SUCCESS;
296}
297
298idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data)
299{
300 if (event_ctx) {
301 idevice_events_unsubscribe(event_ctx);
302 }
303 return idevice_events_subscribe(&event_ctx, callback, user_data);
304}
305
306idevice_error_t idevice_event_unsubscribe(void)
307{
308 if (!event_ctx) {
309 return IDEVICE_E_SUCCESS;
310 }
311 event_ctx->callback = NULL;
312 return idevice_events_unsubscribe(event_ctx);
313}
314
315idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count)
316{
317 usbmuxd_device_info_t *dev_list;
318
319 *devices = NULL;
320 *count = 0;
321
322 if (usbmuxd_get_device_list(&dev_list) < 0) {
323 debug_info("ERROR: usbmuxd is not running!", __func__);
324 return IDEVICE_E_NO_DEVICE;
325 }
326
327 idevice_info_t *newlist = NULL;
328 int i, newcount = 0;
329
330 for (i = 0; dev_list[i].handle > 0; i++) {
331 newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1));
332 newlist[newcount] = malloc(sizeof(struct idevice_info));
333 newlist[newcount]->udid = strdup(dev_list[i].udid);
334 if (dev_list[i].conn_type == CONNECTION_TYPE_USB) {
335 newlist[newcount]->conn_type = CONNECTION_USBMUXD;
336 newlist[newcount]->conn_data = NULL;
337 } else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) {
338 newlist[newcount]->conn_type = CONNECTION_NETWORK;
339 struct sockaddr* saddr = (struct sockaddr*)(dev_list[i].conn_data);
340 size_t addrlen = 0;
341 switch (saddr->sa_family) {
342 case AF_INET:
343 addrlen = sizeof(struct sockaddr_in);
344 break;
345#ifdef AF_INET6
346 case AF_INET6:
347 addrlen = sizeof(struct sockaddr_in6);
348 break;
349#endif
350 default:
351 debug_info("Unsupported address family 0x%02x\n", saddr->sa_family);
352 continue;
353 }
354 newlist[newcount]->conn_data = malloc(addrlen);
355 memcpy(newlist[newcount]->conn_data, dev_list[i].conn_data, addrlen);
356 }
357 newcount++;
358 *devices = newlist;
359 }
360 usbmuxd_device_list_free(&dev_list);
361
362 *count = newcount;
363 newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1));
364 newlist[newcount] = NULL;
365 *devices = newlist;
366
367 return IDEVICE_E_SUCCESS;
368}
369
370idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices)
371{
372 if (devices) {
373 int i = 0;
374 while (devices[i]) {
375 free(devices[i]->udid);
376 free(devices[i]->conn_data);
377 free(devices[i]);
378 i++;
379 }
380 free(devices);
381 }
84 return IDEVICE_E_SUCCESS; 382 return IDEVICE_E_SUCCESS;
85} 383}
86 384
87/**
88 * Get a list of currently available devices.
89 *
90 * @param devices List of uuids of devices that are currently available.
91 * This list is terminated by a NULL pointer.
92 * @param count Number of devices found.
93 *
94 * @return IDEVICE_E_SUCCESS on success or an error value when an error occured.
95 */
96idevice_error_t idevice_get_device_list(char ***devices, int *count) 385idevice_error_t idevice_get_device_list(char ***devices, int *count)
97{ 386{
98 usbmuxd_device_info_t *dev_list; 387 usbmuxd_device_info_t *dev_list;
@@ -101,7 +390,7 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count)
101 *count = 0; 390 *count = 0;
102 391
103 if (usbmuxd_get_device_list(&dev_list) < 0) { 392 if (usbmuxd_get_device_list(&dev_list) < 0) {
104 debug_info("ERROR: usbmuxd is not running!\n", __func__); 393 debug_info("ERROR: usbmuxd is not running!", __func__);
105 return IDEVICE_E_NO_DEVICE; 394 return IDEVICE_E_NO_DEVICE;
106 } 395 }
107 396
@@ -109,9 +398,11 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count)
109 int i, newcount = 0; 398 int i, newcount = 0;
110 399
111 for (i = 0; dev_list[i].handle > 0; i++) { 400 for (i = 0; dev_list[i].handle > 0; i++) {
112 newlist = realloc(*devices, sizeof(char*) * (newcount+1)); 401 if (dev_list[i].conn_type == CONNECTION_TYPE_USB) {
113 newlist[newcount++] = strdup(dev_list[i].uuid); 402 newlist = realloc(*devices, sizeof(char*) * (newcount+1));
114 *devices = newlist; 403 newlist[newcount++] = strdup(dev_list[i].udid);
404 *devices = newlist;
405 }
115 } 406 }
116 usbmuxd_device_list_free(&dev_list); 407 usbmuxd_device_list_free(&dev_list);
117 408
@@ -123,62 +414,101 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count)
123 return IDEVICE_E_SUCCESS; 414 return IDEVICE_E_SUCCESS;
124} 415}
125 416
126/**
127 * Free a list of device uuids.
128 *
129 * @param devices List of uuids to free.
130 *
131 * @return Always returnes IDEVICE_E_SUCCESS.
132 */
133idevice_error_t idevice_device_list_free(char **devices) 417idevice_error_t idevice_device_list_free(char **devices)
134{ 418{
135 if (devices) { 419 if (devices) {
136 int i = 0; 420 int i = 0;
137 while (devices[i++]) { 421 while (devices[i]) {
138 free(devices[i]); 422 free(devices[i]);
423 i++;
139 } 424 }
140 free(devices); 425 free(devices);
141 } 426 }
142 return IDEVICE_E_SUCCESS; 427 return IDEVICE_E_SUCCESS;
143} 428}
144 429
145/** 430void idevice_set_debug_level(int level)
146 * Creates an idevice_t structure for the device specified by uuid, 431{
147 * if the device is available. 432 internal_set_debug_level(level);
148 * 433}
149 * @note The resulting idevice_t structure has to be freed with 434
150 * idevice_free() if it is no longer used. 435static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev)
151 * 436{
152 * @param device Upon calling this function, a pointer to a location of type 437 if (!muxdev)
153 * idevice_t. On successful return, this location will be populated. 438 return NULL;
154 * @param uuid The UUID to match. 439
155 * 440 idevice_t device = (idevice_t)malloc(sizeof(struct idevice_private));
156 * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. 441 if (!device)
157 */ 442 return NULL;
158idevice_error_t idevice_new(idevice_t * device, const char *uuid) 443
444 device->udid = strdup(muxdev->udid);
445 device->mux_id = muxdev->handle;
446 device->version = 0;
447 device->device_class = 0;
448 switch (muxdev->conn_type) {
449 case CONNECTION_TYPE_USB:
450 device->conn_type = CONNECTION_USBMUXD;
451 device->conn_data = NULL;
452 break;
453 case CONNECTION_TYPE_NETWORK:
454 device->conn_type = CONNECTION_NETWORK;
455 struct sockaddr* saddr = (struct sockaddr*)(muxdev->conn_data);
456 size_t addrlen = 0;
457 switch (saddr->sa_family) {
458 case AF_INET:
459 addrlen = sizeof(struct sockaddr_in);
460 break;
461#ifdef AF_INET6
462 case AF_INET6:
463 addrlen = sizeof(struct sockaddr_in6);
464 break;
465#endif
466 default:
467 debug_info("Unsupported address family 0x%02x\n", saddr->sa_family);
468 free(device->udid);
469 free(device);
470 return NULL;
471 }
472 device->conn_data = malloc(addrlen);
473 memcpy(device->conn_data, muxdev->conn_data, addrlen);
474 break;
475 default:
476 device->conn_type = 0;
477 device->conn_data = NULL;
478 break;
479 }
480 return device;
481}
482
483idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options)
159{ 484{
160 usbmuxd_device_info_t muxdev; 485 usbmuxd_device_info_t muxdev;
161 int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); 486 int usbmux_options = 0;
487 if (options & IDEVICE_LOOKUP_USBMUX) {
488 usbmux_options |= DEVICE_LOOKUP_USBMUX;
489 }
490 if (options & IDEVICE_LOOKUP_NETWORK) {
491 usbmux_options |= DEVICE_LOOKUP_NETWORK;
492 }
493 if (options & IDEVICE_LOOKUP_PREFER_NETWORK) {
494 usbmux_options |= DEVICE_LOOKUP_PREFER_NETWORK;
495 }
496 int res = usbmuxd_get_device(udid, &muxdev, usbmux_options);
162 if (res > 0) { 497 if (res > 0) {
163 idevice_t phone = (idevice_t) malloc(sizeof(struct idevice_private)); 498 *device = idevice_from_mux_device(&muxdev);
164 phone->uuid = strdup(muxdev.uuid); 499 if (!*device) {
165 phone->conn_type = CONNECTION_USBMUXD; 500 return IDEVICE_E_UNKNOWN_ERROR;
166 phone->conn_data = (void*)(long)muxdev.handle; 501 }
167 *device = phone;
168 return IDEVICE_E_SUCCESS; 502 return IDEVICE_E_SUCCESS;
169 } 503 }
170 /* other connection types could follow here */
171
172 return IDEVICE_E_NO_DEVICE; 504 return IDEVICE_E_NO_DEVICE;
173} 505}
174 506
175/** 507idevice_error_t idevice_new(idevice_t * device, const char *udid)
176 * Cleans up an idevice structure, then frees the structure itself. 508{
177 * This is a library-level function; deals directly with the device to tear 509 return idevice_new_with_options(device, udid, 0);
178 * down relations, but otherwise is mostly internal. 510}
179 * 511
180 * @param device idevice_t to free.
181 */
182idevice_error_t idevice_free(idevice_t device) 512idevice_error_t idevice_free(idevice_t device)
183{ 513{
184 if (!device) 514 if (!device)
@@ -187,11 +517,8 @@ idevice_error_t idevice_free(idevice_t device)
187 517
188 ret = IDEVICE_E_SUCCESS; 518 ret = IDEVICE_E_SUCCESS;
189 519
190 free(device->uuid); 520 free(device->udid);
191 521
192 if (device->conn_type == CONNECTION_USBMUXD) {
193 device->conn_data = 0;
194 }
195 if (device->conn_data) { 522 if (device->conn_data) {
196 free(device->conn_data); 523 free(device->conn_data);
197 } 524 }
@@ -199,16 +526,6 @@ idevice_error_t idevice_free(idevice_t device)
199 return ret; 526 return ret;
200} 527}
201 528
202/**
203 * Set up a connection to the given device.
204 *
205 * @param device The device to connect to.
206 * @param port The destination port to connect to.
207 * @param connection Pointer to an idevice_connection_t that will be filled
208 * with the necessary data of the connection.
209 *
210 * @return IDEVICE_E_SUCCESS if ok, otherwise an error code.
211 */
212idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) 529idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection)
213{ 530{
214 if (!device) { 531 if (!device) {
@@ -216,31 +533,80 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect
216 } 533 }
217 534
218 if (device->conn_type == CONNECTION_USBMUXD) { 535 if (device->conn_type == CONNECTION_USBMUXD) {
219 int sfd = usbmuxd_connect((uint32_t)(long)device->conn_data, port); 536 int sfd = usbmuxd_connect(device->mux_id, port);
220 if (sfd < 0) { 537 if (sfd < 0) {
221 debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); 538 debug_info("ERROR: Connecting to usbmux device failed: %d (%s)", sfd, strerror(-sfd));
539 switch (-sfd) {
540 case ECONNREFUSED:
541 return IDEVICE_E_CONNREFUSED;
542 case ENODEV:
543 return IDEVICE_E_NO_DEVICE;
544 default:
545 break;
546 }
222 return IDEVICE_E_UNKNOWN_ERROR; 547 return IDEVICE_E_UNKNOWN_ERROR;
223 } 548 }
224 idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); 549 idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private));
225 new_connection->type = CONNECTION_USBMUXD; 550 new_connection->type = CONNECTION_USBMUXD;
226 new_connection->data = (void*)(long)sfd; 551 new_connection->data = (void*)(long)sfd;
227 new_connection->ssl_data = NULL; 552 new_connection->ssl_data = NULL;
553 new_connection->device = device;
554 new_connection->ssl_recv_timeout = (unsigned int)-1;
555 new_connection->status = IDEVICE_E_SUCCESS;
228 *connection = new_connection; 556 *connection = new_connection;
229 return IDEVICE_E_SUCCESS; 557 return IDEVICE_E_SUCCESS;
230 } else { 558 }
231 debug_info("Unknown connection type %d", device->conn_type); 559 if (device->conn_type == CONNECTION_NETWORK) {
560 struct sockaddr* saddr = (struct sockaddr*)(device->conn_data);
561 switch (saddr->sa_family) {
562 case AF_INET:
563#ifdef AF_INET6
564 case AF_INET6:
565#endif
566 break;
567 default:
568 debug_info("Unsupported address family 0x%02x", saddr->sa_family);
569 return IDEVICE_E_UNKNOWN_ERROR;
570 }
571
572 char addrtxt[48];
573 addrtxt[0] = '\0';
574
575 if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) {
576 debug_info("Failed to convert network address: %d (%s)", errno, strerror(errno));
577 }
578
579 debug_info("Connecting to %s port %d...", addrtxt, port);
580
581 int sfd = socket_connect_addr(saddr, port);
582 if (sfd < 0) {
583 int result = errno;
584 debug_info("ERROR: Connecting to network device failed: %d (%s)", result, strerror(result));
585 switch (result) {
586 case ECONNREFUSED:
587 return IDEVICE_E_CONNREFUSED;
588 default:
589 break;
590 }
591 return IDEVICE_E_NO_DEVICE;
592 }
593
594 idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private));
595 new_connection->type = CONNECTION_NETWORK;
596 new_connection->data = (void*)(long)sfd;
597 new_connection->ssl_data = NULL;
598 new_connection->device = device;
599 new_connection->ssl_recv_timeout = (unsigned int)-1;
600
601 *connection = new_connection;
602
603 return IDEVICE_E_SUCCESS;
232 } 604 }
233 605
606 debug_info("Unknown connection type %d", device->conn_type);
234 return IDEVICE_E_UNKNOWN_ERROR; 607 return IDEVICE_E_UNKNOWN_ERROR;
235} 608}
236 609
237/**
238 * Disconnect from the device and clean up the connection structure.
239 *
240 * @param connection The connection to close.
241 *
242 * @return IDEVICE_E_SUCCESS if ok, otherwise an error code.
243 */
244idevice_error_t idevice_disconnect(idevice_connection_t connection) 610idevice_error_t idevice_disconnect(idevice_connection_t connection)
245{ 611{
246 if (!connection) { 612 if (!connection) {
@@ -253,11 +619,19 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection)
253 idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; 619 idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR;
254 if (connection->type == CONNECTION_USBMUXD) { 620 if (connection->type == CONNECTION_USBMUXD) {
255 usbmuxd_disconnect((int)(long)connection->data); 621 usbmuxd_disconnect((int)(long)connection->data);
622 connection->data = NULL;
623 result = IDEVICE_E_SUCCESS;
624 } else if (connection->type == CONNECTION_NETWORK) {
625 socket_close((int)(long)connection->data);
626 connection->data = NULL;
256 result = IDEVICE_E_SUCCESS; 627 result = IDEVICE_E_SUCCESS;
257 } else { 628 } else {
258 debug_info("Unknown connection type %d", connection->type); 629 debug_info("Unknown connection type %d", connection->type);
259 } 630 }
631
260 free(connection); 632 free(connection);
633 connection = NULL;
634
261 return result; 635 return result;
262} 636}
263 637
@@ -271,46 +645,111 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection,
271 } 645 }
272 646
273 if (connection->type == CONNECTION_USBMUXD) { 647 if (connection->type == CONNECTION_USBMUXD) {
274 int res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); 648 int res;
649 do {
650 res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes);
651 } while (res == -EAGAIN);
275 if (res < 0) { 652 if (res < 0) {
276 debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); 653 debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res));
277 return IDEVICE_E_UNKNOWN_ERROR; 654 return IDEVICE_E_UNKNOWN_ERROR;
278 } 655 }
279 return IDEVICE_E_SUCCESS; 656 return IDEVICE_E_SUCCESS;
280 } else {
281 debug_info("Unknown connection type %d", connection->type);
282 } 657 }
658 if (connection->type == CONNECTION_NETWORK) {
659 int s = socket_send((int)(long)connection->data, (void*)data, len);
660 if (s < 0) {
661 *sent_bytes = 0;
662 return IDEVICE_E_UNKNOWN_ERROR;
663 }
664 *sent_bytes = s;
665 return IDEVICE_E_SUCCESS;
666 }
667
668 debug_info("Unknown connection type %d", connection->type);
283 return IDEVICE_E_UNKNOWN_ERROR; 669 return IDEVICE_E_UNKNOWN_ERROR;
284 670
285} 671}
286 672
287/**
288 * Send data to a device via the given connection.
289 *
290 * @param connection The connection to send data over.
291 * @param data Buffer with data to send.
292 * @param len Size of the buffer to send.
293 * @param sent_bytes Pointer to an uint32_t that will be filled
294 * with the number of bytes actually sent.
295 *
296 * @return IDEVICE_E_SUCCESS if ok, otherwise an error code.
297 */
298idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) 673idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes)
299{ 674{
300 if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { 675 if (!connection || !data
676#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
677 || (connection->ssl_data && !connection->ssl_data->session)
678#endif
679 ) {
301 return IDEVICE_E_INVALID_ARG; 680 return IDEVICE_E_INVALID_ARG;
302 } 681 }
303 682
304 if (connection->ssl_data) { 683 if (connection->ssl_data) {
305 ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); 684 connection->status = IDEVICE_E_SUCCESS;
306 if ((uint32_t)sent == (uint32_t)len) { 685 uint32_t sent = 0;
307 *sent_bytes = sent; 686 while (sent < len) {
308 return IDEVICE_E_SUCCESS; 687#if defined(HAVE_OPENSSL)
688 int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent));
689 if (s <= 0) {
690 int sslerr = SSL_get_error(connection->ssl_data->session, s);
691 if (sslerr == SSL_ERROR_WANT_WRITE) {
692 continue;
693 }
694 break;
695 }
696#elif defined(HAVE_GNUTLS)
697 ssize_t s = gnutls_record_send(connection->ssl_data->session, (void*)(data+sent), (size_t)(len-sent));
698#elif defined(HAVE_MBEDTLS)
699 int s = mbedtls_ssl_write(&connection->ssl_data->ctx, (const unsigned char*)(data+sent), (size_t)(len-sent));
700#endif
701 if (s < 0) {
702 break;
703 }
704 sent += s;
705 }
706 debug_info("SSL_write %d, sent %d", len, sent);
707 if (sent < len) {
708 *sent_bytes = 0;
709 return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status;
710 }
711 *sent_bytes = sent;
712 return IDEVICE_E_SUCCESS;
713 }
714 uint32_t sent = 0;
715 while (sent < len) {
716 uint32_t bytes = 0;
717 int s = internal_connection_send(connection, data+sent, len-sent, &bytes);
718 if (s < 0) {
719 break;
720 }
721 sent += bytes;
722 }
723 debug_info("internal_connection_send %d, sent %d", len, sent);
724 if (sent < len) {
725 *sent_bytes = sent;
726 if (sent == 0) {
727 return IDEVICE_E_UNKNOWN_ERROR;
728 }
729 return IDEVICE_E_NOT_ENOUGH_DATA;
730 }
731 *sent_bytes = sent;
732 return IDEVICE_E_SUCCESS;
733}
734
735static inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received)
736{
737 if (conn_error < 0) {
738 switch (conn_error) {
739 case -EAGAIN:
740 if (len) {
741 debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error));
742 } else {
743 debug_info("ERROR: received partial data (%s)", strerror(-conn_error));
744 }
745 return IDEVICE_E_NOT_ENOUGH_DATA;
746 case -ETIMEDOUT:
747 return IDEVICE_E_TIMEOUT;
748 default:
749 return IDEVICE_E_UNKNOWN_ERROR;
309 } 750 }
310 *sent_bytes = 0;
311 return IDEVICE_E_SSL_ERROR;
312 } 751 }
313 return internal_connection_send(connection, data, len, sent_bytes); 752 return IDEVICE_E_SUCCESS;
314} 753}
315 754
316/** 755/**
@@ -324,47 +763,92 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t
324 } 763 }
325 764
326 if (connection->type == CONNECTION_USBMUXD) { 765 if (connection->type == CONNECTION_USBMUXD) {
327 int res = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); 766 int conn_error = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout);
328 if (res < 0) { 767 idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes);
329 debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res)); 768 if (error == IDEVICE_E_UNKNOWN_ERROR) {
330 return IDEVICE_E_UNKNOWN_ERROR; 769 debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error));
331 } 770 }
332 return IDEVICE_E_SUCCESS; 771 return error;
333 } else {
334 debug_info("Unknown connection type %d", connection->type);
335 } 772 }
773 if (connection->type == CONNECTION_NETWORK) {
774 int res = socket_receive_timeout((int)(long)connection->data, data, len, 0, timeout);
775 idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0);
776 if (error == IDEVICE_E_SUCCESS) {
777 *recv_bytes = (uint32_t)res;
778 } else if (error == IDEVICE_E_UNKNOWN_ERROR) {
779 debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res));
780 }
781 return error;
782 }
783
784 debug_info("Unknown connection type %d", connection->type);
336 return IDEVICE_E_UNKNOWN_ERROR; 785 return IDEVICE_E_UNKNOWN_ERROR;
337} 786}
338 787
339/**
340 * Receive data from a device via the given connection.
341 * This function will return after the given timeout even if no data has been
342 * received.
343 *
344 * @param connection The connection to receive data from.
345 * @param data Buffer that will be filled with the received data.
346 * This buffer has to be large enough to hold len bytes.
347 * @param len Buffer size or number of bytes to receive.
348 * @param recv_bytes Number of bytes actually received.
349 * @param timeout Timeout in milliseconds after which this function should
350 * return even if no data has been received.
351 *
352 * @return IDEVICE_E_SUCCESS if ok, otherwise an error code.
353 */
354idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) 788idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout)
355{ 789{
356 if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { 790 if (!connection
791#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
792 || (connection->ssl_data && !connection->ssl_data->session)
793#endif
794 || len == 0
795 ) {
357 return IDEVICE_E_INVALID_ARG; 796 return IDEVICE_E_INVALID_ARG;
358 } 797 }
359 798
360 if (connection->ssl_data) { 799 if (connection->ssl_data) {
361 ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); 800 uint32_t received = 0;
362 if (received > 0) { 801
802 if (connection->ssl_recv_timeout != (unsigned int)-1) {
803 debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout");
804 }
805
806 // this should be reset after the SSL_read call on all codepaths, as
807 // the supplied timeout should only apply to the current read.
808 connection->ssl_recv_timeout = timeout;
809 connection->status = IDEVICE_E_SUCCESS;
810 while (received < len) {
811#if defined(HAVE_OPENSSL)
812 int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received);
813 if (r > 0) {
814 received += r;
815 } else {
816 int sslerr = SSL_get_error(connection->ssl_data->session, r);
817 if (sslerr == SSL_ERROR_WANT_READ) {
818 continue;
819 } else if (sslerr == SSL_ERROR_ZERO_RETURN) {
820 if (connection->status == IDEVICE_E_TIMEOUT) {
821 SSL_set_shutdown(connection->ssl_data->session, 0);
822 }
823 }
824 break;
825 }
826#elif defined(HAVE_GNUTLS)
827 ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received);
828 if (r > 0) {
829 received += r;
830 } else {
831 break;
832 }
833#elif defined(HAVE_MBEDTLS)
834 int r = mbedtls_ssl_read(&connection->ssl_data->ctx, (void*)(data+received), (size_t)len-received);
835 if (r > 0) {
836 received += r;
837 } else {
838 break;
839 }
840#endif
841 }
842 connection->ssl_recv_timeout = (unsigned int)-1;
843
844 debug_info("SSL_read %d, received %d", len, received);
845 if (received < len) {
363 *recv_bytes = received; 846 *recv_bytes = received;
364 return IDEVICE_E_SUCCESS; 847 return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status;
365 } 848 }
366 *recv_bytes = 0; 849
367 return IDEVICE_E_SSL_ERROR; 850 *recv_bytes = received;
851 return IDEVICE_E_SUCCESS;
368 } 852 }
369 return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); 853 return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout);
370} 854}
@@ -384,35 +868,45 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti
384 debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); 868 debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res));
385 return IDEVICE_E_UNKNOWN_ERROR; 869 return IDEVICE_E_UNKNOWN_ERROR;
386 } 870 }
387
388 return IDEVICE_E_SUCCESS; 871 return IDEVICE_E_SUCCESS;
389 } else {
390 debug_info("Unknown connection type %d", connection->type);
391 } 872 }
873 if (connection->type == CONNECTION_NETWORK) {
874 int res = socket_receive((int)(long)connection->data, data, len);
875 if (res < 0) {
876 debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res));
877 return IDEVICE_E_UNKNOWN_ERROR;
878 }
879 *recv_bytes = (uint32_t)res;
880 return IDEVICE_E_SUCCESS;
881 }
882
883 debug_info("Unknown connection type %d", connection->type);
392 return IDEVICE_E_UNKNOWN_ERROR; 884 return IDEVICE_E_UNKNOWN_ERROR;
393} 885}
394 886
395/**
396 * Receive data from a device via the given connection.
397 * This function is like idevice_connection_receive_timeout, but with a
398 * predefined reasonable timeout.
399 *
400 * @param connection The connection to receive data from.
401 * @param data Buffer that will be filled with the received data.
402 * This buffer has to be large enough to hold len bytes.
403 * @param len Buffer size or number of bytes to receive.
404 * @param recv_bytes Number of bytes actually received.
405 *
406 * @return IDEVICE_E_SUCCESS if ok, otherwise an error code.
407 */
408idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) 887idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes)
409{ 888{
410 if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { 889 if (!connection
890#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
891 || (connection->ssl_data && !connection->ssl_data->session)
892#endif
893 ) {
411 return IDEVICE_E_INVALID_ARG; 894 return IDEVICE_E_INVALID_ARG;
412 } 895 }
413 896
414 if (connection->ssl_data) { 897 if (connection->ssl_data) {
898 if (connection->ssl_recv_timeout != (unsigned int)-1) {
899 debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout");
900 connection->ssl_recv_timeout = (unsigned int)-1;
901 }
902#if defined(HAVE_OPENSSL)
903 int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len);
904 debug_info("SSL_read %d, received %d", len, received);
905#elif defined(HAVE_GNUTLS)
415 ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); 906 ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len);
907#elif defined(HAVE_MBEDTLS)
908 int received = mbedtls_ssl_read(&connection->ssl_data->ctx, (unsigned char*)data, (size_t)len);
909#endif
416 if (received > 0) { 910 if (received > 0) {
417 *recv_bytes = received; 911 *recv_bytes = received;
418 return IDEVICE_E_SUCCESS; 912 return IDEVICE_E_SUCCESS;
@@ -423,89 +917,105 @@ idevice_error_t idevice_connection_receive(idevice_connection_t connection, char
423 return internal_connection_receive(connection, data, len, recv_bytes); 917 return internal_connection_receive(connection, data, len, recv_bytes);
424} 918}
425 919
426/** 920idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *fd)
427 * Gets the handle of the device. Depends on the connection type.
428 */
429idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle)
430{ 921{
431 if (!device) 922 if (!connection || !fd) {
432 return IDEVICE_E_INVALID_ARG; 923 return IDEVICE_E_INVALID_ARG;
924 }
433 925
434 if (device->conn_type == CONNECTION_USBMUXD) { 926 if (connection->type == CONNECTION_USBMUXD) {
435 *handle = (uint32_t)(long)device->conn_data; 927 *fd = (int)(long)connection->data;
928 return IDEVICE_E_SUCCESS;
929 }
930 if (connection->type == CONNECTION_NETWORK) {
931 *fd = (int)(long)connection->data;
436 return IDEVICE_E_SUCCESS; 932 return IDEVICE_E_SUCCESS;
437 } else {
438 debug_info("Unknown connection type %d", device->conn_type);
439 } 933 }
934
935 debug_info("Unknown connection type %d", connection->type);
440 return IDEVICE_E_UNKNOWN_ERROR; 936 return IDEVICE_E_UNKNOWN_ERROR;
441} 937}
442 938
443/** 939idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle)
444 * Gets the unique id for the device. 940{
445 */ 941 if (!device || !handle)
446idevice_error_t idevice_get_uuid(idevice_t device, char **uuid) 942 return IDEVICE_E_INVALID_ARG;
943
944 *handle = device->mux_id;
945 return IDEVICE_E_SUCCESS;
946}
947
948idevice_error_t idevice_get_udid(idevice_t device, char **udid)
447{ 949{
448 if (!device || !uuid) 950 if (!device || !udid)
449 return IDEVICE_E_INVALID_ARG; 951 return IDEVICE_E_INVALID_ARG;
450 952
451 *uuid = strdup(device->uuid); 953 if (device->udid) {
954 *udid = strdup(device->udid);
955 }
452 return IDEVICE_E_SUCCESS; 956 return IDEVICE_E_SUCCESS;
453} 957}
454 958
959#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
960typedef ssize_t ssl_cb_ret_type_t;
961#elif defined(HAVE_MBEDTLS)
962typedef int ssl_cb_ret_type_t;
963#endif
964
455/** 965/**
456 * Internally used gnutls callback function for receiving encrypted data. 966 * Internally used SSL callback function for receiving encrypted data.
457 */ 967 */
458static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) 968static ssl_cb_ret_type_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length)
459{ 969{
460 int bytes = 0, pos_start_fill = 0; 970 uint32_t bytes = 0;
461 size_t tbytes = 0; 971 uint32_t pos = 0;
462 int this_len = length;
463 idevice_error_t res; 972 idevice_error_t res;
464 idevice_connection_t connection = (idevice_connection_t)transport; 973 unsigned int timeout = connection->ssl_recv_timeout;
465 char *recv_buffer;
466
467 debug_info("pre-read client wants %zi bytes", length);
468 974
469 recv_buffer = (char *) malloc(sizeof(char) * this_len); 975 debug_info("pre-read length = %zi bytes", length);
470 976
471 /* repeat until we have the full data or an error occurs */ 977 /* repeat until we have the full data or an error occurs */
472 do { 978 do {
473 if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { 979 bytes = 0;
474 debug_info("ERROR: idevice_connection_receive returned %d", res); 980 if (timeout == (unsigned int)-1) {
475 return res; 981 res = internal_connection_receive(connection, buffer + pos, (uint32_t)length - pos, &bytes);
982 } else {
983 res = internal_connection_receive_timeout(connection, buffer + pos, (uint32_t)length - pos, &bytes, (unsigned int)timeout);
476 } 984 }
477 debug_info("post-read we got %i bytes", bytes); 985 if (res != IDEVICE_E_SUCCESS) {
986 if (res != IDEVICE_E_TIMEOUT) {
987 debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res);
988 }
989 connection->status = res;
990 return -1;
991 }
992 debug_info("read %i bytes", bytes);
478 993
479 /* increase read count */ 994 /* increase read count */
480 tbytes += bytes; 995 pos += bytes;
481 996 if (pos < (uint32_t)length) {
482 /* fill the buffer with what we got right now */ 997 debug_info("re-read trying to read missing %i bytes", (uint32_t)length - pos);
483 memcpy(buffer + pos_start_fill, recv_buffer, bytes);
484 pos_start_fill += bytes;
485
486 if (tbytes >= length) {
487 break;
488 } 998 }
999 } while (pos < (uint32_t)length);
489 1000
490 this_len = length - tbytes; 1001 debug_info("post-read received %i bytes", pos);
491 debug_info("re-read trying to read missing %i bytes", this_len);
492 } while (tbytes < length);
493 1002
494 if (recv_buffer) { 1003 return pos;
495 free(recv_buffer);
496 }
497 return tbytes;
498} 1004}
499 1005
500/** 1006/**
501 * Internally used gnutls callback function for sending encrypted data. 1007 * Internally used SSL callback function for sending encrypted data.
502 */ 1008 */
503static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) 1009static ssl_cb_ret_type_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length)
504{ 1010{
505 uint32_t bytes = 0; 1011 uint32_t bytes = 0;
506 idevice_connection_t connection = (idevice_connection_t)transport; 1012 idevice_error_t res;
507 debug_info("pre-send length = %zi", length); 1013 debug_info("pre-send length = %zi bytes", length);
508 internal_connection_send(connection, buffer, length, &bytes); 1014 if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) {
1015 debug_info("ERROR: internal_connection_send returned %d", res);
1016 connection->status = res;
1017 return -1;
1018 }
509 debug_info("post-send sent %i bytes", bytes); 1019 debug_info("post-send sent %i bytes", bytes);
510 return bytes; 1020 return bytes;
511} 1021}
@@ -518,6 +1028,14 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data)
518 if (!ssl_data) 1028 if (!ssl_data)
519 return; 1029 return;
520 1030
1031#if defined(HAVE_OPENSSL)
1032 if (ssl_data->session) {
1033 SSL_free(ssl_data->session);
1034 }
1035 if (ssl_data->ctx) {
1036 SSL_CTX_free(ssl_data->ctx);
1037 }
1038#elif defined(HAVE_GNUTLS)
521 if (ssl_data->session) { 1039 if (ssl_data->session) {
522 gnutls_deinit(ssl_data->session); 1040 gnutls_deinit(ssl_data->session);
523 } 1041 }
@@ -536,20 +1054,118 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data)
536 if (ssl_data->host_privkey) { 1054 if (ssl_data->host_privkey) {
537 gnutls_x509_privkey_deinit(ssl_data->host_privkey); 1055 gnutls_x509_privkey_deinit(ssl_data->host_privkey);
538 } 1056 }
1057#elif defined(HAVE_MBEDTLS)
1058 mbedtls_pk_free(&ssl_data->root_privkey);
1059 mbedtls_x509_crt_free(&ssl_data->certificate);
1060 mbedtls_entropy_free(&ssl_data->entropy);
1061 mbedtls_ctr_drbg_free(&ssl_data->ctr_drbg);
1062 mbedtls_ssl_config_free(&ssl_data->config);
1063 mbedtls_ssl_free(&ssl_data->ctx);
1064#endif
1065}
1066
1067#ifdef HAVE_OPENSSL
1068#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1069static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, size_t len, int argi, long argl, int retvalue, size_t *processed)
1070#else
1071static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue)
1072#endif
1073{
1074 ssize_t bytes = 0;
1075 idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b);
1076#if OPENSSL_VERSION_NUMBER < 0x30000000L
1077 size_t len = (size_t)argi;
1078 size_t *processed = (size_t*)&bytes;
1079#endif
1080 switch (oper) {
1081 case (BIO_CB_READ|BIO_CB_RETURN):
1082 if (argp) {
1083 bytes = internal_ssl_read(conn, (char *)argp, len);
1084 *processed = bytes;
1085 return (long)bytes;
1086 }
1087 return 0;
1088 case (BIO_CB_PUTS|BIO_CB_RETURN):
1089 len = strlen(argp);
1090 // fallthrough
1091 case (BIO_CB_WRITE|BIO_CB_RETURN):
1092 bytes = internal_ssl_write(conn, argp, len);
1093 *processed = bytes;
1094 return (long)bytes;
1095 default:
1096 return retvalue;
1097 }
1098}
1099
1100static BIO *ssl_idevice_bio_new(idevice_connection_t conn)
1101{
1102 BIO *b = BIO_new(BIO_s_null());
1103 if (!b) return NULL;
1104 BIO_set_callback_arg(b, (char *)conn);
1105#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1106 BIO_set_callback_ex(b, ssl_idevice_bio_callback);
1107#else
1108 BIO_set_callback(b, ssl_idevice_bio_callback);
1109#endif
1110 return b;
1111}
1112
1113static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx)
1114{
1115 return 1;
1116}
1117
1118#ifndef STRIP_DEBUG_CODE
1119static const char *ssl_error_to_string(int e)
1120{
1121 switch(e) {
1122 case SSL_ERROR_NONE:
1123 return "SSL_ERROR_NONE";
1124 case SSL_ERROR_SSL:
1125 return ERR_error_string(ERR_get_error(), NULL);
1126 case SSL_ERROR_WANT_READ:
1127 return "SSL_ERROR_WANT_READ";
1128 case SSL_ERROR_WANT_WRITE:
1129 return "SSL_ERROR_WANT_WRITE";
1130 case SSL_ERROR_WANT_X509_LOOKUP:
1131 return "SSL_ERROR_WANT_X509_LOOKUP";
1132 case SSL_ERROR_SYSCALL:
1133 return "SSL_ERROR_SYSCALL";
1134 case SSL_ERROR_ZERO_RETURN:
1135 return "SSL_ERROR_ZERO_RETURN";
1136 case SSL_ERROR_WANT_CONNECT:
1137 return "SSL_ERROR_WANT_CONNECT";
1138 case SSL_ERROR_WANT_ACCEPT:
1139 return "SSL_ERROR_WANT_ACCEPT";
1140 default:
1141 return "UNKOWN_ERROR_VALUE";
1142 }
539} 1143}
1144#endif
1145#endif
540 1146
1147#if defined(HAVE_GNUTLS)
541/** 1148/**
542 * Internally used gnutls callback function that gets called during handshake. 1149 * Internally used gnutls callback function that gets called during handshake.
543 */ 1150 */
544static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) 1151#if GNUTLS_VERSION_NUMBER >= 0x020b07
1152static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st)
1153#else
1154static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st)
1155#endif
545{ 1156{
546 int res = -1; 1157 int res = -1;
547 gnutls_certificate_type_t type = gnutls_certificate_type_get (session); 1158 gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
548 if (type == GNUTLS_CRT_X509) { 1159 if (type == GNUTLS_CRT_X509) {
549 ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr (session); 1160 ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr(session);
550 if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { 1161 if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) {
551 debug_info("Passing certificate"); 1162 debug_info("Passing certificate");
1163#if GNUTLS_VERSION_NUMBER >= 0x020b07
1164 st->cert_type = type;
1165 st->key_type = GNUTLS_PRIVKEY_X509;
1166#else
552 st->type = type; 1167 st->type = type;
1168#endif
553 st->ncerts = 1; 1169 st->ncerts = 1;
554 st->cert.x509 = &ssl_data->host_cert; 1170 st->cert.x509 = &ssl_data->host_cert;
555 st->key.x509 = ssl_data->host_privkey; 1171 st->key.x509 = ssl_data->host_privkey;
@@ -559,46 +1175,204 @@ static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_
559 } 1175 }
560 return res; 1176 return res;
561} 1177}
1178#elif defined(HAVE_MBEDTLS)
1179static void _mbedtls_log_cb(void* ctx, int level, const char* filename, int line, const char* message)
1180{
1181 fprintf(stderr, "[mbedtls][%d] %s:%d => %s", level, filename, line, message);
1182}
1183
1184static int cert_verify_cb(void* ctx, mbedtls_x509_crt* cert, int depth, uint32_t *flags)
1185{
1186 *flags = 0;
1187 return 0;
1188}
1189
1190static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len)
1191{
1192 memset(buf, 4, len);
1193 return 0;
1194}
1195#endif
562 1196
563/**
564 * Enables SSL for the given connection.
565 *
566 * @param connection The connection to enable SSL for.
567 *
568 * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection
569 * is NULL or connection->ssl_data is non-NULL, or IDEVICE_E_SSL_ERROR when
570 * SSL initialization, setup, or handshake fails.
571 */
572idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) 1197idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
573{ 1198{
574 if (!connection || connection->ssl_data) 1199 if (!connection || connection->ssl_data)
575 return IDEVICE_E_INVALID_ARG; 1200 return IDEVICE_E_INVALID_ARG;
576 1201
577 idevice_error_t ret = IDEVICE_E_SSL_ERROR; 1202 idevice_error_t ret = IDEVICE_E_SSL_ERROR;
578 uint32_t return_me = 0; 1203 plist_t pair_record = NULL;
1204
1205 userpref_error_t uerr = userpref_read_pair_record(connection->device->udid, &pair_record);
1206 if (uerr != USERPREF_E_SUCCESS) {
1207 debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s (%d)", connection->device->udid, uerr);
1208 return ret;
1209 }
1210
1211#if defined(HAVE_OPENSSL)
1212 key_data_t root_cert = { NULL, 0 };
1213 key_data_t root_privkey = { NULL, 0 };
1214
1215 pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert);
1216 pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey);
579 1217
1218 if (pair_record)
1219 plist_free(pair_record);
1220
1221 BIO *ssl_bio = ssl_idevice_bio_new(connection);
1222 if (!ssl_bio) {
1223 debug_info("ERROR: Could not create SSL bio.");
1224 return ret;
1225 }
1226
1227 SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
1228 if (ssl_ctx == NULL) {
1229 debug_info("ERROR: Could not create SSL context.");
1230 BIO_free(ssl_bio);
1231 return ret;
1232 }
1233
1234#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || \
1235 (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3060000fL))
1236 SSL_CTX_set_security_level(ssl_ctx, 0);
1237#endif
1238
1239#if OPENSSL_VERSION_NUMBER < 0x10100002L || \
1240 (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL))
1241 /* force use of TLSv1 for older devices */
1242 if (connection->device->version < DEVICE_VERSION(10,0,0)) {
1243#ifdef SSL_OP_NO_TLSv1_1
1244 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
1245#endif
1246#ifdef SSL_OP_NO_TLSv1_2
1247 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
1248#endif
1249#ifdef SSL_OP_NO_TLSv1_3
1250 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
1251#endif
1252 }
1253#else
1254 SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
1255 if (connection->device->version < DEVICE_VERSION(10,0,0)) {
1256 SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION);
1257 if (connection->device->version == 0) {
1258 /*
1259 iOS 1 doesn't understand TLS1_VERSION, it can only speak SSL3_VERSION.
1260 However, modern OpenSSL is usually compiled without SSLv3 support.
1261 So if we set min_proto_version to SSL3_VERSION on an OpenSSL instance which doesn't support it,
1262 it will just ignore min_proto_version altogether and fall back to an even higher version.
1263 To avoid accidentally breaking iOS 2.0+, we set min version to 0 instead.
1264 Here is what documentation says:
1265 Setting the minimum or maximum version to 0,
1266 will enable protocol versions down to the lowest version,
1267 or up to the highest version supported by the library, respectively.
1268 */
1269 SSL_CTX_set_min_proto_version(ssl_ctx, 0);
1270 }
1271 }
1272#endif
1273#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1274#if defined(SSL_OP_IGNORE_UNEXPECTED_EOF)
1275 /*
1276 * For OpenSSL 3 and later, mark close_notify alerts as optional.
1277 * For prior versions of OpenSSL we check for SSL_ERROR_SYSCALL when
1278 * reading instead (this error changes to SSL_ERROR_SSL in OpenSSL 3).
1279 */
1280 SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
1281#endif
1282#if defined(SSL_OP_LEGACY_SERVER_CONNECT)
1283 /*
1284 * Without setting SSL_OP_LEGACY_SERVER_CONNECT, OpenSSL 3 fails with
1285 * error "unsafe legacy renegotiation disabled" when talking to iOS 5
1286 */
1287 SSL_CTX_set_options(ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT);
1288#endif
1289#endif
1290
1291 BIO* membp;
1292 X509* rootCert = NULL;
1293 membp = BIO_new_mem_buf(root_cert.data, root_cert.size);
1294 PEM_read_bio_X509(membp, &rootCert, NULL, NULL);
1295 BIO_free(membp);
1296 if (SSL_CTX_use_certificate(ssl_ctx, rootCert) != 1) {
1297 debug_info("WARNING: Could not load RootCertificate");
1298 }
1299 X509_free(rootCert);
1300 free(root_cert.data);
1301
1302#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1303 EVP_PKEY* rootPrivKey = NULL;
1304 membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size);
1305 PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL);
1306 BIO_free(membp);
1307 if (SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey) != 1) {
1308 debug_info("WARNING: Could not load RootPrivateKey");
1309 }
1310 EVP_PKEY_free(rootPrivKey);
1311#else
1312 RSA* rootPrivKey = NULL;
1313 membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size);
1314 PEM_read_bio_RSAPrivateKey(membp, &rootPrivKey, NULL, NULL);
1315 BIO_free(membp);
1316 if (SSL_CTX_use_RSAPrivateKey(ssl_ctx, rootPrivKey) != 1) {
1317 debug_info("WARNING: Could not load RootPrivateKey");
1318 }
1319 RSA_free(rootPrivKey);
1320#endif
1321 free(root_privkey.data);
1322
1323 SSL *ssl = SSL_new(ssl_ctx);
1324 if (!ssl) {
1325 debug_info("ERROR: Could not create SSL object");
1326 BIO_free(ssl_bio);
1327 SSL_CTX_free(ssl_ctx);
1328 return ret;
1329 }
1330 SSL_set_connect_state(ssl);
1331 SSL_set_verify(ssl, 0, ssl_verify_callback);
1332 SSL_set_bio(ssl, ssl_bio, ssl_bio);
1333
1334 debug_info("Performing SSL handshake");
1335 int ssl_error = 0;
1336 do {
1337 ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl));
1338 if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) {
1339 break;
1340 }
1341#ifdef WIN32
1342 Sleep(100);
1343#else
1344 struct timespec ts = { 0, 100000000 };
1345 nanosleep(&ts, NULL);
1346#endif
1347 } while (1);
1348 if (ssl_error != 0) {
1349 debug_info("ERROR during SSL handshake: %s", ssl_error_to_string(ssl_error));
1350 SSL_free(ssl);
1351 SSL_CTX_free(ssl_ctx);
1352 } else {
1353 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private));
1354 ssl_data_loc->session = ssl;
1355 ssl_data_loc->ctx = ssl_ctx;
1356 connection->ssl_data = ssl_data_loc;
1357 ret = IDEVICE_E_SUCCESS;
1358 debug_info("SSL mode enabled, %s, cipher: %s", SSL_get_version(ssl), SSL_get_cipher(ssl));
1359 }
1360 /* required for proper multi-thread clean up to prevent leaks */
1361 openssl_remove_thread_state();
1362#elif defined(HAVE_GNUTLS)
580 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); 1363 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private));
581 1364
582 /* Set up GnuTLS... */ 1365 /* Set up GnuTLS... */
583 debug_info("enabling SSL mode"); 1366 debug_info("enabling SSL mode");
584 errno = 0; 1367 errno = 0;
585 gnutls_global_init();
586 gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); 1368 gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate);
587 gnutls_certificate_client_set_retrieve_function (ssl_data_loc->certificate, internal_cert_callback); 1369#if GNUTLS_VERSION_NUMBER >= 0x020b07
1370 gnutls_certificate_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback);
1371#else
1372 gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback);
1373#endif
588 gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); 1374 gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT);
589 { 1375 gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-TLS1.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL);
590 int protocol_priority[16] = { GNUTLS_SSL3, 0 };
591 int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 };
592 int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 };
593 int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 };
594 int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
595
596 gnutls_cipher_set_priority(ssl_data_loc->session, cipher_priority);
597 gnutls_compression_set_priority(ssl_data_loc->session, comp_priority);
598 gnutls_kx_set_priority(ssl_data_loc->session, kx_priority);
599 gnutls_protocol_set_priority(ssl_data_loc->session, protocol_priority);
600 gnutls_mac_set_priority(ssl_data_loc->session, mac_priority);
601 }
602 gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); 1376 gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate);
603 gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); 1377 gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc);
604 1378
@@ -607,10 +1381,13 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
607 gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); 1381 gnutls_x509_privkey_init(&ssl_data_loc->root_privkey);
608 gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); 1382 gnutls_x509_privkey_init(&ssl_data_loc->host_privkey);
609 1383
610 userpref_error_t uerr = userpref_get_keys_and_certs(ssl_data_loc->root_privkey, ssl_data_loc->root_cert, ssl_data_loc->host_privkey, ssl_data_loc->host_cert); 1384 pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, ssl_data_loc->root_cert);
611 if (uerr != USERPREF_E_SUCCESS) { 1385 pair_record_import_crt_with_name(pair_record, USERPREF_HOST_CERTIFICATE_KEY, ssl_data_loc->host_cert);
612 debug_info("Error %d when loading keys and certificates! %d", uerr); 1386 pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, ssl_data_loc->root_privkey);
613 } 1387 pair_record_import_key_with_name(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, ssl_data_loc->host_privkey);
1388
1389 if (pair_record)
1390 plist_free(pair_record);
614 1391
615 debug_info("GnuTLS step 1..."); 1392 debug_info("GnuTLS step 1...");
616 gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); 1393 gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection);
@@ -619,46 +1396,146 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
619 debug_info("GnuTLS step 3..."); 1396 debug_info("GnuTLS step 3...");
620 gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); 1397 gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read);
621 debug_info("GnuTLS step 4 -- now handshaking..."); 1398 debug_info("GnuTLS step 4 -- now handshaking...");
622 if (errno) 1399 if (errno) {
623 debug_info("WARN: errno says %s before handshake!", strerror(errno)); 1400 debug_info("WARNING: errno says %s before handshake!", strerror(errno));
624 return_me = gnutls_handshake(ssl_data_loc->session); 1401 }
1402
1403 int return_me = 0;
1404 do {
1405 return_me = gnutls_handshake(ssl_data_loc->session);
1406 } while(return_me == GNUTLS_E_AGAIN || return_me == GNUTLS_E_INTERRUPTED);
1407
625 debug_info("GnuTLS handshake done..."); 1408 debug_info("GnuTLS handshake done...");
626 1409
627 if (return_me != GNUTLS_E_SUCCESS) { 1410 if (return_me != GNUTLS_E_SUCCESS) {
628 internal_ssl_cleanup(ssl_data_loc); 1411 internal_ssl_cleanup(ssl_data_loc);
629 free(ssl_data_loc); 1412 free(ssl_data_loc);
630 debug_info("GnuTLS reported something wrong."); 1413 debug_info("GnuTLS reported something wrong: %s", gnutls_strerror(return_me));
631 gnutls_perror(return_me);
632 debug_info("oh.. errno says %s", strerror(errno)); 1414 debug_info("oh.. errno says %s", strerror(errno));
633 } else { 1415 } else {
634 connection->ssl_data = ssl_data_loc; 1416 connection->ssl_data = ssl_data_loc;
635 ret = IDEVICE_E_SUCCESS; 1417 ret = IDEVICE_E_SUCCESS;
636 debug_info("SSL mode enabled"); 1418 debug_info("SSL mode enabled");
637 } 1419 }
1420#elif defined(HAVE_MBEDTLS)
1421 key_data_t root_cert = { NULL, 0 };
1422 key_data_t root_privkey = { NULL, 0 };
1423
1424 pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert);
1425 pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey);
1426
1427 plist_free(pair_record);
1428
1429 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private));
1430
1431 mbedtls_ssl_init(&ssl_data_loc->ctx);
1432 mbedtls_ssl_config_init(&ssl_data_loc->config);
1433 mbedtls_entropy_init(&ssl_data_loc->entropy);
1434 mbedtls_ctr_drbg_init(&ssl_data_loc->ctr_drbg);
1435
1436 int r = mbedtls_ctr_drbg_seed(&ssl_data_loc->ctr_drbg, mbedtls_entropy_func, &ssl_data_loc->entropy, NULL, 0);
1437 if (r != 0) {
1438 debug_info("ERROR: [mbedtls] mbedtls_ctr_drbg_seed failed: %d", r);
1439 return ret;
1440 }
1441
1442 if (mbedtls_ssl_config_defaults(&ssl_data_loc->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
1443 debug_info("ERROR: [mbedtls] Failed to set config defaults");
1444 return ret;
1445 }
1446
1447 mbedtls_ssl_conf_rng(&ssl_data_loc->config, mbedtls_ctr_drbg_random, &ssl_data_loc->ctr_drbg);
1448
1449 mbedtls_ssl_conf_dbg(&ssl_data_loc->config, _mbedtls_log_cb, NULL);
1450
1451 mbedtls_ssl_conf_verify(&ssl_data_loc->config, cert_verify_cb, NULL);
1452
1453 mbedtls_ssl_setup(&ssl_data_loc->ctx, &ssl_data_loc->config);
1454
1455 mbedtls_ssl_set_bio(&ssl_data_loc->ctx, connection, (mbedtls_ssl_send_t*)&internal_ssl_write, (mbedtls_ssl_recv_t*)&internal_ssl_read, NULL);
1456
1457 mbedtls_x509_crt_init(&ssl_data_loc->certificate);
1458
1459 int crterr = mbedtls_x509_crt_parse(&ssl_data_loc->certificate, root_cert.data, root_cert.size);
1460 if (crterr < 0) {
1461 debug_info("ERROR: [mbedtls] parsing root cert failed: %d", crterr);
1462 return ret;
1463 }
1464
1465 mbedtls_ssl_conf_ca_chain(&ssl_data_loc->config, &ssl_data_loc->certificate, NULL);
1466
1467 mbedtls_pk_init(&ssl_data_loc->root_privkey);
1468
1469#if MBEDTLS_VERSION_NUMBER >= 0x03000000
1470 int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0, &_mbedtls_f_rng, NULL);
1471#else
1472 int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0);
1473#endif
1474 if (pkerr < 0) {
1475 debug_info("ERROR: [mbedtls] parsing private key failed: %d (size=%d)", pkerr, root_privkey.size);
1476 return ret;
1477 }
1478
1479 mbedtls_ssl_conf_own_cert(&ssl_data_loc->config, &ssl_data_loc->certificate, &ssl_data_loc->root_privkey);
1480
1481 int return_me = 0;
1482 do {
1483 return_me = mbedtls_ssl_handshake(&ssl_data_loc->ctx);
1484 } while (return_me == MBEDTLS_ERR_SSL_WANT_READ || return_me == MBEDTLS_ERR_SSL_WANT_WRITE);
1485
1486 if (return_me != 0) {
1487 debug_info("ERROR during SSL handshake: %d", return_me);
1488 internal_ssl_cleanup(ssl_data_loc);
1489 free(ssl_data_loc);
1490 } else {
1491 connection->ssl_data = ssl_data_loc;
1492 ret = IDEVICE_E_SUCCESS;
1493 debug_info("SSL mode enabled, %s, cipher: %s", mbedtls_ssl_get_version(&ssl_data_loc->ctx), mbedtls_ssl_get_ciphersuite(&ssl_data_loc->ctx));
1494 debug_info("SSL mode enabled");
1495 }
1496#endif
638 return ret; 1497 return ret;
639} 1498}
640 1499
641/**
642 * Disable SSL for the given connection.
643 *
644 * @param connection The connection to disable SSL for.
645 *
646 * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection
647 * is NULL. This function also returns IDEVICE_E_SUCCESS when SSL is not
648 * enabled and does no further error checking on cleanup.
649 */
650idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) 1500idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection)
651{ 1501{
1502 return idevice_connection_disable_bypass_ssl(connection, 0);
1503}
1504
1505idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass)
1506{
652 if (!connection) 1507 if (!connection)
653 return IDEVICE_E_INVALID_ARG; 1508 return IDEVICE_E_INVALID_ARG;
654 if (!connection->ssl_data) { 1509 if (!connection->ssl_data) {
655 /* ignore if ssl is not enabled */ 1510 /* ignore if ssl is not enabled */
656 return IDEVICE_E_SUCCESS; 1511 return IDEVICE_E_SUCCESS;
657 } 1512 }
658 1513
659 if (connection->ssl_data->session) { 1514 // some services require plain text communication after SSL handshake
660 gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); 1515 // sending out SSL_shutdown will cause bytes
1516 if (!sslBypass) {
1517#if defined(HAVE_OPENSSL)
1518 if (connection->ssl_data->session) {
1519 /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */
1520 if (SSL_shutdown(connection->ssl_data->session) == 0) {
1521 /* Only try bidirectional shutdown if we know it can complete */
1522 int ssl_error;
1523 if ((ssl_error = SSL_get_error(connection->ssl_data->session, 0)) == SSL_ERROR_NONE) {
1524 SSL_shutdown(connection->ssl_data->session);
1525 } else {
1526 debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error);
1527 }
1528 }
1529 }
1530#elif defined(HAVE_GNUTLS)
1531 if (connection->ssl_data->session) {
1532 gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR);
1533 }
1534#elif defined(HAVE_MBEDTLS)
1535 mbedtls_ssl_close_notify(&connection->ssl_data->ctx);
1536#endif
661 } 1537 }
1538
662 internal_ssl_cleanup(connection->ssl_data); 1539 internal_ssl_cleanup(connection->ssl_data);
663 free(connection->ssl_data); 1540 free(connection->ssl_data);
664 connection->ssl_data = NULL; 1541 connection->ssl_data = NULL;
@@ -667,4 +1544,3 @@ idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection)
667 1544
668 return IDEVICE_E_SUCCESS; 1545 return IDEVICE_E_SUCCESS;
669} 1546}
670
diff --git a/src/idevice.h b/src/idevice.h
index 231b3ab..dd72f9d 100644
--- a/src/idevice.h
+++ b/src/idevice.h
@@ -8,51 +8,97 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef IDEVICE_H
22#define IDEVICE_H
23 21
22#ifndef __DEVICE_H
23#define __DEVICE_H
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#if defined(HAVE_OPENSSL)
30#include <openssl/ssl.h>
31#elif defined(HAVE_GNUTLS)
24#include <gnutls/gnutls.h> 32#include <gnutls/gnutls.h>
25#include <gnutls/x509.h> 33#include <gnutls/x509.h>
34#elif defined(HAVE_MBEDTLS)
35#include <mbedtls/ssl.h>
36#include <mbedtls/entropy.h>
37#include <mbedtls/ctr_drbg.h>
38#endif
26 39
40#ifdef LIBIMOBILEDEVICE_STATIC
41 #define LIBIMOBILEDEVICE_API
42#elif defined(_WIN32)
43 #define LIBIMOBILEDEVICE_API __declspec( dllexport )
44#else
45 #if __GNUC__ >= 4
46 #define LIBIMOBILEDEVICE_API __attribute__((visibility("default")))
47 #else
48 #define LIBIMOBILEDEVICE_API
49 #endif
50#endif
51
52#include "common/userpref.h"
27#include "libimobiledevice/libimobiledevice.h" 53#include "libimobiledevice/libimobiledevice.h"
28 54
29enum connection_type { 55#define DEVICE_VERSION(maj, min, patch) (((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF))
30 CONNECTION_USBMUXD = 1 56
31}; 57#define DEVICE_CLASS_IPHONE 1
58#define DEVICE_CLASS_IPAD 2
59#define DEVICE_CLASS_IPOD 3
60#define DEVICE_CLASS_APPLETV 4
61#define DEVICE_CLASS_WATCH 5
62#define DEVICE_CLASS_UNKNOWN 255
32 63
33struct ssl_data_private { 64struct ssl_data_private {
65#if defined(HAVE_OPENSSL)
66 SSL *session;
67 SSL_CTX *ctx;
68#elif defined(HAVE_GNUTLS)
34 gnutls_certificate_credentials_t certificate; 69 gnutls_certificate_credentials_t certificate;
35 gnutls_session_t session; 70 gnutls_session_t session;
36 gnutls_x509_privkey_t root_privkey; 71 gnutls_x509_privkey_t root_privkey;
37 gnutls_x509_crt_t root_cert; 72 gnutls_x509_crt_t root_cert;
38 gnutls_x509_privkey_t host_privkey; 73 gnutls_x509_privkey_t host_privkey;
39 gnutls_x509_crt_t host_cert; 74 gnutls_x509_crt_t host_cert;
75#elif defined(HAVE_MBEDTLS)
76 mbedtls_ssl_context ctx;
77 mbedtls_ssl_config config;
78 mbedtls_entropy_context entropy;
79 mbedtls_ctr_drbg_context ctr_drbg;
80 mbedtls_x509_crt certificate;
81 mbedtls_pk_context root_privkey;
82#endif
40}; 83};
41typedef struct ssl_data_private *ssl_data_t; 84typedef struct ssl_data_private *ssl_data_t;
42 85
43struct idevice_connection_private { 86struct idevice_connection_private {
44 enum connection_type type; 87 idevice_t device;
88 enum idevice_connection_type type;
45 void *data; 89 void *data;
46 ssl_data_t ssl_data; 90 ssl_data_t ssl_data;
91 unsigned int ssl_recv_timeout;
92 idevice_error_t status;
47}; 93};
48 94
49struct idevice_private { 95struct idevice_private {
50 char *uuid; 96 char *udid;
51 enum connection_type conn_type; 97 uint32_t mux_id;
98 enum idevice_connection_type conn_type;
52 void *conn_data; 99 void *conn_data;
100 int version;
101 int device_class;
53}; 102};
54 103
55idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection);
56idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection);
57
58#endif 104#endif
diff --git a/src/installation_proxy.c b/src/installation_proxy.c
index 4a76dd2..ec19da0 100644
--- a/src/installation_proxy.c
+++ b/src/installation_proxy.c
@@ -2,63 +2,210 @@
2 * installation_proxy.c 2 * installation_proxy.c
3 * com.apple.mobile.installation_proxy service implementation. 3 * com.apple.mobile.installation_proxy service implementation.
4 * 4 *
5 * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21 22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
22#include <string.h> 26#include <string.h>
23#include <stdlib.h> 27#include <stdlib.h>
28#include <inttypes.h>
24#include <unistd.h> 29#include <unistd.h>
25#include <plist/plist.h> 30#include <plist/plist.h>
26 31
27#include "installation_proxy.h" 32#include "installation_proxy.h"
28#include "property_list_service.h" 33#include "property_list_service.h"
29#include "debug.h" 34#include "common/debug.h"
35
36typedef enum {
37 INSTPROXY_COMMAND_TYPE_ASYNC,
38 INSTPROXY_COMMAND_TYPE_SYNC
39} instproxy_command_type_t;
30 40
31struct instproxy_status_data { 41struct instproxy_status_data {
32 instproxy_client_t client; 42 instproxy_client_t client;
43 plist_t command;
33 instproxy_status_cb_t cbfunc; 44 instproxy_status_cb_t cbfunc;
34 char *operation;
35 void *user_data; 45 void *user_data;
36}; 46};
37 47
38/** 48/**
49 * Converts an error string identifier to a instproxy_error_t value.
50 * Used internally to get correct error codes from a response.
51 *
52 * @param name The error name to convert.
53 * @param error_detail Pointer to store error detail text if available. The
54 * caller is reponsible for freeing the allocated buffer after use. If NULL
55 * is passed no error detail will be returned.
56 *
57 * @return A matching instproxy_error_t error code or
58 * INSTPROXY_E_UNKNOWN_ERROR otherwise.
59 */
60static instproxy_error_t instproxy_strtoerr(const char* name)
61{
62 instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR;
63
64 if (strcmp(name, "AlreadyArchived") == 0) {
65 err = INSTPROXY_E_ALREADY_ARCHIVED;
66 } else if (strcmp(name, "APIInternalError") == 0) {
67 err = INSTPROXY_E_API_INTERNAL_ERROR;
68 } else if (strcmp(name, "ApplicationAlreadyInstalled") == 0) {
69 err = INSTPROXY_E_APPLICATION_ALREADY_INSTALLED;
70 } else if (strcmp(name, "ApplicationMoveFailed") == 0) {
71 err = INSTPROXY_E_APPLICATION_MOVE_FAILED;
72 } else if (strcmp(name, "ApplicationSINFCaptureFailed") == 0) {
73 err = INSTPROXY_E_APPLICATION_SINF_CAPTURE_FAILED;
74 } else if (strcmp(name, "ApplicationSandboxFailed") == 0) {
75 err = INSTPROXY_E_APPLICATION_SANDBOX_FAILED;
76 } else if (strcmp(name, "ApplicationVerificationFailed") == 0) {
77 err = INSTPROXY_E_APPLICATION_VERIFICATION_FAILED;
78 } else if (strcmp(name, "ArchiveDestructionFailed") == 0) {
79 err = INSTPROXY_E_ARCHIVE_DESTRUCTION_FAILED;
80 } else if (strcmp(name, "BundleVerificationFailed") == 0) {
81 err = INSTPROXY_E_BUNDLE_VERIFICATION_FAILED;
82 } else if (strcmp(name, "CarrierBundleCopyFailed") == 0) {
83 err = INSTPROXY_E_CARRIER_BUNDLE_COPY_FAILED;
84 } else if (strcmp(name, "CarrierBundleDirectoryCreationFailed") == 0) {
85 err = INSTPROXY_E_CARRIER_BUNDLE_DIRECTORY_CREATION_FAILED;
86 } else if (strcmp(name, "CarrierBundleMissingSupportedSIMs") == 0) {
87 err = INSTPROXY_E_CARRIER_BUNDLE_MISSING_SUPPORTED_SIMS;
88 } else if (strcmp(name, "CommCenterNotificationFailed") == 0) {
89 err = INSTPROXY_E_COMM_CENTER_NOTIFICATION_FAILED;
90 } else if (strcmp(name, "ContainerCreationFailed") == 0) {
91 err = INSTPROXY_E_CONTAINER_CREATION_FAILED;
92 } else if (strcmp(name, "ContainerP0wnFailed") == 0) {
93 err = INSTPROXY_E_CONTAINER_P0WN_FAILED;
94 } else if (strcmp(name, "ContainerRemovalFailed") == 0) {
95 err = INSTPROXY_E_CONTAINER_REMOVAL_FAILED;
96 } else if (strcmp(name, "EmbeddedProfileInstallFailed") == 0) {
97 err = INSTPROXY_E_EMBEDDED_PROFILE_INSTALL_FAILED;
98 } else if (strcmp(name, "ExecutableTwiddleFailed") == 0) {
99 err = INSTPROXY_E_EXECUTABLE_TWIDDLE_FAILED;
100 } else if (strcmp(name, "ExistenceCheckFailed") == 0) {
101 err = INSTPROXY_E_EXISTENCE_CHECK_FAILED;
102 } else if (strcmp(name, "InstallMapUpdateFailed") == 0) {
103 err = INSTPROXY_E_INSTALL_MAP_UPDATE_FAILED;
104 } else if (strcmp(name, "ManifestCaptureFailed") == 0) {
105 err = INSTPROXY_E_MANIFEST_CAPTURE_FAILED;
106 } else if (strcmp(name, "MapGenerationFailed") == 0) {
107 err = INSTPROXY_E_MAP_GENERATION_FAILED;
108 } else if (strcmp(name, "MissingBundleExecutable") == 0) {
109 err = INSTPROXY_E_MISSING_BUNDLE_EXECUTABLE;
110 } else if (strcmp(name, "MissingBundleIdentifier") == 0) {
111 err = INSTPROXY_E_MISSING_BUNDLE_IDENTIFIER;
112 } else if (strcmp(name, "MissingBundlePath") == 0) {
113 err = INSTPROXY_E_MISSING_BUNDLE_PATH;
114 } else if (strcmp(name, "MissingContainer") == 0) {
115 err = INSTPROXY_E_MISSING_CONTAINER;
116 } else if (strcmp(name, "NotificationFailed") == 0) {
117 err = INSTPROXY_E_NOTIFICATION_FAILED;
118 } else if (strcmp(name, "PackageExtractionFailed") == 0) {
119 err = INSTPROXY_E_PACKAGE_EXTRACTION_FAILED;
120 } else if (strcmp(name, "PackageInspectionFailed") == 0) {
121 err = INSTPROXY_E_PACKAGE_INSPECTION_FAILED;
122 } else if (strcmp(name, "PackageMoveFailed") == 0) {
123 err = INSTPROXY_E_PACKAGE_MOVE_FAILED;
124 } else if (strcmp(name, "PathConversionFailed") == 0) {
125 err = INSTPROXY_E_PATH_CONVERSION_FAILED;
126 } else if (strcmp(name, "RestoreContainerFailed") == 0) {
127 err = INSTPROXY_E_RESTORE_CONTAINER_FAILED;
128 } else if (strcmp(name, "SeatbeltProfileRemovalFailed") == 0) {
129 err = INSTPROXY_E_SEATBELT_PROFILE_REMOVAL_FAILED;
130 } else if (strcmp(name, "StageCreationFailed") == 0) {
131 err = INSTPROXY_E_STAGE_CREATION_FAILED;
132 } else if (strcmp(name, "SymlinkFailed") == 0) {
133 err = INSTPROXY_E_SYMLINK_FAILED;
134 } else if (strcmp(name, "UnknownCommand") == 0) {
135 err = INSTPROXY_E_UNKNOWN_COMMAND;
136 } else if (strcmp(name, "iTunesArtworkCaptureFailed") == 0) {
137 err = INSTPROXY_E_ITUNES_ARTWORK_CAPTURE_FAILED;
138 } else if (strcmp(name, "iTunesMetadataCaptureFailed") == 0) {
139 err = INSTPROXY_E_ITUNES_METADATA_CAPTURE_FAILED;
140 } else if (strcmp(name, "DeviceOSVersionTooLow") == 0) {
141 err = INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW;
142 } else if (strcmp(name, "DeviceFamilyNotSupported") == 0) {
143 err = INSTPROXY_E_DEVICE_FAMILY_NOT_SUPPORTED;
144 } else if (strcmp(name, "PackagePatchFailed") == 0) {
145 err = INSTPROXY_E_PACKAGE_PATCH_FAILED;
146 } else if (strcmp(name, "IncorrectArchitecture") == 0) {
147 err = INSTPROXY_E_INCORRECT_ARCHITECTURE;
148 } else if (strcmp(name, "PluginCopyFailed") == 0) {
149 err = INSTPROXY_E_PLUGIN_COPY_FAILED;
150 } else if (strcmp(name, "BreadcrumbFailed") == 0) {
151 err = INSTPROXY_E_BREADCRUMB_FAILED;
152 } else if (strcmp(name, "BreadcrumbUnlockFailed") == 0) {
153 err = INSTPROXY_E_BREADCRUMB_UNLOCK_FAILED;
154 } else if (strcmp(name, "GeoJSONCaptureFailed") == 0) {
155 err = INSTPROXY_E_GEOJSON_CAPTURE_FAILED;
156 } else if (strcmp(name, "NewsstandArtworkCaptureFailed") == 0) {
157 err = INSTPROXY_E_NEWSSTAND_ARTWORK_CAPTURE_FAILED;
158 } else if (strcmp(name, "MissingCommand") == 0) {
159 err = INSTPROXY_E_MISSING_COMMAND;
160 } else if (strcmp(name, "NotEntitled") == 0) {
161 err = INSTPROXY_E_NOT_ENTITLED;
162 } else if (strcmp(name, "MissingPackagePath") == 0) {
163 err = INSTPROXY_E_MISSING_PACKAGE_PATH;
164 } else if (strcmp(name, "MissingContainerPath") == 0) {
165 err = INSTPROXY_E_MISSING_CONTAINER_PATH;
166 } else if (strcmp(name, "MissingApplicationIdentifier") == 0) {
167 err = INSTPROXY_E_MISSING_APPLICATION_IDENTIFIER;
168 } else if (strcmp(name, "MissingAttributeValue") == 0) {
169 err = INSTPROXY_E_MISSING_ATTRIBUTE_VALUE;
170 } else if (strcmp(name, "LookupFailed") == 0) {
171 err = INSTPROXY_E_LOOKUP_FAILED;
172 } else if (strcmp(name, "DictCreationFailed") == 0) {
173 err = INSTPROXY_E_DICT_CREATION_FAILED;
174 } else if (strcmp(name, "InstallProhibited") == 0) {
175 err = INSTPROXY_E_INSTALL_PROHIBITED;
176 } else if (strcmp(name, "UninstallProhibited") == 0) {
177 err = INSTPROXY_E_UNINSTALL_PROHIBITED;
178 } else if (strcmp(name, "MissingBundleVersion") == 0) {
179 err = INSTPROXY_E_MISSING_BUNDLE_VERSION;
180 }
181
182 return err;
183}
184
185/**
39 * Locks an installation_proxy client, used for thread safety. 186 * Locks an installation_proxy client, used for thread safety.
40 * 187 *
41 * @param client The installation_proxy client to lock 188 * @param client The installation_proxy client to lock
42 */ 189 */
43static void instproxy_lock(instproxy_client_t client) 190static void instproxy_lock(instproxy_client_t client)
44{ 191{
45 debug_info("InstallationProxy: Locked"); 192 debug_info("Locked");
46 g_mutex_lock(client->mutex); 193 mutex_lock(&client->mutex);
47} 194}
48 195
49/** 196/**
50 * Unlocks an installation_proxy client, used for thread safety. 197 * Unlocks an installation_proxy client, used for thread safety.
51 * 198 *
52 * @param client The installation_proxy client to lock 199 * @param client The installation_proxy client to lock
53 */ 200 */
54static void instproxy_unlock(instproxy_client_t client) 201static void instproxy_unlock(instproxy_client_t client)
55{ 202{
56 debug_info("InstallationProxy: Unlocked"); 203 debug_info("Unlocked");
57 g_mutex_unlock(client->mutex); 204 mutex_unlock(&client->mutex);
58} 205}
59 206
60/** 207/**
61 * Convert a property_list_service_error_t value to an instproxy_error_t value. 208 * Converts a property_list_service_error_t value to an instproxy_error_t value.
62 * Used internally to get correct error codes. 209 * Used internally to get correct error codes.
63 * 210 *
64 * @param err A property_list_service_error_t error code 211 * @param err A property_list_service_error_t error code
@@ -77,186 +224,80 @@ static instproxy_error_t instproxy_error(property_list_service_error_t err)
77 return INSTPROXY_E_PLIST_ERROR; 224 return INSTPROXY_E_PLIST_ERROR;
78 case PROPERTY_LIST_SERVICE_E_MUX_ERROR: 225 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
79 return INSTPROXY_E_CONN_FAILED; 226 return INSTPROXY_E_CONN_FAILED;
227 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
228 return INSTPROXY_E_RECEIVE_TIMEOUT;
80 default: 229 default:
81 break; 230 break;
82 } 231 }
83 return INSTPROXY_E_UNKNOWN_ERROR; 232 return INSTPROXY_E_UNKNOWN_ERROR;
84} 233}
85 234
86/** 235instproxy_error_t instproxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, instproxy_client_t *client)
87 * Connects to the installation_proxy service on the specified device.
88 *
89 * @param device The device to connect to
90 * @param port Destination port (usually given by lockdownd_start_service).
91 * @param client Pointer that will be set to a newly allocated
92 * instproxy_client_t upon successful return.
93 *
94 * @return INSTPROXY_E_SUCCESS on success, or an INSTPROXY_E_* error value
95 * when an error occured.
96 */
97instproxy_error_t instproxy_client_new(idevice_t device, uint16_t port, instproxy_client_t *client)
98{ 236{
99 /* makes sure thread environment is available */
100 if (!g_thread_supported())
101 g_thread_init(NULL);
102
103 if (!device)
104 return INSTPROXY_E_INVALID_ARG;
105
106 property_list_service_client_t plistclient = NULL; 237 property_list_service_client_t plistclient = NULL;
107 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 238 instproxy_error_t err = instproxy_error(property_list_service_client_new(device, service, &plistclient));
108 return INSTPROXY_E_CONN_FAILED; 239 if (err != INSTPROXY_E_SUCCESS) {
240 return err;
109 } 241 }
110 242
111 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); 243 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private));
112 client_loc->parent = plistclient; 244 client_loc->parent = plistclient;
113 client_loc->mutex = g_mutex_new(); 245 mutex_init(&client_loc->mutex);
114 client_loc->status_updater = NULL; 246 client_loc->receive_status_thread = THREAD_T_NULL;
115 247
116 *client = client_loc; 248 *client = client_loc;
117 return INSTPROXY_E_SUCCESS; 249 return INSTPROXY_E_SUCCESS;
118} 250}
119 251
120/** 252instproxy_error_t instproxy_client_start_service(idevice_t device, instproxy_client_t * client, const char* label)
121 * Disconnects an installation_proxy client from the device and frees up the 253{
122 * installation_proxy client data. 254 instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR;
123 * 255 service_client_factory_start_service(device, INSTPROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(instproxy_client_new), &err);
124 * @param client The installation_proxy client to disconnect and free. 256 return err;
125 * 257}
126 * @return INSTPROXY_E_SUCCESS on success 258
127 * or INSTPROXY_E_INVALID_ARG if client is NULL.
128 */
129instproxy_error_t instproxy_client_free(instproxy_client_t client) 259instproxy_error_t instproxy_client_free(instproxy_client_t client)
130{ 260{
131 if (!client) 261 if (!client)
132 return INSTPROXY_E_INVALID_ARG; 262 return INSTPROXY_E_INVALID_ARG;
133 263
134 property_list_service_client_free(client->parent); 264 property_list_service_client_t parent = client->parent;
135 client->parent = NULL; 265 client->parent = NULL;
136 if (client->status_updater) { 266 if (client->receive_status_thread) {
137 debug_info("joining status_updater"); 267 debug_info("joining receive_status_thread");
138 g_thread_join(client->status_updater); 268 thread_join(client->receive_status_thread);
139 } 269 thread_free(client->receive_status_thread);
140 if (client->mutex) { 270 client->receive_status_thread = THREAD_T_NULL;
141 g_mutex_free(client->mutex);
142 } 271 }
272 property_list_service_client_free(parent);
273 mutex_destroy(&client->mutex);
143 free(client); 274 free(client);
144 275
145 return INSTPROXY_E_SUCCESS; 276 return INSTPROXY_E_SUCCESS;
146} 277}
147 278
148/** 279/**
149 * Send a command with specified options to the device. 280 * Sends a command to the device.
150 * Only used internally. 281 * Only used internally.
151 * 282 *
152 * @param client The connected installation_proxy client. 283 * @param client The connected installation_proxy client.
153 * @param command The command to execute. Required. 284 * @param command The command to execute. Required.
154 * @param client_options The client options to use, as PLIST_DICT, or NULL.
155 * @param appid The ApplicationIdentifier to add or NULL if not required.
156 * @param package_path The installation package path or NULL if not required.
157 * 285 *
158 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if 286 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
159 * an error occured. 287 * an error occurred.
160 */ 288 */
161static instproxy_error_t instproxy_send_command(instproxy_client_t client, const char *command, plist_t client_options, const char *appid, const char *package_path) 289static instproxy_error_t instproxy_send_command(instproxy_client_t client, plist_t command)
162{ 290{
163 if (!client || !command || (client_options && (plist_get_node_type(client_options) != PLIST_DICT))) 291 if (!client || !command)
164 return INSTPROXY_E_INVALID_ARG; 292 return INSTPROXY_E_INVALID_ARG;
165 293
166 plist_t dict = plist_new_dict(); 294 instproxy_error_t res = instproxy_error(property_list_service_send_xml_plist(client->parent, command));
167 if (appid) {
168 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
169 }
170 if (client_options && (plist_dict_get_size(client_options) > 0)) {
171 plist_dict_insert_item(dict, "ClientOptions", plist_copy(client_options));
172 }
173 plist_dict_insert_item(dict, "Command", plist_new_string(command));
174 if (package_path) {
175 plist_dict_insert_item(dict, "PackagePath", plist_new_string(package_path));
176 }
177
178 instproxy_error_t err = instproxy_error(property_list_service_send_xml_plist(client->parent, dict));
179 plist_free(dict);
180 return err;
181}
182 295
183/**
184 * List installed applications. This function runs synchronously.
185 *
186 * @param client The connected installation_proxy client
187 * @param client_options The client options to use, as PLIST_DICT, or NULL.
188 * Valid client options include:
189 * "ApplicationType" -> "User"
190 * "ApplicationType" -> "System"
191 * @param result Pointer that will be set to a plist that will hold an array
192 * of PLIST_DICT holding information about the applications found.
193 *
194 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
195 * an error occured.
196 */
197instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result)
198{
199 if (!client || !client->parent || !result)
200 return INSTPROXY_E_INVALID_ARG;
201
202 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
203
204 instproxy_lock(client);
205 res = instproxy_send_command(client, "Browse", client_options, NULL, NULL);
206 if (res != INSTPROXY_E_SUCCESS) { 296 if (res != INSTPROXY_E_SUCCESS) {
207 debug_info("could not send plist"); 297 debug_info("could not send command plist, error %d", res);
208 goto leave_unlock; 298 return res;
209 }
210
211 int browsing = 0;
212 plist_t apps_array = plist_new_array();
213 plist_t dict = NULL;
214
215 do {
216 browsing = 0;
217 dict = NULL;
218 res = instproxy_error(property_list_service_receive_plist(client->parent, &dict));
219 if (res != INSTPROXY_E_SUCCESS) {
220 break;
221 }
222 if (dict) {
223 uint64_t i;
224 uint64_t current_amount = 0;
225 char *status = NULL;
226 plist_t camount = plist_dict_get_item(dict, "CurrentAmount");
227 plist_t pstatus = plist_dict_get_item(dict, "Status");
228 if (camount) {
229 plist_get_uint_val(camount, &current_amount);
230 }
231 if (current_amount > 0) {
232 plist_t current_list = plist_dict_get_item(dict, "CurrentList");
233 for (i = 0; current_list && (i < current_amount); i++) {
234 plist_t item = plist_array_get_item(current_list, i);
235 plist_array_append_item(apps_array, plist_copy(item));
236 }
237 }
238 if (pstatus) {
239 plist_get_string_val(pstatus, &status);
240 }
241 if (status) {
242 if (!strcmp(status, "BrowsingApplications")) {
243 browsing = 1;
244 } else if (!strcmp(status, "Complete")) {
245 debug_info("Browsing applications completed");
246 res = INSTPROXY_E_SUCCESS;
247 }
248 free(status);
249 }
250 plist_free(dict);
251 }
252 } while (browsing);
253
254 if (res == INSTPROXY_E_SUCCESS) {
255 *result = apps_array;
256 } 299 }
257 300
258leave_unlock:
259 instproxy_unlock(client);
260 return res; 301 return res;
261} 302}
262 303
@@ -269,78 +310,99 @@ leave_unlock:
269 * 310 *
270 * @param client The connected installation proxy client 311 * @param client The connected installation proxy client
271 * @param status_cb Pointer to a callback function or NULL 312 * @param status_cb Pointer to a callback function or NULL
272 * @param operation Operation name. Will be passed to the callback function 313 * @param command Operation specificiation in plist. Will be passed to the
273 * in async mode or shown in debug messages in sync mode. 314 * status_cb callback.
274 * @param user_data Callback data passed to status_cb. 315 * @param user_data Callback data passed to status_cb.
275 */ 316 */
276static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) 317static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client, plist_t command, instproxy_status_cb_t status_cb, void *user_data)
277{ 318{
278 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 319 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
279 int ok = 1; 320 int complete = 0;
280 plist_t dict = NULL; 321 plist_t node = NULL;
322 char* command_name = NULL;
323 char* status_name = NULL;
324 char* error_name = NULL;
325 char* error_description = NULL;
326 uint64_t error_code = 0;
327#ifndef STRIP_DEBUG_CODE
328 int percent_complete = 0;
329#endif
330
331 instproxy_command_get_name(command, &command_name);
281 332
282 do { 333 do {
334 /* receive status response */
283 instproxy_lock(client); 335 instproxy_lock(client);
284 res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 30000)); 336 res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000));
285 instproxy_unlock(client); 337 instproxy_unlock(client);
286 if (res != INSTPROXY_E_SUCCESS) { 338
339 /* break out if we have a communication problem */
340 if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) {
287 debug_info("could not receive plist, error %d", res); 341 debug_info("could not receive plist, error %d", res);
288 break; 342 break;
289 } 343 }
290 if (dict) { 344
291 /* invoke callback function */ 345 /* parse status response */
292 if (status_cb) { 346 if (node) {
293 status_cb(operation, dict, user_data); 347 /* check status for possible error to allow reporting it and aborting it gracefully */
348 res = instproxy_status_get_error(node, &error_name, &error_description, &error_code);
349 if (res != INSTPROXY_E_SUCCESS) {
350 debug_info("command: %s, error %d, code 0x%08"PRIx64", name: %s, description: \"%s\"", command_name, res, error_code, error_name, error_description ? error_description: "N/A");
351 complete = 1;
352 }
353
354 if (error_name) {
355 free(error_name);
356 error_name = NULL;
294 } 357 }
295 /* check for 'Error', so we can abort cleanly */ 358
296 plist_t err = plist_dict_get_item(dict, "Error"); 359 if (error_description) {
297 if (err) { 360 free(error_description);
361 error_description = NULL;
362 }
363
364 /* check status from response */
365 instproxy_status_get_name(node, &status_name);
366 if (!status_name) {
367 debug_info("ignoring message without Status key:");
368 debug_plist(node);
369 } else {
370 if (!strcmp(status_name, "Complete")) {
371 complete = 1;
372 } else {
373 res = INSTPROXY_E_OP_IN_PROGRESS;
374 }
298#ifndef STRIP_DEBUG_CODE 375#ifndef STRIP_DEBUG_CODE
299 char *err_msg = NULL; 376 percent_complete = -1;
300 plist_get_string_val(err, &err_msg); 377 instproxy_status_get_percent_complete(node, &percent_complete);
301 if (err_msg) { 378 if (percent_complete >= 0) {
302 debug_info("(%s): ERROR: %s", operation, err_msg); 379 debug_info("command: %s, status: %s, percent (%d%%)", command_name, status_name, percent_complete);
303 free(err_msg); 380 } else {
381 debug_info("command: %s, status: %s", command_name, status_name);
304 } 382 }
305#endif 383#endif
306 ok = 0; 384 free(status_name);
307 res = INSTPROXY_E_OP_FAILED; 385 status_name = NULL;
308 } 386 }
309 /* get 'Status' */ 387
310 plist_t status = plist_dict_get_item(dict, "Status"); 388 /* invoke status callback function */
311 if (status) { 389 if (status_cb) {
312 char *status_msg = NULL; 390 status_cb(command, node, user_data);
313 plist_get_string_val(status, &status_msg);
314 if (status_msg) {
315 if (!strcmp(status_msg, "Complete")) {
316 ok = 0;
317 res = INSTPROXY_E_SUCCESS;
318 }
319#ifndef STRIP_DEBUG_CODE
320 plist_t npercent = plist_dict_get_item(dict, "PercentComplete");
321 if (npercent) {
322 uint64_t val = 0;
323 int percent;
324 plist_get_uint_val(npercent, &val);
325 percent = val;
326 debug_info("(%s): %s (%d%%)", operation, status_msg, percent);
327 } else {
328 debug_info("(%s): %s", operation, status_msg);
329 }
330#endif
331 free(status_msg);
332 }
333 } 391 }
334 plist_free(dict); 392
335 dict = NULL; 393 plist_free(node);
394 node = NULL;
336 } 395 }
337 } while (ok && client->parent); 396 } while (!complete && client->parent);
397
398 if (command_name)
399 free(command_name);
338 400
339 return res; 401 return res;
340} 402}
341 403
342/** 404/**
343 * Internally used status updater thread function that will call the specified 405 * Internally used "receive status" thread function that will call the specified
344 * callback function when status update messages (or error messages) are 406 * callback function when status update messages (or error messages) are
345 * received. 407 * received.
346 * 408 *
@@ -349,20 +411,27 @@ static instproxy_error_t instproxy_perform_operation(instproxy_client_t client,
349 * 411 *
350 * @return Always NULL. 412 * @return Always NULL.
351 */ 413 */
352static gpointer instproxy_status_updater(gpointer arg) 414static void* instproxy_receive_status_loop_thread(void* arg)
353{ 415{
354 struct instproxy_status_data *data = (struct instproxy_status_data*)arg; 416 struct instproxy_status_data *data = (struct instproxy_status_data*)arg;
355 417
356 /* run until the operation is complete or an error occurs */ 418 /* run until the command is complete or an error occurs */
357 (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation, data->user_data); 419 (void)instproxy_receive_status_loop(data->client, data->command, data->cbfunc, data->user_data);
358 420
359 /* cleanup */ 421 /* cleanup */
360 instproxy_lock(data->client); 422 instproxy_lock(data->client);
423
361 debug_info("done, cleaning up."); 424 debug_info("done, cleaning up.");
362 if (data->operation) { 425
363 free(data->operation); 426 if (data->command) {
427 plist_free(data->command);
428 }
429
430 if (data->client->receive_status_thread) {
431 thread_free(data->client->receive_status_thread);
432 data->client->receive_status_thread = THREAD_T_NULL;
364 } 433 }
365 data->client->status_updater = NULL; 434
366 instproxy_unlock(data->client); 435 instproxy_unlock(data->client);
367 free(data); 436 free(data);
368 437
@@ -370,379 +439,493 @@ static gpointer instproxy_status_updater(gpointer arg)
370} 439}
371 440
372/** 441/**
373 * Internally used helper function that creates a status updater thread which 442 * Internally used helper function that creates a "receive status" thread which
374 * will call the passed callback function when status updates occur. 443 * will call the passed callback function when a status is received.
375 * If status_cb is NULL no thread will be created, but the operation will 444 *
376 * run synchronously until it completes or an error occurs. 445 * If async is 0 no thread will be created and the command will run
446 * synchronously until it completes or an error occurs.
377 * 447 *
378 * @param client The connected installation proxy client 448 * @param client The connected installation proxy client
379 * @param status_cb Pointer to a callback function or NULL 449 * @param command Operation name. Will be passed to the callback function
380 * @param operation Operation name. Will be passed to the callback function
381 * in async mode or shown in debug messages in sync mode. 450 * in async mode or shown in debug messages in sync mode.
451 * @param async A boolean indicating if receive loop should be run
452 * asynchronously or block.
453 * @param status_cb Pointer to a callback function or NULL.
382 * @param user_data Callback data passed to status_cb. 454 * @param user_data Callback data passed to status_cb.
383 * 455 *
384 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or 456 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or
385 * when the operation completed successfully (sync). 457 * when the command completed successfully (sync).
386 * An INSTPROXY_E_* error value is returned if an error occured. 458 * An INSTPROXY_E_* error value is returned if an error occurred.
387 */ 459 */
388static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) 460static instproxy_error_t instproxy_receive_status_loop_with_callback(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data)
389{ 461{
462 if (!client || !client->parent || !command) {
463 return INSTPROXY_E_INVALID_ARG;
464 }
465
466 if (client->receive_status_thread) {
467 return INSTPROXY_E_OP_IN_PROGRESS;
468 }
469
390 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 470 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
391 if (status_cb) { 471 if (async == INSTPROXY_COMMAND_TYPE_ASYNC) {
392 /* async mode */ 472 /* async mode */
393 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); 473 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data));
394 if (data) { 474 if (data) {
395 data->client = client; 475 data->client = client;
476 data->command = plist_copy(command);
396 data->cbfunc = status_cb; 477 data->cbfunc = status_cb;
397 data->operation = strdup(operation);
398 data->user_data = user_data; 478 data->user_data = user_data;
399 479
400 client->status_updater = g_thread_create(instproxy_status_updater, data, TRUE, NULL); 480 if (thread_new(&client->receive_status_thread, instproxy_receive_status_loop_thread, data) == 0) {
401 if (client->status_updater) {
402 res = INSTPROXY_E_SUCCESS; 481 res = INSTPROXY_E_SUCCESS;
403 } 482 }
404 } 483 }
405 } else { 484 } else {
406 /* sync mode */ 485 /* sync mode as a fallback */
407 res = instproxy_perform_operation(client, NULL, operation, NULL); 486 res = instproxy_receive_status_loop(client, command, status_cb, user_data);
408 } 487 }
488
409 return res; 489 return res;
410} 490}
411 491
412
413/** 492/**
414 * Internal function used by instproxy_install and instproxy_upgrade. 493 * Internal core function to send a command and process the response.
415 * 494 *
416 * @param client The connected installation_proxy client 495 * @param client The connected installation_proxy client
417 * @param pkg_path Path of the installation package (inside the AFC jail) 496 * @param command The command specification dictionary.
418 * @param client_options The client options to use, as PLIST_DICT, or NULL. 497 * @param async A boolean indicating whether the receive loop should be run
419 * @param status_cb Callback function for progress and status information. If 498 * asynchronously or block until completing the command.
420 * NULL is passed, this function will run synchronously. 499 * @param status_cb Callback function to call if a command status is received.
421 * @param command The command to execute.
422 * @param user_data Callback data passed to status_cb. 500 * @param user_data Callback data passed to status_cb.
423 * 501 *
424 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if 502 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
425 * an error occured. 503 * an error occurred.
426 */ 504 */
427static instproxy_error_t instproxy_install_or_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, const char *command, void *user_data) 505static instproxy_error_t instproxy_perform_command(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data)
428{ 506{
429 if (!client || !client->parent || !pkg_path) { 507 if (!client || !client->parent || !command) {
430 return INSTPROXY_E_INVALID_ARG; 508 return INSTPROXY_E_INVALID_ARG;
431 } 509 }
432 if (client->status_updater) { 510
511 if (client->receive_status_thread) {
433 return INSTPROXY_E_OP_IN_PROGRESS; 512 return INSTPROXY_E_OP_IN_PROGRESS;
434 } 513 }
435 514
515 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
516
517 /* send command */
436 instproxy_lock(client); 518 instproxy_lock(client);
437 instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path); 519 res = instproxy_send_command(client, command);
438 instproxy_unlock(client); 520 instproxy_unlock(client);
439 521
440 if (res != INSTPROXY_E_SUCCESS) { 522 /* loop until status or error is received */
441 debug_info("could not send plist, error %d", res); 523 res = instproxy_receive_status_loop_with_callback(client, command, async, status_cb, user_data);
442 return res;
443 }
444 524
445 return instproxy_create_status_updater(client, status_cb, command, user_data); 525 return res;
446} 526}
447 527
448/** 528instproxy_error_t instproxy_browse_with_callback(instproxy_client_t client, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
449 * Install an application on the device.
450 *
451 * @param client The connected installation_proxy client
452 * @param pkg_path Path of the installation package (inside the AFC jail)
453 * @param client_options The client options to use, as PLIST_DICT, or NULL.
454 * Valid options include:
455 * "iTunesMetadata" -> PLIST_DATA
456 * "ApplicationSINF" -> PLIST_DATA
457 * "PackageType" -> "Developer"
458 * If PackageType -> Developer is specified, then pkg_path points to
459 * an .app directory instead of an install package.
460 * @param status_cb Callback function for progress and status information. If
461 * NULL is passed, this function will run synchronously.
462 * @param user_data Callback data passed to status_cb.
463 *
464 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
465 * an error occured.
466 *
467 * @note If a callback function is given (async mode), this function returns
468 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
469 * created successfully; any error occuring during the operation has to be
470 * handled inside the specified callback function.
471 */
472instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
473{ 529{
474 return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Install", user_data); 530 if (!client || !client->parent || !status_cb)
531 return INSTPROXY_E_INVALID_ARG;
532
533 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
534
535 plist_t command = plist_new_dict();
536 plist_dict_set_item(command, "Command", plist_new_string("Browse"));
537 if (client_options)
538 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
539
540 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, (void*)user_data);
541
542 plist_free(command);
543
544 return res;
475} 545}
476 546
477/** 547static void instproxy_append_current_list_to_result_cb(plist_t command, plist_t status, void *user_data)
478 * Upgrade an application on the device. This function is nearly the same as
479 * instproxy_install; the difference is that the installation progress on the
480 * device is faster if the application is already installed.
481 *
482 * @param client The connected installation_proxy client
483 * @param pkg_path Path of the installation package (inside the AFC jail)
484 * @param client_options The client options to use, as PLIST_DICT, or NULL.
485 * Valid options include:
486 * "iTunesMetadata" -> PLIST_DATA
487 * "ApplicationSINF" -> PLIST_DATA
488 * "PackageType" -> "Developer"
489 * If PackageType -> Developer is specified, then pkg_path points to
490 * an .app directory instead of an install package.
491 * @param status_cb Callback function for progress and status information. If
492 * NULL is passed, this function will run synchronously.
493 * @param user_data Callback data passed to status_cb.
494 *
495 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
496 * an error occured.
497 *
498 * @note If a callback function is given (async mode), this function returns
499 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
500 * created successfully; any error occuring during the operation has to be
501 * handled inside the specified callback function.
502 */
503instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
504{ 548{
505 return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Upgrade", user_data); 549 plist_t *result_array = (plist_t*)user_data;
550 uint64_t current_amount = 0;
551 plist_t current_list = NULL;
552 uint64_t i;
553
554 instproxy_status_get_current_list(status, NULL, NULL, &current_amount, &current_list);
555
556 debug_info("current_amount: %d", current_amount);
557
558 if (current_amount > 0) {
559 for (i = 0; current_list && (i < current_amount); i++) {
560 plist_t item = plist_array_get_item(current_list, i);
561 plist_array_append_item(*result_array, plist_copy(item));
562 }
563 }
564
565 if (current_list)
566 plist_free(current_list);
506} 567}
507 568
508/** 569instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result)
509 * Uninstall an application from the device.
510 *
511 * @param client The connected installation proxy client
512 * @param appid ApplicationIdentifier of the app to uninstall
513 * @param client_options The client options to use, as PLIST_DICT, or NULL.
514 * Currently there are no known client options, so pass NULL here.
515 * @param status_cb Callback function for progress and status information. If
516 * NULL is passed, this function will run synchronously.
517 * @param user_data Callback data passed to status_cb.
518 *
519 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
520 * an error occured.
521 *
522 * @note If a callback function is given (async mode), this function returns
523 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
524 * created successfully; any error occuring during the operation has to be
525 * handled inside the specified callback function.
526 */
527instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
528{ 570{
529 if (!client || !client->parent || !appid) { 571 if (!client || !client->parent || !result)
530 return INSTPROXY_E_INVALID_ARG; 572 return INSTPROXY_E_INVALID_ARG;
531 }
532
533 if (client->status_updater) {
534 return INSTPROXY_E_OP_IN_PROGRESS;
535 }
536 573
537 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 574 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
538 plist_t dict = plist_new_dict();
539 plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid));
540 plist_dict_insert_item(dict, "Command", plist_new_string("Uninstall"));
541 575
542 instproxy_lock(client); 576 plist_t result_array = plist_new_array();
543 res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL);
544 instproxy_unlock(client);
545 577
546 plist_free(dict); 578 plist_t command = plist_new_dict();
579 plist_dict_set_item(command, "Command", plist_new_string("Browse"));
580 if (client_options)
581 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
547 582
548 if (res != INSTPROXY_E_SUCCESS) { 583 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_append_current_list_to_result_cb, (void*)&result_array);
549 debug_info("could not send plist, error %d", res); 584
550 return res; 585 if (res == INSTPROXY_E_SUCCESS) {
586 *result = result_array;
587 } else {
588 plist_free(result_array);
551 } 589 }
552 590
553 return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data); 591 plist_free(command);
592
593 return res;
554} 594}
555 595
556/** 596static void instproxy_copy_lookup_result_cb(plist_t command, plist_t status, void *user_data)
557 * List archived applications. This function runs synchronously. 597{
558 * 598 plist_t* result = (plist_t*)user_data;
559 * @see instproxy_archive 599
560 * 600 plist_t node = plist_dict_get_item(status, "LookupResult");
561 * @param client The connected installation_proxy client 601 if (node) {
562 * @param client_options The client options to use, as PLIST_DICT, or NULL. 602 *result = plist_copy(node);
563 * Currently there are no known client options, so pass NULL here. 603 }
564 * @param result Pointer that will be set to a plist containing a PLIST_DICT 604}
565 * holding information about the archived applications found. 605
566 * 606instproxy_error_t instproxy_lookup(instproxy_client_t client, const char** appids, plist_t client_options, plist_t *result)
567 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
568 * an error occured.
569 */
570instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result)
571{ 607{
608 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
609 int i = 0;
610 plist_t lookup_result = NULL;
611 plist_t command = NULL;
612 plist_t appid_array = NULL;
613 plist_t node = NULL;
614
572 if (!client || !client->parent || !result) 615 if (!client || !client->parent || !result)
573 return INSTPROXY_E_INVALID_ARG; 616 return INSTPROXY_E_INVALID_ARG;
574 617
575 instproxy_lock(client); 618 command = plist_new_dict();
576 instproxy_error_t res = instproxy_send_command(client, "LookupArchives", client_options, NULL, NULL); 619 plist_dict_set_item(command, "Command", plist_new_string("Lookup"));
620 if (client_options) {
621 node = plist_copy(client_options);
622 } else if (appids) {
623 node = plist_new_dict();
624 }
577 625
578 if (res != INSTPROXY_E_SUCCESS) { 626 /* add bundle identifiers to client options */
579 debug_info("could not send plist, error %d", res); 627 if (appids) {
580 goto leave_unlock; 628 appid_array = plist_new_array();
629 while (appids[i]) {
630 plist_array_append_item(appid_array, plist_new_string(appids[i]));
631 i++;
632 }
633 plist_dict_set_item(node, "BundleIDs", appid_array);
581 } 634 }
582 635
583 res = instproxy_error(property_list_service_receive_plist(client->parent, result)); 636 if (node) {
584 if (res != INSTPROXY_E_SUCCESS) { 637 plist_dict_set_item(command, "ClientOptions", node);
585 debug_info("could not receive plist, error %d", res);
586 goto leave_unlock;
587 } 638 }
588 639
589 res = INSTPROXY_E_SUCCESS; 640 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result);
641
642 if (res == INSTPROXY_E_SUCCESS) {
643 *result = lookup_result;
644 } else {
645 plist_free(lookup_result);
646 }
647
648 plist_free(command);
649
650 return res;
651}
652
653instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
654{
655 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
656
657 plist_t command = plist_new_dict();
658 plist_dict_set_item(command, "Command", plist_new_string("Install"));
659 if (client_options)
660 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
661 plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
662
663 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
664
665 plist_free(command);
666
667 return res;
668}
669
670instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
671{
672 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
673
674 plist_t command = plist_new_dict();
675 plist_dict_set_item(command, "Command", plist_new_string("Upgrade"));
676 if (client_options)
677 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
678 plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
679
680 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
681
682 plist_free(command);
683
684 return res;
685}
686
687instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
688{
689 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
690
691 plist_t command = plist_new_dict();
692 plist_dict_set_item(command, "Command", plist_new_string("Uninstall"));
693 if (client_options)
694 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
695 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
696
697 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
698
699 plist_free(command);
700
701 return res;
702}
703
704instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result)
705{
706 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
707
708 plist_t command = plist_new_dict();
709 plist_dict_set_item(command, "Command", plist_new_string("LookupArchives"));
710 if (client_options)
711 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
712
713 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)result);
714
715 plist_free(command);
590 716
591leave_unlock:
592 instproxy_unlock(client);
593 return res; 717 return res;
594} 718}
595 719
596/**
597 * Archive an application on the device.
598 * This function tells the device to make an archive of the specified
599 * application. This results in the device creating a ZIP archive in the
600 * 'ApplicationArchives' directory and uninstalling the application.
601 *
602 * @param client The connected installation proxy client
603 * @param appid ApplicationIdentifier of the app to archive.
604 * @param client_options The client options to use, as PLIST_DICT, or NULL.
605 * Valid options include:
606 * "SkipUninstall" -> Boolean
607 * "ArchiveType" -> "ApplicationOnly"
608 * @param status_cb Callback function for progress and status information. If
609 * NULL is passed, this function will run synchronously.
610 * @param user_data Callback data passed to status_cb.
611 *
612 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
613 * an error occured.
614 *
615 * @note If a callback function is given (async mode), this function returns
616 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
617 * created successfully; any error occuring during the operation has to be
618 * handled inside the specified callback function.
619 */
620instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) 720instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
621{ 721{
622 if (!client || !client->parent || !appid) 722 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
723
724 plist_t command = plist_new_dict();
725 plist_dict_set_item(command, "Command", plist_new_string("Archive"));
726 if (client_options)
727 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
728 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
729
730 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
731
732 plist_free(command);
733
734 return res;
735}
736
737instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
738{
739 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
740
741 plist_t command = plist_new_dict();
742 plist_dict_set_item(command, "Command", plist_new_string("Restore"));
743 if (client_options)
744 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
745 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
746
747 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
748
749 plist_free(command);
750
751 return res;
752}
753
754instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
755{
756 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
757
758 plist_t command = plist_new_dict();
759 plist_dict_set_item(command, "Command", plist_new_string("RemoveArchive"));
760 if (client_options)
761 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
762 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
763
764 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
765
766 plist_free(command);
767
768 return res;
769}
770
771instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, const char** capabilities, plist_t client_options, plist_t *result)
772{
773 if (!client || !capabilities || !result)
623 return INSTPROXY_E_INVALID_ARG; 774 return INSTPROXY_E_INVALID_ARG;
624 775
625 if (client->status_updater) { 776 plist_t lookup_result = NULL;
626 return INSTPROXY_E_OP_IN_PROGRESS; 777
778 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
779
780 plist_t command = plist_new_dict();
781 plist_dict_set_item(command, "Command", plist_new_string("CheckCapabilitiesMatch"));
782 if (client_options)
783 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
784
785 if (capabilities) {
786 int i = 0;
787 plist_t capabilities_array = plist_new_array();
788 while (capabilities[i]) {
789 plist_array_append_item(capabilities_array, plist_new_string(capabilities[i]));
790 i++;
791 }
792 plist_dict_set_item(command, "Capabilities", capabilities_array);
627 } 793 }
628 794
629 instproxy_lock(client); 795 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result);
630 instproxy_error_t res = instproxy_send_command(client, "Archive", client_options, appid, NULL);
631 instproxy_unlock(client);
632 796
633 if (res != INSTPROXY_E_SUCCESS) { 797 if (res == INSTPROXY_E_SUCCESS) {
634 debug_info("could not send plist, error %d", res); 798 *result = lookup_result;
635 return res; 799 } else {
800 plist_free(lookup_result);
636 } 801 }
637 return instproxy_create_status_updater(client, status_cb, "Archive", user_data); 802
803 plist_free(command);
804
805 return res;
638} 806}
639 807
640/** 808instproxy_error_t instproxy_status_get_error(plist_t status, char **name, char** description, uint64_t* code)
641 * Restore a previously archived application on the device.
642 * This function is the counterpart to instproxy_archive.
643 * @see instproxy_archive
644 *
645 * @param client The connected installation proxy client
646 * @param appid ApplicationIdentifier of the app to restore.
647 * @param client_options The client options to use, as PLIST_DICT, or NULL.
648 * Currently there are no known client options, so pass NULL here.
649 * @param status_cb Callback function for progress and status information. If
650 * NULL is passed, this function will run synchronously.
651 * @param user_data Callback data passed to status_cb.
652 *
653 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
654 * an error occured.
655 *
656 * @note If a callback function is given (async mode), this function returns
657 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
658 * created successfully; any error occuring during the operation has to be
659 * handled inside the specified callback function.
660 */
661instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
662{ 809{
663 if (!client || !client->parent || !appid) 810 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
811
812 if (!status || !name)
664 return INSTPROXY_E_INVALID_ARG; 813 return INSTPROXY_E_INVALID_ARG;
665 814
666 if (client->status_updater) { 815 plist_t node = plist_dict_get_item(status, "Error");
667 return INSTPROXY_E_OP_IN_PROGRESS; 816 if (node) {
817 plist_get_string_val(node, name);
818 } else {
819 /* no error here */
820 res = INSTPROXY_E_SUCCESS;
668 } 821 }
669 822
670 instproxy_lock(client); 823 if (code != NULL) {
671 instproxy_error_t res = instproxy_send_command(client, "Restore", client_options, appid, NULL); 824 *code = 0;
672 instproxy_unlock(client); 825 node = plist_dict_get_item(status, "ErrorDetail");
826 if (node) {
827 plist_get_uint_val(node, code);
828 *code &= 0xffffffff;
829 }
830 }
673 831
674 if (res != INSTPROXY_E_SUCCESS) { 832 if (description != NULL) {
675 debug_info("could not send plist, error %d", res); 833 node = plist_dict_get_item(status, "ErrorDescription");
676 return res; 834 if (node) {
835 plist_get_string_val(node, description);
836 }
837 }
838
839 if (*name) {
840 res = instproxy_strtoerr(*name);
677 } 841 }
678 return instproxy_create_status_updater(client, status_cb, "Restore", user_data); 842
843 return res;
679} 844}
680 845
681/** 846void instproxy_status_get_name(plist_t status, char **name)
682 * Removes a previously archived application from the device.
683 * This function removes the ZIP archive from the 'ApplicationArchives'
684 * directory.
685 *
686 * @param client The connected installation proxy client
687 * @param appid ApplicationIdentifier of the archived app to remove.
688 * @param client_options The client options to use, as PLIST_DICT, or NULL.
689 * Currently there are no known client options, so passing NULL is fine.
690 * @param status_cb Callback function for progress and status information. If
691 * NULL is passed, this function will run synchronously.
692 * @param user_data Callback data passed to status_cb.
693 *
694 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
695 * an error occured.
696 *
697 * @note If a callback function is given (async mode), this function returns
698 * INSTPROXY_E_SUCCESS immediately if the status updater thread has been
699 * created successfully; any error occuring during the operation has to be
700 * handled inside the specified callback function.
701 */
702instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
703{ 847{
704 if (!client || !client->parent || !appid) 848 if (name) {
705 return INSTPROXY_E_INVALID_ARG; 849 plist_t node = plist_dict_get_item(status, "Status");
850 if (node) {
851 plist_get_string_val(node, name);
852 } else {
853 *name = NULL;
854 }
855 }
856}
706 857
707 if (client->status_updater) { 858void instproxy_status_get_percent_complete(plist_t status, int *percent)
708 return INSTPROXY_E_OP_IN_PROGRESS; 859{
860 uint64_t val = 0;
861 if (percent) {
862 plist_t node = plist_dict_get_item(status, "PercentComplete");
863 if (node) {
864 plist_get_uint_val(node, &val);
865 *percent = val;
866 }
709 } 867 }
868}
710 869
711 instproxy_lock(client); 870void instproxy_status_get_current_list(plist_t status, uint64_t* total, uint64_t* current_index, uint64_t* current_amount, plist_t* list)
712 instproxy_error_t res = instproxy_send_command(client, "RemoveArchive", client_options, appid, NULL); 871{
713 instproxy_unlock(client); 872 plist_t node = NULL;
873
874 if (status && plist_get_node_type(status) == PLIST_DICT) {
875 /* command specific logic: parse browsed list */
876 if (list != NULL) {
877 node = plist_dict_get_item(status, "CurrentList");
878 if (node) {
879 *current_amount = plist_array_get_size(node);
880 *list = plist_copy(node);
881 }
882 }
714 883
715 if (res != INSTPROXY_E_SUCCESS) { 884 if (total != NULL) {
716 debug_info("could not send plist, error %d", res); 885 node = plist_dict_get_item(status, "Total");
717 return res; 886 if (node) {
887 plist_get_uint_val(node, total);
888 }
889 }
890
891 if (current_amount != NULL) {
892 node = plist_dict_get_item(status, "CurrentAmount");
893 if (node) {
894 plist_get_uint_val(node, current_amount);
895 }
896 }
897
898 if (current_index != NULL) {
899 node = plist_dict_get_item(status, "CurrentIndex");
900 if (node) {
901 plist_get_uint_val(node, current_index);
902 }
903 }
718 } 904 }
719 return instproxy_create_status_updater(client, status_cb, "RemoveArchive", user_data);
720} 905}
721 906
722/** 907void instproxy_command_get_name(plist_t command, char** name)
723 * Create a new client_options plist. 908{
724 * 909 if (name) {
725 * @return A new plist_t of type PLIST_DICT. 910 plist_t node = plist_dict_get_item(command, "Command");
726 */ 911 if (node) {
727plist_t instproxy_client_options_new() 912 plist_get_string_val(node, name);
913 } else {
914 *name = NULL;
915 }
916 }
917}
918
919plist_t instproxy_client_options_new(void)
728{ 920{
729 return plist_new_dict(); 921 return plist_new_dict();
730} 922}
731 923
732/**
733 * Add one or more new key:value pairs to the given client_options.
734 *
735 * @param client_options The client options to modify.
736 * @param ... KEY, VALUE, [KEY, VALUE], NULL
737 *
738 * @note The keys and values passed are expected to be strings, except for
739 * "ApplicationSINF" and "iTunesMetadata" expecting a plist node of type
740 * PLIST_DATA as value, or "SkipUninstall" needing int as value.
741 */
742void instproxy_client_options_add(plist_t client_options, ...) 924void instproxy_client_options_add(plist_t client_options, ...)
743{ 925{
744 if (!client_options) 926 if (!client_options)
745 return; 927 return;
928
746 va_list args; 929 va_list args;
747 va_start(args, client_options); 930 va_start(args, client_options);
748 char *arg = va_arg(args, char*); 931 char *arg = va_arg(args, char*);
@@ -750,21 +933,21 @@ void instproxy_client_options_add(plist_t client_options, ...)
750 char *key = strdup(arg); 933 char *key = strdup(arg);
751 if (!strcmp(key, "SkipUninstall")) { 934 if (!strcmp(key, "SkipUninstall")) {
752 int intval = va_arg(args, int); 935 int intval = va_arg(args, int);
753 plist_dict_insert_item(client_options, key, plist_new_bool(intval)); 936 plist_dict_set_item(client_options, key, plist_new_bool(intval));
754 } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata")) { 937 } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata") || !strcmp(key, "ReturnAttributes") || !strcmp(key, "BundleIDs")) {
755 plist_t plistval = va_arg(args, plist_t); 938 plist_t plistval = va_arg(args, plist_t);
756 if (!plistval) { 939 if (!plistval) {
757 free(key); 940 free(key);
758 break; 941 break;
759 } 942 }
760 plist_dict_insert_item(client_options, key, plist_copy(plistval)); 943 plist_dict_set_item(client_options, key, plist_copy(plistval));
761 } else { 944 } else {
762 char *strval = va_arg(args, char*); 945 char *strval = va_arg(args, char*);
763 if (!strval) { 946 if (!strval) {
764 free(key); 947 free(key);
765 break; 948 break;
766 } 949 }
767 plist_dict_insert_item(client_options, key, plist_new_string(strval)); 950 plist_dict_set_item(client_options, key, plist_new_string(strval));
768 } 951 }
769 free(key); 952 free(key);
770 arg = va_arg(args, char*); 953 arg = va_arg(args, char*);
@@ -772,15 +955,106 @@ void instproxy_client_options_add(plist_t client_options, ...)
772 va_end(args); 955 va_end(args);
773} 956}
774 957
775/** 958void instproxy_client_options_set_return_attributes(plist_t client_options, ...)
776 * Free client_options plist. 959{
777 * 960 if (!client_options)
778 * @param client_options The client options plist to free. Does nothing if NULL 961 return;
779 * is passed. 962
780 */ 963 plist_t return_attributes = plist_new_array();
964
965 va_list args;
966 va_start(args, client_options);
967 char *arg = va_arg(args, char*);
968 while (arg) {
969 char *attribute = strdup(arg);
970 plist_array_append_item(return_attributes, plist_new_string(attribute));
971 free(attribute);
972 arg = va_arg(args, char*);
973 }
974 va_end(args);
975
976 plist_dict_set_item(client_options, "ReturnAttributes", return_attributes);
977}
978
781void instproxy_client_options_free(plist_t client_options) 979void instproxy_client_options_free(plist_t client_options)
782{ 980{
783 if (client_options) { 981 if (client_options) {
784 plist_free(client_options); 982 plist_free(client_options);
785 } 983 }
786} 984}
985
986instproxy_error_t instproxy_client_get_path_for_bundle_identifier(instproxy_client_t client, const char* bundle_id, char** path)
987{
988 if (!client || !client->parent || !bundle_id)
989 return INSTPROXY_E_INVALID_ARG;
990
991 plist_t apps = NULL;
992
993 // create client options for any application types
994 plist_t client_opts = instproxy_client_options_new();
995 instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL);
996
997 // only return attributes we need
998 instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "CFBundleExecutable", "Path", NULL);
999
1000 // only query for specific appid
1001 const char* appids[] = {bundle_id, NULL};
1002
1003 // query device for list of apps
1004 instproxy_error_t ierr = instproxy_lookup(client, appids, client_opts, &apps);
1005
1006 instproxy_client_options_free(client_opts);
1007
1008 if (ierr != INSTPROXY_E_SUCCESS) {
1009 return ierr;
1010 }
1011
1012 plist_t app_found = plist_access_path(apps, 1, bundle_id);
1013 if (!app_found) {
1014 if (apps)
1015 plist_free(apps);
1016 *path = NULL;
1017 return INSTPROXY_E_OP_FAILED;
1018 }
1019
1020 char* path_str = NULL;
1021 plist_t path_p = plist_dict_get_item(app_found, "Path");
1022 if (path_p) {
1023 plist_get_string_val(path_p, &path_str);
1024 }
1025
1026 char* exec_str = NULL;
1027 plist_t exec_p = plist_dict_get_item(app_found, "CFBundleExecutable");
1028 if (exec_p) {
1029 plist_get_string_val(exec_p, &exec_str);
1030 }
1031
1032 if (!path_str) {
1033 debug_info("app path not found");
1034 return INSTPROXY_E_OP_FAILED;
1035 }
1036
1037 if (!exec_str) {
1038 debug_info("bundle executable not found");
1039 return INSTPROXY_E_OP_FAILED;
1040 }
1041
1042 plist_free(apps);
1043
1044 char* ret = (char*)malloc(strlen(path_str) + 1 + strlen(exec_str) + 1);
1045 strcpy(ret, path_str);
1046 strcat(ret, "/");
1047 strcat(ret, exec_str);
1048
1049 *path = ret;
1050
1051 if (path_str) {
1052 free(path_str);
1053 }
1054
1055 if (exec_str) {
1056 free(exec_str);
1057 }
1058
1059 return INSTPROXY_E_SUCCESS;
1060}
diff --git a/src/installation_proxy.h b/src/installation_proxy.h
index b497d62..5bdbb71 100644
--- a/src/installation_proxy.h
+++ b/src/installation_proxy.h
@@ -2,34 +2,36 @@
2 * installation_proxy.h 2 * installation_proxy.h
3 * com.apple.mobile.installation_proxy service header file. 3 * com.apple.mobile.installation_proxy service header file.
4 * 4 *
5 * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21#ifndef IINSTALLATION_PROXY_H
22#define IINSTALLATION_PROXY_H
23 22
24#include <glib.h> 23#ifndef __INSTALLATION_PROXY_H
24#define __INSTALLATION_PROXY_H
25 25
26#include "idevice.h"
26#include "libimobiledevice/installation_proxy.h" 27#include "libimobiledevice/installation_proxy.h"
27#include "property_list_service.h" 28#include "property_list_service.h"
29#include <libimobiledevice-glue/thread.h>
28 30
29struct instproxy_client_private { 31struct instproxy_client_private {
30 property_list_service_client_t parent; 32 property_list_service_client_t parent;
31 GMutex *mutex; 33 mutex_t mutex;
32 GThread *status_updater; 34 THREAD_T receive_status_thread;
33}; 35};
34 36
35#endif 37#endif
diff --git a/src/libimobiledevice-1.0.pc.in b/src/libimobiledevice-1.0.pc.in
new file mode 100644
index 0000000..f00c392
--- /dev/null
+++ b/src/libimobiledevice-1.0.pc.in
@@ -0,0 +1,12 @@
1prefix=@prefix@
2exec_prefix=@exec_prefix@
3libdir=@libdir@
4includedir=@includedir@
5
6Name: @PACKAGE_NAME@
7Description: A library to communicate with services running on Apple iOS devices.
8Version: @PACKAGE_VERSION@
9Libs: -L${libdir} -limobiledevice-1.0
10Cflags: -I${includedir}
11Requires: libplist-2.0 >= @LIBPLIST_VERSION@
12Requires.private: libusbmuxd-2.0 >= @LIBUSBMUXD_VERSION@ libimobiledevice-glue-1.0 >= @LIMD_GLUE_VERSION@ @ssl_requires@
diff --git a/src/lockdown-cu.c b/src/lockdown-cu.c
new file mode 100644
index 0000000..1afc2c5
--- /dev/null
+++ b/src/lockdown-cu.c
@@ -0,0 +1,1193 @@
1/*
2 * lockdown-cu.c
3 * com.apple.mobile.lockdownd service CU additions
4 *
5 * Copyright (c) 2021 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#include <string.h>
27#include <stdlib.h>
28#define _GNU_SOURCE 1
29#define __USE_GNU 1
30#include <stdio.h>
31#include <ctype.h>
32#include <unistd.h>
33#include <plist/plist.h>
34
35#include "idevice.h"
36#include "lockdown.h"
37#include "common/debug.h"
38
39#ifdef HAVE_WIRELESS_PAIRING
40
41#include <libimobiledevice-glue/utils.h>
42#include <libimobiledevice-glue/socket.h>
43#include <libimobiledevice-glue/opack.h>
44#include <libimobiledevice-glue/tlv.h>
45
46#if defined(HAVE_OPENSSL)
47#include <openssl/hmac.h>
48#include <openssl/evp.h>
49#include <openssl/rand.h>
50#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL)
51#include <openssl/chacha.h>
52#include <openssl/poly1305.h>
53#endif
54#elif defined(HAVE_GCRYPT)
55#include <gcrypt.h>
56#elif defined(HAVE_MBEDTLS)
57#include <mbedtls/md.h>
58#include <mbedtls/chachapoly.h>
59#endif
60
61#ifdef __APPLE__
62#include <sys/sysctl.h>
63#include <SystemConfiguration/SystemConfiguration.h>
64#include <CoreFoundation/CoreFoundation.h>
65#include <TargetConditionals.h>
66#endif
67
68#include "property_list_service.h"
69#include "common/userpref.h"
70
71#include "endianness.h"
72
73#include "srp.h"
74#include "ed25519.h"
75
76/* {{{ SRP6a parameters */
77static const unsigned char kSRPModulus3072[384] = {
78 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
79 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
80 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
81 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
82 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
83 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
84 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6,
85 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05,
86 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f,
87 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb,
88 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04,
89 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b,
90 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f,
91 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18,
92 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10,
93 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33,
94 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a,
95 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7,
96 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d,
97 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64,
98 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c,
99 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2,
100 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e,
101 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
102};
103
104static const unsigned char kSRPGenerator5 = 5;
105/* }}} */
106
107/* {{{ HKDF */
108#if defined(HAVE_OPENSSL)
109#define MD_ALGO_SHA512 EVP_sha512()
110typedef const EVP_MD* MD_ALGO_TYPE_T;
111#define MD_ALGO_DIGEST_SIZE EVP_MD_size
112#define MD_MAX_DIGEST_SIZE EVP_MAX_MD_SIZE
113
114#elif defined(HAVE_GCRYPT)
115#define MD_ALGO_SHA512 GCRY_MD_SHA512
116typedef int MD_ALGO_TYPE_T;
117#define MD_ALGO_DIGEST_SIZE gcry_md_get_algo_dlen
118#define MD_MAX_DIGEST_SIZE 64
119
120static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len)
121{
122 gcry_md_hd_t hd;
123 if (gcry_md_open(&hd, md, GCRY_MD_FLAG_HMAC)) {
124 debug_info("gcry_md_open() failed");
125 return;
126 }
127 if (gcry_md_setkey(hd, key, key_len)) {
128 gcry_md_close (hd);
129 debug_info("gcry_md_setkey() failed");
130 return;
131 }
132 gcry_md_write(hd, data, data_len);
133
134 unsigned char* digest = gcry_md_read(hd, md);
135 if (!digest) {
136 gcry_md_close(hd);
137 debug_info("gcry_md_read() failed");
138 return;
139 }
140
141 *out_len = gcry_md_get_algo_dlen(md);
142 memcpy(out, digest, *out_len);
143 gcry_md_close(hd);
144}
145#elif defined(HAVE_MBEDTLS)
146#define MD_ALGO_SHA512 MBEDTLS_MD_SHA512
147typedef mbedtls_md_type_t MD_ALGO_TYPE_T;
148#define MD_ALGO_DIGEST_SIZE(x) mbedtls_md_get_size(mbedtls_md_info_from_type(x))
149#define MD_MAX_DIGEST_SIZE MBEDTLS_MD_MAX_SIZE
150
151static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len)
152{
153 mbedtls_md_context_t mdctx;
154 mbedtls_md_init(&mdctx);
155 int mr = mbedtls_md_setup(&mdctx, mbedtls_md_info_from_type(md), 1);
156 if (mr != 0) {
157 debug_info("mbedtls_md_setup() failed: %d", mr);
158 return;
159 }
160
161 mr = mbedtls_md_hmac_starts(&mdctx, key, key_len);
162 if (mr != 0) {
163 mbedtls_md_free(&mdctx);
164 debug_info("mbedtls_md_hmac_starts() failed: %d", mr);
165 return;
166 }
167
168 mbedtls_md_hmac_update(&mdctx, data, data_len);
169
170 mr = mbedtls_md_hmac_finish(&mdctx, out);
171 if (mr == 0) {
172 *out_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md));
173 } else {
174 debug_info("mbedtls_md_hmac_finish() failed: %d", mr);
175 }
176 mbedtls_md_free(&mdctx);
177}
178#endif
179
180static void hkdf_md_extract(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* input_key_material, unsigned int input_key_material_len, unsigned char* out, unsigned int* out_len)
181{
182 unsigned char empty_salt[MD_MAX_DIGEST_SIZE];
183 if (!md || !out || !out_len || !*out_len) return;
184 if (salt_len == 0) {
185 salt_len = MD_ALGO_DIGEST_SIZE(md);
186 salt = (unsigned char*)empty_salt;
187 }
188 HMAC(md, salt, salt_len, input_key_material, input_key_material_len, out, out_len);
189}
190
191static void hkdf_md_expand(MD_ALGO_TYPE_T md, unsigned char* prk, unsigned int prk_len, unsigned char* info, unsigned int info_len, unsigned char* out, unsigned int* out_len)
192{
193 if (!md || !out || !out_len || !*out_len) return;
194 unsigned int md_size = MD_ALGO_DIGEST_SIZE(md);
195 if (*out_len > 255 * md_size) {
196 *out_len = 0;
197 return;
198 }
199 int blocks_needed = (*out_len) / md_size;
200 if (((*out_len) % md_size) != 0) blocks_needed++;
201 unsigned int okm_len = 0;
202 unsigned char okm_block[MD_MAX_DIGEST_SIZE];
203 unsigned int okm_block_len = 0;
204 int i;
205 for (i = 0; i < blocks_needed; i++) {
206 unsigned int output_block_len = okm_block_len + info_len + 1;
207 unsigned char* output_block = malloc(output_block_len);
208 if (okm_block_len > 0) {
209 memcpy(output_block, okm_block, okm_block_len);
210 }
211 memcpy(output_block + okm_block_len, info, info_len);
212 output_block[okm_block_len + info_len] = (uint8_t)(i+1);
213
214 HMAC(md, prk, prk_len, output_block, output_block_len, okm_block, &okm_block_len);
215 if (okm_len < *out_len) {
216 memcpy(out + okm_len, okm_block, (okm_len + okm_block_len > *out_len) ? *out_len - okm_len : okm_block_len);
217 }
218 okm_len += okm_block_len;
219 free(output_block);
220 }
221}
222
223static void hkdf_md(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* info, unsigned int info_len, unsigned char* initial_key_material, unsigned int initial_key_material_size, unsigned char* out, unsigned int *out_len)
224{
225 if (!md || !initial_key_material || !out || !out_len || !*out_len) return;
226
227 unsigned char prk[MD_MAX_DIGEST_SIZE];
228 unsigned int prk_len = MD_ALGO_DIGEST_SIZE(md);
229
230 hkdf_md_extract(md, salt, salt_len, initial_key_material, initial_key_material_size, prk, &prk_len);
231 if (prk_len > 0) {
232 hkdf_md_expand(md, prk, prk_len, info, info_len, out, out_len);
233 } else {
234 *out_len = 0;
235 }
236}
237/* }}} */
238
239/* {{{ chacha20 poly1305 encryption/decryption */
240#if defined(HAVE_OPENSSL) && defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL)
241/* {{{ From: OpenBSD's e_chacha20poly1305.c */
242/*
243 * Copyright (c) 2015 Reyk Floter <reyk@openbsd.org>
244 * Copyright (c) 2014, Google Inc.
245 *
246 * Permission to use, copy, modify, and/or distribute this software for any
247 * purpose with or without fee is hereby granted, provided that the above
248 * copyright notice and this permission notice appear in all copies.
249 *
250 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
251 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
252 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
253 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
254 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
255 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
256 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
257 */
258static void
259poly1305_update_with_length(poly1305_state *poly1305,
260 const unsigned char *data, size_t data_len)
261{
262 size_t j = data_len;
263 unsigned char length_bytes[8];
264 unsigned i;
265
266 for (i = 0; i < sizeof(length_bytes); i++) {
267 length_bytes[i] = j;
268 j >>= 8;
269 }
270
271 if (data != NULL)
272 CRYPTO_poly1305_update(poly1305, data, data_len);
273 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
274}
275
276static void
277poly1305_update_with_pad16(poly1305_state *poly1305,
278 const unsigned char *data, size_t data_len)
279{
280 static const unsigned char zero_pad16[16];
281 size_t pad_len;
282
283 CRYPTO_poly1305_update(poly1305, data, data_len);
284
285 /* pad16() is defined in RFC 7539 2.8.1. */
286 if ((pad_len = data_len % 16) == 0)
287 return;
288
289 CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len);
290}
291/* }}} */
292#endif
293
294static void chacha20_poly1305_encrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len)
295{
296#if defined(HAVE_OPENSSL)
297#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL)
298#if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL)
299 const EVP_AEAD *aead = EVP_aead_chacha20_poly1305();
300 EVP_AEAD_CTX ctx;
301 EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL);
302 EVP_AEAD_CTX_seal(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len);
303#else
304 unsigned char poly1305_key[32];
305 poly1305_state poly1305;
306 uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32;
307 const unsigned char* iv = nonce + 4;
308
309 memset(poly1305_key, 0, sizeof(poly1305_key));
310 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr);
311
312 CRYPTO_poly1305_init(&poly1305, poly1305_key);
313 poly1305_update_with_pad16(&poly1305, ad, ad_len);
314 CRYPTO_chacha_20(out, in, in_len, key, iv, ctr + 1);
315 poly1305_update_with_pad16(&poly1305, out, in_len);
316 poly1305_update_with_length(&poly1305, NULL, ad_len);
317 poly1305_update_with_length(&poly1305, NULL, in_len);
318
319 CRYPTO_poly1305_finish(&poly1305, out + in_len);
320
321 *out_len = in_len + 16;
322#endif
323#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
324 int outl = 0;
325 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
326 EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce);
327 EVP_EncryptUpdate(ctx, out, &outl, in, in_len);
328 *out_len = outl;
329 outl = 0;
330 EVP_EncryptFinal_ex(ctx, out + *out_len, &outl);
331 *out_len += outl;
332 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, out + *out_len);
333 EVP_CIPHER_CTX_free(ctx);
334 *out_len += 16;
335#else
336#error Please use a newer version of OpenSSL (>= 1.1.0)
337#endif
338#elif defined(HAVE_GCRYPT)
339#if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700)
340 gcry_cipher_hd_t hd;
341 if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) {
342 debug_info("gcry_cipher_open() failed");
343 return;
344 }
345 gcry_cipher_setkey(hd, key, 32);
346 gcry_cipher_setiv(hd, nonce, 12);
347 gcry_cipher_authenticate(hd, ad, ad_len);
348 *out_len = in_len + 16;
349 if (gcry_cipher_encrypt(hd, out, *out_len, in, in_len)) {
350 *out_len = 0;
351 }
352 gcry_cipher_gettag(hd, out+in_len, 16);
353 gcry_cipher_close(hd);
354#else
355#error Please use a newer version of libgcrypt (>= 1.7.0)
356#endif
357#elif defined (HAVE_MBEDTLS)
358 mbedtls_chachapoly_context ctx;
359 mbedtls_chachapoly_init(&ctx);
360 mbedtls_chachapoly_setkey(&ctx, key);
361 if (mbedtls_chachapoly_encrypt_and_tag(&ctx, in_len, nonce, ad, ad_len, in, out, out+in_len) != 0) {
362 *out_len = 0;
363 }
364 mbedtls_chachapoly_free(&ctx);
365#else
366#error chacha20_poly1305_encrypt_96 is not implemented
367#endif
368}
369
370static void chacha20_poly1305_encrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len)
371{
372 unsigned char _nonce[12];
373 *(uint32_t*)(&_nonce[0]) = 0;
374 memcpy(&_nonce[4], nonce, 8);
375 chacha20_poly1305_encrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len);
376}
377
378static void chacha20_poly1305_decrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len)
379{
380#if defined(HAVE_OPENSSL)
381#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL)
382#if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL)
383 const EVP_AEAD *aead = EVP_aead_chacha20_poly1305();
384 EVP_AEAD_CTX ctx;
385 EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL);
386 EVP_AEAD_CTX_open(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len);
387#else
388 unsigned char mac[16];
389 unsigned char poly1305_key[32];
390 poly1305_state poly1305;
391 size_t plaintext_len = in_len - 16;
392 uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32;
393 const unsigned char *iv = nonce + 4;
394
395 memset(poly1305_key, 0, sizeof(poly1305_key));
396 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr);
397
398 CRYPTO_poly1305_init(&poly1305, poly1305_key);
399 poly1305_update_with_pad16(&poly1305, ad, ad_len);
400 poly1305_update_with_pad16(&poly1305, in, plaintext_len);
401 poly1305_update_with_length(&poly1305, NULL, ad_len);
402 poly1305_update_with_length(&poly1305, NULL, plaintext_len);
403
404 CRYPTO_poly1305_finish(&poly1305, mac);
405
406 if (memcmp(mac, in + plaintext_len, 16) != 0) {
407 *out_len = 0;
408 return;
409 }
410
411 CRYPTO_chacha_20(out, in, plaintext_len, key, iv, ctr + 1);
412 *out_len = plaintext_len;
413#endif
414#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
415 int outl = 0;
416 size_t plaintext_len = in_len - 16;
417 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
418 EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce);
419 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, in + plaintext_len);
420 EVP_DecryptUpdate(ctx, out, &outl, in, plaintext_len);
421 *out_len = outl;
422 outl = 0;
423 if (EVP_DecryptFinal_ex(ctx, out + *out_len, &outl) == 1) {
424 *out_len += outl;
425 } else {
426 *out_len = 0;
427 }
428 EVP_CIPHER_CTX_free(ctx);
429#else
430#error Please use a newer version of OpenSSL (>= 1.1.0)
431#endif
432#elif defined(HAVE_GCRYPT)
433#if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700)
434 gcry_cipher_hd_t hd;
435 if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) {
436 debug_info("gcry_cipher_open() failed");
437 return;
438 }
439 gcry_cipher_setkey(hd, key, 32);
440 gcry_cipher_setiv(hd, nonce, 12);
441 gcry_cipher_authenticate(hd, ad, ad_len);
442 unsigned int plaintext_len = in_len - 16;
443 gcry_cipher_decrypt(hd, out, *out_len, in, plaintext_len);
444 if (gcry_cipher_checktag(hd, in + plaintext_len, 16) == 0) {
445 *out_len = plaintext_len;
446 } else {
447 *out_len = 0;
448 }
449 gcry_cipher_close(hd);
450#else
451#error Please use a newer version of libgcrypt (>= 1.7.0)
452#endif
453#elif defined(HAVE_MBEDTLS)
454 mbedtls_chachapoly_context ctx;
455 mbedtls_chachapoly_init(&ctx);
456 mbedtls_chachapoly_setkey(&ctx, key);
457 unsigned int plaintext_len = in_len - 16;
458 if (mbedtls_chachapoly_auth_decrypt(&ctx, plaintext_len, nonce, ad, ad_len, in + plaintext_len, in, out) == 0) {
459 *out_len = plaintext_len;
460 } else {
461 *out_len = 0;
462 }
463 mbedtls_chachapoly_free(&ctx);
464#else
465#error chacha20_poly1305_decrypt_96 is not implemented
466#endif
467}
468
469static void chacha20_poly1305_decrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len)
470{
471 unsigned char _nonce[12];
472 *(uint32_t*)(&_nonce[0]) = 0;
473 memcpy(&_nonce[4], nonce, 8);
474 chacha20_poly1305_decrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len);
475}
476/* }}} */
477
478#define PAIRING_ERROR(x) \
479 debug_info(x); \
480 if (pairing_callback) { \
481 pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, (char*)x, NULL); \
482 }
483
484#define PAIRING_ERROR_FMT(...) \
485 sprintf(tmp, __VA_ARGS__); \
486 debug_info(tmp); \
487 if (pairing_callback) { \
488 pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, tmp, NULL); \
489 }
490
491#endif /* HAVE_WIRELESS_PAIRING */
492
493lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdownd_cu_pairing_cb_t pairing_callback, void* cb_user_data, plist_t host_info, plist_t acl)
494{
495#ifdef HAVE_WIRELESS_PAIRING
496 if (!client || !pairing_callback || (host_info && plist_get_node_type(host_info) != PLIST_DICT) || (acl && plist_get_node_type(acl) != PLIST_DICT))
497 return LOCKDOWN_E_INVALID_ARG;
498
499 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
500
501 if (client->device && client->device->version == 0) {
502 plist_t p_version = NULL;
503 if (lockdownd_get_value(client, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) {
504 int vers[3] = {0, 0, 0};
505 char *s_version = NULL;
506 plist_get_string_val(p_version, &s_version);
507 if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
508 client->device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
509 }
510 free(s_version);
511 }
512 plist_free(p_version);
513 }
514
515 char* pairing_uuid = NULL;
516 if (host_info) {
517 plist_t accountid = plist_dict_get_item(host_info, "accountID");
518 if (accountid && plist_get_node_type(accountid) == PLIST_STRING) {
519 plist_get_string_val(accountid, &pairing_uuid);
520 }
521 }
522 if (!pairing_uuid) {
523 userpref_read_system_buid(&pairing_uuid);
524 }
525 if (!pairing_uuid) {
526 pairing_uuid = generate_uuid();
527 }
528 unsigned int pairing_uuid_len = strlen(pairing_uuid);
529
530 SRP_initialize_library();
531
532 SRP* srp = SRP_new(SRP6a_sha512_client_method());
533 if (!srp) {
534 PAIRING_ERROR("Failed to initialize SRP")
535 return LOCKDOWN_E_UNKNOWN_ERROR;
536 }
537
538 char tmp[256];
539 plist_t dict = NULL;
540 uint8_t current_state = 0;
541 uint8_t final_state = 6;
542
543 unsigned char* salt = NULL;
544 unsigned int salt_size = 0;
545 unsigned char* pubkey = NULL;
546 unsigned int pubkey_size = 0;
547
548 unsigned char setup_encryption_key[32];
549
550 cstr *thekey = NULL;
551
552 do {
553 current_state++;
554
555 dict = plist_new_dict();
556 plist_dict_set_item(dict, "Request", plist_new_string("CUPairingCreate"));
557 if (current_state == 1) {
558 plist_dict_set_item(dict, "Flags", plist_new_uint(1));
559 } else {
560 plist_dict_set_item(dict, "Flags", plist_new_uint(0));
561 }
562
563 tlv_buf_t tlv = tlv_buf_new();
564
565 if (current_state == 1) {
566 /* send method */
567 tlv_buf_append(tlv, 0x00, 1, (void*)"\x00"); // 0x00 (Method), 1 bytes, 00
568 } else if (current_state == 3) {
569 /* generate public key */
570 cstr* own_pub = NULL;
571 SRP_gen_pub(srp, &own_pub);
572
573 if (!own_pub) {
574 PAIRING_ERROR("[SRP] Failed to generate public key")
575 ret = LOCKDOWN_E_PAIRING_FAILED;
576 break;
577 }
578
579 /* compute key from remote's public key */
580 if (SRP_compute_key(srp, &thekey, pubkey, pubkey_size) != 0) {
581 cstr_free(own_pub);
582 PAIRING_ERROR("[SRP] Failed to compute key")
583 ret = LOCKDOWN_E_PAIRING_FAILED;
584 break;
585 }
586
587 /* compute response */
588 cstr *response = NULL;
589 SRP_respond(srp, &response);
590
591 /* send our public key + response */
592 tlv_buf_append(tlv, 0x03, own_pub->length, own_pub->data);
593 tlv_buf_append(tlv, 0x04, response->length, response->data);
594 cstr_free(response);
595 cstr_free(own_pub);
596 } else if (current_state == 5) {
597 /* send encrypted info */
598
599 static const char PAIR_SETUP_ENCRYPT_SALT[] = "Pair-Setup-Encrypt-Salt";
600 static const char PAIR_SETUP_ENCRYPT_INFO[] = "Pair-Setup-Encrypt-Info";
601 static const char PAIR_SETUP_CONTROLLER_SIGN_SALT[] = "Pair-Setup-Controller-Sign-Salt";
602 static const char PAIR_SETUP_CONTROLLER_SIGN_INFO[] = "Pair-Setup-Controller-Sign-Info";
603
604 // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Encrypt-Salt + Pair-Setup-Encrypt-Info
605 // result used as key for chacha20-poly1305
606 unsigned int setup_encryption_key_len = sizeof(setup_encryption_key);
607 hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_ENCRYPT_SALT, sizeof(PAIR_SETUP_ENCRYPT_SALT)-1, (unsigned char*)PAIR_SETUP_ENCRYPT_INFO, sizeof(PAIR_SETUP_ENCRYPT_INFO)-1, (unsigned char*)thekey->data, thekey->length, setup_encryption_key, &setup_encryption_key_len);
608
609 unsigned char ed25519_pubkey[32];
610 unsigned char ed25519_privkey[64];
611 unsigned char ed25519seed[32];
612 ed25519_create_seed(ed25519seed);
613
614 ed25519_create_keypair(ed25519_pubkey, ed25519_privkey, ed25519seed);
615
616 unsigned int signbuf_len = pairing_uuid_len + 64;
617 unsigned char* signbuf = malloc(signbuf_len);
618 unsigned int hkdf_len = 32;
619 // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Controller-Sign-Salt + Pair-Setup-Controller-Sign-Info
620 hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_SALT, sizeof(PAIR_SETUP_CONTROLLER_SIGN_SALT)-1, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_INFO, sizeof(PAIR_SETUP_CONTROLLER_SIGN_INFO)-1, (unsigned char*)thekey->data, thekey->length, signbuf, &hkdf_len);
621
622 memcpy(signbuf + 32, pairing_uuid, pairing_uuid_len);
623 memcpy(signbuf + 32 + pairing_uuid_len, ed25519_pubkey, 32);
624
625 unsigned char ed_sig[64];
626 ed25519_sign(ed_sig, signbuf, 0x64, ed25519_pubkey, ed25519_privkey);
627
628 tlv_buf_t tlvbuf = tlv_buf_new();
629 tlv_buf_append(tlvbuf, 0x01, pairing_uuid_len, (void*)pairing_uuid);
630 tlv_buf_append(tlvbuf, 0x03, sizeof(ed25519_pubkey), ed25519_pubkey);
631 tlv_buf_append(tlvbuf, 0x0a, sizeof(ed_sig), ed_sig);
632
633 /* ACL */
634 unsigned char* odata = NULL;
635 unsigned int olen = 0;
636 if (acl) {
637 opack_encode_from_plist(acl, &odata, &olen);
638 } else {
639 /* defaut ACL */
640 plist_t acl_plist = plist_new_dict();
641 plist_dict_set_item(acl_plist, "com.apple.ScreenCapture", plist_new_bool(1));
642 plist_dict_set_item(acl_plist, "com.apple.developer", plist_new_bool(1));
643 opack_encode_from_plist(acl_plist, &odata, &olen);
644 plist_free(acl_plist);
645 }
646 tlv_buf_append(tlvbuf, 0x12, olen, odata);
647 free(odata);
648
649 /* HOST INFORMATION */
650 char hostname[256];
651#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
652 CFStringRef cname = SCDynamicStoreCopyComputerName(NULL, NULL);
653 CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8);
654 CFRelease(cname);
655#else
656#ifdef WIN32
657 DWORD hostname_len = sizeof(hostname);
658 GetComputerName(hostname, &hostname_len);
659#else
660 gethostname(hostname, sizeof(hostname));
661#endif
662#endif
663
664 char modelname[256];
665 modelname[0] = '\0';
666#ifdef __APPLE__
667 size_t len = sizeof(modelname);
668 sysctlbyname("hw.model", &modelname, &len, NULL, 0);
669#endif
670 if (strlen(modelname) == 0) {
671 strcpy(modelname, "HackbookPro13,37");
672 }
673
674 unsigned char primary_mac_addr[6] = { 0, 0, 0, 0, 0, 0 };
675 if (get_primary_mac_address(primary_mac_addr) != 0) {
676 debug_info("Failed to get primary mac address");
677 }
678 debug_info("Primary mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", primary_mac_addr[0], primary_mac_addr[1], primary_mac_addr[2], primary_mac_addr[3], primary_mac_addr[4], primary_mac_addr[5]);
679
680 // "OPACK" encoded device info
681 plist_t info_plist = plist_new_dict();
682 //plist_dict_set_item(info_plist, "altIRK", plist_new_data((char*)altIRK, 16));
683 plist_dict_set_item(info_plist, "accountID", plist_new_string(pairing_uuid));
684 plist_dict_set_item(info_plist, "model", plist_new_string(modelname));
685 plist_dict_set_item(info_plist, "name", plist_new_string(hostname));
686 plist_dict_set_item(info_plist, "mac", plist_new_data((char*)primary_mac_addr, 6));
687 if (host_info) {
688 plist_dict_merge(&info_plist, host_info);
689 }
690 opack_encode_from_plist(info_plist, &odata, &olen);
691 plist_free(info_plist);
692 tlv_buf_append(tlvbuf, 0x11, olen, odata);
693 free(odata);
694
695 size_t encrypted_len = tlvbuf->length + 16;
696 unsigned char* encrypted_buf = (unsigned char*)malloc(encrypted_len);
697
698 chacha20_poly1305_encrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg05", NULL, 0, tlvbuf->data, tlvbuf->length, encrypted_buf, &encrypted_len);
699
700 tlv_buf_free(tlvbuf);
701
702 tlv_buf_append(tlv, 0x05, encrypted_len, encrypted_buf);
703 free(encrypted_buf);
704 } else {
705 tlv_buf_free(tlv);
706 PAIRING_ERROR("[SRP] Invalid state");
707 ret = LOCKDOWN_E_PAIRING_FAILED;
708 break;
709 }
710 tlv_buf_append(tlv, 0x06, 1, &current_state);
711 plist_dict_set_item(dict, "Payload", plist_new_data((char*)tlv->data, tlv->length));
712 tlv_buf_free(tlv);
713
714 plist_dict_set_item(dict, "Label", plist_new_string(client->label));
715 plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2));
716
717 ret = lockdownd_send(client, dict);
718 plist_free(dict);
719 dict = NULL;
720
721 if (ret != LOCKDOWN_E_SUCCESS) {
722 break;
723 }
724
725 current_state++;
726
727 ret = lockdownd_receive(client, &dict);
728 if (ret != LOCKDOWN_E_SUCCESS) {
729 break;
730 }
731 ret = lockdown_check_result(dict, "CUPairingCreate");
732 if (ret != LOCKDOWN_E_SUCCESS) {
733 break;
734 }
735
736 plist_t extresp = plist_dict_get_item(dict, "ExtendedResponse");
737 if (!extresp) {
738 ret = LOCKDOWN_E_PLIST_ERROR;
739 break;
740 }
741 plist_t blob = plist_dict_get_item(extresp, "Payload");
742 if (!blob) {
743 ret = LOCKDOWN_E_PLIST_ERROR;
744 break;
745 }
746 uint64_t data_len = 0;
747 const char* data = plist_get_data_ptr(blob, &data_len);
748
749 uint8_t state = 0;
750 if (!tlv_data_get_uint8(data, data_len, 0x06, &state)) {
751 PAIRING_ERROR("[SRP] ERROR: Could not find state in response");
752 ret = LOCKDOWN_E_PAIRING_FAILED;
753 break;
754 }
755 if (state != current_state) {
756 PAIRING_ERROR_FMT("[SRP] ERROR: Unexpected state %d, expected %d", state, current_state);
757 ret = LOCKDOWN_E_PAIRING_FAILED;
758 break;
759 }
760
761 unsigned int errval = 0;
762 uint64_t u64val = 0;
763 tlv_data_get_uint(data, data_len, 0x07, &u64val);
764debug_buffer(data, data_len);
765 errval = (unsigned int)u64val;
766 if (errval > 0) {
767 if (errval == 3) {
768 u64val = 0;
769 tlv_data_get_uint(data, data_len, 0x08, &u64val);
770 if (u64val > 0) {
771 uint32_t retry_delay = (uint32_t)u64val;
772 PAIRING_ERROR_FMT("[SRP] Pairing is blocked for another %u seconds", retry_delay)
773 ret = LOCKDOWN_E_PAIRING_FAILED;
774 break;
775 }
776 } else if (errval == 2 && state == 4) {
777 PAIRING_ERROR_FMT("[SRP] Invalid PIN")
778 ret = LOCKDOWN_E_PAIRING_FAILED;
779 break;
780 } else {
781 PAIRING_ERROR_FMT("[SRP] Received error %u in state %d.", errval, state);
782 ret = LOCKDOWN_E_PAIRING_FAILED;
783 break;
784 }
785 }
786
787 if (state == 2) {
788 /* receive salt and public key */
789 if (!tlv_data_copy_data(data, data_len, 0x02, (void**)&salt, &salt_size)) {
790 PAIRING_ERROR("[SRP] ERROR: Could not find salt in response");
791 ret = LOCKDOWN_E_PAIRING_FAILED;
792 break;
793 }
794 if (!tlv_data_copy_data(data, data_len, 0x03, (void**)&pubkey, &pubkey_size)) {
795 PAIRING_ERROR("[SRP] ERROR: Could not find public key in response");
796
797 ret = LOCKDOWN_E_PAIRING_FAILED;
798 break;
799 }
800
801 const char PAIR_SETUP[] = "Pair-Setup";
802 if (SRP_set_user_raw(srp, (const unsigned char*)PAIR_SETUP, sizeof(PAIR_SETUP)-1) != 0) {
803 PAIRING_ERROR("[SRP] Failed to set SRP user");
804 ret = LOCKDOWN_E_PAIRING_FAILED;
805 break;
806 }
807
808 /* kSRPParameters_3072_SHA512 */
809 if (SRP_set_params(srp, kSRPModulus3072, sizeof(kSRPModulus3072), &kSRPGenerator5, 1, salt, salt_size) != 0) {
810 PAIRING_ERROR("[SRP] Failed to set SRP parameters");
811 ret = LOCKDOWN_E_PAIRING_FAILED;
812 break;
813
814 }
815
816 if (pairing_callback) {
817 char pin[64];
818 unsigned int pin_len = sizeof(pin);
819 pairing_callback(LOCKDOWN_CU_PAIRING_PIN_REQUESTED, cb_user_data, pin, &pin_len);
820
821 SRP_set_auth_password_raw(srp, (const unsigned char*)pin, pin_len);
822 }
823 } else if (state == 4) {
824 /* receive proof */
825 unsigned char* proof = NULL;
826 unsigned int proof_len = 0;
827
828 if (!tlv_data_copy_data(data, data_len, 0x04, (void**)&proof, &proof_len)) {
829 PAIRING_ERROR("[SRP] ERROR: Could not find proof data in response");
830 ret = LOCKDOWN_E_PAIRING_FAILED;
831 break;
832 }
833
834 /* verify */
835 int vrfy_result = SRP_verify(srp, proof, proof_len);
836 free(proof);
837
838 if (vrfy_result == 0) {
839 debug_info("[SRP] PIN verified successfully");
840 } else {
841 PAIRING_ERROR("[SRP] PIN verification failure");
842 ret = LOCKDOWN_E_PAIRING_FAILED;
843 break;
844 }
845
846 } else if (state == 6) {
847 int srp_pair_success = 0;
848 plist_t node = plist_dict_get_item(extresp, "doSRPPair");
849 if (node) {
850 const char* strv = plist_get_string_ptr(node, NULL);
851 if (strcmp(strv, "succeed") == 0) {
852 srp_pair_success = 1;
853 }
854 }
855 if (!srp_pair_success) {
856 PAIRING_ERROR("SRP Pairing failed");
857 ret = LOCKDOWN_E_PAIRING_FAILED;
858 break;
859 }
860
861 /* receive encrypted info */
862 unsigned char* encrypted_buf = NULL;
863 unsigned int enc_len = 0;
864 if (!tlv_data_copy_data(data, data_len, 0x05, (void**)&encrypted_buf, &enc_len)) {
865 PAIRING_ERROR("[SRP] ERROR: Could not find encrypted data in response");
866 ret = LOCKDOWN_E_PAIRING_FAILED;
867 break;
868 }
869 size_t plain_len = enc_len-16;
870 unsigned char* plain_buf = malloc(plain_len);
871 chacha20_poly1305_decrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg06", NULL, 0, encrypted_buf, enc_len, plain_buf, &plain_len);
872 free(encrypted_buf);
873
874 unsigned char* dev_info = NULL;
875 unsigned int dev_info_len = 0;
876 int res = tlv_data_copy_data(plain_buf, plain_len, 0x11, (void**)&dev_info, &dev_info_len);
877 free(plain_buf);
878 if (!res) {
879 PAIRING_ERROR("[SRP] ERROR: Failed to locate device info in response");
880 ret = LOCKDOWN_E_PAIRING_FAILED;
881 break;
882 }
883 plist_t device_info = NULL;
884 opack_decode_to_plist(dev_info, dev_info_len, &device_info);
885 free(dev_info);
886
887 if (!device_info) {
888 PAIRING_ERROR("[SRP] ERROR: Failed to parse device info");
889 ret = LOCKDOWN_E_PAIRING_FAILED;
890 break;
891 }
892
893 if (pairing_callback) {
894 pairing_callback(LOCKDOWN_CU_PAIRING_DEVICE_INFO, cb_user_data, device_info, NULL);
895 }
896 plist_free(device_info);
897 } else {
898 PAIRING_ERROR("[SRP] ERROR: Invalid state");
899 ret = LOCKDOWN_E_PAIRING_FAILED;
900 break;
901 }
902 plist_free(dict);
903 dict = NULL;
904
905 } while (current_state != final_state);
906
907 plist_free(dict);
908
909 free(salt);
910 free(pubkey);
911
912 SRP_free(srp);
913 srp = NULL;
914
915 if (ret != LOCKDOWN_E_SUCCESS) {
916 if (thekey) {
917 cstr_free(thekey);
918 }
919 return ret;
920 }
921
922 free(client->cu_key);
923 client->cu_key = malloc(thekey->length);
924 memcpy(client->cu_key, thekey->data, thekey->length);
925 client->cu_key_len = thekey->length;
926 cstr_free(thekey);
927
928 return LOCKDOWN_E_SUCCESS;
929#else
930 debug_info("not supported");
931 return LOCKDOWN_E_UNKNOWN_ERROR;
932#endif
933}
934
935lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t client, const char* request, plist_t request_payload, plist_t* reply)
936{
937#ifdef HAVE_WIRELESS_PAIRING
938 if (!client || !request)
939 return LOCKDOWN_E_INVALID_ARG;
940
941 if (!client->cu_key)
942 return LOCKDOWN_E_NO_RUNNING_SESSION;
943
944 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
945
946 /* derive keys */
947 unsigned char cu_write_key[32];
948 unsigned int cu_write_key_len = sizeof(cu_write_key);
949 static const char WRITE_KEY_SALT_MDLD[] = "WriteKeySaltMDLD";
950 static const char WRITE_KEY_INFO_MDLD[] = "WriteKeyInfoMDLD";
951 hkdf_md(MD_ALGO_SHA512, (unsigned char*)WRITE_KEY_SALT_MDLD, sizeof(WRITE_KEY_SALT_MDLD)-1, (unsigned char*)WRITE_KEY_INFO_MDLD, sizeof(WRITE_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_write_key, &cu_write_key_len);
952
953 unsigned char cu_read_key[32];
954 unsigned int cu_read_key_len = sizeof(cu_write_key);
955 static const char READ_KEY_SALT_MDLD[] = "ReadKeySaltMDLD";
956 static const char READ_KEY_INFO_MDLD[] = "ReadKeyInfoMDLD";
957 hkdf_md(MD_ALGO_SHA512, (unsigned char*)READ_KEY_SALT_MDLD, sizeof(READ_KEY_SALT_MDLD)-1, (unsigned char*)READ_KEY_INFO_MDLD, sizeof(READ_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_read_key, &cu_read_key_len);
958
959 // Starting with iOS/tvOS 11.2 and WatchOS 4.2, this nonce is random and sent along with the request. Before, the request doesn't have a nonce and it uses hardcoded nonce "sendone01234".
960 unsigned char cu_nonce[12] = "sendone01234"; // guaranteed to be random by fair dice troll
961 if (client->device->version >= DEVICE_VERSION(11,2,0)) {
962#if defined(HAVE_OPENSSL)
963 RAND_bytes(cu_nonce, sizeof(cu_nonce));
964#elif defined(HAVE_GCRYPT)
965 gcry_create_nonce(cu_nonce, sizeof(cu_nonce));
966#endif
967 }
968
969 debug_plist(request_payload);
970
971 /* convert request payload to binary */
972 uint32_t bin_len = 0;
973 char* bin = NULL;
974 plist_to_bin(request_payload, &bin, &bin_len);
975
976 /* encrypt request */
977 size_t encrypted_len = bin_len + 16;
978 unsigned char* encrypted_buf = malloc(encrypted_len);
979 chacha20_poly1305_encrypt_96(cu_write_key, cu_nonce, NULL, 0, (unsigned char*)bin, bin_len, encrypted_buf, &encrypted_len);
980 free(bin);
981 bin = NULL;
982
983 plist_t dict = plist_new_dict();
984 plist_dict_set_item(dict,"Request", plist_new_string(request));
985 plist_dict_set_item(dict, "Payload", plist_new_data((char*)encrypted_buf, encrypted_len));
986 free(encrypted_buf);
987 plist_dict_set_item(dict, "Nonce", plist_new_data((char*)cu_nonce, sizeof(cu_nonce)));
988 plist_dict_set_item(dict, "Label", plist_new_string(client->label));
989 plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2));
990
991 /* send to device */
992 ret = lockdownd_send(client, dict);
993 plist_free(dict);
994 dict = NULL;
995
996 if (ret != LOCKDOWN_E_SUCCESS)
997 return ret;
998
999 /* Now get device's answer */
1000 ret = lockdownd_receive(client, &dict);
1001 if (ret != LOCKDOWN_E_SUCCESS)
1002 return ret;
1003
1004 ret = lockdown_check_result(dict, request);
1005 if (ret != LOCKDOWN_E_SUCCESS) {
1006 plist_free(dict);
1007 return ret;
1008 }
1009
1010 /* get payload */
1011 plist_t blob = plist_dict_get_item(dict, "Payload");
1012 if (!blob) {
1013 plist_free(dict);
1014 return LOCKDOWN_E_DICT_ERROR;
1015 }
1016
1017 uint64_t dl = 0;
1018 const char* dt = plist_get_data_ptr(blob, &dl);
1019
1020 /* see if we have a nonce */
1021 blob = plist_dict_get_item(dict, "Nonce");
1022 const unsigned char* rnonce = (unsigned char*)"receiveone01";
1023 if (blob) {
1024 uint64_t rl = 0;
1025 rnonce = (const unsigned char*)plist_get_data_ptr(blob, &rl);
1026 }
1027
1028 /* decrypt payload */
1029 size_t decrypted_len = dl-16;
1030 unsigned char* decrypted = malloc(decrypted_len);
1031 chacha20_poly1305_decrypt_96(cu_read_key, (unsigned char*)rnonce, NULL, 0, (unsigned char*)dt, dl, decrypted, &decrypted_len);
1032 plist_free(dict);
1033 dict = NULL;
1034
1035 plist_from_memory((const char*)decrypted, decrypted_len, &dict, NULL);
1036 if (!dict) {
1037 ret = LOCKDOWN_E_PLIST_ERROR;
1038 debug_info("Failed to parse PLIST from decrypted payload:");
1039 debug_buffer((const char*)decrypted, decrypted_len);
1040 free(decrypted);
1041 return ret;
1042 }
1043 free(decrypted);
1044
1045 debug_plist(dict);
1046
1047 if (reply) {
1048 *reply = dict;
1049 } else {
1050 plist_free(dict);
1051 }
1052
1053 return LOCKDOWN_E_SUCCESS;
1054#else
1055 debug_info("not supported");
1056 return LOCKDOWN_E_UNKNOWN_ERROR;
1057#endif
1058}
1059
1060lockdownd_error_t lockdownd_get_value_cu(lockdownd_client_t client, const char* domain, const char* key, plist_t* value)
1061{
1062#ifdef HAVE_WIRELESS_PAIRING
1063 if (!client)
1064 return LOCKDOWN_E_INVALID_ARG;
1065
1066 if (!client->cu_key)
1067 return LOCKDOWN_E_NO_RUNNING_SESSION;
1068
1069 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1070
1071 plist_t request = plist_new_dict();
1072 if (domain) {
1073 plist_dict_set_item(request, "Domain", plist_new_string(domain));
1074 }
1075 if (key) {
1076 plist_dict_set_item(request, "Key", plist_new_string(key));
1077 }
1078
1079 plist_t reply = NULL;
1080 ret = lockdownd_cu_send_request_and_get_reply(client, "GetValueCU", request, &reply);
1081 plist_free(request);
1082 if (ret != LOCKDOWN_E_SUCCESS) {
1083 return ret;
1084 }
1085
1086 plist_t value_node = plist_dict_get_item(reply, "Value");
1087 if (value_node) {
1088 debug_info("has a value");
1089 *value = plist_copy(value_node);
1090 }
1091 plist_free(reply);
1092
1093 return ret;
1094#else
1095 debug_info("not supported");
1096 return LOCKDOWN_E_UNKNOWN_ERROR;
1097#endif
1098}
1099
1100lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client)
1101{
1102#ifdef HAVE_WIRELESS_PAIRING
1103 if (!client)
1104 return LOCKDOWN_E_INVALID_ARG;
1105
1106 if (!client->cu_key)
1107 return LOCKDOWN_E_NO_RUNNING_SESSION;
1108
1109 lockdownd_error_t ret;
1110
1111 plist_t wifi_mac = NULL;
1112 ret = lockdownd_get_value_cu(client, NULL, "WiFiAddress", &wifi_mac);
1113 if (ret != LOCKDOWN_E_SUCCESS) {
1114 return ret;
1115 }
1116
1117 plist_t pubkey = NULL;
1118 ret = lockdownd_get_value_cu(client, NULL, "DevicePublicKey", &pubkey);
1119 if (ret != LOCKDOWN_E_SUCCESS) {
1120 plist_free(wifi_mac);
1121 return ret;
1122 }
1123
1124 key_data_t public_key = { NULL, 0 };
1125 uint64_t data_len = 0;
1126 plist_get_data_val(pubkey, (char**)&public_key.data, &data_len);
1127 public_key.size = (unsigned int)data_len;
1128 plist_free(pubkey);
1129
1130 plist_t pair_record_plist = plist_new_dict();
1131 pair_record_generate_keys_and_certs(pair_record_plist, public_key);
1132
1133 char* host_id = NULL;
1134 char* system_buid = NULL;
1135
1136 /* set SystemBUID */
1137 userpref_read_system_buid(&system_buid);
1138 if (system_buid) {
1139 plist_dict_set_item(pair_record_plist, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid));
1140 free(system_buid);
1141 }
1142
1143 /* set HostID */
1144 host_id = generate_uuid();
1145 pair_record_set_host_id(pair_record_plist, host_id);
1146 free(host_id);
1147
1148 plist_t request_pair_record = plist_copy(pair_record_plist);
1149 /* remove stuff that is private */
1150 plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY);
1151 plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY);
1152
1153 plist_t request = plist_new_dict();
1154 plist_dict_set_item(request, "PairRecord", request_pair_record);
1155 plist_t pairing_opts = plist_new_dict();
1156 plist_dict_set_item(pairing_opts, "ExtendedPairingErrors", plist_new_bool(1));
1157 plist_dict_set_item(request, "PairingOptions", pairing_opts);
1158
1159 plist_t reply = NULL;
1160 ret = lockdownd_cu_send_request_and_get_reply(client, "PairCU", request, &reply);
1161 plist_free(request);
1162 if (ret != LOCKDOWN_E_SUCCESS) {
1163 plist_free(wifi_mac);
1164 return ret;
1165 }
1166
1167 char *s_udid = NULL;
1168 plist_t p_udid = plist_dict_get_item(reply, "UDID");
1169 if (p_udid) {
1170 plist_get_string_val(p_udid, &s_udid);
1171 }
1172 plist_t ebag = plist_dict_get_item(reply, "EscrowBag");
1173 if (ebag) {
1174 plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(ebag));
1175 }
1176 plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, wifi_mac);
1177 plist_free(reply);
1178
1179 if (userpref_save_pair_record(s_udid, 0, pair_record_plist) != 0) {
1180 printf("Failed to save pair record for UDID %s\n", s_udid);
1181 }
1182 free(s_udid);
1183 s_udid = NULL;
1184 plist_free(pair_record_plist);
1185
1186 ret = LOCKDOWN_E_SUCCESS;
1187
1188 return ret;
1189#else
1190 debug_info("not supported");
1191 return LOCKDOWN_E_UNKNOWN_ERROR;
1192#endif
1193}
diff --git a/src/lockdown.c b/src/lockdown.c
index 935f24e..256bff0 100644
--- a/src/lockdown.c
+++ b/src/lockdown.c
@@ -2,6 +2,8 @@
2 * lockdown.c 2 * lockdown.c
3 * com.apple.mobile.lockdownd service implementation. 3 * com.apple.mobile.lockdownd service implementation.
4 * 4 *
5 * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved.
6 * Copyright (c) 2014-2015 Nikias Bassen All Rights Reserved.
5 * Copyright (c) 2010 Bryan Forbes All Rights Reserved. 7 * Copyright (c) 2010 Bryan Forbes All Rights Reserved.
6 * Copyright (c) 2008 Zach C. All Rights Reserved. 8 * Copyright (c) 2008 Zach C. All Rights Reserved.
7 * 9 *
@@ -20,36 +22,125 @@
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */ 23 */
22 24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
23#include <string.h> 29#include <string.h>
24#include <stdlib.h> 30#include <stdlib.h>
25#define _GNU_SOURCE 1 31#define _GNU_SOURCE 1
26#define __USE_GNU 1 32#define __USE_GNU 1
27#include <stdio.h> 33#include <stdio.h>
28#include <ctype.h> 34#include <ctype.h>
29#include <glib.h> 35#include <unistd.h>
30#include <libtasn1.h>
31#include <gnutls/x509.h>
32#include <plist/plist.h> 36#include <plist/plist.h>
37#include <libimobiledevice-glue/utils.h>
33 38
34#include "property_list_service.h" 39#include "property_list_service.h"
35#include "lockdown.h" 40#include "lockdown.h"
36#include "idevice.h" 41#include "idevice.h"
37#include "debug.h" 42#include "common/debug.h"
38#include "userpref.h" 43#include "common/userpref.h"
39 44#include "asprintf.h"
40#define RESULT_SUCCESS 0 45
41#define RESULT_FAILURE 1 46#ifdef WIN32
42 47#include <windows.h>
43const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { 48#define sleep(x) Sleep(x*1000)
44 {"PKCS1", 536872976, 0}, 49#endif
45 {0, 1073741836, 0}, 50
46 {"RSAPublicKey", 536870917, 0}, 51struct st_lockdownd_error_str_map {
47 {"modulus", 1073741827, 0}, 52 const char *lockdown_errstr;
48 {"publicExponent", 3, 0}, 53 const char *errstr;
49 {0, 0, 0} 54 lockdownd_error_t errcode;
55};
56
57static struct st_lockdownd_error_str_map lockdownd_error_str_map[] = {
58 { "InvalidResponse", "Invalid response", LOCKDOWN_E_INVALID_RESPONSE },
59 { "MissingKey", "Missing key", LOCKDOWN_E_MISSING_KEY },
60 { "MissingValue", "Missing value", LOCKDOWN_E_MISSING_VALUE },
61 { "GetProhibited", "Get value prohibited", LOCKDOWN_E_GET_PROHIBITED },
62 { "SetProhibited", "Set value prohibited", LOCKDOWN_E_SET_PROHIBITED },
63 { "RemoveProhibited", "Remove value prohibited", LOCKDOWN_E_REMOVE_PROHIBITED },
64 { "ImmutableValue", "Immutable value", LOCKDOWN_E_IMMUTABLE_VALUE },
65 { "PasswordProtected", "Password protected", LOCKDOWN_E_PASSWORD_PROTECTED },
66 { "UserDeniedPairing", "User denied pairing", LOCKDOWN_E_USER_DENIED_PAIRING },
67 { "PairingDialogResponsePending", "Pairing dialog response pending", LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING },
68 { "MissingHostID", "Missing HostID", LOCKDOWN_E_MISSING_HOST_ID },
69 { "InvalidHostID", "Invalid HostID", LOCKDOWN_E_INVALID_HOST_ID },
70 { "SessionActive", "Session active", LOCKDOWN_E_SESSION_ACTIVE },
71 { "SessionInactive", "Session inactive", LOCKDOWN_E_SESSION_INACTIVE },
72 { "MissingSessionID", "Missing session ID", LOCKDOWN_E_MISSING_SESSION_ID },
73 { "InvalidSessionID", "Invalid session ID", LOCKDOWN_E_INVALID_SESSION_ID },
74 { "MissingService", "Missing service", LOCKDOWN_E_MISSING_SERVICE },
75 { "InvalidService", "Invalid service", LOCKDOWN_E_INVALID_SERVICE },
76 { "ServiceLimit", "Service limit reached", LOCKDOWN_E_SERVICE_LIMIT },
77 { "MissingPairRecord", "Missing pair record", LOCKDOWN_E_MISSING_PAIR_RECORD },
78 { "SavePairRecordFailed", "Saving pair record failed", LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED },
79 { "InvalidPairRecord", "Invalid pair record", LOCKDOWN_E_INVALID_PAIR_RECORD },
80 { "InvalidActivationRecord", "Invalid activation record", LOCKDOWN_E_INVALID_ACTIVATION_RECORD },
81 { "MissingActivationRecord", "Missing activation record", LOCKDOWN_E_MISSING_ACTIVATION_RECORD },
82 { "ServiceProhibited", "Service prohibited", LOCKDOWN_E_SERVICE_PROHIBITED },
83 { "EscrowLocked", "Escrow lockded", LOCKDOWN_E_ESCROW_LOCKED },
84 { "PairingProhibitedOverThisConnection", "Pairing prohibited over this connection", LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION },
85 { "FMiPProtected", "Find My iPhone/iPod/iPad protected", LOCKDOWN_E_FMIP_PROTECTED },
86 { "MCProtected", "MC protected" , LOCKDOWN_E_MC_PROTECTED },
87 { "MCChallengeRequired", "MC challenge required", LOCKDOWN_E_MC_CHALLENGE_REQUIRED },
88 { NULL, NULL, 0 }
50}; 89};
51 90
52/** 91/**
92 * Convert an error string identifier to a lockdownd_error_t value.
93 * Used internally to get correct error codes from a response.
94 *
95 * @param name The error name to convert.
96 *
97 * @return A matching lockdownd_error_t error code,
98 * LOCKDOWN_E_UNKNOWN_ERROR otherwise.
99 */
100static lockdownd_error_t lockdownd_strtoerr(const char* name)
101{
102 lockdownd_error_t err = LOCKDOWN_E_UNKNOWN_ERROR;
103 int i = 0;
104 while (lockdownd_error_str_map[i].lockdown_errstr) {
105 if (strcmp(lockdownd_error_str_map[i].lockdown_errstr, name) == 0) {
106 return lockdownd_error_str_map[i].errcode;
107 }
108 i++;
109 }
110 return err;
111}
112
113/**
114 * Convert a property_list_service_error_t value to a lockdownd_error_t
115 * value. Used internally to get correct error codes.
116 *
117 * @param err A property_list_service_error_t error code
118 *
119 * @return A matching lockdownd_error_t error code,
120 * LOCKDOWND_E_UNKNOWN_ERROR otherwise.
121 */
122static lockdownd_error_t lockdownd_error(property_list_service_error_t err)
123{
124 switch (err) {
125 case PROPERTY_LIST_SERVICE_E_SUCCESS:
126 return LOCKDOWN_E_SUCCESS;
127 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
128 return LOCKDOWN_E_INVALID_ARG;
129 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
130 return LOCKDOWN_E_PLIST_ERROR;
131 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
132 return LOCKDOWN_E_MUX_ERROR;
133 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
134 return LOCKDOWN_E_SSL_ERROR;
135 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
136 return LOCKDOWN_E_RECEIVE_TIMEOUT;
137 default:
138 break;
139 }
140 return LOCKDOWN_E_UNKNOWN_ERROR;
141}
142
143/**
53 * Internally used function for checking the result from lockdown's answer 144 * Internally used function for checking the result from lockdown's answer
54 * plist to a previously sent request. 145 * plist to a previously sent request.
55 * 146 *
@@ -57,58 +148,66 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
57 * @param query_match Name of the request to match or NULL if no match is 148 * @param query_match Name of the request to match or NULL if no match is
58 * required. 149 * required.
59 * 150 *
60 * @return RESULT_SUCCESS when the result is 'Success', 151 * @return LOCKDOWN_E_SUCCESS when the result is 'Success',
61 * RESULT_FAILURE when the result is 'Failure', 152 * LOCKDOWN_E_UNKNOWN_ERROR when the result is 'Failure',
62 * or a negative value if an error occured during evaluation. 153 * or a specific error code if derieved from the result.
63 */ 154 */
64static int lockdown_check_result(plist_t dict, const char *query_match) 155lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match)
65{ 156{
66 int ret = -1; 157 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
67 158
68 plist_t query_node = plist_dict_get_item(dict, "Request"); 159 plist_t query_node = plist_dict_get_item(dict, "Request");
69 if (!query_node) { 160 if (!query_node) {
70 return ret; 161 return ret;
71 } 162 }
163
72 if (plist_get_node_type(query_node) != PLIST_STRING) { 164 if (plist_get_node_type(query_node) != PLIST_STRING) {
73 return ret; 165 return ret;
74 } else {
75 char *query_value = NULL;
76 plist_get_string_val(query_node, &query_value);
77 if (!query_value) {
78 return ret;
79 }
80 if (query_match && (strcmp(query_value, query_match) != 0)) {
81 free(query_value);
82 return ret;
83 }
84 free(query_value);
85 } 166 }
86 167
87 plist_t result_node = plist_dict_get_item(dict, "Result"); 168 const char *query_value = plist_get_string_ptr(query_node, NULL);
88 if (!result_node) { 169 if (!query_value) {
89 return ret; 170 return ret;
90 } 171 }
91 172
92 plist_type result_type = plist_get_node_type(result_node); 173 if (query_match && (strcmp(query_value, query_match) != 0)) {
93 174 return ret;
94 if (result_type == PLIST_STRING) { 175 }
95
96 char *result_value = NULL;
97 176
98 plist_get_string_val(result_node, &result_value); 177 /* Check for 'Error' in reply */
178 plist_t err_node = plist_dict_get_item(dict, "Error");
179 if (err_node) {
180 if (plist_get_node_type(err_node) == PLIST_STRING) {
181 const char *err_value = plist_get_string_ptr(err_node, NULL);
182 if (err_value) {
183 debug_info("ERROR: %s", err_value);
184 ret = lockdownd_strtoerr(err_value);
185 } else {
186 debug_info("ERROR: unknown error occurred");
187 }
188 }
189 return ret;
190 }
99 191
192 plist_t result_node = plist_dict_get_item(dict, "Result");
193 if (!result_node) {
194 /* With iOS 5+ 'Result' is not present anymore.
195 If there is no 'Error', we can just assume success. */
196 return LOCKDOWN_E_SUCCESS;
197 }
198 if (plist_get_node_type(result_node) == PLIST_STRING) {
199 const char *result_value = plist_get_string_ptr(result_node, NULL);
100 if (result_value) { 200 if (result_value) {
101 if (!strcmp(result_value, "Success")) { 201 if (!strcmp(result_value, "Success")) {
102 ret = RESULT_SUCCESS; 202 ret = LOCKDOWN_E_SUCCESS;
103 } else if (!strcmp(result_value, "Failure")) { 203 } else if (!strcmp(result_value, "Failure")) {
104 ret = RESULT_FAILURE; 204 ret = LOCKDOWN_E_UNKNOWN_ERROR;
105 } else { 205 } else {
106 debug_info("ERROR: unknown result value '%s'", result_value); 206 debug_info("ERROR: unknown result value '%s'", result_value);
107 } 207 }
108 } 208 }
109 if (result_value)
110 free(result_value);
111 } 209 }
210
112 return ret; 211 return ret;
113} 212}
114 213
@@ -123,20 +222,10 @@ static void plist_dict_add_label(plist_t plist, const char *label)
123{ 222{
124 if (plist && label) { 223 if (plist && label) {
125 if (plist_get_node_type(plist) == PLIST_DICT) 224 if (plist_get_node_type(plist) == PLIST_DICT)
126 plist_dict_insert_item(plist, "Label", plist_new_string(label)); 225 plist_dict_set_item(plist, "Label", plist_new_string(label));
127 } 226 }
128} 227}
129 228
130/**
131 * Closes the lockdownd session by sending the StopSession request.
132 *
133 * @see lockdownd_start_session
134 *
135 * @param client The lockdown client
136 * @param session_id The id of a running session
137 *
138 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
139 */
140lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) 229lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id)
141{ 230{
142 if (!client) 231 if (!client)
@@ -151,8 +240,8 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *
151 240
152 plist_t dict = plist_new_dict(); 241 plist_t dict = plist_new_dict();
153 plist_dict_add_label(dict, client->label); 242 plist_dict_add_label(dict, client->label);
154 plist_dict_insert_item(dict,"Request", plist_new_string("StopSession")); 243 plist_dict_set_item(dict,"Request", plist_new_string("StopSession"));
155 plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id)); 244 plist_dict_set_item(dict,"SessionID", plist_new_string(session_id));
156 245
157 debug_info("stopping session %s", session_id); 246 debug_info("stopping session %s", session_id);
158 247
@@ -168,64 +257,74 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *
168 return LOCKDOWN_E_PLIST_ERROR; 257 return LOCKDOWN_E_PLIST_ERROR;
169 } 258 }
170 259
171 ret = LOCKDOWN_E_UNKNOWN_ERROR; 260 ret = lockdown_check_result(dict, "StopSession");
172 if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) { 261 if (ret == LOCKDOWN_E_SUCCESS) {
173 debug_info("success"); 262 debug_info("success");
174 ret = LOCKDOWN_E_SUCCESS;
175 } 263 }
264
176 plist_free(dict); 265 plist_free(dict);
177 dict = NULL; 266 dict = NULL;
267
268 if (client->session_id) {
269 free(client->session_id);
270 client->session_id = NULL;
271 }
272
178 if (client->ssl_enabled) { 273 if (client->ssl_enabled) {
179 property_list_service_disable_ssl(client->parent); 274 property_list_service_disable_ssl(client->parent);
275 client->ssl_enabled = 0;
180 } 276 }
277
181 return ret; 278 return ret;
182} 279}
183 280
184/** 281static lockdownd_error_t lockdownd_client_free_simple(lockdownd_client_t client)
185 * Closes the lockdownd client session if one is running and frees up the
186 * lockdownd_client struct.
187 *
188 * @param client The lockdown client
189 *
190 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
191 */
192lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
193{ 282{
194 if (!client) 283 if (!client)
195 return LOCKDOWN_E_INVALID_ARG; 284 return LOCKDOWN_E_INVALID_ARG;
196 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
197 285
198 if (client->session_id) { 286 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
199 lockdownd_stop_session(client, client->session_id);
200 free(client->session_id);
201 }
202 287
203 if (client->parent) { 288 if (client->parent) {
204 lockdownd_goodbye(client);
205
206 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { 289 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) {
207 ret = LOCKDOWN_E_SUCCESS; 290 ret = LOCKDOWN_E_SUCCESS;
208 } 291 }
209 } 292 }
210 293
211 if (client->uuid) { 294 if (client->session_id) {
212 free(client->uuid); 295 free(client->session_id);
296 client->session_id = NULL;
213 } 297 }
214 if (client->label) { 298 if (client->label) {
215 free(client->label); 299 free(client->label);
216 } 300 }
301 if (client->cu_key) {
302 free(client->cu_key);
303 client->cu_key = NULL;
304 }
217 305
218 free(client); 306 free(client);
307 client = NULL;
308
309 return ret;
310}
311
312lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
313{
314 if (!client)
315 return LOCKDOWN_E_INVALID_ARG;
316
317 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
318
319 if (client->session_id) {
320 lockdownd_stop_session(client, client->session_id);
321 }
322
323 ret = lockdownd_client_free_simple(client);
324
219 return ret; 325 return ret;
220} 326}
221 327
222/**
223 * Sets the label to send for requests to lockdownd.
224 *
225 * @param client The lockdown client
226 * @param label The label to set or NULL to disable sending a label
227 *
228 */
229void lockdownd_client_set_label(lockdownd_client_t client, const char *label) 328void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
230{ 329{
231 if (client) { 330 if (client) {
@@ -236,69 +335,22 @@ void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
236 } 335 }
237} 336}
238 337
239/**
240 * Receives a plist from lockdownd.
241 *
242 * @param client The lockdownd client
243 * @param plist The plist to store the received data
244 *
245 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or
246 * plist is NULL
247 */
248lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) 338lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist)
249{ 339{
250 if (!client || !plist || (plist && *plist)) 340 if (!client || !plist || (plist && *plist))
251 return LOCKDOWN_E_INVALID_ARG; 341 return LOCKDOWN_E_INVALID_ARG;
252 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
253 property_list_service_error_t err;
254
255 err = property_list_service_receive_plist(client->parent, plist);
256 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
257 ret = LOCKDOWN_E_UNKNOWN_ERROR;
258 }
259
260 if (!*plist)
261 ret = LOCKDOWN_E_PLIST_ERROR;
262 342
263 return ret; 343 return lockdownd_error(property_list_service_receive_plist(client->parent, plist));
264} 344}
265 345
266/**
267 * Sends a plist to lockdownd.
268 *
269 * @note This function is low-level and should only be used if you need to send
270 * a new type of message.
271 *
272 * @param client The lockdownd client
273 * @param plist The plist to send
274 *
275 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or
276 * plist is NULL
277 */
278lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) 346lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist)
279{ 347{
280 if (!client || !plist) 348 if (!client || !plist)
281 return LOCKDOWN_E_INVALID_ARG; 349 return LOCKDOWN_E_INVALID_ARG;
282 350
283 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 351 return lockdownd_error(property_list_service_send_xml_plist(client->parent, plist));
284 idevice_error_t err;
285
286 err = property_list_service_send_xml_plist(client->parent, plist);
287 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
288 ret = LOCKDOWN_E_UNKNOWN_ERROR;
289 }
290 return ret;
291} 352}
292 353
293/**
294 * Query the type of the service daemon. Depending on whether the device is
295 * queried in normal mode or restore mode, different types will be returned.
296 *
297 * @param client The lockdownd client
298 * @param type The type returned by the service daemon. Pass NULL to ignore.
299 *
300 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
301 */
302lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) 354lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
303{ 355{
304 if (!client) 356 if (!client)
@@ -308,7 +360,7 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
308 360
309 plist_t dict = plist_new_dict(); 361 plist_t dict = plist_new_dict();
310 plist_dict_add_label(dict, client->label); 362 plist_dict_add_label(dict, client->label);
311 plist_dict_insert_item(dict,"Request", plist_new_string("QueryType")); 363 plist_dict_set_item(dict,"Request", plist_new_string("QueryType"));
312 364
313 debug_info("called"); 365 debug_info("called");
314 ret = lockdownd_send(client, dict); 366 ret = lockdownd_send(client, dict);
@@ -322,14 +374,21 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
322 return ret; 374 return ret;
323 375
324 ret = LOCKDOWN_E_UNKNOWN_ERROR; 376 ret = LOCKDOWN_E_UNKNOWN_ERROR;
325 if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) { 377 plist_t type_node = plist_dict_get_item(dict, "Type");
378 if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) {
379 char* typestr = NULL;
380 plist_get_string_val(type_node, &typestr);
381 debug_info("success with type %s", typestr);
326 /* return the type if requested */ 382 /* return the type if requested */
327 if (type != NULL) { 383 if (type != NULL) {
328 plist_t type_node = plist_dict_get_item(dict, "Type"); 384 *type = typestr;
329 plist_get_string_val(type_node, type); 385 } else {
386 free(typestr);
330 } 387 }
331 debug_info("success with type %s", *type);
332 ret = LOCKDOWN_E_SUCCESS; 388 ret = LOCKDOWN_E_SUCCESS;
389 } else {
390 debug_info("hmm. QueryType response does not contain a type?!");
391 debug_plist(dict);
333 } 392 }
334 plist_free(dict); 393 plist_free(dict);
335 dict = NULL; 394 dict = NULL;
@@ -337,16 +396,6 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
337 return ret; 396 return ret;
338} 397}
339 398
340/**
341 * Retrieves a preferences plist using an optional domain and/or key name.
342 *
343 * @param client An initialized lockdownd client.
344 * @param domain The domain to query on or NULL for global domain
345 * @param key The key name to request or NULL to query for all keys
346 * @param value A plist node representing the result value node
347 *
348 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
349 */
350lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) 399lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value)
351{ 400{
352 if (!client) 401 if (!client)
@@ -359,12 +408,12 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
359 dict = plist_new_dict(); 408 dict = plist_new_dict();
360 plist_dict_add_label(dict, client->label); 409 plist_dict_add_label(dict, client->label);
361 if (domain) { 410 if (domain) {
362 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 411 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
363 } 412 }
364 if (key) { 413 if (key) {
365 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 414 plist_dict_set_item(dict,"Key", plist_new_string(key));
366 } 415 }
367 plist_dict_insert_item(dict,"Request", plist_new_string("GetValue")); 416 plist_dict_set_item(dict,"Request", plist_new_string("GetValue"));
368 417
369 /* send to device */ 418 /* send to device */
370 ret = lockdownd_send(client, dict); 419 ret = lockdownd_send(client, dict);
@@ -380,10 +429,11 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
380 if (ret != LOCKDOWN_E_SUCCESS) 429 if (ret != LOCKDOWN_E_SUCCESS)
381 return ret; 430 return ret;
382 431
383 if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) { 432 ret = lockdown_check_result(dict, "GetValue");
433 if (ret == LOCKDOWN_E_SUCCESS) {
384 debug_info("success"); 434 debug_info("success");
385 ret = LOCKDOWN_E_SUCCESS;
386 } 435 }
436
387 if (ret != LOCKDOWN_E_SUCCESS) { 437 if (ret != LOCKDOWN_E_SUCCESS) {
388 plist_free(dict); 438 plist_free(dict);
389 return ret; 439 return ret;
@@ -400,17 +450,6 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
400 return ret; 450 return ret;
401} 451}
402 452
403/**
404 * Sets a preferences value using a plist and optional by domain and/or key name.
405 *
406 * @param client an initialized lockdownd client.
407 * @param domain the domain to query on or NULL for global domain
408 * @param key the key name to set the value or NULL to set a value dict plist
409 * @param value a plist node of any node type representing the value to set
410 *
411 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or
412 * value is NULL
413 */
414lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) 453lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value)
415{ 454{
416 if (!client || !value) 455 if (!client || !value)
@@ -423,13 +462,13 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
423 dict = plist_new_dict(); 462 dict = plist_new_dict();
424 plist_dict_add_label(dict, client->label); 463 plist_dict_add_label(dict, client->label);
425 if (domain) { 464 if (domain) {
426 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 465 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
427 } 466 }
428 if (key) { 467 if (key) {
429 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 468 plist_dict_set_item(dict,"Key", plist_new_string(key));
430 } 469 }
431 plist_dict_insert_item(dict,"Request", plist_new_string("SetValue")); 470 plist_dict_set_item(dict,"Request", plist_new_string("SetValue"));
432 plist_dict_insert_item(dict,"Value", value); 471 plist_dict_set_item(dict,"Value", value);
433 472
434 /* send to device */ 473 /* send to device */
435 ret = lockdownd_send(client, dict); 474 ret = lockdownd_send(client, dict);
@@ -445,9 +484,9 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
445 if (ret != LOCKDOWN_E_SUCCESS) 484 if (ret != LOCKDOWN_E_SUCCESS)
446 return ret; 485 return ret;
447 486
448 if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) { 487 ret = lockdown_check_result(dict, "SetValue");
488 if (ret == LOCKDOWN_E_SUCCESS) {
449 debug_info("success"); 489 debug_info("success");
450 ret = LOCKDOWN_E_SUCCESS;
451 } 490 }
452 491
453 if (ret != LOCKDOWN_E_SUCCESS) { 492 if (ret != LOCKDOWN_E_SUCCESS) {
@@ -459,17 +498,6 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
459 return ret; 498 return ret;
460} 499}
461 500
462/**
463 * Removes a preference node by domain and/or key name.
464 *
465 * @note: Use with caution as this could remove vital information on the device
466 *
467 * @param client An initialized lockdownd client.
468 * @param domain The domain to query on or NULL for global domain
469 * @param key The key name to remove or NULL remove all keys for the current domain
470 *
471 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
472 */
473lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) 501lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key)
474{ 502{
475 if (!client) 503 if (!client)
@@ -482,12 +510,12 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
482 dict = plist_new_dict(); 510 dict = plist_new_dict();
483 plist_dict_add_label(dict, client->label); 511 plist_dict_add_label(dict, client->label);
484 if (domain) { 512 if (domain) {
485 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 513 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
486 } 514 }
487 if (key) { 515 if (key) {
488 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 516 plist_dict_set_item(dict,"Key", plist_new_string(key));
489 } 517 }
490 plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue")); 518 plist_dict_set_item(dict,"Request", plist_new_string("RemoveValue"));
491 519
492 /* send to device */ 520 /* send to device */
493 ret = lockdownd_send(client, dict); 521 ret = lockdownd_send(client, dict);
@@ -503,9 +531,9 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
503 if (ret != LOCKDOWN_E_SUCCESS) 531 if (ret != LOCKDOWN_E_SUCCESS)
504 return ret; 532 return ret;
505 533
506 if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) { 534 ret = lockdown_check_result(dict, "RemoveValue");
535 if (ret == LOCKDOWN_E_SUCCESS) {
507 debug_info("success"); 536 debug_info("success");
508 ret = LOCKDOWN_E_SUCCESS;
509 } 537 }
510 538
511 if (ret != LOCKDOWN_E_SUCCESS) { 539 if (ret != LOCKDOWN_E_SUCCESS) {
@@ -517,16 +545,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
517 return ret; 545 return ret;
518} 546}
519 547
520/** 548lockdownd_error_t lockdownd_get_device_udid(lockdownd_client_t client, char **udid)
521 * Returns the unique id of the device from lockdownd.
522 *
523 * @param client An initialized lockdownd client.
524 * @param uuid Holds the unique id of the device. The caller is responsible
525 * for freeing the memory.
526 *
527 * @return LOCKDOWN_E_SUCCESS on success
528 */
529lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uuid)
530{ 549{
531 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 550 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
532 plist_t value = NULL; 551 plist_t value = NULL;
@@ -535,7 +554,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu
535 if (ret != LOCKDOWN_E_SUCCESS) { 554 if (ret != LOCKDOWN_E_SUCCESS) {
536 return ret; 555 return ret;
537 } 556 }
538 plist_get_string_val(value, uuid); 557 plist_get_string_val(value, udid);
539 558
540 plist_free(value); 559 plist_free(value);
541 value = NULL; 560 value = NULL;
@@ -551,7 +570,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu
551 * 570 *
552 * @return LOCKDOWN_E_SUCCESS on success 571 * @return LOCKDOWN_E_SUCCESS on success
553 */ 572 */
554lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key) 573static lockdownd_error_t lockdownd_get_device_public_key_as_key_data(lockdownd_client_t client, key_data_t *public_key)
555{ 574{
556 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 575 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
557 plist_t value = NULL; 576 plist_t value = NULL;
@@ -572,15 +591,6 @@ lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnu
572 return ret; 591 return ret;
573} 592}
574 593
575/**
576 * Retrieves the name of the device from lockdownd set by the user.
577 *
578 * @param client An initialized lockdownd client.
579 * @param device_name Holds the name of the device. The caller is
580 * responsible for freeing the memory.
581 *
582 * @return LOCKDOWN_E_SUCCESS on success
583 */
584lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) 594lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name)
585{ 595{
586 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 596 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
@@ -598,29 +608,19 @@ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **de
598 return ret; 608 return ret;
599} 609}
600 610
601/**
602 * Creates a new lockdownd client for the device.
603 *
604 * @note This function does not pair with the device or start a session. This
605 * has to be done manually by the caller after the client is created.
606 * The device disconnects automatically if the lockdown connection idles
607 * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon
608 * as the connection is no longer needed.
609 *
610 * @param device The device to create a lockdownd client for
611 * @param client The pointer to the location of the new lockdownd_client
612 * @param label The label to use for communication. Usually the program name.
613 *
614 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
615 */
616lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) 611lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label)
617{ 612{
618 if (!client) 613 if (!device || !client)
619 return LOCKDOWN_E_INVALID_ARG; 614 return LOCKDOWN_E_INVALID_ARG;
620 615
616 static struct lockdownd_service_descriptor service = {
617 .port = 0xf27e,
618 .ssl_enabled = 0
619 };
620
621 property_list_service_client_t plistclient = NULL; 621 property_list_service_client_t plistclient = NULL;
622 if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 622 if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
623 debug_info("could not connect to lockdownd (device %s)", device->uuid); 623 debug_info("could not connect to lockdownd (device %s)", device->udid);
624 return LOCKDOWN_E_MUX_ERROR; 624 return LOCKDOWN_E_MUX_ERROR;
625 } 625 }
626 626
@@ -628,7 +628,14 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli
628 client_loc->parent = plistclient; 628 client_loc->parent = plistclient;
629 client_loc->ssl_enabled = 0; 629 client_loc->ssl_enabled = 0;
630 client_loc->session_id = NULL; 630 client_loc->session_id = NULL;
631 client_loc->uuid = NULL; 631 client_loc->device = device;
632 client_loc->cu_key = NULL;
633 client_loc->cu_key_len = 0;
634
635 if (device->udid) {
636 debug_info("device udid: %s", device->udid);
637 }
638
632 client_loc->label = label ? strdup(label) : NULL; 639 client_loc->label = label ? strdup(label) : NULL;
633 640
634 *client = client_loc; 641 *client = client_loc;
@@ -636,23 +643,6 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli
636 return LOCKDOWN_E_SUCCESS; 643 return LOCKDOWN_E_SUCCESS;
637} 644}
638 645
639/**
640 * Creates a new lockdownd client for the device and starts initial handshake.
641 * The handshake consists out of query_type, validate_pair, pair and
642 * start_session calls. It uses the internal pairing record management.
643 *
644 * @note The device disconnects automatically if the lockdown connection idles
645 * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon
646 * as the connection is no longer needed.
647 *
648 * @param device The device to create a lockdownd client for
649 * @param client The pointer to the location of the new lockdownd_client
650 * @param label The label to use for communication. Usually the program name.
651 * Pass NULL to disable sending the label in requests to lockdownd.
652 *
653 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
654 * LOCKDOWN_E_INVALID_CONF if configuration data is wrong
655 */
656lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) 646lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label)
657{ 647{
658 if (!client) 648 if (!client)
@@ -660,6 +650,7 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
660 650
661 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 651 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
662 lockdownd_client_t client_loc = NULL; 652 lockdownd_client_t client_loc = NULL;
653 plist_t pair_record = NULL;
663 char *host_id = NULL; 654 char *host_id = NULL;
664 char *type = NULL; 655 char *type = NULL;
665 656
@@ -670,60 +661,125 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
670 } 661 }
671 662
672 /* perform handshake */ 663 /* perform handshake */
673 if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) { 664 ret = lockdownd_query_type(client_loc, &type);
665 if (LOCKDOWN_E_SUCCESS != ret) {
674 debug_info("QueryType failed in the lockdownd client."); 666 debug_info("QueryType failed in the lockdownd client.");
675 ret = LOCKDOWN_E_NOT_ENOUGH_DATA; 667 } else if (strcmp("com.apple.mobile.lockdown", type) != 0) {
676 } else { 668 debug_info("Warning QueryType request returned \"%s\".", type);
677 if (strcmp("com.apple.mobile.lockdown", type)) { 669 }
678 debug_info("Warning QueryType request returned \"%s\".", type); 670 free(type);
671
672 if (device->version == 0) {
673 plist_t p_version = NULL;
674 if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) {
675 int vers[3] = {0, 0, 0};
676 char *s_version = NULL;
677 plist_get_string_val(p_version, &s_version);
678 if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
679 device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]);
680 }
681 free(s_version);
679 } 682 }
680 if (type) 683 plist_free(p_version);
681 free(type);
682 } 684 }
683 685 if (device->device_class == 0) {
684 ret = idevice_get_uuid(device, &client_loc->uuid); 686 plist_t p_device_class = NULL;
685 if (LOCKDOWN_E_SUCCESS != ret) { 687 if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) {
686 debug_info("failed to get device uuid."); 688 char* s_device_class = NULL;
689 plist_get_string_val(p_device_class, &s_device_class);
690 if (s_device_class != NULL) {
691 if (!strcmp(s_device_class, "iPhone")) {
692 device->device_class = DEVICE_CLASS_IPHONE;
693 } else if (!strcmp(s_device_class, "iPad")) {
694 device->device_class = DEVICE_CLASS_IPAD;
695 } else if (!strcmp(s_device_class, "iPod")) {
696 device->device_class = DEVICE_CLASS_IPOD;
697 } else if (!strcmp(s_device_class, "Watch")) {
698 device->device_class = DEVICE_CLASS_WATCH;
699 } else if (!strcmp(s_device_class, "AppleTV")) {
700 device->device_class = DEVICE_CLASS_APPLETV;
701 } else {
702 device->device_class = DEVICE_CLASS_UNKNOWN;
703 }
704 free(s_device_class);
705 }
706 }
707 plist_free(p_device_class);
687 } 708 }
688 debug_info("device uuid: %s", client_loc->uuid);
689 709
690 userpref_get_host_id(&host_id); 710 userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record);
691 if (LOCKDOWN_E_SUCCESS == ret && !host_id) { 711 if (uerr == USERPREF_E_READ_ERROR) {
712 debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid);
713 lockdownd_client_free(client_loc);
714 return LOCKDOWN_E_RECEIVE_TIMEOUT;
715 }
716 if (pair_record) {
717 pair_record_get_host_id(pair_record, &host_id);
718 }
719 if (LOCKDOWN_E_SUCCESS == ret && pair_record && !host_id) {
692 ret = LOCKDOWN_E_INVALID_CONF; 720 ret = LOCKDOWN_E_INVALID_CONF;
693 } 721 }
694 722
695 if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid)) 723 if (LOCKDOWN_E_SUCCESS == ret && !pair_record) {
724 /* attempt pairing */
725 free(host_id);
726 host_id = NULL;
696 ret = lockdownd_pair(client_loc, NULL); 727 ret = lockdownd_pair(client_loc, NULL);
728 }
697 729
698 /* in any case, we need to validate pairing to receive trusted host status */ 730 plist_free(pair_record);
699 ret = lockdownd_validate_pair(client_loc, NULL); 731 pair_record = NULL;
700 732
701 /* if not paired yet, let's do it now */ 733 if (device->version < DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) {
702 if (LOCKDOWN_E_INVALID_HOST_ID == ret) { 734 /* for older devices, we need to validate pairing to receive trusted host status */
703 ret = lockdownd_pair(client_loc, NULL); 735 ret = lockdownd_validate_pair(client_loc, NULL);
704 if (LOCKDOWN_E_SUCCESS == ret) { 736
705 ret = lockdownd_validate_pair(client_loc, NULL); 737 /* if not paired yet, let's do it now */
738 if (LOCKDOWN_E_INVALID_HOST_ID == ret) {
739 free(host_id);
740 host_id = NULL;
741 ret = lockdownd_pair(client_loc, NULL);
742 if (LOCKDOWN_E_SUCCESS == ret) {
743 ret = lockdownd_validate_pair(client_loc, NULL);
744 }
706 } 745 }
707 } 746 }
708 747
709 if (LOCKDOWN_E_SUCCESS == ret) { 748 if (LOCKDOWN_E_SUCCESS == ret) {
749 if (!host_id) {
750 uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record);
751 if (uerr == USERPREF_E_READ_ERROR) {
752 debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid);
753 lockdownd_client_free(client_loc);
754 return LOCKDOWN_E_RECEIVE_TIMEOUT;
755 } else if (uerr == USERPREF_E_NOENT) {
756 debug_info("ERROR: No pair record for %s", client_loc->device->udid);
757 lockdownd_client_free(client_loc);
758 return LOCKDOWN_E_INVALID_CONF;
759 } else if (uerr != USERPREF_E_SUCCESS) {
760 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client_loc->device->udid);
761 lockdownd_client_free(client_loc);
762 return LOCKDOWN_E_INVALID_CONF;
763 }
764 if (pair_record) {
765 pair_record_get_host_id(pair_record, &host_id);
766 plist_free(pair_record);
767 }
768 }
769
710 ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); 770 ret = lockdownd_start_session(client_loc, host_id, NULL, NULL);
711 if (LOCKDOWN_E_SUCCESS != ret) { 771 if (LOCKDOWN_E_SUCCESS != ret) {
712 debug_info("Session opening failed."); 772 debug_info("Session opening failed.");
713 } 773 }
714 774
715 if (host_id) {
716 free(host_id);
717 host_id = NULL;
718 }
719 } 775 }
720 776
721 if (LOCKDOWN_E_SUCCESS == ret) { 777 if (LOCKDOWN_E_SUCCESS == ret) {
722 *client = client_loc; 778 *client = client_loc;
723 } else { 779 } else {
724 lockdownd_client_free(client_loc); 780 lockdownd_client_free(client_loc);
725 } 781 }
726 782 free(host_id);
727 return ret; 783 return ret;
728} 784}
729 785
@@ -740,68 +796,77 @@ static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_recor
740 if (!pair_record) 796 if (!pair_record)
741 return NULL; 797 return NULL;
742 798
743 char *host_id_loc = pair_record->host_id;
744
745 /* setup request plist */ 799 /* setup request plist */
746 plist_t dict = plist_new_dict(); 800 plist_t dict = plist_new_dict();
747 plist_dict_insert_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); 801 plist_dict_set_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate)));
748 plist_dict_insert_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); 802 plist_dict_set_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate)));
749 if (!pair_record->host_id) 803 plist_dict_set_item(dict, "HostID", plist_new_string(pair_record->host_id));
750 userpref_get_host_id(&host_id_loc); 804 plist_dict_set_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate)));
751 plist_dict_insert_item(dict, "HostID", plist_new_string(host_id_loc)); 805 plist_dict_set_item(dict, "SystemBUID", plist_new_string(pair_record->system_buid));
752 plist_dict_insert_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate)));
753
754 if (!pair_record->host_id)
755 free(host_id_loc);
756 806
757 return dict; 807 return dict;
758} 808}
759 809
760/** 810/**
761 * Generates a new pairing record plist and required certificates for the 811 * Generates a pair record plist with required certificates for a specific
762 * supplied public key of the device and the host_id of the caller's host 812 * device. If a pairing exists, it is loaded from the computer instead of being
763 * computer. 813 * generated.
764 * 814 *
765 * @param public_key The public key of the device. 815 * @param pair_record_plist Holds the pair record.
766 * @param host_id The HostID to use for the pair record plist.
767 * @param pair_record_plist Holds the generated pair record.
768 * 816 *
769 * @return LOCKDOWN_E_SUCCESS on success 817 * @return LOCKDOWN_E_SUCCESS on success
770 */ 818 */
771static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist) 819static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t *pair_record)
772{ 820{
773 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 821 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
774 822
775 gnutls_datum_t device_cert = { NULL, 0 }; 823 key_data_t public_key = { NULL, 0 };
776 gnutls_datum_t host_cert = { NULL, 0 }; 824 char* host_id = NULL;
777 gnutls_datum_t root_cert = { NULL, 0 }; 825 char* system_buid = NULL;
778 826
779 ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); 827 /* retrieve device public key */
828 ret = lockdownd_get_device_public_key_as_key_data(client, &public_key);
780 if (ret != LOCKDOWN_E_SUCCESS) { 829 if (ret != LOCKDOWN_E_SUCCESS) {
781 return ret; 830 debug_info("device refused to send public key.");
831 goto leave;
832 }
833 debug_info("device public key follows:\n%.*s", public_key.size, public_key.data);
834
835 *pair_record = plist_new_dict();
836
837 /* generate keys and certificates into pair record */
838 userpref_error_t uret = USERPREF_E_SUCCESS;
839 uret = pair_record_generate_keys_and_certs(*pair_record, public_key);
840 switch(uret) {
841 case USERPREF_E_INVALID_ARG:
842 ret = LOCKDOWN_E_INVALID_ARG;
843 break;
844 case USERPREF_E_INVALID_CONF:
845 ret = LOCKDOWN_E_INVALID_CONF;
846 break;
847 case USERPREF_E_SSL_ERROR:
848 ret = LOCKDOWN_E_SSL_ERROR;
849 default:
850 break;
782 } 851 }
783 852
784 char *host_id_loc = host_id; 853 /* set SystemBUID */
854 userpref_read_system_buid(&system_buid);
855 if (system_buid) {
856 plist_dict_set_item(*pair_record, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid));
857 }
785 858
786 if (!host_id) 859 /* set HostID */
787 userpref_get_host_id(&host_id_loc); 860 host_id = generate_uuid();
861 pair_record_set_host_id(*pair_record, host_id);
788 862
789 /* setup request plist */ 863leave:
790 *pair_record_plist = plist_new_dict(); 864 if (host_id)
791 plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size)); 865 free(host_id);
792 plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size)); 866 if (system_buid)
793 plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc)); 867 free(system_buid);
794 plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size)); 868 if (public_key.data)
795 869 free(public_key.data);
796 if (!host_id)
797 free(host_id_loc);
798
799 if (device_cert.data)
800 free(device_cert.data);
801 if (host_cert.data)
802 free(host_cert.data);
803 if (root_cert.data)
804 free(root_cert.data);
805 870
806 return ret; 871 return ret;
807} 872}
@@ -809,11 +874,13 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c
809/** 874/**
810 * Function used internally by lockdownd_pair() and lockdownd_validate_pair() 875 * Function used internally by lockdownd_pair() and lockdownd_validate_pair()
811 * 876 *
812 * @param client The lockdown client to pair with. 877 * @param client The lockdown client
813 * @param pair_record The pair record to use for pairing. If NULL is passed, then 878 * @param pair_record The pair record to use for pairing. If NULL is passed, then
814 * the pair records from the current machine are used. New records will be 879 * the pair records from the current machine are used. New records will be
815 * generated automatically when pairing is done for the first time. 880 * generated automatically when pairing is done for the first time.
816 * @param verb This is either "Pair", "ValidatePair" or "Unpair". 881 * @param verb This is either "Pair", "ValidatePair" or "Unpair".
882 * @param options The pairing options to pass.
883 * @param response If non-NULL a pointer to lockdownd's response dictionary is returned.
817 * 884 *
818 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, 885 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
819 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, 886 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
@@ -821,73 +888,103 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c
821 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, 888 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
822 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id 889 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
823 */ 890 */
824static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb) 891static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb, plist_t options, plist_t *result)
825{ 892{
826 if (!client) 893 if (!client)
827 return LOCKDOWN_E_INVALID_ARG; 894 return LOCKDOWN_E_INVALID_ARG;
828 895
829 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 896 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
830 plist_t dict = NULL; 897 plist_t dict = NULL;
831 plist_t dict_record = NULL; 898 plist_t pair_record_plist = NULL;
832 gnutls_datum_t public_key = { NULL, 0 }; 899 plist_t wifi_node = NULL;
833 int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ 900 int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */
834 901
835 if (pair_record && pair_record->host_id) { 902 if (pair_record && pair_record->system_buid && pair_record->host_id) {
836 /* valid pair_record passed? */ 903 /* valid pair_record passed? */
837 if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { 904 if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) {
838 return LOCKDOWN_E_PLIST_ERROR; 905 return LOCKDOWN_E_PLIST_ERROR;
839 } 906 }
840 907
841 /* use passed pair_record */ 908 /* use passed pair_record */
842 dict_record = lockdownd_pair_record_to_plist(pair_record); 909 pair_record_plist = lockdownd_pair_record_to_plist(pair_record);
843 910
844 pairing_mode = 1; 911 pairing_mode = 1;
845 } else { 912 } else {
846 ret = lockdownd_get_device_public_key(client, &public_key); 913 /* generate a new pair record if pairing */
847 if (ret != LOCKDOWN_E_SUCCESS) { 914 if (!strcmp("Pair", verb)) {
848 if (public_key.data) 915 ret = pair_record_generate(client, &pair_record_plist);
849 free(public_key.data); 916
850 debug_info("device refused to send public key."); 917 if (ret != LOCKDOWN_E_SUCCESS) {
851 return ret; 918 if (pair_record_plist)
852 } 919 plist_free(pair_record_plist);
853 debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); 920 return ret;
854 /* get libimobiledevice pair_record */ 921 }
855 ret = generate_pair_record_plist(public_key, NULL, &dict_record); 922
856 if (ret != LOCKDOWN_E_SUCCESS) { 923 /* get wifi mac now, if we get it later we fail on iOS 7 which causes a reconnect */
857 if (dict_record) 924 lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node);
858 plist_free(dict_record); 925 } else {
859 return ret; 926 /* use existing pair record */
927 userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record_plist);
928 if (uerr == USERPREF_E_READ_ERROR) {
929 debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid);
930 return LOCKDOWN_E_RECEIVE_TIMEOUT;
931 } else if (uerr == USERPREF_E_NOENT) {
932 debug_info("ERROR: No pair record for %s", client->device->udid);
933 return LOCKDOWN_E_INVALID_CONF;
934 } else if (uerr != USERPREF_E_SUCCESS) {
935 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid);
936 return LOCKDOWN_E_INVALID_CONF;
937 }
860 } 938 }
861 } 939 }
862 940
863 /* Setup Pair request plist */ 941 plist_t request_pair_record = plist_copy(pair_record_plist);
942
943 /* remove stuff that is private */
944 plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY);
945 plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY);
946
947 /* setup pair request plist */
864 dict = plist_new_dict(); 948 dict = plist_new_dict();
865 plist_dict_add_label(dict, client->label); 949 plist_dict_add_label(dict, client->label);
866 plist_dict_insert_item(dict,"PairRecord", dict_record); 950 plist_dict_set_item(dict, "PairRecord", request_pair_record);
867 plist_dict_insert_item(dict, "Request", plist_new_string(verb)); 951 plist_dict_set_item(dict, "Request", plist_new_string(verb));
952 plist_dict_set_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION));
953
954 if (options) {
955 plist_dict_set_item(dict, "PairingOptions", plist_copy(options));
956 }
868 957
869 /* send to device */ 958 /* send to device */
870 ret = lockdownd_send(client, dict); 959 ret = lockdownd_send(client, dict);
871 plist_free(dict); 960 plist_free(dict);
872 dict = NULL; 961 dict = NULL;
873 962
874 if (ret != LOCKDOWN_E_SUCCESS) 963 if (ret != LOCKDOWN_E_SUCCESS) {
964 plist_free(pair_record_plist);
965 if (wifi_node)
966 plist_free(wifi_node);
875 return ret; 967 return ret;
968 }
876 969
877 /* Now get device's answer */ 970 /* Now get device's answer */
878 ret = lockdownd_receive(client, &dict); 971 ret = lockdownd_receive(client, &dict);
879 972
880 if (ret != LOCKDOWN_E_SUCCESS) 973 if (ret != LOCKDOWN_E_SUCCESS) {
974 plist_free(pair_record_plist);
975 if (wifi_node)
976 plist_free(wifi_node);
881 return ret; 977 return ret;
978 }
882 979
883 if (strcmp(verb, "Unpair") == 0) { 980 if (strcmp(verb, "Unpair") == 0) {
884 /* workaround for Unpair giving back ValidatePair, 981 /* workaround for Unpair giving back ValidatePair,
885 * seems to be a bug in the device's fw */ 982 * seems to be a bug in the device's fw */
886 if (lockdown_check_result(dict, NULL) != RESULT_SUCCESS) { 983 if (lockdown_check_result(dict, NULL) != LOCKDOWN_E_SUCCESS) {
887 ret = LOCKDOWN_E_PAIRING_FAILED; 984 ret = LOCKDOWN_E_PAIRING_FAILED;
888 } 985 }
889 } else { 986 } else {
890 if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) { 987 if (lockdown_check_result(dict, verb) != LOCKDOWN_E_SUCCESS) {
891 ret = LOCKDOWN_E_PAIRING_FAILED; 988 ret = LOCKDOWN_E_PAIRING_FAILED;
892 } 989 }
893 } 990 }
@@ -896,13 +993,32 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_
896 if (ret == LOCKDOWN_E_SUCCESS) { 993 if (ret == LOCKDOWN_E_SUCCESS) {
897 debug_info("%s success", verb); 994 debug_info("%s success", verb);
898 if (!pairing_mode) { 995 if (!pairing_mode) {
996 debug_info("internal pairing mode");
899 if (!strcmp("Unpair", verb)) { 997 if (!strcmp("Unpair", verb)) {
900 /* remove public key from config */ 998 /* remove public key from config */
901 userpref_remove_device_public_key(client->uuid); 999 userpref_delete_pair_record(client->device->udid);
902 } else { 1000 } else {
903 /* store public key in config */ 1001 if (!strcmp("Pair", verb)) {
904 userpref_set_device_public_key(client->uuid, public_key); 1002 /* add returned escrow bag if available */
1003 plist_t extra_node = plist_dict_get_item(dict, USERPREF_ESCROW_BAG_KEY);
1004 if (extra_node && plist_get_node_type(extra_node) == PLIST_DATA) {
1005 debug_info("Saving EscrowBag from response in pair record");
1006 plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(extra_node));
1007 }
1008
1009 /* save previously retrieved wifi mac address in pair record */
1010 if (wifi_node) {
1011 debug_info("Saving WiFiAddress from device in pair record");
1012 plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_node));
1013 plist_free(wifi_node);
1014 wifi_node = NULL;
1015 }
1016
1017 userpref_save_pair_record(client->device->udid, client->device->mux_id, pair_record_plist);
1018 }
905 } 1019 }
1020 } else {
1021 debug_info("external pairing mode");
906 } 1022 }
907 } else { 1023 } else {
908 debug_info("%s failure", verb); 1024 debug_info("%s failure", verb);
@@ -914,92 +1030,60 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_
914 plist_get_string_val(error_node, &value); 1030 plist_get_string_val(error_node, &value);
915 if (value) { 1031 if (value) {
916 /* the first pairing fails if the device is password protected */ 1032 /* the first pairing fails if the device is password protected */
917 if (!strcmp(value, "PasswordProtected")) { 1033 ret = lockdownd_strtoerr(value);
918 ret = LOCKDOWN_E_PASSWORD_PROTECTED;
919 } else if (!strcmp(value, "InvalidHostID")) {
920 ret = LOCKDOWN_E_INVALID_HOST_ID;
921 }
922 free(value); 1034 free(value);
923 } 1035 }
924
925 plist_free(error_node);
926 error_node = NULL;
927 } 1036 }
928 } 1037 }
929 plist_free(dict); 1038
930 dict = NULL; 1039 if (pair_record_plist) {
931 if (public_key.data) 1040 plist_free(pair_record_plist);
932 free(public_key.data); 1041 pair_record_plist = NULL;
1042 }
1043
1044 if (wifi_node) {
1045 plist_free(wifi_node);
1046 wifi_node = NULL;
1047 }
1048
1049 if (result) {
1050 *result = dict;
1051 } else {
1052 plist_free(dict);
1053 dict = NULL;
1054 }
1055
933 return ret; 1056 return ret;
934} 1057}
935 1058
936/**
937 * Pairs the device using the supplied pair record.
938 *
939 * @param client The lockdown client to pair with.
940 * @param pair_record The pair record to use for pairing. If NULL is passed, then
941 * the pair records from the current machine are used. New records will be
942 * generated automatically when pairing is done for the first time.
943 *
944 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
945 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
946 * LOCKDOWN_E_PAIRING_FAILED if the pairing failed,
947 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
948 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
949 */
950lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) 1059lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
951{ 1060{
952 return lockdownd_do_pair(client, pair_record, "Pair"); 1061
1062 plist_t options = plist_new_dict();
1063 plist_dict_set_item(options, "ExtendedPairingErrors", plist_new_bool(1));
1064
1065 lockdownd_error_t ret = lockdownd_do_pair(client, pair_record, "Pair", options, NULL);
1066
1067 plist_free(options);
1068
1069 return ret;
1070}
1071
1072lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response)
1073{
1074 return lockdownd_do_pair(client, pair_record, "Pair", options, response);
953} 1075}
954 1076
955/**
956 * Validates if the device is paired with the given HostID. If succeeded them
957 * specified host will become trusted host of the device indicated by the
958 * lockdownd preference named TrustedHostAttached. Otherwise the host must because
959 * paired using lockdownd_pair() first.
960 *
961 * @param client The lockdown client to pair with.
962 * @param pair_record The pair record to validate pairing with. If NULL is
963 * passed, then the pair record is read from the internal pairing record
964 * management.
965 *
966 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
967 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
968 * LOCKDOWN_E_PAIRING_FAILED if the pairing failed,
969 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
970 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
971 */
972lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) 1077lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
973{ 1078{
974 return lockdownd_do_pair(client, pair_record, "ValidatePair"); 1079 return lockdownd_do_pair(client, pair_record, "ValidatePair", NULL, NULL);
975} 1080}
976 1081
977/**
978 * Unpairs the device with the given HostID and removes the pairing records
979 * from the device and host if the internal pairing record management is used.
980 *
981 * @param client The lockdown client to pair with.
982 * @param pair_record The pair record to use for unpair. If NULL is passed, then
983 * the pair records from the current machine are used.
984 *
985 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
986 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
987 * LOCKDOWN_E_PAIRING_FAILED if the pairing failed,
988 * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected,
989 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
990 */
991lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) 1082lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
992{ 1083{
993 return lockdownd_do_pair(client, pair_record, "Unpair"); 1084 return lockdownd_do_pair(client, pair_record, "Unpair", NULL, NULL);
994} 1085}
995 1086
996/**
997 * Tells the device to immediately enter recovery mode.
998 *
999 * @param client The lockdown client
1000 *
1001 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
1002 */
1003lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) 1087lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1004{ 1088{
1005 if (!client) 1089 if (!client)
@@ -1009,7 +1093,7 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1009 1093
1010 plist_t dict = plist_new_dict(); 1094 plist_t dict = plist_new_dict();
1011 plist_dict_add_label(dict, client->label); 1095 plist_dict_add_label(dict, client->label);
1012 plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery")); 1096 plist_dict_set_item(dict,"Request", plist_new_string("EnterRecovery"));
1013 1097
1014 debug_info("telling device to enter recovery mode"); 1098 debug_info("telling device to enter recovery mode");
1015 1099
@@ -1019,23 +1103,17 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1019 1103
1020 ret = lockdownd_receive(client, &dict); 1104 ret = lockdownd_receive(client, &dict);
1021 1105
1022 if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) { 1106 ret = lockdown_check_result(dict, "EnterRecovery");
1107 if (ret == LOCKDOWN_E_SUCCESS) {
1023 debug_info("success"); 1108 debug_info("success");
1024 ret = LOCKDOWN_E_SUCCESS;
1025 } 1109 }
1110
1026 plist_free(dict); 1111 plist_free(dict);
1027 dict = NULL; 1112 dict = NULL;
1113
1028 return ret; 1114 return ret;
1029} 1115}
1030 1116
1031/**
1032 * Sends the Goodbye request to lockdownd signaling the end of communication.
1033 *
1034 * @param client The lockdown client
1035 *
1036 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
1037 * LOCKDOWN_E_PLIST_ERROR if the device did not acknowledge the request
1038 */
1039lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) 1117lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1040{ 1118{
1041 if (!client) 1119 if (!client)
@@ -1045,7 +1123,7 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1045 1123
1046 plist_t dict = plist_new_dict(); 1124 plist_t dict = plist_new_dict();
1047 plist_dict_add_label(dict, client->label); 1125 plist_dict_add_label(dict, client->label);
1048 plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); 1126 plist_dict_set_item(dict,"Request", plist_new_string("Goodbye"));
1049 1127
1050 debug_info("called"); 1128 debug_info("called");
1051 1129
@@ -1059,187 +1137,17 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1059 return LOCKDOWN_E_PLIST_ERROR; 1137 return LOCKDOWN_E_PLIST_ERROR;
1060 } 1138 }
1061 1139
1062 if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) { 1140 ret = lockdown_check_result(dict, "Goodbye");
1141 if (ret == LOCKDOWN_E_SUCCESS) {
1063 debug_info("success"); 1142 debug_info("success");
1064 ret = LOCKDOWN_E_SUCCESS;
1065 } 1143 }
1144
1066 plist_free(dict); 1145 plist_free(dict);
1067 dict = NULL; 1146 dict = NULL;
1068 return ret;
1069}
1070
1071/**
1072 * Generates the device certificate from the public key as well as the host
1073 * and root certificates.
1074 *
1075 * @param public_key The public key of the device to use for generation.
1076 * @param odevice_cert Holds the generated device certificate.
1077 * @param ohost_cert Holds the generated host certificate.
1078 * @param oroot_cert Holds the generated root certificate.
1079 *
1080 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a parameter is NULL,
1081 * LOCKDOWN_E_INVALID_CONF if the internal configuration system failed,
1082 * LOCKDOWN_E_SSL_ERROR if the certificates could not be generated
1083 */
1084lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * odevice_cert,
1085 gnutls_datum_t * ohost_cert, gnutls_datum_t * oroot_cert)
1086{
1087 if (!public_key.data || !odevice_cert || !ohost_cert || !oroot_cert)
1088 return LOCKDOWN_E_INVALID_ARG;
1089 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1090 userpref_error_t uret = USERPREF_E_UNKNOWN_ERROR;
1091
1092 gnutls_datum_t modulus = { NULL, 0 };
1093 gnutls_datum_t exponent = { NULL, 0 };
1094
1095 /* now decode the PEM encoded key */
1096 gnutls_datum_t der_pub_key;
1097 if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) {
1098
1099 /* initalize asn.1 parser */
1100 ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY;
1101 if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) {
1102
1103 ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY;
1104 asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key);
1105
1106 if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) {
1107
1108 /* get size to read */
1109 int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, (int*)&modulus.size);
1110 int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, (int*)&exponent.size);
1111
1112 modulus.data = gnutls_malloc(modulus.size);
1113 exponent.data = gnutls_malloc(exponent.size);
1114
1115 ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size);
1116 ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size);
1117 if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2)
1118 ret = LOCKDOWN_E_SUCCESS;
1119 }
1120 if (asn1_pub_key)
1121 asn1_delete_structure(&asn1_pub_key);
1122 }
1123 if (pkcs1)
1124 asn1_delete_structure(&pkcs1);
1125 }
1126
1127 /* now generate certificates */
1128 if (LOCKDOWN_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) {
1129
1130 gnutls_global_init();
1131 gnutls_datum_t essentially_null = { (unsigned char*)strdup("abababababababab"), strlen("abababababababab") };
1132
1133 gnutls_x509_privkey_t fake_privkey, root_privkey, host_privkey;
1134 gnutls_x509_crt_t dev_cert, root_cert, host_cert;
1135
1136 gnutls_x509_privkey_init(&fake_privkey);
1137 gnutls_x509_crt_init(&dev_cert);
1138 gnutls_x509_crt_init(&root_cert);
1139 gnutls_x509_crt_init(&host_cert);
1140
1141 if (GNUTLS_E_SUCCESS ==
1142 gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null,
1143 &essentially_null, &essentially_null)) {
1144
1145 gnutls_x509_privkey_init(&root_privkey);
1146 gnutls_x509_privkey_init(&host_privkey);
1147
1148 uret = userpref_get_keys_and_certs(root_privkey, root_cert, host_privkey, host_cert);
1149
1150 if (USERPREF_E_SUCCESS == uret) {
1151 /* generate device certificate */
1152 gnutls_x509_crt_set_key(dev_cert, fake_privkey);
1153 gnutls_x509_crt_set_serial(dev_cert, "\x00", 1);
1154 gnutls_x509_crt_set_version(dev_cert, 3);
1155 gnutls_x509_crt_set_ca_status(dev_cert, 0);
1156 gnutls_x509_crt_set_activation_time(dev_cert, time(NULL));
1157 gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
1158 gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey);
1159
1160 if (LOCKDOWN_E_SUCCESS == ret) {
1161 /* if everything went well, export in PEM format */
1162 size_t export_size = 0;
1163 gnutls_datum_t dev_pem = { NULL, 0 };
1164 gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size);
1165 dev_pem.data = gnutls_malloc(export_size);
1166 gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &export_size);
1167 dev_pem.size = export_size;
1168
1169 gnutls_datum_t pem_root_cert = { NULL, 0 };
1170 gnutls_datum_t pem_host_cert = { NULL, 0 };
1171
1172 uret = userpref_get_certs_as_pem(&pem_root_cert, &pem_host_cert);
1173
1174 if (USERPREF_E_SUCCESS == uret) {
1175 /* copy buffer for output */
1176 odevice_cert->data = malloc(dev_pem.size);
1177 memcpy(odevice_cert->data, dev_pem.data, dev_pem.size);
1178 odevice_cert->size = dev_pem.size;
1179
1180 ohost_cert->data = malloc(pem_host_cert.size);
1181 memcpy(ohost_cert->data, pem_host_cert.data, pem_host_cert.size);
1182 ohost_cert->size = pem_host_cert.size;
1183
1184 oroot_cert->data = malloc(pem_root_cert.size);
1185 memcpy(oroot_cert->data, pem_root_cert.data, pem_root_cert.size);
1186 oroot_cert->size = pem_root_cert.size;
1187
1188 g_free(pem_root_cert.data);
1189 g_free(pem_host_cert.data);
1190
1191 if (dev_pem.data)
1192 gnutls_free(dev_pem.data);
1193 }
1194 }
1195 }
1196
1197 switch(uret) {
1198 case USERPREF_E_INVALID_ARG:
1199 ret = LOCKDOWN_E_INVALID_ARG;
1200 break;
1201 case USERPREF_E_INVALID_CONF:
1202 ret = LOCKDOWN_E_INVALID_CONF;
1203 break;
1204 case USERPREF_E_SSL_ERROR:
1205 ret = LOCKDOWN_E_SSL_ERROR;
1206 default:
1207 break;
1208 }
1209 }
1210
1211 if (essentially_null.data)
1212 free(essentially_null.data);
1213 gnutls_x509_crt_deinit(dev_cert);
1214 gnutls_x509_crt_deinit(root_cert);
1215 gnutls_x509_crt_deinit(host_cert);
1216 gnutls_x509_privkey_deinit(fake_privkey);
1217 gnutls_x509_privkey_deinit(root_privkey);
1218 gnutls_x509_privkey_deinit(host_privkey);
1219
1220 }
1221
1222 gnutls_free(modulus.data);
1223 gnutls_free(exponent.data);
1224
1225 gnutls_free(der_pub_key.data);
1226 1147
1227 return ret; 1148 return ret;
1228} 1149}
1229 1150
1230/**
1231 * Opens a session with lockdownd and switches to SSL mode if device wants it.
1232 *
1233 * @param client The lockdownd client
1234 * @param host_id The HostID of the computer
1235 * @param session_id The new session_id of the created session
1236 * @param ssl_enabled Whether SSL communication is used in the session
1237 *
1238 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a client or
1239 * host_id is NULL, LOCKDOWN_E_PLIST_ERROR if the response plist had errors,
1240 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the supplied HostID,
1241 * LOCKDOWN_E_SSL_ERROR if enabling SSL communication failed
1242 */
1243lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) 1151lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled)
1244{ 1152{
1245 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 1153 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
@@ -1251,14 +1159,28 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1251 /* if we have a running session, stop current one first */ 1159 /* if we have a running session, stop current one first */
1252 if (client->session_id) { 1160 if (client->session_id) {
1253 lockdownd_stop_session(client, client->session_id); 1161 lockdownd_stop_session(client, client->session_id);
1254 free(client->session_id);
1255 } 1162 }
1256 1163
1257 /* setup request plist */ 1164 /* setup request plist */
1258 dict = plist_new_dict(); 1165 dict = plist_new_dict();
1259 plist_dict_add_label(dict, client->label); 1166 plist_dict_add_label(dict, client->label);
1260 plist_dict_insert_item(dict,"HostID", plist_new_string(host_id)); 1167 plist_dict_set_item(dict,"Request", plist_new_string("StartSession"));
1261 plist_dict_insert_item(dict,"Request", plist_new_string("StartSession")); 1168
1169 /* add host id */
1170 if (host_id) {
1171 plist_dict_set_item(dict, "HostID", plist_new_string(host_id));
1172 }
1173
1174 /* add system buid */
1175 char *system_buid = NULL;
1176 userpref_read_system_buid(&system_buid);
1177 if (system_buid) {
1178 plist_dict_set_item(dict, "SystemBUID", plist_new_string(system_buid));
1179 if (system_buid) {
1180 free(system_buid);
1181 system_buid = NULL;
1182 }
1183 }
1262 1184
1263 ret = lockdownd_send(client, dict); 1185 ret = lockdownd_send(client, dict);
1264 plist_free(dict); 1186 plist_free(dict);
@@ -1272,17 +1194,8 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1272 if (!dict) 1194 if (!dict)
1273 return LOCKDOWN_E_PLIST_ERROR; 1195 return LOCKDOWN_E_PLIST_ERROR;
1274 1196
1275 if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) { 1197 ret = lockdown_check_result(dict, "StartSession");
1276 plist_t error_node = plist_dict_get_item(dict, "Error"); 1198 if (ret == LOCKDOWN_E_SUCCESS) {
1277 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1278 char *error = NULL;
1279 plist_get_string_val(error_node, &error);
1280 if (!strcmp(error, "InvalidHostID")) {
1281 ret = LOCKDOWN_E_INVALID_HOST_ID;
1282 }
1283 free(error);
1284 }
1285 } else {
1286 uint8_t use_ssl = 0; 1199 uint8_t use_ssl = 0;
1287 1200
1288 plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); 1201 plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL");
@@ -1299,6 +1212,7 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1299 if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) { 1212 if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) {
1300 plist_get_string_val(session_node, &client->session_id); 1213 plist_get_string_val(session_node, &client->session_id);
1301 } 1214 }
1215
1302 if (client->session_id) { 1216 if (client->session_id) {
1303 debug_info("SessionID: %s", client->session_id); 1217 debug_info("SessionID: %s", client->session_id);
1304 if (session_id != NULL) 1218 if (session_id != NULL)
@@ -1306,18 +1220,15 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1306 } else { 1220 } else {
1307 debug_info("Failed to get SessionID!"); 1221 debug_info("Failed to get SessionID!");
1308 } 1222 }
1309 debug_info("Enable SSL Session: %s", (use_ssl?"true":"false")); 1223
1224 debug_info("Enable SSL Session: %s", (use_ssl ? "true" : "false"));
1225
1310 if (use_ssl) { 1226 if (use_ssl) {
1311 ret = property_list_service_enable_ssl(client->parent); 1227 ret = lockdownd_error(property_list_service_enable_ssl(client->parent));
1312 if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) { 1228 client->ssl_enabled = (ret == LOCKDOWN_E_SUCCESS ? 1 : 0);
1313 client->ssl_enabled = 1;
1314 } else {
1315 ret = LOCKDOWN_E_SSL_ERROR;
1316 client->ssl_enabled = 0;
1317 }
1318 } else { 1229 } else {
1319 client->ssl_enabled = 0;
1320 ret = LOCKDOWN_E_SUCCESS; 1230 ret = LOCKDOWN_E_SUCCESS;
1231 client->ssl_enabled = 0;
1321 } 1232 }
1322 } 1233 }
1323 1234
@@ -1328,40 +1239,96 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1328} 1239}
1329 1240
1330/** 1241/**
1331 * Requests to start a service and retrieve it's port on success. 1242 * Internal function used by lockdownd_do_start_service to create the
1243 * StartService request's plist.
1332 * 1244 *
1333 * @param client The lockdownd client 1245 * @param client The lockdownd client
1334 * @param service The name of the service to start 1246 * @param identifier The identifier of the service to start
1335 * @param port The port number the service was started on 1247 * @param send_escrow_bag Should we send the device's escrow bag with the request
1336 1248 * @param request The request's plist on success, NULL on failure
1337 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter 1249 *
1250 * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_CONF on failure
1251 * to read the escrow bag from the device's record (when used).
1252 */
1253static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_t client, const char *identifier, int send_escrow_bag, plist_t *request)
1254{
1255 plist_t dict = plist_new_dict();
1256
1257 /* create the basic request params */
1258 plist_dict_add_label(dict, client->label);
1259 plist_dict_set_item(dict, "Request", plist_new_string("StartService"));
1260 plist_dict_set_item(dict, "Service", plist_new_string(identifier));
1261
1262 /* if needed - get the escrow bag for the device and send it with the request */
1263 if (send_escrow_bag) {
1264 /* get the pairing record */
1265 plist_t pair_record = NULL;
1266 userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record);
1267 if (uerr == USERPREF_E_READ_ERROR) {
1268 debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid);
1269 plist_free(dict);
1270 return LOCKDOWN_E_RECEIVE_TIMEOUT;
1271 } else if (uerr == USERPREF_E_NOENT) {
1272 debug_info("ERROR: No pair record for %s", client->device->udid);
1273 plist_free(dict);
1274 return LOCKDOWN_E_INVALID_CONF;
1275 } else if (uerr != USERPREF_E_SUCCESS) {
1276 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid);
1277 plist_free(dict);
1278 return LOCKDOWN_E_INVALID_CONF;
1279 }
1280
1281 /* try to read the escrow bag from the record */
1282 plist_t escrow_bag = plist_dict_get_item(pair_record, USERPREF_ESCROW_BAG_KEY);
1283 if (!escrow_bag || (PLIST_DATA != plist_get_node_type(escrow_bag))) {
1284 debug_info("ERROR: Failed to retrieve the escrow bag from the device's record");
1285 plist_free(dict);
1286 plist_free(pair_record);
1287 return LOCKDOWN_E_INVALID_CONF;
1288 }
1289
1290 debug_info("Adding escrow bag to StartService for %s", identifier);
1291 plist_dict_set_item(dict, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag));
1292 plist_free(pair_record);
1293 }
1294
1295 *request = dict;
1296 return LOCKDOWN_E_SUCCESS;
1297}
1298
1299/**
1300 * Function used internally by lockdownd_start_service and lockdownd_start_service_with_escrow_bag.
1301 *
1302 * @param client The lockdownd client
1303 * @param identifier The identifier of the service to start
1304 * @param send_escrow_bag Should we send the device's escrow bag with the request
1305 * @param descriptor The service descriptor on success or NULL on failure
1306 *
1307 * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_ARG if a parameter
1338 * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known 1308 * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known
1339 * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because 1309 * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because
1340 * started by the device 1310 * started by the device, LOCKDOWN_E_INVALID_CONF if the host id or escrow bag (when
1311 * used) are missing from the device record.
1341 */ 1312 */
1342lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port) 1313static lockdownd_error_t lockdownd_do_start_service(lockdownd_client_t client, const char *identifier, int send_escrow_bag, lockdownd_service_descriptor_t *service)
1343{ 1314{
1344 if (!client || !service || !port) 1315 if (!client || !identifier || !service)
1345 return LOCKDOWN_E_INVALID_ARG; 1316 return LOCKDOWN_E_INVALID_ARG;
1346 1317
1347 char *host_id = NULL; 1318 if (*service) {
1348 userpref_get_host_id(&host_id); 1319 // reset fields if service descriptor is reused
1349 if (!host_id) 1320 (*service)->port = 0;
1350 return LOCKDOWN_E_INVALID_CONF; 1321 (*service)->ssl_enabled = 0;
1351 if (!client->session_id) 1322 }
1352 return LOCKDOWN_E_NO_RUNNING_SESSION;
1353 1323
1354 plist_t dict = NULL; 1324 plist_t dict = NULL;
1355 uint16_t port_loc = 0; 1325 uint16_t port_loc = 0;
1356 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 1326 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1357 1327
1358 free(host_id); 1328 /* create StartService request */
1359 host_id = NULL; 1329 ret = lockdownd_build_start_service_request(client, identifier, send_escrow_bag, &dict);
1360 1330 if (LOCKDOWN_E_SUCCESS != ret)
1361 dict = plist_new_dict(); 1331 return ret;
1362 plist_dict_add_label(dict, client->label);
1363 plist_dict_insert_item(dict,"Request", plist_new_string("StartService"));
1364 plist_dict_insert_item(dict,"Service", plist_new_string(service));
1365 1332
1366 /* send to device */ 1333 /* send to device */
1367 ret = lockdownd_send(client, dict); 1334 ret = lockdownd_send(client, dict);
@@ -1379,57 +1346,63 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char
1379 if (!dict) 1346 if (!dict)
1380 return LOCKDOWN_E_PLIST_ERROR; 1347 return LOCKDOWN_E_PLIST_ERROR;
1381 1348
1382 ret = LOCKDOWN_E_UNKNOWN_ERROR; 1349 ret = lockdown_check_result(dict, "StartService");
1383 if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) { 1350 if (ret == LOCKDOWN_E_SUCCESS) {
1384 plist_t port_value_node = plist_dict_get_item(dict, "Port"); 1351 if (*service == NULL)
1385 1352 *service = (lockdownd_service_descriptor_t)malloc(sizeof(struct lockdownd_service_descriptor));
1386 if (port_value_node && (plist_get_node_type(port_value_node) == PLIST_UINT)) { 1353 (*service)->port = 0;
1354 (*service)->ssl_enabled = 0;
1355 (*service)->identifier = strdup(identifier);
1356
1357 /* read service port number */
1358 plist_t node = plist_dict_get_item(dict, "Port");
1359 if (node && (plist_get_node_type(node) == PLIST_UINT)) {
1387 uint64_t port_value = 0; 1360 uint64_t port_value = 0;
1388 plist_get_uint_val(port_value_node, &port_value); 1361 plist_get_uint_val(node, &port_value);
1389 1362
1390 if (port_value) { 1363 if (port_value) {
1391 port_loc = port_value; 1364 port_loc = port_value;
1392 ret = LOCKDOWN_E_SUCCESS; 1365 ret = LOCKDOWN_E_SUCCESS;
1393 } 1366 }
1394 if (port && ret == LOCKDOWN_E_SUCCESS) 1367 if (port_loc && ret == LOCKDOWN_E_SUCCESS) {
1395 *port = port_loc; 1368 (*service)->port = port_loc;
1369 }
1370 }
1371
1372 /* check if the service requires SSL */
1373 node = plist_dict_get_item(dict, "EnableServiceSSL");
1374 if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) {
1375 uint8_t b = 0;
1376 plist_get_bool_val(node, &b);
1377 (*service)->ssl_enabled = b;
1396 } 1378 }
1397 } else { 1379 } else {
1398 ret = LOCKDOWN_E_START_SERVICE_FAILED;
1399 plist_t error_node = plist_dict_get_item(dict, "Error"); 1380 plist_t error_node = plist_dict_get_item(dict, "Error");
1400 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { 1381 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1401 char *error = NULL; 1382 char *error = NULL;
1402 plist_get_string_val(error_node, &error); 1383 plist_get_string_val(error_node, &error);
1403 if (!strcmp(error, "InvalidService")) { 1384 ret = lockdownd_strtoerr(error);
1404 ret = LOCKDOWN_E_INVALID_SERVICE;
1405 }
1406 free(error); 1385 free(error);
1407 } 1386 }
1408 } 1387 }
1409 1388
1410 plist_free(dict); 1389 plist_free(dict);
1411 dict = NULL; 1390 dict = NULL;
1391
1412 return ret; 1392 return ret;
1413} 1393}
1414 1394
1415/** 1395lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service)
1416 * Activates the device. Only works within an open session. 1396{
1417 * The ActivationRecord plist dictionary must be obtained using the 1397 return lockdownd_do_start_service(client, identifier, 0, service);
1418 * activation protocol requesting from Apple's https webservice. 1398}
1419 * 1399
1420 * @see http://iphone-docs.org/doku.php?id=docs:protocols:activation 1400lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service)
1421 * 1401{
1422 * @param client The lockdown client 1402 return lockdownd_do_start_service(client, identifier, 1, service);
1423 * @param activation_record The activation record plist dictionary 1403}
1424 * 1404
1425 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or 1405lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record)
1426 * activation_record is NULL, LOCKDOWN_E_NO_RUNNING_SESSION if no session is
1427 * open, LOCKDOWN_E_PLIST_ERROR if the received plist is broken,
1428 * LOCKDOWN_E_ACTIVATION_FAILED if the activation failed,
1429 * LOCKDOWN_E_INVALID_ACTIVATION_RECORD if the device reports that the
1430 * activation_record is invalid
1431 */
1432lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record)
1433{ 1406{
1434 if (!client) 1407 if (!client)
1435 return LOCKDOWN_E_INVALID_ARG; 1408 return LOCKDOWN_E_INVALID_ARG;
@@ -1444,8 +1417,8 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati
1444 1417
1445 plist_t dict = plist_new_dict(); 1418 plist_t dict = plist_new_dict();
1446 plist_dict_add_label(dict, client->label); 1419 plist_dict_add_label(dict, client->label);
1447 plist_dict_insert_item(dict,"Request", plist_new_string("Activate")); 1420 plist_dict_set_item(dict,"Request", plist_new_string("Activate"));
1448 plist_dict_insert_item(dict,"ActivationRecord", plist_copy(activation_record)); 1421 plist_dict_set_item(dict,"ActivationRecord", plist_copy(activation_record));
1449 1422
1450 ret = lockdownd_send(client, dict); 1423 ret = lockdownd_send(client, dict);
1451 plist_free(dict); 1424 plist_free(dict);
@@ -1457,39 +1430,17 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati
1457 return LOCKDOWN_E_PLIST_ERROR; 1430 return LOCKDOWN_E_PLIST_ERROR;
1458 } 1431 }
1459 1432
1460 ret = LOCKDOWN_E_ACTIVATION_FAILED; 1433 ret = lockdown_check_result(dict, "Activate");
1461 if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) { 1434 if (ret == LOCKDOWN_E_SUCCESS) {
1462 debug_info("success"); 1435 debug_info("success");
1463 ret = LOCKDOWN_E_SUCCESS;
1464
1465 } else {
1466 plist_t error_node = plist_dict_get_item(dict, "Error");
1467 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1468 char *error = NULL;
1469 plist_get_string_val(error_node, &error);
1470 if (!strcmp(error, "InvalidActivationRecord")) {
1471 ret = LOCKDOWN_E_INVALID_ACTIVATION_RECORD;
1472 }
1473 free(error);
1474 }
1475 } 1436 }
1476 1437
1477 plist_free(dict); 1438 plist_free(dict);
1478 dict = NULL; 1439 dict = NULL;
1479 1440
1480 return ret; 1441 return ret;
1481} 1442}
1482 1443
1483/**
1484 * Deactivates the device, returning it to the locked “Activate with iTunes”
1485 * screen.
1486 *
1487 * @param client The lockdown client
1488 *
1489 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
1490 * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open,
1491 * LOCKDOWN_E_PLIST_ERROR if the received plist is broken
1492 */
1493lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) 1444lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1494{ 1445{
1495 if (!client) 1446 if (!client)
@@ -1502,7 +1453,7 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1502 1453
1503 plist_t dict = plist_new_dict(); 1454 plist_t dict = plist_new_dict();
1504 plist_dict_add_label(dict, client->label); 1455 plist_dict_add_label(dict, client->label);
1505 plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate")); 1456 plist_dict_set_item(dict,"Request", plist_new_string("Deactivate"));
1506 1457
1507 ret = lockdownd_send(client, dict); 1458 ret = lockdownd_send(client, dict);
1508 plist_free(dict); 1459 plist_free(dict);
@@ -1514,11 +1465,11 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1514 return LOCKDOWN_E_PLIST_ERROR; 1465 return LOCKDOWN_E_PLIST_ERROR;
1515 } 1466 }
1516 1467
1517 ret = LOCKDOWN_E_UNKNOWN_ERROR; 1468 ret = lockdown_check_result(dict, "Deactivate");
1518 if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) { 1469 if (ret == LOCKDOWN_E_SUCCESS) {
1519 debug_info("success"); 1470 debug_info("success");
1520 ret = LOCKDOWN_E_SUCCESS;
1521 } 1471 }
1472
1522 plist_free(dict); 1473 plist_free(dict);
1523 dict = NULL; 1474 dict = NULL;
1524 1475
@@ -1537,19 +1488,6 @@ static void str_remove_spaces(char *source)
1537 *dest = 0; 1488 *dest = 0;
1538} 1489}
1539 1490
1540/**
1541 * Calculates and returns the data classes the device supports from lockdownd.
1542 *
1543 * @param client An initialized lockdownd client.
1544 * @param classes A pointer to store an array of class names. The caller is responsible
1545 * for freeing the memory which can be done using mobilesync_data_classes_free().
1546 * @param count The number of items in the classes array.
1547 *
1548 * @return LOCKDOWN_E_SUCCESS on success,
1549 * LOCKDOWN_E_INVALID_ARG when client is NULL,
1550 * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open,
1551 * LOCKDOWN_E_PLIST_ERROR if the received plist is broken
1552 */
1553lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) 1491lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count)
1554{ 1492{
1555 if (!client) 1493 if (!client)
@@ -1583,14 +1521,16 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha
1583 } 1521 }
1584 1522
1585 while((value = plist_array_get_item(dict, *count)) != NULL) { 1523 while((value = plist_array_get_item(dict, *count)) != NULL) {
1586 plist_get_string_val(value, &val); 1524 plist_get_string_val(value, &val);
1587 newlist = realloc(*classes, sizeof(char*) * (*count+1)); 1525 newlist = realloc(*classes, sizeof(char*) * (*count+1));
1588 str_remove_spaces(val); 1526 str_remove_spaces(val);
1589 asprintf(&newlist[*count], "com.apple.%s", val); 1527 if (asprintf(&newlist[*count], "com.apple.%s", val) < 0) {
1590 free(val); 1528 debug_info("ERROR: asprintf failed");
1591 val = NULL; 1529 }
1592 *classes = newlist; 1530 free(val);
1593 *count = *count+1; 1531 val = NULL;
1532 *classes = newlist;
1533 *count = *count+1;
1594 } 1534 }
1595 1535
1596 newlist = realloc(*classes, sizeof(char*) * (*count+1)); 1536 newlist = realloc(*classes, sizeof(char*) * (*count+1));
@@ -1603,14 +1543,6 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha
1603 return LOCKDOWN_E_SUCCESS; 1543 return LOCKDOWN_E_SUCCESS;
1604} 1544}
1605 1545
1606
1607/**
1608 * Frees memory of an allocated array of data classes as returned by lockdownd_get_sync_data_classes()
1609 *
1610 * @param classes An array of class names to free.
1611 *
1612 * @return LOCKDOWN_E_SUCCESS on success
1613 */
1614lockdownd_error_t lockdownd_data_classes_free(char **classes) 1546lockdownd_error_t lockdownd_data_classes_free(char **classes)
1615{ 1547{
1616 if (classes) { 1548 if (classes) {
@@ -1622,3 +1554,51 @@ lockdownd_error_t lockdownd_data_classes_free(char **classes)
1622 } 1554 }
1623 return LOCKDOWN_E_SUCCESS; 1555 return LOCKDOWN_E_SUCCESS;
1624} 1556}
1557
1558lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service)
1559{
1560 if (service) {
1561 free(service->identifier);
1562 free(service);
1563 }
1564
1565 return LOCKDOWN_E_SUCCESS;
1566}
1567
1568const char* lockdownd_strerror(lockdownd_error_t err)
1569{
1570 switch (err) {
1571 case LOCKDOWN_E_SUCCESS:
1572 return "Success";
1573 case LOCKDOWN_E_INVALID_ARG:
1574 return "Invalid argument";
1575 case LOCKDOWN_E_INVALID_CONF:
1576 return "Invalid configuration";
1577 case LOCKDOWN_E_PLIST_ERROR:
1578 return "PropertyList error";
1579 case LOCKDOWN_E_PAIRING_FAILED:
1580 return "Pairing failed";
1581 case LOCKDOWN_E_SSL_ERROR:
1582 return "SSL error";
1583 case LOCKDOWN_E_DICT_ERROR:
1584 return "Invalid dictionary";
1585 case LOCKDOWN_E_RECEIVE_TIMEOUT:
1586 return "Receive timeout";
1587 case LOCKDOWN_E_MUX_ERROR:
1588 return "Mux error";
1589 case LOCKDOWN_E_NO_RUNNING_SESSION:
1590 return "No running session";
1591 case LOCKDOWN_E_UNKNOWN_ERROR:
1592 return "Unknown Error";
1593 default: {
1594 int i = 0;
1595 while (lockdownd_error_str_map[i].lockdown_errstr) {
1596 if (lockdownd_error_str_map[i].errcode == err) {
1597 return lockdownd_error_str_map[i].errstr;
1598 }
1599 i++;
1600 }
1601 } break;
1602 }
1603 return "Unknown Error";
1604}
diff --git a/src/lockdown.h b/src/lockdown.h
index a25e59d..ba291ec 100644
--- a/src/lockdown.h
+++ b/src/lockdown.h
@@ -1,7 +1,8 @@
1/* 1/*
2 * lockdownd.h 2 * lockdown.h
3 * Defines lockdown stuff, like the client struct. 3 * Defines lockdown stuff, like the client struct.
4 * 4 *
5 * Copyright (c) 2014 Martin Szulecki All Rights Reserved.
5 * Copyright (c) 2008 Zach C. All Rights Reserved. 6 * Copyright (c) 2008 Zach C. All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
@@ -19,24 +20,25 @@
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21 22
22#ifndef LOCKDOWND_H 23#ifndef __LOCKDOWND_H
23#define LOCKDOWND_H 24#define __LOCKDOWND_H
24
25#include <gnutls/gnutls.h>
26 25
26#include "idevice.h"
27#include "libimobiledevice/lockdown.h" 27#include "libimobiledevice/lockdown.h"
28#include "property_list_service.h" 28#include "property_list_service.h"
29 29
30#define LOCKDOWN_PROTOCOL_VERSION "2"
31
30struct lockdownd_client_private { 32struct lockdownd_client_private {
31 property_list_service_client_t parent; 33 property_list_service_client_t parent;
32 int ssl_enabled; 34 int ssl_enabled;
33 char *session_id; 35 char *session_id;
34 char *uuid;
35 char *label; 36 char *label;
37 idevice_t device;
38 unsigned char* cu_key;
39 unsigned int cu_key_len;
36}; 40};
37 41
38lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key); 42lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match);
39lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * device_cert,
40 gnutls_datum_t * host_cert, gnutls_datum_t * root_cert);
41 43
42#endif 44#endif
diff --git a/src/misagent.c b/src/misagent.c
new file mode 100644
index 0000000..e3da997
--- /dev/null
+++ b/src/misagent.c
@@ -0,0 +1,290 @@
1/*
2 * misagent.c
3 * com.apple.misagent service implementation.
4 *
5 * Copyright (c) 2012 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <plist/plist.h>
29#include <stdio.h>
30
31#include "misagent.h"
32#include "property_list_service.h"
33#include "common/debug.h"
34
35/**
36 * Convert a property_list_service_error_t value to a misagent_error_t
37 * value. Used internally to get correct error codes.
38 *
39 * @param err A property_list_service_error_t error code
40 *
41 * @return A matching misagent_error_t error code,
42 * MISAGENT_E_UNKNOWN_ERROR otherwise.
43 */
44static misagent_error_t misagent_error(property_list_service_error_t err)
45{
46 switch (err) {
47 case PROPERTY_LIST_SERVICE_E_SUCCESS:
48 return MISAGENT_E_SUCCESS;
49 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
50 return MISAGENT_E_INVALID_ARG;
51 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
52 return MISAGENT_E_PLIST_ERROR;
53 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
54 return MISAGENT_E_CONN_FAILED;
55 default:
56 break;
57 }
58 return MISAGENT_E_UNKNOWN_ERROR;
59}
60
61/**
62 * Checks the response from misagent to determine if the operation
63 * was successful or an error occurred. Internally used only.
64 *
65 * @param response a PLIST_DICT received from device's misagent
66 * @param status_code pointer to an int that will be set to the status code
67 * contained in the response
68 */
69static misagent_error_t misagent_check_result(plist_t response, int* status_code)
70{
71 if (plist_get_node_type(response) != PLIST_DICT) {
72 return MISAGENT_E_PLIST_ERROR;
73 }
74
75 plist_t node = plist_dict_get_item(response, "Status");
76 if (!node || (plist_get_node_type(node) != PLIST_UINT)) {
77 return MISAGENT_E_PLIST_ERROR;
78 }
79
80 uint64_t val = -1LL;
81 plist_get_uint_val(node, &val);
82 if ((int64_t)val == -1LL) {
83 return MISAGENT_E_PLIST_ERROR;
84 }
85 *status_code = (int)(val & 0xFFFFFFFF);
86 if (*status_code == 0) {
87 return MISAGENT_E_SUCCESS;
88 }
89 return MISAGENT_E_REQUEST_FAILED;
90}
91
92misagent_error_t misagent_client_new(idevice_t device, lockdownd_service_descriptor_t service, misagent_client_t *client)
93{
94 property_list_service_client_t plistclient = NULL;
95 misagent_error_t err = misagent_error(property_list_service_client_new(device, service, &plistclient));
96 if (err != MISAGENT_E_SUCCESS) {
97 return err;
98 }
99
100 misagent_client_t client_loc = (misagent_client_t) malloc(sizeof(struct misagent_client_private));
101 client_loc->parent = plistclient;
102 client_loc->last_error = 0;
103
104 *client = client_loc;
105 return MISAGENT_E_SUCCESS;
106}
107
108misagent_error_t misagent_client_start_service(idevice_t device, misagent_client_t * client, const char* label)
109{
110 misagent_error_t err = MISAGENT_E_UNKNOWN_ERROR;
111 service_client_factory_start_service(device, MISAGENT_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(misagent_client_new), &err);
112 return err;
113}
114
115misagent_error_t misagent_client_free(misagent_client_t client)
116{
117 if (!client)
118 return MISAGENT_E_INVALID_ARG;
119
120 misagent_error_t err = MISAGENT_E_SUCCESS;
121 if (client->parent && client->parent->parent) {
122 misagent_error(property_list_service_client_free(client->parent));
123 }
124 client->parent = NULL;
125 free(client);
126
127 return err;
128}
129
130misagent_error_t misagent_install(misagent_client_t client, plist_t profile)
131{
132 if (!client || !client->parent || !profile || (plist_get_node_type(profile) != PLIST_DATA))
133 return MISAGENT_E_INVALID_ARG;
134
135 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
136
137 plist_t dict = plist_new_dict();
138 plist_dict_set_item(dict, "MessageType", plist_new_string("Install"));
139 plist_dict_set_item(dict, "Profile", plist_copy(profile));
140 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
141
142 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
143 plist_free(dict);
144 dict = NULL;
145
146 if (res != MISAGENT_E_SUCCESS) {
147 debug_info("could not send plist, error %d", res);
148 return res;
149 }
150
151 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
152 if (res != MISAGENT_E_SUCCESS) {
153 debug_info("could not receive response, error %d", res);
154 return res;
155 }
156 if (!dict) {
157 debug_info("could not get response plist");
158 return MISAGENT_E_UNKNOWN_ERROR;
159 }
160
161 res = misagent_check_result(dict, &client->last_error);
162 plist_free(dict);
163
164 return res;
165}
166
167misagent_error_t misagent_copy(misagent_client_t client, plist_t* profiles)
168{
169 if (!client || !client->parent || !profiles)
170 return MISAGENT_E_INVALID_ARG;
171
172 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
173
174 plist_t dict = plist_new_dict();
175 plist_dict_set_item(dict, "MessageType", plist_new_string("Copy"));
176 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
177
178 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
179 plist_free(dict);
180 dict = NULL;
181
182 if (res != MISAGENT_E_SUCCESS) {
183 debug_info("could not send plist, error %d", res);
184 return res;
185 }
186
187 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
188 if (res != MISAGENT_E_SUCCESS) {
189 debug_info("could not receive response, error %d", res);
190 return res;
191 }
192 if (!dict) {
193 debug_info("could not get response plist");
194 return MISAGENT_E_UNKNOWN_ERROR;
195 }
196
197 res = misagent_check_result(dict, &client->last_error);
198 if (res == MISAGENT_E_SUCCESS) {
199 *profiles = plist_copy(plist_dict_get_item(dict, "Payload"));
200 }
201 plist_free(dict);
202
203 return res;
204
205}
206
207misagent_error_t misagent_copy_all(misagent_client_t client, plist_t* profiles)
208{
209 if (!client || !client->parent || !profiles)
210 return MISAGENT_E_INVALID_ARG;
211
212 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
213
214 plist_t dict = plist_new_dict();
215 plist_dict_set_item(dict, "MessageType", plist_new_string("CopyAll"));
216 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
217
218 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
219 plist_free(dict);
220 dict = NULL;
221
222 if (res != MISAGENT_E_SUCCESS) {
223 debug_info("could not send plist, error %d", res);
224 return res;
225 }
226
227 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
228 if (res != MISAGENT_E_SUCCESS) {
229 debug_info("could not receive response, error %d", res);
230 return res;
231 }
232 if (!dict) {
233 debug_info("could not get response plist");
234 return MISAGENT_E_UNKNOWN_ERROR;
235 }
236
237 res = misagent_check_result(dict, &client->last_error);
238 if (res == MISAGENT_E_SUCCESS) {
239 *profiles = plist_copy(plist_dict_get_item(dict, "Payload"));
240 }
241 plist_free(dict);
242
243 return res;
244
245}
246
247misagent_error_t misagent_remove(misagent_client_t client, const char* profileID)
248{
249 if (!client || !client->parent || !profileID)
250 return MISAGENT_E_INVALID_ARG;
251
252 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
253
254 plist_t dict = plist_new_dict();
255 plist_dict_set_item(dict, "MessageType", plist_new_string("Remove"));
256 plist_dict_set_item(dict, "ProfileID", plist_new_string(profileID));
257 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
258
259 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
260 plist_free(dict);
261 dict = NULL;
262
263 if (res != MISAGENT_E_SUCCESS) {
264 debug_info("could not send plist, error %d", res);
265 return res;
266 }
267
268 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
269 if (res != MISAGENT_E_SUCCESS) {
270 debug_info("could not receive response, error %d", res);
271 return res;
272 }
273 if (!dict) {
274 debug_info("could not get response plist");
275 return MISAGENT_E_UNKNOWN_ERROR;
276 }
277
278 res = misagent_check_result(dict, &client->last_error);
279 plist_free(dict);
280
281 return res;
282}
283
284int misagent_get_status_code(misagent_client_t client)
285{
286 if (!client) {
287 return -1;
288 }
289 return client->last_error;
290}
diff --git a/src/misagent.h b/src/misagent.h
new file mode 100644
index 0000000..e394087
--- /dev/null
+++ b/src/misagent.h
@@ -0,0 +1,34 @@
1/*
2 * misagent.h
3 * com.apple.misagent service header file.
4 *
5 * Copyright (c) 2012 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __MISAGENT_H
23#define __MISAGENT_H
24
25#include "idevice.h"
26#include "libimobiledevice/misagent.h"
27#include "property_list_service.h"
28
29struct misagent_client_private {
30 property_list_service_client_t parent;
31 int last_error;
32};
33
34#endif
diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c
index 367bee0..6df50c4 100644
--- a/src/mobile_image_mounter.c
+++ b/src/mobile_image_mounter.c
@@ -2,23 +2,26 @@
2 * mobile_image_mounter.c 2 * mobile_image_mounter.c
3 * com.apple.mobile.mobile_image_mounter service implementation. 3 * com.apple.mobile.mobile_image_mounter service implementation.
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
22#include <string.h> 25#include <string.h>
23#include <stdlib.h> 26#include <stdlib.h>
24#include <unistd.h> 27#include <unistd.h>
@@ -26,7 +29,7 @@
26 29
27#include "mobile_image_mounter.h" 30#include "mobile_image_mounter.h"
28#include "property_list_service.h" 31#include "property_list_service.h"
29#include "debug.h" 32#include "common/debug.h"
30 33
31/** 34/**
32 * Locks a mobile_image_mounter client, used for thread safety. 35 * Locks a mobile_image_mounter client, used for thread safety.
@@ -35,17 +38,17 @@
35 */ 38 */
36static void mobile_image_mounter_lock(mobile_image_mounter_client_t client) 39static void mobile_image_mounter_lock(mobile_image_mounter_client_t client)
37{ 40{
38 g_mutex_lock(client->mutex); 41 mutex_lock(&client->mutex);
39} 42}
40 43
41/** 44/**
42 * Unlocks a mobile_image_mounter client, used for thread safety. 45 * Unlocks a mobile_image_mounter client, used for thread safety.
43 * 46 *
44 * @param client mobile_image_mounter client to unlock 47 * @param client mobile_image_mounter client to unlock
45 */ 48 */
46static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client) 49static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client)
47{ 50{
48 g_mutex_unlock(client->mutex); 51 mutex_unlock(&client->mutex);
49} 52}
50 53
51/** 54/**
@@ -75,51 +78,30 @@ static mobile_image_mounter_error_t mobile_image_mounter_error(property_list_ser
75 return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; 78 return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
76} 79}
77 80
78/** 81mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, lockdownd_service_descriptor_t service, mobile_image_mounter_client_t *client)
79 * Connects to the mobile_image_mounter service on the specified device.
80 *
81 * @param device The device to connect to.
82 * @param port Destination port (usually given by lockdownd_start_service).
83 * @param client Pointer that will be set to a newly allocated
84 * mobile_image_mounter_client_t upon successful return.
85 *
86 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success,
87 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if device is NULL,
88 * or MOBILE_IMAGE_MOUNTER_E_CONN_FAILED if the connection to the
89 * device could not be established.
90 */
91mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, uint16_t port, mobile_image_mounter_client_t *client)
92{ 82{
93 /* makes sure thread environment is available */
94 if (!g_thread_supported())
95 g_thread_init(NULL);
96
97 if (!device)
98 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
99
100 property_list_service_client_t plistclient = NULL; 83 property_list_service_client_t plistclient = NULL;
101 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 84 mobile_image_mounter_error_t err = mobile_image_mounter_error(property_list_service_client_new(device, service, &plistclient));
102 return MOBILE_IMAGE_MOUNTER_E_CONN_FAILED; 85 if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
86 return err;
103 } 87 }
104 88
105 mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_private)); 89 mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_private));
106 client_loc->parent = plistclient; 90 client_loc->parent = plistclient;
107 91
108 client_loc->mutex = g_mutex_new(); 92 mutex_init(&client_loc->mutex);
109 93
110 *client = client_loc; 94 *client = client_loc;
111 return MOBILE_IMAGE_MOUNTER_E_SUCCESS; 95 return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
112} 96}
113 97
114/** 98mobile_image_mounter_error_t mobile_image_mounter_start_service(idevice_t device, mobile_image_mounter_client_t * client, const char* label)
115 * Disconnects a mobile_image_mounter client from the device and frees up the 99{
116 * mobile_image_mounter client data. 100 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
117 * 101 service_client_factory_start_service(device, MOBILE_IMAGE_MOUNTER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobile_image_mounter_new), &err);
118 * @param client The mobile_image_mounter client to disconnect and free. 102 return err;
119 * 103}
120 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, 104
121 * or MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is NULL.
122 */
123mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) 105mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client)
124{ 106{
125 if (!client) 107 if (!client)
@@ -127,27 +109,12 @@ mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_clie
127 109
128 property_list_service_client_free(client->parent); 110 property_list_service_client_free(client->parent);
129 client->parent = NULL; 111 client->parent = NULL;
130 if (client->mutex) { 112 mutex_destroy(&client->mutex);
131 g_mutex_free(client->mutex);
132 }
133 free(client); 113 free(client);
134 114
135 return MOBILE_IMAGE_MOUNTER_E_SUCCESS; 115 return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
136} 116}
137 117
138/**
139 * Tells if the image of ImageType is already mounted.
140 *
141 * @param client The client use
142 * @param image_type The type of the image to look up
143 * @param result Pointer to a plist that will receive the result of the
144 * operation.
145 *
146 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the
147 * operation has failed. Check the resulting plist for further information.
148 *
149 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, or an error code on error
150 */
151mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result) 118mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result)
152{ 119{
153 if (!client || !image_type || !result) { 120 if (!client || !image_type || !result) {
@@ -156,8 +123,8 @@ mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_moun
156 mobile_image_mounter_lock(client); 123 mobile_image_mounter_lock(client);
157 124
158 plist_t dict = plist_new_dict(); 125 plist_t dict = plist_new_dict();
159 plist_dict_insert_item(dict,"Command", plist_new_string("LookupImage")); 126 plist_dict_set_item(dict,"Command", plist_new_string("LookupImage"));
160 plist_dict_insert_item(dict,"ImageType", plist_new_string(image_type)); 127 plist_dict_set_item(dict,"ImageType", plist_new_string(image_type));
161 128
162 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); 129 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
163 plist_free(dict); 130 plist_free(dict);
@@ -177,39 +144,139 @@ leave_unlock:
177 return res; 144 return res;
178} 145}
179 146
180/** 147static mobile_image_mounter_error_t process_result(plist_t result, const char *expected_status)
181 * Mounts an image on the device. 148{
182 * 149 mobile_image_mounter_error_t res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
183 * @param client The connected mobile_image_mounter client. 150 char* strval = NULL;
184 * @param image_path The absolute path of the image to mount. The image must 151 plist_t node;
185 * be present before calling this function. 152
186 * @param image_signature Pointer to a buffer holding the images' signature 153 node = plist_dict_get_item(result, "Error");
187 * @param signature_length Length of the signature image_signature points to 154 if (node && plist_get_node_type(node) == PLIST_STRING) {
188 * @param image_type Type of image to mount 155 plist_get_string_val(node, &strval);
189 * @param result Pointer to a plist that will receive the result of the 156 }
190 * operation. 157 if (strval) {
191 * 158 if (!strcmp(strval, "DeviceLocked")) {
192 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the 159 debug_info("Device is locked, can't mount");
193 * operation has failed. Check the resulting plist for further information. 160 res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED;
194 * Note that there is no unmounting function. The mount persists until the 161 } else {
195 * device is rebooted. 162 debug_info("Unhandled error '%s' received", strval);
196 * 163 }
197 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, 164 free(strval);
198 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if on ore more parameters are 165 return res;
199 * invalid, or another error code otherwise. 166 }
200 */ 167
201mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *image_signature, uint16_t signature_length, const char *image_type, plist_t *result) 168 node = plist_dict_get_item(result, "Status");
169 if (node && plist_get_node_type(node) == PLIST_STRING) {
170 plist_get_string_val(node, &strval);
171 }
172 if (!strval) {
173 debug_info("Error: Unexpected response received!");
174 } else if (strcmp(strval, expected_status) == 0) {
175 res = MOBILE_IMAGE_MOUNTER_E_SUCCESS;
176 } else {
177 debug_info("Error: didn't get %s but %s", expected_status, strval);
178 }
179 free(strval);
180
181 return res;
182}
183
184mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const unsigned char *signature, unsigned int signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata)
185{
186 if (!client || !image_type || (image_size == 0) || !upload_cb) {
187 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
188 }
189 mobile_image_mounter_lock(client);
190 plist_t result = NULL;
191
192 plist_t dict = plist_new_dict();
193 plist_dict_set_item(dict, "Command", plist_new_string("ReceiveBytes"));
194 if (signature && signature_size != 0)
195 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
196 plist_dict_set_item(dict, "ImageSize", plist_new_uint(image_size));
197 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
198
199 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
200 plist_free(dict);
201
202 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
203 debug_info("Error sending XML plist to device!");
204 goto leave_unlock;
205 }
206
207 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
208 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
209 debug_info("Error receiving response from device!");
210 goto leave_unlock;
211 }
212 res = process_result(result, "ReceiveBytesAck");
213 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
214 goto leave_unlock;
215 }
216
217 size_t tx = 0;
218 size_t bufsize = 65536;
219 unsigned char *buf = (unsigned char*)malloc(bufsize);
220 if (!buf) {
221 debug_info("Out of memory");
222 res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
223 goto leave_unlock;
224 }
225 debug_info("uploading image (%d bytes)", (int)image_size);
226 while (tx < image_size) {
227 size_t remaining = image_size - tx;
228 size_t amount = (remaining < bufsize) ? remaining : bufsize;
229 ssize_t r = upload_cb(buf, amount, userdata);
230 if (r < 0) {
231 debug_info("upload_cb returned %d", (int)r);
232 break;
233 }
234 uint32_t sent = 0;
235 if (service_send(client->parent->parent, (const char*)buf, (uint32_t)r, &sent) != SERVICE_E_SUCCESS) {
236 debug_info("service_send failed");
237 break;
238 }
239 tx += r;
240 }
241 free(buf);
242 if (tx < image_size) {
243 debug_info("Error: failed to upload image");
244 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
245 goto leave_unlock;
246 }
247 debug_info("image uploaded");
248
249 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
250 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
251 debug_info("Error receiving response from device!");
252 goto leave_unlock;
253 }
254 res = process_result(result, "Complete");
255
256leave_unlock:
257 mobile_image_mounter_unlock(client);
258 if (result)
259 plist_free(result);
260 return res;
261
262}
263
264mobile_image_mounter_error_t mobile_image_mounter_mount_image_with_options(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t options, plist_t *result)
202{ 265{
203 if (!client || !image_path || !image_signature || (signature_length == 0) || !image_type || !result) { 266 if (!client || !image_path || !image_type || !result) {
204 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; 267 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
205 } 268 }
206 mobile_image_mounter_lock(client); 269 mobile_image_mounter_lock(client);
207 270
208 plist_t dict = plist_new_dict(); 271 plist_t dict = plist_new_dict();
209 plist_dict_insert_item(dict, "Command", plist_new_string("MountImage")); 272 plist_dict_set_item(dict, "Command", plist_new_string("MountImage"));
210 plist_dict_insert_item(dict, "ImagePath", plist_new_string(image_path)); 273 plist_dict_set_item(dict, "ImagePath", plist_new_string(image_path));
211 plist_dict_insert_item(dict, "ImageSignature", plist_new_data(image_signature, signature_length)); 274 if (signature && signature_size != 0)
212 plist_dict_insert_item(dict, "ImageType", plist_new_string(image_type)); 275 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
276 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
277 if (PLIST_IS_DICT(options)) {
278 plist_dict_merge(&dict, options);
279 }
213 280
214 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); 281 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
215 plist_free(dict); 282 plist_free(dict);
@@ -229,17 +296,56 @@ leave_unlock:
229 return res; 296 return res;
230} 297}
231 298
232/** 299mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t *result)
233 * Hangs up the connection to the mobile_image_mounter service. 300{
234 * This functions has to be called before freeing up a mobile_image_mounter 301 return mobile_image_mounter_mount_image_with_options(client, image_path, signature, signature_size, image_type, NULL, result);
235 * instance. If not, errors appear in the device's syslog. 302}
236 * 303
237 * @param client The client to hang up 304mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path)
238 * 305{
239 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, 306 if (!client || !mount_path) {
240 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is invalid, 307 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
241 * or another error code otherwise. 308 }
242 */ 309 mobile_image_mounter_lock(client);
310
311 plist_t dict = plist_new_dict();
312 plist_dict_set_item(dict, "Command", plist_new_string("UnmountImage"));
313 plist_dict_set_item(dict, "MountPath", plist_new_string(mount_path));
314 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
315 plist_free(dict);
316
317 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
318 debug_info("%s: Error sending XML plist to device!", __func__);
319 goto leave_unlock;
320 }
321
322 plist_t result = NULL;
323 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
324 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
325 debug_info("%s: Error receiving response from device!", __func__);
326 } else {
327 plist_t p_error = plist_dict_get_item(result, "Error");
328 if (p_error) {
329 plist_t p_detailed = plist_dict_get_item(result, "DetailedError");
330 const char* detailederr = (p_detailed) ? plist_get_string_ptr(p_detailed, NULL) : "";
331 const char* errstr = plist_get_string_ptr(p_error, NULL);
332 if (errstr && !strcmp(errstr, "UnknownCommand")) {
333 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
334 } else if (errstr && !strcmp(errstr, "DeviceLocked")) {
335 res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED;
336 } else if (strstr(detailederr, "no matching entry")) {
337 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
338 } else {
339 res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
340 }
341 }
342 }
343
344leave_unlock:
345 mobile_image_mounter_unlock(client);
346 return res;
347}
348
243mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) 349mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)
244{ 350{
245 if (!client) { 351 if (!client) {
@@ -248,7 +354,7 @@ mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_cl
248 mobile_image_mounter_lock(client); 354 mobile_image_mounter_lock(client);
249 355
250 plist_t dict = plist_new_dict(); 356 plist_t dict = plist_new_dict();
251 plist_dict_insert_item(dict, "Command", plist_new_string("Hangup")); 357 plist_dict_set_item(dict, "Command", plist_new_string("Hangup"));
252 358
253 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); 359 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
254 plist_free(dict); 360 plist_free(dict);
@@ -272,3 +378,215 @@ leave_unlock:
272 mobile_image_mounter_unlock(client); 378 mobile_image_mounter_unlock(client);
273 return res; 379 return res;
274} 380}
381
382mobile_image_mounter_error_t mobile_image_mounter_query_developer_mode_status(mobile_image_mounter_client_t client, plist_t *result)
383{
384 if (!client || !result) {
385 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
386 }
387 mobile_image_mounter_lock(client);
388
389 plist_t dict = plist_new_dict();
390 plist_dict_set_item(dict, "Command", plist_new_string("QueryDeveloperModeStatus"));
391 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
392 plist_free(dict);
393
394 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
395 debug_info("%s: Error sending XML plist to device!", __func__);
396 goto leave_unlock;
397 }
398
399 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result));
400 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
401 debug_info("%s: Error receiving response from device!", __func__);
402 }
403
404leave_unlock:
405 mobile_image_mounter_unlock(client);
406 return res;
407}
408
409mobile_image_mounter_error_t mobile_image_mounter_query_nonce(mobile_image_mounter_client_t client, const char* image_type, unsigned char** nonce, unsigned int* nonce_size)
410{
411 if (!client || !nonce || !nonce_size) {
412 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
413 }
414 mobile_image_mounter_lock(client);
415
416 plist_t dict = plist_new_dict();
417 plist_dict_set_item(dict, "Command", plist_new_string("QueryNonce"));
418 if (image_type) {
419 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
420 }
421 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
422 plist_free(dict);
423
424 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
425 debug_info("%s: Error sending XML plist to device!", __func__);
426 goto leave_unlock;
427 }
428
429 plist_t result = NULL;
430 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
431 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
432 debug_info("%s: Error receiving response from device!", __func__);
433 } else {
434 plist_t p_nonce = plist_dict_get_item(result, "PersonalizationNonce");
435 if (!p_nonce) {
436 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
437 } else {
438 uint64_t nonce_size_ = 0;
439 plist_get_data_val(p_nonce, (char**)nonce, &nonce_size_);
440 if (*nonce) {
441 *nonce_size = (unsigned int)nonce_size_;
442 } else {
443 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
444 }
445 }
446 }
447 plist_free(result);
448
449leave_unlock:
450 mobile_image_mounter_unlock(client);
451 return res;
452}
453
454mobile_image_mounter_error_t mobile_image_mounter_query_personalization_identifiers(mobile_image_mounter_client_t client, const char* image_type, plist_t *result)
455{
456 if (!client || !result) {
457 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
458 }
459 mobile_image_mounter_lock(client);
460
461 plist_t dict = plist_new_dict();
462 plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationIdentifiers"));
463 if (image_type) {
464 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
465 }
466 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
467 plist_free(dict);
468
469 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
470 debug_info("%s: Error sending XML plist to device!", __func__);
471 goto leave_unlock;
472 }
473
474 plist_t _result = NULL;
475 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &_result));
476 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
477 debug_info("%s: Error receiving response from device!", __func__);
478 }
479 *result = plist_copy(plist_dict_get_item(_result, "PersonalizationIdentifiers"));
480 if (!*result) {
481 debug_info("%s: Response did not contain PersonalizationIdentifiers!", __func__);
482 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
483 }
484
485leave_unlock:
486 mobile_image_mounter_unlock(client);
487 return res;
488}
489
490mobile_image_mounter_error_t mobile_image_mounter_query_personalization_manifest(mobile_image_mounter_client_t client, const char* image_type, const unsigned char* signature, unsigned int signature_size, unsigned char** manifest, unsigned int* manifest_size)
491{
492 if (!client || !image_type || !signature || !signature_size || !manifest || !manifest_size) {
493 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
494 }
495 mobile_image_mounter_lock(client);
496
497 plist_t dict = plist_new_dict();
498 plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationManifest"));
499 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
500 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
501 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
502
503 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
504 plist_free(dict);
505
506 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
507 debug_info("%s: Error sending XML plist to device!", __func__);
508 goto leave_unlock;
509 }
510
511 plist_t result = NULL;
512 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
513 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
514 debug_info("%s: Error receiving response from device!", __func__);
515 } else {
516 plist_t p_manifest = plist_dict_get_item(result, "ImageSignature");
517 if (!p_manifest) {
518 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
519 } else {
520 uint64_t manifest_size_ = 0;
521 plist_get_data_val(p_manifest, (char**)manifest, &manifest_size_);
522 if (*manifest) {
523 *manifest_size = (unsigned int)manifest_size_;
524 } else {
525 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
526 }
527 }
528 }
529 plist_free(result);
530
531leave_unlock:
532 mobile_image_mounter_unlock(client);
533 return res;
534}
535
536mobile_image_mounter_error_t mobile_image_mounter_roll_personalization_nonce(mobile_image_mounter_client_t client)
537{
538 if (!client) {
539 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
540 }
541 mobile_image_mounter_lock(client);
542
543 plist_t dict = plist_new_dict();
544 plist_dict_set_item(dict, "Command", plist_new_string("RollPersonalizationNonce"));
545 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
546 plist_free(dict);
547
548 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
549 debug_info("%s: Error sending XML plist to device!", __func__);
550 goto leave_unlock;
551 }
552
553 plist_t result = NULL;
554 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
555 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
556 debug_info("%s: Error receiving response from device!", __func__);
557 }
558 plist_free(result);
559
560leave_unlock:
561 mobile_image_mounter_unlock(client);
562 return res;
563}
564
565mobile_image_mounter_error_t mobile_image_mounter_roll_cryptex_nonce(mobile_image_mounter_client_t client)
566{
567 if (!client) {
568 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
569 }
570 mobile_image_mounter_lock(client);
571
572 plist_t dict = plist_new_dict();
573 plist_dict_set_item(dict, "Command", plist_new_string("RollCryptexNonce"));
574 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
575 plist_free(dict);
576
577 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
578 debug_info("%s: Error sending XML plist to device!", __func__);
579 goto leave_unlock;
580 }
581
582 plist_t result = NULL;
583 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
584 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
585 debug_info("%s: Error receiving response from device!", __func__);
586 }
587 plist_free(result);
588
589leave_unlock:
590 mobile_image_mounter_unlock(client);
591 return res;
592}
diff --git a/src/mobile_image_mounter.h b/src/mobile_image_mounter.h
index 2615dbc..9a8fcdd 100644
--- a/src/mobile_image_mounter.h
+++ b/src/mobile_image_mounter.h
@@ -8,27 +8,28 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef IMOBILE_IMAGE_MOUNTER_H
22#define IMOBILE_IMAGE_MOUNTER_H
23 21
24#include <glib.h> 22#ifndef __MOBILE_IMAGE_MOUNTER_H
23#define __MOBILE_IMAGE_MOUNTER_H
25 24
25#include "idevice.h"
26#include "libimobiledevice/mobile_image_mounter.h" 26#include "libimobiledevice/mobile_image_mounter.h"
27#include "property_list_service.h" 27#include "property_list_service.h"
28#include <libimobiledevice-glue/thread.h>
28 29
29struct mobile_image_mounter_client_private { 30struct mobile_image_mounter_client_private {
30 property_list_service_client_t parent; 31 property_list_service_client_t parent;
31 GMutex *mutex; 32 mutex_t mutex;
32}; 33};
33 34
34#endif 35#endif
diff --git a/src/mobileactivation.c b/src/mobileactivation.c
new file mode 100644
index 0000000..fce5f16
--- /dev/null
+++ b/src/mobileactivation.c
@@ -0,0 +1,314 @@
1/*
2 * mobileactivation.c
3 * com.apple.mobileactivationd service implementation.
4 *
5 * Copyright (c) 2016-2017 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include "mobileactivation.h"
28#include "property_list_service.h"
29#include "common/debug.h"
30
31/**
32 * Convert a property_list_service_error_t value to a mobileactivation_error_t value.
33 * Used internally to get correct error codes.
34 *
35 * @param err An property_list_service_error_t error code
36 *
37 * @return A matching mobileactivation_error_t error code,
38 * MOBILEACTIVATION_E_UNKNOWN_ERROR otherwise.
39 */
40static mobileactivation_error_t mobileactivation_error(property_list_service_error_t err)
41{
42 switch (err) {
43 case PROPERTY_LIST_SERVICE_E_SUCCESS:
44 return MOBILEACTIVATION_E_SUCCESS;
45 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
46 return MOBILEACTIVATION_E_INVALID_ARG;
47 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
48 return MOBILEACTIVATION_E_PLIST_ERROR;
49 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
50 return MOBILEACTIVATION_E_MUX_ERROR;
51 default:
52 break;
53 }
54 return MOBILEACTIVATION_E_UNKNOWN_ERROR;
55}
56
57mobileactivation_error_t mobileactivation_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobileactivation_client_t *client)
58{
59 if (!device || !service || service->port == 0 || !client || *client) {
60 return MOBILEACTIVATION_E_INVALID_ARG;
61 }
62
63 property_list_service_client_t plistclient = NULL;
64 if (property_list_service_client_new(device, service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
65 return MOBILEACTIVATION_E_MUX_ERROR;
66 }
67
68 /* create client object */
69 mobileactivation_client_t client_loc = (mobileactivation_client_t) malloc(sizeof(struct mobileactivation_client_private));
70 client_loc->parent = plistclient;
71
72 /* all done, return success */
73 *client = client_loc;
74 return MOBILEACTIVATION_E_SUCCESS;
75}
76
77mobileactivation_error_t mobileactivation_client_start_service(idevice_t device, mobileactivation_client_t * client, const char* label)
78{
79 mobileactivation_error_t err = MOBILEACTIVATION_E_UNKNOWN_ERROR;
80 service_client_factory_start_service(device, MOBILEACTIVATION_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobileactivation_client_new), &err);
81 return err;
82}
83
84mobileactivation_error_t mobileactivation_client_free(mobileactivation_client_t client)
85{
86 if (!client)
87 return MOBILEACTIVATION_E_INVALID_ARG;
88
89 if (property_list_service_client_free(client->parent) != PROPERTY_LIST_SERVICE_E_SUCCESS) {
90 return MOBILEACTIVATION_E_UNKNOWN_ERROR;
91 }
92 free(client);
93 return MOBILEACTIVATION_E_SUCCESS;
94}
95
96static plist_t plist_data_from_plist(plist_t plist)
97{
98 if (plist && plist_get_node_type(plist) == PLIST_DATA) {
99 return plist_copy(plist);
100 }
101 plist_t result = NULL;
102 char *xml = NULL;
103 uint32_t xml_len = 0;
104 plist_to_xml(plist, &xml, &xml_len);
105 result = plist_new_data(xml, xml_len);
106 free(xml);
107 return result;
108}
109
110static mobileactivation_error_t mobileactivation_check_result(plist_t dict, const char *command)
111{
112 if (!dict || plist_get_node_type(dict) != PLIST_DICT) {
113 return MOBILEACTIVATION_E_PLIST_ERROR;
114 }
115
116 plist_t err_node = plist_dict_get_item(dict, "Error");
117 if (!err_node) {
118 return MOBILEACTIVATION_E_SUCCESS;
119 }
120
121 char *errmsg = NULL;
122 plist_get_string_val(err_node, &errmsg);
123 debug_info("ERROR: %s: %s", command, errmsg);
124 free(errmsg);
125 return MOBILEACTIVATION_E_REQUEST_FAILED;
126}
127
128static mobileactivation_error_t mobileactivation_send_command_plist(mobileactivation_client_t client, plist_t command, plist_t *result)
129{
130 if (!client || !command)
131 return MOBILEACTIVATION_E_INVALID_ARG;
132
133 plist_t cmd = plist_dict_get_item(command, "Command");
134 char* command_str = NULL;
135 if (cmd) {
136 plist_get_string_val(cmd, &command_str);
137 }
138 if (!command_str)
139 return MOBILEACTIVATION_E_INVALID_ARG;
140
141 mobileactivation_error_t ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
142 *result = NULL;
143
144 ret = mobileactivation_error(property_list_service_send_binary_plist(client->parent, command));
145
146 plist_t dict = NULL;
147 ret = mobileactivation_error(property_list_service_receive_plist(client->parent, &dict));
148 if (!dict) {
149 debug_info("ERROR: Did not get reply for %s command", command_str);
150 free(command_str);
151 return MOBILEACTIVATION_E_PLIST_ERROR;
152 }
153
154 *result = dict;
155 ret = mobileactivation_check_result(dict, command_str);
156 free(command_str);
157 return ret;
158}
159
160static mobileactivation_error_t mobileactivation_send_command(mobileactivation_client_t client, const char* command, plist_t value, plist_t *result)
161{
162 if (!client || !command || !result)
163 return MOBILEACTIVATION_E_INVALID_ARG;
164
165 mobileactivation_error_t ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
166 *result = NULL;
167
168 plist_t dict = plist_new_dict();
169 plist_dict_set_item(dict, "Command", plist_new_string(command));
170 if (value) {
171 plist_dict_set_item(dict, "Value", plist_copy(value));
172 }
173
174 ret = mobileactivation_send_command_plist(client, dict, result);
175 plist_free(dict);
176 return ret;
177}
178
179mobileactivation_error_t mobileactivation_get_activation_state(mobileactivation_client_t client, plist_t *state)
180{
181 if (!client || !state)
182 return MOBILEACTIVATION_E_INVALID_ARG;
183
184 plist_t result = NULL;
185 mobileactivation_error_t ret = mobileactivation_send_command(client, "GetActivationStateRequest", NULL, &result);
186 if (ret == MOBILEACTIVATION_E_SUCCESS) {
187 plist_t node = plist_dict_get_item(result, "Value");
188 if (!node) {
189 debug_info("ERROR: GetActivationStateRequest command returned success but has no value in reply");
190 ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
191 } else {
192 *state = plist_copy(node);
193 }
194 }
195 plist_free(result);
196 result = NULL;
197
198 return ret;
199}
200
201mobileactivation_error_t mobileactivation_create_activation_session_info(mobileactivation_client_t client, plist_t *blob)
202{
203 if (!client || !blob)
204 return MOBILEACTIVATION_E_INVALID_ARG;
205
206 plist_t result = NULL;
207 mobileactivation_error_t ret = mobileactivation_send_command(client, "CreateTunnel1SessionInfoRequest", NULL, &result);
208 if (ret == MOBILEACTIVATION_E_SUCCESS) {
209 plist_t node = plist_dict_get_item(result, "Value");
210 if (!node) {
211 debug_info("ERROR: CreateTunnel1SessionInfoRequest command returned success but has no value in reply");
212 ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
213 } else {
214 *blob = plist_copy(node);
215 }
216 }
217
218 return ret;
219}
220
221mobileactivation_error_t mobileactivation_create_activation_info(mobileactivation_client_t client, plist_t *info)
222{
223 if (!client || !info)
224 return MOBILEACTIVATION_E_INVALID_ARG;
225
226 plist_t result = NULL;
227 mobileactivation_error_t ret = mobileactivation_send_command(client, "CreateActivationInfoRequest", NULL, &result);
228 if (ret == MOBILEACTIVATION_E_SUCCESS) {
229 plist_t node = plist_dict_get_item(result, "Value");
230 if (!node) {
231 debug_info("ERROR: CreateActivationInfoRequest command returned success but has no value in reply");
232 ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
233 } else {
234 *info = plist_copy(node);
235 }
236 }
237 plist_free(result);
238 result = NULL;
239
240 return ret;
241}
242
243mobileactivation_error_t mobileactivation_create_activation_info_with_session(mobileactivation_client_t client, plist_t handshake_response, plist_t *info)
244{
245 if (!client || !info)
246 return MOBILEACTIVATION_E_INVALID_ARG;
247
248 plist_t result = NULL;
249 plist_t data = plist_data_from_plist(handshake_response);
250 mobileactivation_error_t ret = mobileactivation_send_command(client, "CreateTunnel1ActivationInfoRequest", data, &result);
251 plist_free(data);
252 if (ret == MOBILEACTIVATION_E_SUCCESS) {
253 plist_t node = plist_dict_get_item(result, "Value");
254 if (!node) {
255 debug_info("ERROR: CreateTunnel1ActivationInfoRequest command returned success but has no value in reply");
256 ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
257 } else {
258 *info = plist_copy(node);
259 }
260 }
261 plist_free(result);
262 result = NULL;
263
264 return ret;
265}
266
267mobileactivation_error_t mobileactivation_activate(mobileactivation_client_t client, plist_t activation_record)
268{
269 if (!client || !activation_record)
270 return MOBILEACTIVATION_E_INVALID_ARG;
271
272 plist_t result = NULL;
273 mobileactivation_error_t ret = mobileactivation_send_command(client, "HandleActivationInfoRequest", activation_record, &result);
274 plist_free(result);
275 result = NULL;
276
277 return ret;
278}
279
280mobileactivation_error_t mobileactivation_activate_with_session(mobileactivation_client_t client, plist_t activation_record, plist_t headers)
281{
282 if (!client || !activation_record)
283 return MOBILEACTIVATION_E_INVALID_ARG;
284
285 plist_t result = NULL;
286
287 plist_t dict = plist_new_dict();
288 plist_dict_set_item(dict, "Command", plist_new_string("HandleActivationInfoWithSessionRequest"));
289 plist_dict_set_item(dict, "Value", plist_data_from_plist(activation_record));
290 if (headers) {
291 plist_dict_set_item(dict, "ActivationResponseHeaders", plist_copy(headers));
292 }
293
294 mobileactivation_error_t ret = mobileactivation_send_command_plist(client, dict, &result);
295 plist_free(dict);
296 plist_free(result);
297 result = NULL;
298
299 return ret;
300}
301
302
303mobileactivation_error_t mobileactivation_deactivate(mobileactivation_client_t client)
304{
305 if (!client)
306 return MOBILEACTIVATION_E_INVALID_ARG;
307
308 plist_t result = NULL;
309 mobileactivation_error_t ret = mobileactivation_send_command(client, "DeactivateRequest", NULL, &result);
310 plist_free(result);
311 result = NULL;
312
313 return ret;
314}
diff --git a/src/mobileactivation.h b/src/mobileactivation.h
new file mode 100644
index 0000000..a8dff5d
--- /dev/null
+++ b/src/mobileactivation.h
@@ -0,0 +1,33 @@
1/*
2 * mobileactivation.h
3 * com.apple.mobileactivationd service header file.
4 *
5 * Copyright (c) 2016 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __MOBILEACTIVATION_H
23#define __MOBILEACTIVATION_H
24
25#include "idevice.h"
26#include "libimobiledevice/mobileactivation.h"
27#include "property_list_service.h"
28
29struct mobileactivation_client_private {
30 property_list_service_client_t parent;
31};
32
33#endif
diff --git a/src/mobilebackup.c b/src/mobilebackup.c
index fcff60d..36986a4 100644
--- a/src/mobilebackup.c
+++ b/src/mobilebackup.c
@@ -1,36 +1,41 @@
1/* 1/*
2 * mobilebackup.c 2 * mobilebackup.c
3 * Contains functions for the built-in MobileBackup client. 3 * Contains functions for the built-in MobileBackup client.
4 * 4 *
5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
5 * Copyright (c) 2009 Martin Szulecki All Rights Reserved. 6 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21 22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
22#include <plist/plist.h> 26#include <plist/plist.h>
23#include <string.h> 27#include <string.h>
24#include <stdlib.h> 28#include <stdlib.h>
29#include <stdio.h>
25 30
26#include "mobilebackup.h" 31#include "mobilebackup.h"
27#include "device_link_service.h" 32#include "device_link_service.h"
28#include "debug.h" 33#include "common/debug.h"
29 34
30#define MBACKUP_VERSION_INT1 100 35#define MBACKUP_VERSION_INT1 100
31#define MBACKUP_VERSION_INT2 0 36#define MBACKUP_VERSION_INT2 0
32 37
33#define IS_FLAG_SET(x, y) ((x & y) == y) 38#define IS_FLAG_SET(x, y) (((x) & (y)) == (y))
34 39
35/** 40/**
36 * Convert an device_link_service_error_t value to an mobilebackup_error_t value. 41 * Convert an device_link_service_error_t value to an mobilebackup_error_t value.
@@ -52,6 +57,10 @@ static mobilebackup_error_t mobilebackup_error(device_link_service_error_t err)
52 return MOBILEBACKUP_E_PLIST_ERROR; 57 return MOBILEBACKUP_E_PLIST_ERROR;
53 case DEVICE_LINK_SERVICE_E_MUX_ERROR: 58 case DEVICE_LINK_SERVICE_E_MUX_ERROR:
54 return MOBILEBACKUP_E_MUX_ERROR; 59 return MOBILEBACKUP_E_MUX_ERROR;
60 case DEVICE_LINK_SERVICE_E_SSL_ERROR:
61 return MOBILEBACKUP_E_SSL_ERROR;
62 case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT:
63 return MOBILEBACKUP_E_RECEIVE_TIMEOUT;
55 case DEVICE_LINK_SERVICE_E_BAD_VERSION: 64 case DEVICE_LINK_SERVICE_E_BAD_VERSION:
56 return MOBILEBACKUP_E_BAD_VERSION; 65 return MOBILEBACKUP_E_BAD_VERSION;
57 default: 66 default:
@@ -60,26 +69,13 @@ static mobilebackup_error_t mobilebackup_error(device_link_service_error_t err)
60 return MOBILEBACKUP_E_UNKNOWN_ERROR; 69 return MOBILEBACKUP_E_UNKNOWN_ERROR;
61} 70}
62 71
63/** 72mobilebackup_error_t mobilebackup_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobilebackup_client_t * client)
64 * Connects to the mobilebackup service on the specified device.
65 *
66 * @param device The device to connect to.
67 * @param port Destination port (usually given by lockdownd_start_service).
68 * @param client Pointer that will be set to a newly allocated
69 * mobilebackup_client_t upon successful return.
70 *
71 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID ARG if one
72 * or more parameters are invalid, or DEVICE_LINK_SERVICE_E_BAD_VERSION if
73 * the mobilebackup version on the device is newer.
74 */
75mobilebackup_error_t mobilebackup_client_new(idevice_t device, uint16_t port,
76 mobilebackup_client_t * client)
77{ 73{
78 if (!device || port == 0 || !client || *client) 74 if (!device || !service || service->port == 0 || !client || *client)
79 return MOBILEBACKUP_E_INVALID_ARG; 75 return MOBILEBACKUP_E_INVALID_ARG;
80 76
81 device_link_service_client_t dlclient = NULL; 77 device_link_service_client_t dlclient = NULL;
82 mobilebackup_error_t ret = mobilebackup_error(device_link_service_client_new(device, port, &dlclient)); 78 mobilebackup_error_t ret = mobilebackup_error(device_link_service_client_new(device, service, &dlclient));
83 if (ret != MOBILEBACKUP_E_SUCCESS) { 79 if (ret != MOBILEBACKUP_E_SUCCESS) {
84 return ret; 80 return ret;
85 } 81 }
@@ -100,36 +96,26 @@ mobilebackup_error_t mobilebackup_client_new(idevice_t device, uint16_t port,
100 return ret; 96 return ret;
101} 97}
102 98
103/** 99mobilebackup_error_t mobilebackup_client_start_service(idevice_t device, mobilebackup_client_t * client, const char* label)
104 * Disconnects a mobilebackup client from the device and frees up the 100{
105 * mobilebackup client data. 101 mobilebackup_error_t err = MOBILEBACKUP_E_UNKNOWN_ERROR;
106 * 102 service_client_factory_start_service(device, MOBILEBACKUP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobilebackup_client_new), &err);
107 * @param client The mobilebackup client to disconnect and free. 103 return err;
108 * 104}
109 * @return MOBILEBACKUP_E_SUCCESS on success, or MOBILEBACKUP_E_INVALID_ARG 105
110 * if client is NULL.
111 */
112mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client) 106mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client)
113{ 107{
114 if (!client) 108 if (!client)
115 return MOBILEBACKUP_E_INVALID_ARG; 109 return MOBILEBACKUP_E_INVALID_ARG;
116 mobilebackup_error_t err = MOBILEBACKUP_E_SUCCESS; 110 mobilebackup_error_t err = MOBILEBACKUP_E_SUCCESS;
117 if (client->parent) { 111 if (client->parent) {
118 device_link_service_disconnect(client->parent); 112 device_link_service_disconnect(client->parent, NULL);
119 err = mobilebackup_error(device_link_service_client_free(client->parent)); 113 err = mobilebackup_error(device_link_service_client_free(client->parent));
120 } 114 }
121 free(client); 115 free(client);
122 return err; 116 return err;
123} 117}
124 118
125/**
126 * Polls the device for mobilebackup data.
127 *
128 * @param client The mobilebackup client
129 * @param plist A pointer to the location where the plist should be stored
130 *
131 * @return an error code
132 */
133mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t * plist) 119mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t * plist)
134{ 120{
135 if (!client) 121 if (!client)
@@ -138,17 +124,6 @@ mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t
138 return ret; 124 return ret;
139} 125}
140 126
141/**
142 * Sends mobilebackup data to the device
143 *
144 * @note This function is low-level and should only be used if you need to send
145 * a new type of message.
146 *
147 * @param client The mobilebackup client
148 * @param plist The location of the plist to send
149 *
150 * @return an error code
151 */
152mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist) 127mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist)
153{ 128{
154 if (!client || !plist) 129 if (!client || !plist)
@@ -186,7 +161,7 @@ static mobilebackup_error_t mobilebackup_send_message(mobilebackup_client_t clie
186 } else { 161 } else {
187 dict = plist_new_dict(); 162 dict = plist_new_dict();
188 } 163 }
189 plist_dict_insert_item(dict, "BackupMessageTypeKey", plist_new_string(message)); 164 plist_dict_set_item(dict, "BackupMessageTypeKey", plist_new_string(message));
190 165
191 /* send it as DLMessageProcessMessage */ 166 /* send it as DLMessageProcessMessage */
192 err = mobilebackup_error(device_link_service_send_process_message(client->parent, dict)); 167 err = mobilebackup_error(device_link_service_send_process_message(client->parent, dict));
@@ -266,23 +241,6 @@ leave:
266 return err; 241 return err;
267} 242}
268 243
269/**
270 * Request a backup from the connected device.
271 *
272 * @param client The connected MobileBackup client to use.
273 * @param backup_manifest The backup manifest, a plist_t of type PLIST_DICT
274 * containing the backup state of the last backup. For a first-time backup
275 * set this parameter to NULL.
276 * @param base_path The base path on the device to use for the backup
277 * operation, usually "/".
278 * @param proto_version A string denoting the version of the backup protocol
279 * to use. Latest known version is "1.6"
280 *
281 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if
282 * one of the parameters is invalid, MOBILEBACKUP_E_PLIST_ERROR if
283 * backup_manifest is not of type PLIST_DICT, MOBILEBACKUP_E_MUX_ERROR
284 * if a communication error occurs, MOBILEBACKUP_E_REPLY_NOT_OK
285 */
286mobilebackup_error_t mobilebackup_request_backup(mobilebackup_client_t client, plist_t backup_manifest, const char *base_path, const char *proto_version) 244mobilebackup_error_t mobilebackup_request_backup(mobilebackup_client_t client, plist_t backup_manifest, const char *base_path, const char *proto_version)
287{ 245{
288 if (!client || !client->parent || !base_path || !proto_version) 246 if (!client || !client->parent || !base_path || !proto_version)
@@ -296,10 +254,10 @@ mobilebackup_error_t mobilebackup_request_backup(mobilebackup_client_t client, p
296 /* construct request plist */ 254 /* construct request plist */
297 plist_t dict = plist_new_dict(); 255 plist_t dict = plist_new_dict();
298 if (backup_manifest) 256 if (backup_manifest)
299 plist_dict_insert_item(dict, "BackupManifestKey", plist_copy(backup_manifest)); 257 plist_dict_set_item(dict, "BackupManifestKey", plist_copy(backup_manifest));
300 plist_dict_insert_item(dict, "BackupComputerBasePathKey", plist_new_string(base_path)); 258 plist_dict_set_item(dict, "BackupComputerBasePathKey", plist_new_string(base_path));
301 plist_dict_insert_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest")); 259 plist_dict_set_item(dict, "BackupMessageTypeKey", plist_new_string("BackupMessageBackupRequest"));
302 plist_dict_insert_item(dict, "BackupProtocolVersion", plist_new_string(proto_version)); 260 plist_dict_set_item(dict, "BackupProtocolVersion", plist_new_string(proto_version));
303 261
304 /* send request */ 262 /* send request */
305 err = mobilebackup_send_message(client, NULL, dict); 263 err = mobilebackup_send_message(client, NULL, dict);
@@ -322,7 +280,15 @@ mobilebackup_error_t mobilebackup_request_backup(mobilebackup_client_t client, p
322 char *str = NULL; 280 char *str = NULL;
323 plist_get_string_val(node, &str); 281 plist_get_string_val(node, &str);
324 if (str) { 282 if (str) {
325 if (strcmp(str, proto_version) != 0) { 283 int maj = 0;
284 int min = 0;
285 sscanf(str, "%u.%u", &maj, &min);
286 uint32_t this_ver = ((maj & 0xFF) << 8) | (min & 0xFF);
287 maj = 0;
288 min = 0;
289 sscanf(proto_version, "%u.%u", &maj, &min);
290 uint32_t proto_ver = ((maj & 0xFF) << 8) | (min & 0xFF);
291 if (this_ver > proto_ver) {
326 err = MOBILEBACKUP_E_BAD_VERSION; 292 err = MOBILEBACKUP_E_BAD_VERSION;
327 } 293 }
328 free(str); 294 free(str);
@@ -343,41 +309,11 @@ leave:
343 return err; 309 return err;
344} 310}
345 311
346/**
347 * Sends a confirmation to the device that a backup file has been received.
348 *
349 * @param client The connected MobileBackup client to use.
350 *
351 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if
352 * client is invalid, or MOBILEBACKUP_E_MUX_ERROR if a communication error
353 * occurs.
354 */
355mobilebackup_error_t mobilebackup_send_backup_file_received(mobilebackup_client_t client) 312mobilebackup_error_t mobilebackup_send_backup_file_received(mobilebackup_client_t client)
356{ 313{
357 return mobilebackup_send_message(client, "kBackupMessageBackupFileReceived", NULL); 314 return mobilebackup_send_message(client, "kBackupMessageBackupFileReceived", NULL);
358} 315}
359 316
360/**
361 * Request that a backup should be restored to the connected device.
362 *
363 * @param client The connected MobileBackup client to use.
364 * @param backup_manifest The backup manifest, a plist_t of type PLIST_DICT
365 * containing the backup state to be restored.
366 * @param flags Flags to send with the request. Currently this is a combination
367 * of the following mobilebackup_flags_t:
368 * MB_RESTORE_NOTIFY_SPRINGBOARD - let SpringBoard show a 'Restore' screen
369 * MB_RESTORE_PRESERVE_SETTINGS - do not overwrite any settings
370 * MB_RESTORE_PRESERVE_CAMERA_ROLL - preserve the photos of the camera roll
371 * @param proto_version A string denoting the version of the backup protocol
372 * to use. Latest known version is "1.6". Ideally this value should be
373 * extracted from the given manifest plist.
374 *
375 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if
376 * one of the parameters is invalid, MOBILEBACKUP_E_PLIST_ERROR if
377 * backup_manifest is not of type PLIST_DICT, MOBILEBACKUP_E_MUX_ERROR
378 * if a communication error occurs, or MOBILEBACKUP_E_REPLY_NOT_OK
379 * if the device did not accept the request.
380 */
381mobilebackup_error_t mobilebackup_request_restore(mobilebackup_client_t client, plist_t backup_manifest, mobilebackup_flags_t flags, const char *proto_version) 317mobilebackup_error_t mobilebackup_request_restore(mobilebackup_client_t client, plist_t backup_manifest, mobilebackup_flags_t flags, const char *proto_version)
382{ 318{
383 if (!client || !client->parent || !backup_manifest || !proto_version) 319 if (!client || !client->parent || !backup_manifest || !proto_version)
@@ -390,13 +326,13 @@ mobilebackup_error_t mobilebackup_request_restore(mobilebackup_client_t client,
390 326
391 /* construct request plist */ 327 /* construct request plist */
392 plist_t dict = plist_new_dict(); 328 plist_t dict = plist_new_dict();
393 plist_dict_insert_item(dict, "BackupManifestKey", plist_copy(backup_manifest)); 329 plist_dict_set_item(dict, "BackupManifestKey", plist_copy(backup_manifest));
394 plist_dict_insert_item(dict, "BackupMessageTypeKey", plist_new_string("kBackupMessageRestoreRequest")); 330 plist_dict_set_item(dict, "BackupMessageTypeKey", plist_new_string("kBackupMessageRestoreRequest"));
395 plist_dict_insert_item(dict, "BackupProtocolVersion", plist_new_string(proto_version)); 331 plist_dict_set_item(dict, "BackupProtocolVersion", plist_new_string(proto_version));
396 /* add flags */ 332 /* add flags */
397 plist_dict_insert_item(dict, "BackupNotifySpringBoard", plist_new_bool(IS_FLAG_SET(flags, MB_RESTORE_NOTIFY_SPRINGBOARD))); 333 plist_dict_set_item(dict, "BackupNotifySpringBoard", plist_new_bool(IS_FLAG_SET(flags, MB_RESTORE_NOTIFY_SPRINGBOARD)));
398 plist_dict_insert_item(dict, "BackupPreserveSettings", plist_new_bool(IS_FLAG_SET(flags, MB_RESTORE_PRESERVE_SETTINGS))); 334 plist_dict_set_item(dict, "BackupPreserveSettings", plist_new_bool(IS_FLAG_SET(flags, MB_RESTORE_PRESERVE_SETTINGS)));
399 plist_dict_insert_item(dict, "BackupPreserveCameraRoll", plist_new_bool(IS_FLAG_SET(flags, MB_RESTORE_PRESERVE_CAMERA_ROLL))); 335 plist_dict_set_item(dict, "BackupPreserveCameraRoll", plist_new_bool(IS_FLAG_SET(flags, MB_RESTORE_PRESERVE_CAMERA_ROLL)));
400 336
401 /* send request */ 337 /* send request */
402 err = mobilebackup_send_message(client, NULL, dict); 338 err = mobilebackup_send_message(client, NULL, dict);
@@ -419,7 +355,15 @@ mobilebackup_error_t mobilebackup_request_restore(mobilebackup_client_t client,
419 char *str = NULL; 355 char *str = NULL;
420 plist_get_string_val(node, &str); 356 plist_get_string_val(node, &str);
421 if (str) { 357 if (str) {
422 if (strcmp(str, proto_version) != 0) { 358 int maj = 0;
359 int min = 0;
360 sscanf(str, "%u.%u", &maj, &min);
361 uint32_t this_ver = ((maj & 0xFF) << 8) | (min & 0xFF);
362 maj = 0;
363 min = 0;
364 sscanf(proto_version, "%u.%u", &maj, &min);
365 uint32_t proto_ver = ((maj & 0xFF) << 8) | (min & 0xFF);
366 if (this_ver > proto_ver) {
423 err = MOBILEBACKUP_E_BAD_VERSION; 367 err = MOBILEBACKUP_E_BAD_VERSION;
424 } 368 }
425 free(str); 369 free(str);
@@ -432,63 +376,16 @@ leave:
432 return err; 376 return err;
433} 377}
434 378
435/**
436 * Receive a confirmation from the device that it successfully received
437 * a restore file.
438 *
439 * @param client The connected MobileBackup client to use.
440 * @param result Pointer to a plist_t that will be set to the received plist
441 * for further processing. The caller has to free it using plist_free().
442 * Note that it will be set to NULL if the operation itself fails due to
443 * a communication or plist error.
444 * If this parameter is NULL, it will be ignored.
445 *
446 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if
447 * client is invalid, MOBILEBACKUP_E_REPLY_NOT_OK if the expected
448 * 'BackupMessageRestoreFileReceived' message could not be received,
449 * MOBILEBACKUP_E_PLIST_ERROR if the received message is not a valid backup
450 * message plist, or MOBILEBACKUP_E_MUX_ERROR if a communication error
451 * occurs.
452 */
453mobilebackup_error_t mobilebackup_receive_restore_file_received(mobilebackup_client_t client, plist_t *result) 379mobilebackup_error_t mobilebackup_receive_restore_file_received(mobilebackup_client_t client, plist_t *result)
454{ 380{
455 return mobilebackup_receive_message(client, "BackupMessageRestoreFileReceived", result); 381 return mobilebackup_receive_message(client, "BackupMessageRestoreFileReceived", result);
456} 382}
457 383
458/**
459 * Receive a confirmation from the device that it successfully received
460 * application data file.
461 *
462 * @param client The connected MobileBackup client to use.
463 * @param result Pointer to a plist_t that will be set to the received plist
464 * for further processing. The caller has to free it using plist_free().
465 * Note that it will be set to NULL if the operation itself fails due to
466 * a communication or plist error.
467 * If this parameter is NULL, it will be ignored.
468 *
469 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if
470 * client is invalid, MOBILEBACKUP_E_REPLY_NOT_OK if the expected
471 * 'BackupMessageRestoreApplicationReceived' message could not be received,
472 * MOBILEBACKUP_E_PLIST_ERROR if the received message is not a valid backup
473 * message plist, or MOBILEBACKUP_E_MUX_ERROR if a communication error
474 * occurs.
475 */
476mobilebackup_error_t mobilebackup_receive_restore_application_received(mobilebackup_client_t client, plist_t *result) 384mobilebackup_error_t mobilebackup_receive_restore_application_received(mobilebackup_client_t client, plist_t *result)
477{ 385{
478 return mobilebackup_receive_message(client, "BackupMessageRestoreApplicationReceived", result); 386 return mobilebackup_receive_message(client, "BackupMessageRestoreApplicationReceived", result);
479} 387}
480 388
481/**
482 * Tells the device that the restore process is complete and waits for the
483 * device to close the connection. After that, the device should reboot.
484 *
485 * @param client The connected MobileBackup client to use.
486 *
487 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if
488 * client is invalid, MOBILEBACKUP_E_PLIST_ERROR if the received disconnect
489 * message plist is invalid, or MOBILEBACKUP_E_MUX_ERROR if a communication
490 * error occurs.
491 */
492mobilebackup_error_t mobilebackup_send_restore_complete(mobilebackup_client_t client) 389mobilebackup_error_t mobilebackup_send_restore_complete(mobilebackup_client_t client)
493{ 390{
494 mobilebackup_error_t err = mobilebackup_send_message(client, "BackupMessageRestoreComplete", NULL); 391 mobilebackup_error_t err = mobilebackup_send_message(client, "BackupMessageRestoreComplete", NULL);
@@ -534,16 +431,6 @@ mobilebackup_error_t mobilebackup_send_restore_complete(mobilebackup_client_t cl
534 return err; 431 return err;
535} 432}
536 433
537/**
538 * Sends a backup error message to the device.
539 *
540 * @param client The connected MobileBackup client to use.
541 * @param reason A string describing the reason for the error message.
542 *
543 * @return MOBILEBACKUP_E_SUCCESS on success, MOBILEBACKUP_E_INVALID_ARG if
544 * one of the parameters is invalid, or MOBILEBACKUP_E_MUX_ERROR if a
545 * communication error occurs.
546 */
547mobilebackup_error_t mobilebackup_send_error(mobilebackup_client_t client, const char *reason) 434mobilebackup_error_t mobilebackup_send_error(mobilebackup_client_t client, const char *reason)
548{ 435{
549 if (!client || !client->parent || !reason) 436 if (!client || !client->parent || !reason)
@@ -553,7 +440,7 @@ mobilebackup_error_t mobilebackup_send_error(mobilebackup_client_t client, const
553 440
554 /* construct error plist */ 441 /* construct error plist */
555 plist_t dict = plist_new_dict(); 442 plist_t dict = plist_new_dict();
556 plist_dict_insert_item(dict, "BackupErrorReasonKey", plist_new_string(reason)); 443 plist_dict_set_item(dict, "BackupErrorReasonKey", plist_new_string(reason));
557 444
558 err = mobilebackup_send_message(client, "BackupMessageError", dict); 445 err = mobilebackup_send_message(client, "BackupMessageError", dict);
559 plist_free(dict); 446 plist_free(dict);
diff --git a/src/mobilebackup.h b/src/mobilebackup.h
index 2c5be62..04ec479 100644
--- a/src/mobilebackup.h
+++ b/src/mobilebackup.h
@@ -1,26 +1,29 @@
1 /* 1/*
2 * mobilebackup.h 2 * mobilebackup.h
3 * Definitions for the mobilebackup service 3 * Definitions for the mobilebackup service
4 * 4 *
5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
5 * Copyright (c) 2009 Martin Szulecki All Rights Reserved. 6 * Copyright (c) 2009 Martin Szulecki All Rights Reserved.
6 * 7 *
7 * This library is free software; you can redistribute it and/or 8 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
11 * 12 *
12 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
16 * 17 *
17 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 21 */
21#ifndef MOBILEBACKUP_H
22#define MOBILEBACKUP_H
23 22
23#ifndef __MOBILEBACKUP_H
24#define __MOBILEBACKUP_H
25
26#include "idevice.h"
24#include "libimobiledevice/mobilebackup.h" 27#include "libimobiledevice/mobilebackup.h"
25#include "device_link_service.h" 28#include "device_link_service.h"
26 29
diff --git a/src/mobilebackup2.c b/src/mobilebackup2.c
new file mode 100644
index 0000000..a8d673f
--- /dev/null
+++ b/src/mobilebackup2.c
@@ -0,0 +1,386 @@
1/*
2 * mobilebackup2.c
3 * Contains functions for the built-in MobileBackup2 client (iOS4+ only)
4 *
5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <plist/plist.h>
26#include <string.h>
27#include <stdlib.h>
28
29#include "mobilebackup2.h"
30#include "device_link_service.h"
31#include "common/debug.h"
32
33#define MBACKUP2_VERSION_INT1 400
34#define MBACKUP2_VERSION_INT2 0
35
36#define IS_FLAG_SET(x, y) (((x) & (y)) == (y))
37
38/**
39 * Convert an device_link_service_error_t value to an mobilebackup2_error_t value.
40 * Used internally to get correct error codes from the underlying
41 * device_link_service.
42 *
43 * @param err An device_link_service_error_t error code
44 *
45 * @return A matching mobilebackup2_error_t error code,
46 * MOBILEBACKUP2_E_UNKNOWN_ERROR otherwise.
47 */
48static mobilebackup2_error_t mobilebackup2_error(device_link_service_error_t err)
49{
50 switch (err) {
51 case DEVICE_LINK_SERVICE_E_SUCCESS:
52 return MOBILEBACKUP2_E_SUCCESS;
53 case DEVICE_LINK_SERVICE_E_INVALID_ARG:
54 return MOBILEBACKUP2_E_INVALID_ARG;
55 case DEVICE_LINK_SERVICE_E_PLIST_ERROR:
56 return MOBILEBACKUP2_E_PLIST_ERROR;
57 case DEVICE_LINK_SERVICE_E_MUX_ERROR:
58 return MOBILEBACKUP2_E_MUX_ERROR;
59 case DEVICE_LINK_SERVICE_E_SSL_ERROR:
60 return MOBILEBACKUP2_E_SSL_ERROR;
61 case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT:
62 return MOBILEBACKUP2_E_RECEIVE_TIMEOUT;
63 case DEVICE_LINK_SERVICE_E_BAD_VERSION:
64 return MOBILEBACKUP2_E_BAD_VERSION;
65 default:
66 break;
67 }
68 return MOBILEBACKUP2_E_UNKNOWN_ERROR;
69}
70
71mobilebackup2_error_t mobilebackup2_client_new(idevice_t device, lockdownd_service_descriptor_t service,
72 mobilebackup2_client_t * client)
73{
74 if (!device || !service || service->port == 0 || !client || *client)
75 return MOBILEBACKUP2_E_INVALID_ARG;
76
77 device_link_service_client_t dlclient = NULL;
78 mobilebackup2_error_t ret = mobilebackup2_error(device_link_service_client_new(device, service, &dlclient));
79 if (ret != MOBILEBACKUP2_E_SUCCESS) {
80 return ret;
81 }
82
83 mobilebackup2_client_t client_loc = (mobilebackup2_client_t) malloc(sizeof(struct mobilebackup2_client_private));
84 client_loc->parent = dlclient;
85
86 /* perform handshake */
87 ret = mobilebackup2_error(device_link_service_version_exchange(dlclient, MBACKUP2_VERSION_INT1, MBACKUP2_VERSION_INT2));
88 if (ret != MOBILEBACKUP2_E_SUCCESS) {
89 debug_info("version exchange failed, error %d", ret);
90 mobilebackup2_client_free(client_loc);
91 return ret;
92 }
93
94 *client = client_loc;
95
96 return ret;
97}
98
99mobilebackup2_error_t mobilebackup2_client_start_service(idevice_t device, mobilebackup2_client_t * client, const char* label)
100{
101 mobilebackup2_error_t err = MOBILEBACKUP2_E_UNKNOWN_ERROR;
102 service_client_factory_start_service(device, MOBILEBACKUP2_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobilebackup2_client_new), &err);
103 return err;
104}
105
106mobilebackup2_error_t mobilebackup2_client_free(mobilebackup2_client_t client)
107{
108 if (!client)
109 return MOBILEBACKUP2_E_INVALID_ARG;
110 mobilebackup2_error_t err = MOBILEBACKUP2_E_SUCCESS;
111 if (client->parent) {
112 device_link_service_disconnect(client->parent, NULL);
113 err = mobilebackup2_error(device_link_service_client_free(client->parent));
114 }
115 free(client);
116 return err;
117}
118
119mobilebackup2_error_t mobilebackup2_send_message(mobilebackup2_client_t client, const char *message, plist_t options)
120{
121 if (!client || !client->parent || (!message && !options))
122 return MOBILEBACKUP2_E_INVALID_ARG;
123
124 if (options && (plist_get_node_type(options) != PLIST_DICT)) {
125 return MOBILEBACKUP2_E_INVALID_ARG;
126 }
127
128 mobilebackup2_error_t err;
129
130 if (message) {
131 plist_t dict = NULL;
132 if (options) {
133 dict = plist_copy(options);
134 } else {
135 dict = plist_new_dict();
136 }
137 plist_dict_set_item(dict, "MessageName", plist_new_string(message));
138
139 /* send it as DLMessageProcessMessage */
140 err = mobilebackup2_error(device_link_service_send_process_message(client->parent, dict));
141 plist_free(dict);
142 } else {
143 err = mobilebackup2_error(device_link_service_send_process_message(client->parent, options));
144 }
145 if (err != MOBILEBACKUP2_E_SUCCESS) {
146 debug_info("ERROR: Could not send message '%s' (%d)!", message, err);
147 }
148 return err;
149}
150
151/**
152 * Receives a plist from the device and checks if the value for the
153 * MessageName key matches the value passed in the message parameter.
154 *
155 * @param client The connected MobileBackup client to use.
156 * @param message The expected message to check.
157 * @param result Pointer to a plist_t that will be set to the received plist
158 * for further processing. The caller has to free it using plist_free().
159 * Note that it will be set to NULL if the operation itself fails due to
160 * a communication or plist error.
161 * If this parameter is NULL, it will be ignored.
162 *
163 * @return MOBILEBACKUP2_E_SUCCESS on success, MOBILEBACKUP2_E_INVALID_ARG if
164 * client or message is invalid, MOBILEBACKUP2_E_REPLY_NOT_OK if the
165 * expected message could not be received, MOBILEBACKUP2_E_PLIST_ERROR if
166 * the received message is not a valid backup message plist (i.e. the
167 * MessageName key is not present), or MOBILEBACKUP2_E_MUX_ERROR
168 * if a communication error occurs.
169 */
170static mobilebackup2_error_t internal_mobilebackup2_receive_message(mobilebackup2_client_t client, const char *message, plist_t *result)
171{
172 if (!client || !client->parent || !message)
173 return MOBILEBACKUP2_E_INVALID_ARG;
174
175 if (result)
176 *result = NULL;
177 mobilebackup2_error_t err;
178
179 plist_t dict = NULL;
180
181 /* receive DLMessageProcessMessage */
182 err = mobilebackup2_error(device_link_service_receive_process_message(client->parent, &dict));
183 if (err != MOBILEBACKUP2_E_SUCCESS) {
184 goto leave;
185 }
186
187 plist_t node = plist_dict_get_item(dict, "MessageName");
188 if (!node) {
189 debug_info("ERROR: MessageName key not found in plist!");
190 err = MOBILEBACKUP2_E_PLIST_ERROR;
191 goto leave;
192 }
193
194 char *str = NULL;
195 plist_get_string_val(node, &str);
196 if (str && (strcmp(str, message) == 0)) {
197 err = MOBILEBACKUP2_E_SUCCESS;
198 } else {
199 debug_info("ERROR: MessageName value does not match '%s'!", message);
200 err = MOBILEBACKUP2_E_REPLY_NOT_OK;
201 }
202 if (str)
203 free(str);
204
205 if (result) {
206 *result = dict;
207 dict = NULL;
208 }
209leave:
210 if (dict) {
211 plist_free(dict);
212 }
213
214 return err;
215}
216
217mobilebackup2_error_t mobilebackup2_receive_message(mobilebackup2_client_t client, plist_t *msg_plist, char **dlmessage)
218{
219 return mobilebackup2_error(device_link_service_receive_message(client->parent, msg_plist, dlmessage));
220}
221
222mobilebackup2_error_t mobilebackup2_send_raw(mobilebackup2_client_t client, const char *data, uint32_t length, uint32_t *bytes)
223{
224 if (!client || !client->parent || !data || (length == 0) || !bytes)
225 return MOBILEBACKUP2_E_INVALID_ARG;
226
227 *bytes = 0;
228
229 service_client_t raw = client->parent->parent->parent;
230
231 int bytes_loc = 0;
232 uint32_t sent = 0;
233 do {
234 bytes_loc = 0;
235 service_send(raw, data+sent, length-sent, (uint32_t*)&bytes_loc);
236 if (bytes_loc <= 0)
237 break;
238 sent += bytes_loc;
239 } while (sent < length);
240 if (sent > 0) {
241 *bytes = sent;
242 return MOBILEBACKUP2_E_SUCCESS;
243 }
244 return MOBILEBACKUP2_E_MUX_ERROR;
245}
246
247mobilebackup2_error_t mobilebackup2_receive_raw(mobilebackup2_client_t client, char *data, uint32_t length, uint32_t *bytes)
248{
249 if (!client || !client->parent || !data || (length == 0) || !bytes)
250 return MOBILEBACKUP2_E_INVALID_ARG;
251
252 service_client_t raw = client->parent->parent->parent;
253
254 *bytes = 0;
255
256 int bytes_loc = 0;
257 uint32_t received = 0;
258 do {
259 bytes_loc = 0;
260 service_receive(raw, data+received, length-received, (uint32_t*)&bytes_loc);
261 if (bytes_loc <= 0) break;
262 received += bytes_loc;
263 } while (received < length);
264 if (received > 0) {
265 *bytes = received;
266 return MOBILEBACKUP2_E_SUCCESS;
267 }
268 if (received == 0) {
269 return MOBILEBACKUP2_E_SUCCESS;
270 }
271 return MOBILEBACKUP2_E_MUX_ERROR;
272}
273
274mobilebackup2_error_t mobilebackup2_version_exchange(mobilebackup2_client_t client, double local_versions[], char count, double *remote_version)
275{
276 int i;
277
278 if (!client || !client->parent)
279 return MOBILEBACKUP2_E_INVALID_ARG;
280
281 plist_t dict = plist_new_dict();
282 plist_t array = plist_new_array();
283 for (i = 0; i < count; i++) {
284 plist_array_append_item(array, plist_new_real(local_versions[i]));
285 }
286 plist_dict_set_item(dict, "SupportedProtocolVersions", array);
287
288 mobilebackup2_error_t err = mobilebackup2_send_message(client, "Hello", dict);
289 plist_free(dict);
290
291 if (err != MOBILEBACKUP2_E_SUCCESS)
292 goto leave;
293
294 dict = NULL;
295 err = internal_mobilebackup2_receive_message(client, "Response", &dict);
296 if (err != MOBILEBACKUP2_E_SUCCESS)
297 goto leave;
298
299 /* check if we received an error */
300 plist_t node = plist_dict_get_item(dict, "ErrorCode");
301 if (!node || (plist_get_node_type(node) != PLIST_UINT)) {
302 err = MOBILEBACKUP2_E_PLIST_ERROR;
303 goto leave;
304 }
305
306 uint64_t val = 0;
307 plist_get_uint_val(node, &val);
308 if (val != 0) {
309 if (val == 1) {
310 err = MOBILEBACKUP2_E_NO_COMMON_VERSION;
311 } else {
312 err = MOBILEBACKUP2_E_REPLY_NOT_OK;
313 }
314 goto leave;
315 }
316
317 /* retrieve the protocol version of the device */
318 node = plist_dict_get_item(dict, "ProtocolVersion");
319 if (!node || (plist_get_node_type(node) != PLIST_REAL)) {
320 err = MOBILEBACKUP2_E_PLIST_ERROR;
321 goto leave;
322 }
323
324 *remote_version = 0.0;
325 plist_get_real_val(node, remote_version);
326leave:
327 if (dict)
328 plist_free(dict);
329 return err;
330}
331
332mobilebackup2_error_t mobilebackup2_send_request(mobilebackup2_client_t client, const char *request, const char *target_identifier, const char *source_identifier, plist_t options)
333{
334 if (!client || !client->parent || !request || !target_identifier)
335 return MOBILEBACKUP2_E_INVALID_ARG;
336
337 plist_t dict = plist_new_dict();
338 plist_dict_set_item(dict, "TargetIdentifier", plist_new_string(target_identifier));
339 if (source_identifier) {
340 plist_dict_set_item(dict, "SourceIdentifier", plist_new_string(source_identifier));
341 }
342 if (options) {
343 plist_dict_set_item(dict, "Options", plist_copy(options));
344 }
345 if (!strcmp(request, "Unback") && options) {
346 plist_t node = plist_dict_get_item(options, "Password");
347 if (node) {
348 plist_dict_set_item(dict, "Password", plist_copy(node));
349 }
350 }
351 if (!strcmp(request, "EnableCloudBackup") && options) {
352 plist_t node = plist_dict_get_item(options, "CloudBackupState");
353 if (node) {
354 plist_dict_set_item(dict, "CloudBackupState", plist_copy(node));
355 }
356 }
357 mobilebackup2_error_t err = mobilebackup2_send_message(client, request, dict);
358 plist_free(dict);
359
360 return err;
361}
362
363mobilebackup2_error_t mobilebackup2_send_status_response(mobilebackup2_client_t client, int status_code, const char *status1, plist_t status2)
364{
365 if (!client || !client->parent)
366 return MOBILEBACKUP2_E_INVALID_ARG;
367
368 plist_t array = plist_new_array();
369 plist_array_append_item(array, plist_new_string("DLMessageStatusResponse"));
370 plist_array_append_item(array, plist_new_uint(status_code));
371 if (status1) {
372 plist_array_append_item(array, plist_new_string(status1));
373 } else {
374 plist_array_append_item(array, plist_new_string("___EmptyParameterString___"));
375 }
376 if (status2) {
377 plist_array_append_item(array, plist_copy(status2));
378 } else {
379 plist_array_append_item(array, plist_new_string("___EmptyParameterString___"));
380 }
381
382 mobilebackup2_error_t err = mobilebackup2_error(device_link_service_send(client->parent, array));
383 plist_free(array);
384
385 return err;
386}
diff --git a/src/mobilebackup2.h b/src/mobilebackup2.h
new file mode 100644
index 0000000..e232b97
--- /dev/null
+++ b/src/mobilebackup2.h
@@ -0,0 +1,33 @@
1/*
2 * mobilebackup2.h
3 * Definitions for the mobilebackup2 service (iOS4+)
4 *
5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __MOBILEBACKUP2_H
23#define __MOBILEBACKUP2_H
24
25#include "idevice.h"
26#include "libimobiledevice/mobilebackup2.h"
27#include "device_link_service.h"
28
29struct mobilebackup2_client_private {
30 device_link_service_client_t parent;
31};
32
33#endif
diff --git a/src/mobilesync.c b/src/mobilesync.c
index ee9af5f..9b81a49 100644
--- a/src/mobilesync.c
+++ b/src/mobilesync.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * mobilesync.c 2 * mobilesync.c
3 * Contains functions for the built-in MobileSync client. 3 * Contains functions for the built-in MobileSync client.
4 * 4 *
5 * Copyright (c) 2010 Bryan Forbes All Rights Reserved. 5 * Copyright (c) 2010 Bryan Forbes All Rights Reserved.
6 * Copyright (c) 2009 Jonathan Beck All Rights Reserved. 6 * Copyright (c) 2009 Jonathan Beck All Rights Reserved.
7 * 7 *
@@ -9,31 +9,32 @@
9 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
12 * 12 *
13 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
17 * 17 *
18 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */ 21 */
22 22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
23#define _GNU_SOURCE 1 26#define _GNU_SOURCE 1
24#define __USE_GNU 1 27#define __USE_GNU 1
25
26#include <plist/plist.h> 28#include <plist/plist.h>
27#include <string.h> 29#include <string.h>
28#include <stdlib.h> 30#include <stdlib.h>
29#include <stdio.h> 31#include <stdio.h>
30#include <glib.h>
31 32
32#include "mobilesync.h" 33#include "mobilesync.h"
33#include "device_link_service.h" 34#include "device_link_service.h"
34#include "debug.h" 35#include "common/debug.h"
35 36
36#define MSYNC_VERSION_INT1 100 37#define MSYNC_VERSION_INT1 400
37#define MSYNC_VERSION_INT2 100 38#define MSYNC_VERSION_INT2 100
38 39
39#define EMPTY_PARAMETER_STRING "___EmptyParameterString___" 40#define EMPTY_PARAMETER_STRING "___EmptyParameterString___"
@@ -58,6 +59,10 @@ static mobilesync_error_t mobilesync_error(device_link_service_error_t err)
58 return MOBILESYNC_E_PLIST_ERROR; 59 return MOBILESYNC_E_PLIST_ERROR;
59 case DEVICE_LINK_SERVICE_E_MUX_ERROR: 60 case DEVICE_LINK_SERVICE_E_MUX_ERROR:
60 return MOBILESYNC_E_MUX_ERROR; 61 return MOBILESYNC_E_MUX_ERROR;
62 case DEVICE_LINK_SERVICE_E_SSL_ERROR:
63 return MOBILESYNC_E_SSL_ERROR;
64 case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT:
65 return MOBILESYNC_E_RECEIVE_TIMEOUT;
61 case DEVICE_LINK_SERVICE_E_BAD_VERSION: 66 case DEVICE_LINK_SERVICE_E_BAD_VERSION:
62 return MOBILESYNC_E_BAD_VERSION; 67 return MOBILESYNC_E_BAD_VERSION;
63 default: 68 default:
@@ -66,27 +71,14 @@ static mobilesync_error_t mobilesync_error(device_link_service_error_t err)
66 return MOBILESYNC_E_UNKNOWN_ERROR; 71 return MOBILESYNC_E_UNKNOWN_ERROR;
67} 72}
68 73
69/** 74mobilesync_error_t mobilesync_client_new(idevice_t device, lockdownd_service_descriptor_t service,
70 * Connects to the mobilesync service on the specified device.
71 *
72 * @param device The device to connect to.
73 * @param port Destination port (usually given by lockdownd_start_service()).
74 * @param client Pointer that will be set to a newly allocated
75 * #mobilesync_client_t upon successful return.
76 *
77 * @retval MOBILESYNC_E_SUCCESS on success
78 * @retval MOBILESYNC_E_INVALID_ARG if one or more parameters are invalid
79 * @retval DEVICE_LINK_SERVICE_E_BAD_VERSION if the mobilesync version on
80 * the device is newer.
81 */
82mobilesync_error_t mobilesync_client_new(idevice_t device, uint16_t port,
83 mobilesync_client_t * client) 75 mobilesync_client_t * client)
84{ 76{
85 if (!device || port == 0 || !client || *client) 77 if (!device || !service || service->port == 0 || !client || *client)
86 return MOBILESYNC_E_INVALID_ARG; 78 return MOBILESYNC_E_INVALID_ARG;
87 79
88 device_link_service_client_t dlclient = NULL; 80 device_link_service_client_t dlclient = NULL;
89 mobilesync_error_t ret = mobilesync_error(device_link_service_client_new(device, port, &dlclient)); 81 mobilesync_error_t ret = mobilesync_error(device_link_service_client_new(device, service, &dlclient));
90 if (ret != MOBILESYNC_E_SUCCESS) { 82 if (ret != MOBILESYNC_E_SUCCESS) {
91 return ret; 83 return ret;
92 } 84 }
@@ -109,33 +101,23 @@ mobilesync_error_t mobilesync_client_new(idevice_t device, uint16_t port,
109 return ret; 101 return ret;
110} 102}
111 103
112/** 104mobilesync_error_t mobilesync_client_start_service(idevice_t device, mobilesync_client_t * client, const char* label)
113 * Disconnects a mobilesync client from the device and frees up the 105{
114 * mobilesync client data. 106 mobilesync_error_t err = MOBILESYNC_E_UNKNOWN_ERROR;
115 * 107 service_client_factory_start_service(device, MOBILESYNC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobilesync_client_new), &err);
116 * @param client The mobilesync client to disconnect and free. 108 return err;
117 * 109}
118 * @retval MOBILESYNC_E_SUCCESS on success 110
119 * @retval MOBILESYNC_E_INVALID_ARG if \a client is NULL.
120 */
121mobilesync_error_t mobilesync_client_free(mobilesync_client_t client) 111mobilesync_error_t mobilesync_client_free(mobilesync_client_t client)
122{ 112{
123 if (!client) 113 if (!client)
124 return MOBILESYNC_E_INVALID_ARG; 114 return MOBILESYNC_E_INVALID_ARG;
125 device_link_service_disconnect(client->parent); 115 device_link_service_disconnect(client->parent, "All done, thanks for the memories");
126 mobilesync_error_t err = mobilesync_error(device_link_service_client_free(client->parent)); 116 mobilesync_error_t err = mobilesync_error(device_link_service_client_free(client->parent));
127 free(client); 117 free(client);
128 return err; 118 return err;
129} 119}
130 120
131/**
132 * Polls the device for mobilesync data.
133 *
134 * @param client The mobilesync client
135 * @param plist A pointer to the location where the plist should be stored
136 *
137 * @return an error code
138 */
139mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plist) 121mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plist)
140{ 122{
141 if (!client) 123 if (!client)
@@ -144,17 +126,6 @@ mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plis
144 return ret; 126 return ret;
145} 127}
146 128
147/**
148 * Sends mobilesync data to the device
149 *
150 * @note This function is low-level and should only be used if you need to send
151 * a new type of message.
152 *
153 * @param client The mobilesync client
154 * @param plist The location of the plist to send
155 *
156 * @return an error code
157 */
158mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist) 129mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist)
159{ 130{
160 if (!client || !plist) 131 if (!client || !plist)
@@ -162,26 +133,7 @@ mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist)
162 return mobilesync_error(device_link_service_send(client->parent, plist)); 133 return mobilesync_error(device_link_service_send(client->parent, plist));
163} 134}
164 135
165/** 136mobilesync_error_t mobilesync_start(mobilesync_client_t client, const char *data_class, mobilesync_anchors_t anchors, uint64_t computer_data_class_version, mobilesync_sync_type_t *sync_type, uint64_t *device_data_class_version, char** error_description)
166 * Requests starting synchronization of a data class with the device
167 *
168 * @param client The mobilesync client
169 * @param data_class The data class identifier to sync
170 * @param anchors The anchors required to exchange with the device. The anchors
171 * allow the device to tell if the synchronization information on the computer
172 * and device are consistent to determine what sync type is required.
173 * @param computer_data_class_version The version of the data class storage on the computer
174 * @param sync_type A pointer to store the sync type reported by the device_anchor
175 * @param device_data_class_version The version of the data class storage on the device
176 *
177 * @retval MOBILESYNC_E_SUCCESS on success
178 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
179 * @retval MOBILESYNC_E_PLIST_ERROR if the received plist is not of valid form
180 * @retval MOBILESYNC_E_SYNC_REFUSED if the device refused to sync
181 * @retval MOBILESYNC_E_CANCELLED if the device explicitly cancelled the
182 * sync request
183 */
184mobilesync_error_t mobilesync_start(mobilesync_client_t client, const char *data_class, mobilesync_anchors_t anchors, uint64_t computer_data_class_version, mobilesync_sync_type_t *sync_type, uint64_t *device_data_class_version)
185{ 137{
186 if (!client || client->data_class || !data_class || 138 if (!client || client->data_class || !data_class ||
187 !anchors || !anchors->computer_anchor) { 139 !anchors || !anchors->computer_anchor) {
@@ -194,6 +146,8 @@ mobilesync_error_t mobilesync_start(mobilesync_client_t client, const char *data
194 plist_t msg = NULL; 146 plist_t msg = NULL;
195 plist_t response_type_node = NULL; 147 plist_t response_type_node = NULL;
196 148
149 *error_description = NULL;
150
197 msg = plist_new_array(); 151 msg = plist_new_array();
198 plist_array_append_item(msg, plist_new_string("SDMessageSyncDataClassWithDevice")); 152 plist_array_append_item(msg, plist_new_string("SDMessageSyncDataClassWithDevice"));
199 plist_array_append_item(msg, plist_new_string(data_class)); 153 plist_array_append_item(msg, plist_new_string(data_class));
@@ -233,23 +187,19 @@ mobilesync_error_t mobilesync_start(mobilesync_client_t client, const char *data
233 goto out; 187 goto out;
234 } 188 }
235 189
190 // did the device refuse to sync with the computer?
236 if (!strcmp(response_type, "SDMessageRefuseToSyncDataClassWithComputer")) { 191 if (!strcmp(response_type, "SDMessageRefuseToSyncDataClassWithComputer")) {
237 char *reason = NULL;
238 err = MOBILESYNC_E_SYNC_REFUSED; 192 err = MOBILESYNC_E_SYNC_REFUSED;
239 plist_get_string_val(plist_array_get_item(msg, 2), &reason); 193 plist_get_string_val(plist_array_get_item(msg, 2), error_description);
240 debug_info("Device refused sync: %s", reason); 194 debug_info("Device refused sync: %s", error_description);
241 free(reason);
242 reason = NULL;
243 goto out; 195 goto out;
244 } 196 }
245 197
198 // did the device cancel the session?
246 if (!strcmp(response_type, "SDMessageCancelSession")) { 199 if (!strcmp(response_type, "SDMessageCancelSession")) {
247 char *reason = NULL;
248 err = MOBILESYNC_E_CANCELLED; 200 err = MOBILESYNC_E_CANCELLED;
249 plist_get_string_val(plist_array_get_item(msg, 2), &reason); 201 plist_get_string_val(plist_array_get_item(msg, 2), error_description);
250 debug_info("Device cancelled: %s", reason); 202 debug_info("Device cancelled: %s", error_description);
251 free(reason);
252 reason = NULL;
253 goto out; 203 goto out;
254 } 204 }
255 205
@@ -309,17 +259,6 @@ mobilesync_error_t mobilesync_start(mobilesync_client_t client, const char *data
309 return err; 259 return err;
310} 260}
311 261
312/**
313 * Finish a synchronization session of a data class on the device.
314 * A session must have previously been started using mobilesync_start().
315 *
316 * @param client The mobilesync client
317 *
318 * @retval MOBILESYNC_E_SUCCESS on success
319 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
320 * @retval MOBILESYNC_E_PLIST_ERROR if the received plist is not of valid
321 * form
322 */
323mobilesync_error_t mobilesync_finish(mobilesync_client_t client) 262mobilesync_error_t mobilesync_finish(mobilesync_client_t client)
324{ 263{
325 if (!client || !client->data_class) { 264 if (!client || !client->data_class) {
@@ -395,7 +334,7 @@ static mobilesync_error_t mobilesync_get_records(mobilesync_client_t client, con
395 msg = plist_new_array(); 334 msg = plist_new_array();
396 plist_array_append_item(msg, plist_new_string(operation)); 335 plist_array_append_item(msg, plist_new_string(operation));
397 plist_array_append_item(msg, plist_new_string(client->data_class)); 336 plist_array_append_item(msg, plist_new_string(client->data_class));
398 337
399 err = mobilesync_send(client, msg); 338 err = mobilesync_send(client, msg);
400 339
401 if (msg) { 340 if (msg) {
@@ -405,49 +344,16 @@ static mobilesync_error_t mobilesync_get_records(mobilesync_client_t client, con
405 return err; 344 return err;
406} 345}
407 346
408/**
409 * Requests to receive all records of the currently set data class from the device.
410 * The actually changes are retrieved using mobilesync_receive_changes() after this
411 * request has been successful.
412 *
413 * @param client The mobilesync client
414 *
415 * @retval MOBILESYNC_E_SUCCESS on success
416 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
417 */
418mobilesync_error_t mobilesync_get_all_records_from_device(mobilesync_client_t client) 347mobilesync_error_t mobilesync_get_all_records_from_device(mobilesync_client_t client)
419{ 348{
420 return mobilesync_get_records(client, "SDMessageGetAllRecordsFromDevice"); 349 return mobilesync_get_records(client, "SDMessageGetAllRecordsFromDevice");
421} 350}
422 351
423/**
424 * Requests to receive only changed records of the currently set data class from the device.
425 * The actually changes are retrieved using mobilesync_receive_changes() after this
426 * request has been successful.
427 *
428 * @param client The mobilesync client
429 *
430 * @retval MOBILESYNC_E_SUCCESS on success
431 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
432 */
433mobilesync_error_t mobilesync_get_changes_from_device(mobilesync_client_t client) 352mobilesync_error_t mobilesync_get_changes_from_device(mobilesync_client_t client)
434{ 353{
435 return mobilesync_get_records(client, "SDMessageGetChangesFromDevice"); 354 return mobilesync_get_records(client, "SDMessageGetChangesFromDevice");
436} 355}
437 356
438/**
439 * Receives changed entitites of the currently set data class from the device
440 *
441 * @param client The mobilesync client
442 * @param entities A pointer to store the changed entity records as a PLIST_DICT
443 * @param is_last_record A pointer to store a flag indicating if this submission is the last one
444 * @param actions A pointer to additional flags the device is sending or NULL to ignore
445 *
446 * @retval MOBILESYNC_E_SUCCESS on success
447 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
448 * @retval MOBILESYNC_E_CANCELLED if the device explicitly cancelled the
449 * session
450 */
451mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_t *entities, uint8_t *is_last_record, plist_t *actions) 357mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_t *entities, uint8_t *is_last_record, plist_t *actions)
452{ 358{
453 if (!client || !client->data_class) { 359 if (!client || !client->data_class) {
@@ -497,7 +403,7 @@ mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_
497 403
498 if (actions != NULL) { 404 if (actions != NULL) {
499 actions_node = plist_array_get_item(msg, 4); 405 actions_node = plist_array_get_item(msg, 4);
500 if (plist_get_node_type(actions) == PLIST_DICT) 406 if (plist_get_node_type(actions_node) == PLIST_DICT)
501 *actions = plist_copy(actions_node); 407 *actions = plist_copy(actions_node);
502 else 408 else
503 *actions = NULL; 409 *actions = NULL;
@@ -515,17 +421,6 @@ mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_
515 return err; 421 return err;
516} 422}
517 423
518/**
519 * Requests the device to delete all records of the current data class
520 *
521 * @note The operation must be called after starting synchronization.
522 *
523 * @param client The mobilesync client
524 *
525 * @retval MOBILESYNC_E_SUCCESS on success
526 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
527 * @retval MOBILESYNC_E_PLIST_ERROR if the received plist is not of valid form
528 */
529mobilesync_error_t mobilesync_clear_all_records_on_device(mobilesync_client_t client) 424mobilesync_error_t mobilesync_clear_all_records_on_device(mobilesync_client_t client)
530{ 425{
531 if (!client || !client->data_class) { 426 if (!client || !client->data_class) {
@@ -578,7 +473,7 @@ mobilesync_error_t mobilesync_clear_all_records_on_device(mobilesync_client_t cl
578 goto out; 473 goto out;
579 } 474 }
580 475
581 if (strcmp(response_type, "SDMessageDeviceWillClearAllRecords")) { 476 if (strcmp(response_type, "SDMessageDeviceWillClearAllRecords") != 0) {
582 err = MOBILESYNC_E_PLIST_ERROR; 477 err = MOBILESYNC_E_PLIST_ERROR;
583 } 478 }
584 479
@@ -595,14 +490,6 @@ mobilesync_error_t mobilesync_clear_all_records_on_device(mobilesync_client_t cl
595 return err; 490 return err;
596} 491}
597 492
598/**
599 * Acknowledges to the device that the changes have been merged on the computer
600 *
601 * @param client The mobilesync client
602 *
603 * @retval MOBILESYNC_E_SUCCESS on success
604 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
605 */
606mobilesync_error_t mobilesync_acknowledge_changes_from_device(mobilesync_client_t client) 493mobilesync_error_t mobilesync_acknowledge_changes_from_device(mobilesync_client_t client)
607{ 494{
608 if (!client || !client->data_class) { 495 if (!client || !client->data_class) {
@@ -637,23 +524,6 @@ static plist_t create_process_changes_message(const char *data_class, plist_t en
637 return msg; 524 return msg;
638} 525}
639 526
640/**
641 * Verifies if the device is ready to receive changes from the computer.
642 * This call changes the synchronization direction so that mobilesync_send_changes()
643 * can be used to send changes to the device.
644 *
645 * @param client The mobilesync client
646 *
647 * @retval MOBILESYNC_E_SUCCESS on success
648 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
649 * @retval MOBILESYNC_E_PLIST_ERROR if the received plist is not of valid form
650 * @retval MOBILESYNC_E_WRONG_DIRECTION if the current sync direction does
651 * not permit this call
652 * @retval MOBILESYNC_E_CANCELLED if the device explicitly cancelled the
653 * session
654 * @retval MOBILESYNC_E_NOT_READY if the device is not ready to start
655 * receiving any changes
656 */
657mobilesync_error_t mobilesync_ready_to_send_changes_from_computer(mobilesync_client_t client) 527mobilesync_error_t mobilesync_ready_to_send_changes_from_computer(mobilesync_client_t client)
658{ 528{
659 if (!client || !client->data_class) { 529 if (!client || !client->data_class) {
@@ -721,20 +591,6 @@ mobilesync_error_t mobilesync_ready_to_send_changes_from_computer(mobilesync_cli
721 return err; 591 return err;
722} 592}
723 593
724/**
725 * Sends changed entities of the currently set data class to the device
726 *
727 * @param client The mobilesync client
728 * @param entities The changed entity records as a PLIST_DICT
729 * @param is_last_record A flag indicating if this submission is the last one
730 * @param actions Additional actions for the device created with mobilesync_actions_new()
731 * or NULL if no actions should be passed
732 *
733 * @retval MOBILESYNC_E_SUCCESS on success
734 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid,
735 * @retval MOBILESYNC_E_WRONG_DIRECTION if the current sync direction does
736 * not permit this call
737 */
738mobilesync_error_t mobilesync_send_changes(mobilesync_client_t client, plist_t entities, uint8_t is_last_record, plist_t actions) 594mobilesync_error_t mobilesync_send_changes(mobilesync_client_t client, plist_t entities, uint8_t is_last_record, plist_t actions)
739{ 595{
740 if (!client || !client->data_class || !entities) { 596 if (!client || !client->data_class || !entities) {
@@ -763,21 +619,6 @@ mobilesync_error_t mobilesync_send_changes(mobilesync_client_t client, plist_t e
763 return err; 619 return err;
764} 620}
765 621
766/**
767 * Receives any remapped identifiers reported after the device merged submitted changes.
768 *
769 * @param client The mobilesync client
770 * @param mapping A pointer to an array plist containing a dict of identifier remappings
771 *
772 * @retval MOBILESYNC_E_SUCCESS on success
773 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
774 * @retval MOBILESYNC_E_PLIST_ERROR if the received plist is not of valid
775 * form
776 * @retval MOBILESYNC_E_WRONG_DIRECTION if the current sync direction does
777 * not permit this call
778 * @retval MOBILESYNC_E_CANCELLED if the device explicitly cancelled the
779 * session
780 */
781mobilesync_error_t mobilesync_remap_identifiers(mobilesync_client_t client, plist_t *mapping) 622mobilesync_error_t mobilesync_remap_identifiers(mobilesync_client_t client, plist_t *mapping)
782{ 623{
783 if (!client || !client->data_class) { 624 if (!client || !client->data_class) {
@@ -847,15 +688,6 @@ mobilesync_error_t mobilesync_remap_identifiers(mobilesync_client_t client, plis
847 return err; 688 return err;
848} 689}
849 690
850/**
851 * Cancels a running synchronization session with a device at any time.
852 *
853 * @param client The mobilesync client
854 * @param reason The reason to supply to the device for cancelling
855 *
856 * @retval MOBILESYNC_E_SUCCESS on success
857 * @retval MOBILESYNC_E_INVALID_ARG if one of the parameters is invalid
858 */
859mobilesync_error_t mobilesync_cancel(mobilesync_client_t client, const char* reason) 691mobilesync_error_t mobilesync_cancel(mobilesync_client_t client, const char* reason)
860{ 692{
861 if (!client || !client->data_class || !reason) { 693 if (!client || !client->data_class || !reason) {
@@ -882,18 +714,9 @@ mobilesync_error_t mobilesync_cancel(mobilesync_client_t client, const char* rea
882 return err; 714 return err;
883} 715}
884 716
885/**
886 * Allocates memory for a new anchors struct initialized with the passed anchors.
887 *
888 * @param device_anchor An anchor the device reported the last time or NULL
889 * if none is known yet which for instance is true on first synchronization.
890 * @param computer_anchor An arbitrary string to use as anchor for the computer.
891 *
892 * @return A new #mobilesync_anchors_t struct. Must be freed using mobilesync_anchors_free().
893 */
894mobilesync_anchors_t mobilesync_anchors_new(const char *device_anchor, const char *computer_anchor) 717mobilesync_anchors_t mobilesync_anchors_new(const char *device_anchor, const char *computer_anchor)
895{ 718{
896 mobilesync_anchors_t anchors = (mobilesync_anchors_t) malloc(sizeof(mobilesync_anchors)); 719 mobilesync_anchors_t anchors = (mobilesync_anchors_t) malloc(sizeof(mobilesync_anchors));
897 if (device_anchor != NULL) { 720 if (device_anchor != NULL) {
898 anchors->device_anchor = strdup(device_anchor); 721 anchors->device_anchor = strdup(device_anchor);
899 } else { 722 } else {
@@ -908,11 +731,6 @@ mobilesync_anchors_t mobilesync_anchors_new(const char *device_anchor, const cha
908 return anchors; 731 return anchors;
909} 732}
910 733
911/**
912 * Free memory used by anchors.
913 *
914 * @param anchors The anchors to free.
915 */
916void mobilesync_anchors_free(mobilesync_anchors_t anchors) 734void mobilesync_anchors_free(mobilesync_anchors_t anchors)
917{ 735{
918 if (anchors->device_anchor != NULL) { 736 if (anchors->device_anchor != NULL) {
@@ -927,28 +745,11 @@ void mobilesync_anchors_free(mobilesync_anchors_t anchors)
927 anchors = NULL; 745 anchors = NULL;
928} 746}
929 747
930/** 748plist_t mobilesync_actions_new(void)
931 * Create a new actions plist to use in mobilesync_send_changes().
932 *
933 * @return A new plist_t of type PLIST_DICT.
934 */
935plist_t mobilesync_actions_new()
936{ 749{
937 return plist_new_dict(); 750 return plist_new_dict();
938} 751}
939 752
940/**
941 * Add one or more new key:value pairs to the given actions plist.
942 *
943 * @param actions The actions to modify.
944 * @param ... KEY, VALUE, [KEY, VALUE], NULL
945 *
946 * @note The known keys so far are "SyncDeviceLinkEntityNamesKey" which expects
947 * an array of entity names, followed by a count paramter as well as
948 * "SyncDeviceLinkAllRecordsOfPulledEntityTypeSentKey" which expects an
949 * integer to use as a boolean value indicating that the device should
950 * link submitted changes and report remapped identifiers.
951 */
952void mobilesync_actions_add(plist_t actions, ...) 753void mobilesync_actions_add(plist_t actions, ...)
953{ 754{
954 if (!actions) 755 if (!actions)
@@ -969,10 +770,10 @@ void mobilesync_actions_add(plist_t actions, ...)
969 plist_array_append_item(array, plist_new_string(entity_names[i])); 770 plist_array_append_item(array, plist_new_string(entity_names[i]));
970 } 771 }
971 772
972 plist_dict_insert_item(actions, key, array); 773 plist_dict_set_item(actions, key, array);
973 } else if (!strcmp(key, "SyncDeviceLinkAllRecordsOfPulledEntityTypeSentKey")) { 774 } else if (!strcmp(key, "SyncDeviceLinkAllRecordsOfPulledEntityTypeSentKey")) {
974 int link_records = va_arg(args, int); 775 int link_records = va_arg(args, int);
975 plist_dict_insert_item(actions, key, plist_new_bool(link_records)); 776 plist_dict_set_item(actions, key, plist_new_bool(link_records));
976 } 777 }
977 free(key); 778 free(key);
978 key = NULL; 779 key = NULL;
@@ -981,11 +782,6 @@ void mobilesync_actions_add(plist_t actions, ...)
981 va_end(args); 782 va_end(args);
982} 783}
983 784
984/**
985 * Free actions plist.
986 *
987 * @param actions The actions plist to free. Does nothing if NULL is passed.
988 */
989void mobilesync_actions_free(plist_t actions) 785void mobilesync_actions_free(plist_t actions)
990{ 786{
991 if (actions) { 787 if (actions) {
diff --git a/src/mobilesync.h b/src/mobilesync.h
index 24e61af..3b5ece9 100644
--- a/src/mobilesync.h
+++ b/src/mobilesync.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * mobilesync.h 2 * mobilesync.h
3 * Definitions for the built-in MobileSync client 3 * Definitions for the built-in MobileSync client
4 * 4 *
5 * Copyright (c) 2010 Bryan Forbes All Rights Reserved. 5 * Copyright (c) 2010 Bryan Forbes All Rights Reserved.
6 * Copyright (c) 2009 Jonathan Beck All Rights Reserved. 6 * Copyright (c) 2009 Jonathan Beck All Rights Reserved.
7 * 7 *
@@ -9,19 +9,21 @@
9 * modify it under the terms of the GNU Lesser General Public 9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either 10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version. 11 * version 2.1 of the License, or (at your option) any later version.
12 * 12 *
13 * This library is distributed in the hope that it will be useful, 13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details. 16 * Lesser General Public License for more details.
17 * 17 *
18 * You should have received a copy of the GNU Lesser General Public 18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software 19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */ 21 */
22#ifndef MOBILESYNC_H
23#define MOBILESYNC_H
24 22
23#ifndef __MOBILESYNC_H
24#define __MOBILESYNC_H
25
26#include "idevice.h"
25#include "libimobiledevice/mobilesync.h" 27#include "libimobiledevice/mobilesync.h"
26#include "device_link_service.h" 28#include "device_link_service.h"
27 29
diff --git a/src/notification_proxy.c b/src/notification_proxy.c
index 80a82c4..60b2e03 100644
--- a/src/notification_proxy.c
+++ b/src/notification_proxy.c
@@ -8,17 +8,20 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
22#include <string.h> 25#include <string.h>
23#include <stdlib.h> 26#include <stdlib.h>
24#include <unistd.h> 27#include <unistd.h>
@@ -26,7 +29,11 @@
26 29
27#include "notification_proxy.h" 30#include "notification_proxy.h"
28#include "property_list_service.h" 31#include "property_list_service.h"
29#include "debug.h" 32#include "common/debug.h"
33
34#ifdef WIN32
35#define sleep(x) Sleep(x*1000)
36#endif
30 37
31struct np_thread { 38struct np_thread {
32 np_client_t client; 39 np_client_t client;
@@ -41,19 +48,19 @@ struct np_thread {
41 */ 48 */
42static void np_lock(np_client_t client) 49static void np_lock(np_client_t client)
43{ 50{
44 debug_info("NP: Locked"); 51 debug_info("Locked");
45 g_mutex_lock(client->mutex); 52 mutex_lock(&client->mutex);
46} 53}
47 54
48/** 55/**
49 * Unlocks a notification_proxy client, used for thread safety. 56 * Unlocks a notification_proxy client, used for thread safety.
50 * 57 *
51 * @param client notification_proxy client to unlock 58 * @param client notification_proxy client to unlock
52 */ 59 */
53static void np_unlock(np_client_t client) 60static void np_unlock(np_client_t client)
54{ 61{
55 debug_info("NP: Unlocked"); 62 debug_info("Unlocked");
56 g_mutex_unlock(client->mutex); 63 mutex_unlock(&client->mutex);
57} 64}
58 65
59/** 66/**
@@ -82,78 +89,85 @@ static np_error_t np_error(property_list_service_error_t err)
82 return NP_E_UNKNOWN_ERROR; 89 return NP_E_UNKNOWN_ERROR;
83} 90}
84 91
85/** 92np_error_t np_client_new(idevice_t device, lockdownd_service_descriptor_t service, np_client_t *client)
86 * Connects to the notification_proxy on the specified device.
87 *
88 * @param device The device to connect to.
89 * @param port Destination port (usually given by lockdownd_start_service).
90 * @param client Pointer that will be set to a newly allocated np_client_t
91 * upon successful return.
92 *
93 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when device is NULL,
94 * or NP_E_CONN_FAILED when the connection to the device could not be
95 * established.
96 */
97np_error_t np_client_new(idevice_t device, uint16_t port, np_client_t *client)
98{ 93{
99 /* makes sure thread environment is available */
100 if (!g_thread_supported())
101 g_thread_init(NULL);
102
103 if (!device)
104 return NP_E_INVALID_ARG;
105
106 property_list_service_client_t plistclient = NULL; 94 property_list_service_client_t plistclient = NULL;
107 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 95 np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient));
108 return NP_E_CONN_FAILED; 96 if (err != NP_E_SUCCESS) {
97 return err;
109 } 98 }
110 99
111 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); 100 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private));
112 client_loc->parent = plistclient; 101 client_loc->parent = plistclient;
113 102
114 client_loc->mutex = g_mutex_new(); 103 mutex_init(&client_loc->mutex);
115 104 client_loc->notifier = THREAD_T_NULL;
116 client_loc->notifier = NULL;
117 105
118 *client = client_loc; 106 *client = client_loc;
119 return NP_E_SUCCESS; 107 return NP_E_SUCCESS;
120} 108}
121 109
122/** 110np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label)
123 * Disconnects a notification_proxy client from the device and frees up the 111{
124 * notification_proxy client data. 112 np_error_t err = NP_E_UNKNOWN_ERROR;
125 * 113 service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err);
126 * @param client The notification_proxy client to disconnect and free. 114 return err;
127 * 115}
128 * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL. 116
129 */
130np_error_t np_client_free(np_client_t client) 117np_error_t np_client_free(np_client_t client)
131{ 118{
119 plist_t dict;
120 property_list_service_client_t parent;
121
132 if (!client) 122 if (!client)
133 return NP_E_INVALID_ARG; 123 return NP_E_INVALID_ARG;
134 124
135 property_list_service_client_free(client->parent); 125 dict = plist_new_dict();
126 plist_dict_set_item(dict,"Command", plist_new_string("Shutdown"));
127 property_list_service_send_xml_plist(client->parent, dict);
128 plist_free(dict);
129
130 parent = client->parent;
131 /* notifies the client->notifier thread that it should terminate */
136 client->parent = NULL; 132 client->parent = NULL;
133
137 if (client->notifier) { 134 if (client->notifier) {
138 debug_info("joining np callback"); 135 debug_info("joining np callback");
139 g_thread_join(client->notifier); 136 thread_join(client->notifier);
140 } 137 thread_free(client->notifier);
141 if (client->mutex) { 138 client->notifier = THREAD_T_NULL;
142 g_mutex_free(client->mutex); 139 } else {
140 dict = NULL;
141 property_list_service_receive_plist(parent, &dict);
142 if (dict) {
143#ifndef STRIP_DEBUG_CODE
144 char *cmd_value = NULL;
145 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
146 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
147 plist_get_string_val(cmd_value_node, &cmd_value);
148 }
149 if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
150 // this is the expected answer
151 } else {
152 debug_info("Did not get ProxyDeath but:");
153 debug_plist(dict);
154 }
155 if (cmd_value) {
156 free(cmd_value);
157 }
158#endif
159 plist_free(dict);
160 }
143 } 161 }
162
163 property_list_service_client_free(parent);
164
165 mutex_destroy(&client->mutex);
144 free(client); 166 free(client);
145 167
146 return NP_E_SUCCESS; 168 return NP_E_SUCCESS;
147} 169}
148 170
149/**
150 * Sends a notification to the device's notification_proxy.
151 *
152 * @param client The client to send to
153 * @param notification The notification message to send
154 *
155 * @return NP_E_SUCCESS on success, or an error returned by np_plist_send
156 */
157np_error_t np_post_notification(np_client_t client, const char *notification) 171np_error_t np_post_notification(np_client_t client, const char *notification)
158{ 172{
159 if (!client || !notification) { 173 if (!client || !notification) {
@@ -162,66 +176,24 @@ np_error_t np_post_notification(np_client_t client, const char *notification)
162 np_lock(client); 176 np_lock(client);
163 177
164 plist_t dict = plist_new_dict(); 178 plist_t dict = plist_new_dict();
165 plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification")); 179 plist_dict_set_item(dict,"Command", plist_new_string("PostNotification"));
166 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 180 plist_dict_set_item(dict,"Name", plist_new_string(notification));
167 181
168 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 182 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
169 plist_free(dict); 183 plist_free(dict);
170 184
171 dict = plist_new_dict();
172 plist_dict_insert_item(dict,"Command", plist_new_string("Shutdown"));
173
174 res = np_error(property_list_service_send_xml_plist(client->parent, dict));
175 plist_free(dict);
176
177 if (res != NP_E_SUCCESS) { 185 if (res != NP_E_SUCCESS) {
178 debug_info("Error sending XML plist to device!"); 186 debug_info("Error sending XML plist to device!");
179 } 187 }
180
181 // try to read an answer, we just ignore errors here
182 dict = NULL;
183 property_list_service_receive_plist(client->parent, &dict);
184 if (dict) {
185#ifndef STRIP_DEBUG_CODE
186 char *cmd_value = NULL;
187 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
188 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
189 plist_get_string_val(cmd_value_node, &cmd_value);
190 }
191
192 if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
193 // this is the expected answer
194 } else {
195 debug_plist(dict);
196 }
197 g_free(cmd_value);
198#endif
199 plist_free(dict);
200 }
201
202 np_unlock(client); 188 np_unlock(client);
203 return res; 189 return res;
204} 190}
205 191
206/** 192static np_error_t internal_np_observe_notification(np_client_t client, const char *notification)
207 * Tells the device to send a notification on the specified event.
208 *
209 * @param client The client to send to
210 * @param notification The notifications that should be observed.
211 *
212 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client or
213 * notification are NULL, or an error returned by np_plist_send.
214 */
215np_error_t np_observe_notification( np_client_t client, const char *notification )
216{ 193{
217 if (!client || !notification) {
218 return NP_E_INVALID_ARG;
219 }
220 np_lock(client);
221
222 plist_t dict = plist_new_dict(); 194 plist_t dict = plist_new_dict();
223 plist_dict_insert_item(dict,"Command", plist_new_string("ObserveNotification")); 195 plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification"));
224 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 196 plist_dict_set_item(dict,"Name", plist_new_string(notification));
225 197
226 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 198 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
227 if (res != NP_E_SUCCESS) { 199 if (res != NP_E_SUCCESS) {
@@ -229,21 +201,20 @@ np_error_t np_observe_notification( np_client_t client, const char *notification
229 } 201 }
230 plist_free(dict); 202 plist_free(dict);
231 203
204 return res;
205}
206
207np_error_t np_observe_notification( np_client_t client, const char *notification )
208{
209 if (!client || !notification) {
210 return NP_E_INVALID_ARG;
211 }
212 np_lock(client);
213 np_error_t res = internal_np_observe_notification(client, notification);
232 np_unlock(client); 214 np_unlock(client);
233 return res; 215 return res;
234} 216}
235 217
236/**
237 * Tells the device to send a notification on specified events.
238 *
239 * @param client The client to send to
240 * @param notification_spec Specification of the notifications that should be
241 * observed. This is expected to be an array of const char* that MUST have a
242 * terminating NULL entry.
243 *
244 * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client is null,
245 * or an error returned by np_observe_notification.
246 */
247np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) 218np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)
248{ 219{
249 int i = 0; 220 int i = 0;
@@ -258,13 +229,15 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
258 return NP_E_INVALID_ARG; 229 return NP_E_INVALID_ARG;
259 } 230 }
260 231
232 np_lock(client);
261 while (notifications[i]) { 233 while (notifications[i]) {
262 res = np_observe_notification(client, notifications[i]); 234 res = internal_np_observe_notification(client, notifications[i]);
263 if (res != NP_E_SUCCESS) { 235 if (res != NP_E_SUCCESS) {
264 break; 236 break;
265 } 237 }
266 i++; 238 i++;
267 } 239 }
240 np_unlock(client);
268 241
269 return res; 242 return res;
270} 243}
@@ -277,7 +250,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
277 * with the notification that has been received. 250 * with the notification that has been received.
278 * 251 *
279 * @return 0 if a notification has been received or nothing has been received, 252 * @return 0 if a notification has been received or nothing has been received,
280 * or a negative value if an error occured. 253 * or a negative value if an error occurred.
281 * 254 *
282 * @note You probably want to check out np_set_notify_callback 255 * @note You probably want to check out np_set_notify_callback
283 * @see np_set_notify_callback 256 * @see np_set_notify_callback
@@ -292,11 +265,15 @@ static int np_get_notification(np_client_t client, char **notification)
292 265
293 np_lock(client); 266 np_lock(client);
294 267
295 property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); 268 property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500);
296 if (!dict) { 269 if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) {
297 debug_info("NotificationProxy: no notification received!"); 270 debug_info("NotificationProxy: no notification received!");
298 res = 0; 271 res = 0;
299 } else { 272 } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
273 debug_info("NotificationProxy: error %d occurred!", perr);
274 res = perr;
275 }
276 if (dict) {
300 char *cmd_value = NULL; 277 char *cmd_value = NULL;
301 plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); 278 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
302 279
@@ -315,11 +292,11 @@ static int np_get_notification(np_client_t client, char **notification)
315 res = -2; 292 res = -2;
316 if (name_value_node && name_value) { 293 if (name_value_node && name_value) {
317 *notification = name_value; 294 *notification = name_value;
318 debug_info("got notification %s\n", __func__, name_value); 295 debug_info("got notification %s", __func__, name_value);
319 res = 0; 296 res = 0;
320 } 297 }
321 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { 298 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
322 debug_info("ERROR: NotificationProxy died!"); 299 debug_info("NotificationProxy died!");
323 res = -1; 300 res = -1;
324 } else if (cmd_value) { 301 } else if (cmd_value) {
325 debug_info("unknown NotificationProxy command '%s' received!", cmd_value); 302 debug_info("unknown NotificationProxy command '%s' received!", cmd_value);
@@ -342,7 +319,7 @@ static int np_get_notification(np_client_t client, char **notification)
342/** 319/**
343 * Internally used thread function. 320 * Internally used thread function.
344 */ 321 */
345gpointer np_notifier( gpointer arg ) 322void* np_notifier( void* arg )
346{ 323{
347 char *notification = NULL; 324 char *notification = NULL;
348 struct np_thread *npt = (struct np_thread*)arg; 325 struct np_thread *npt = (struct np_thread*)arg;
@@ -351,7 +328,10 @@ gpointer np_notifier( gpointer arg )
351 328
352 debug_info("starting callback."); 329 debug_info("starting callback.");
353 while (npt->client->parent) { 330 while (npt->client->parent) {
354 np_get_notification(npt->client, &notification); 331 if (np_get_notification(npt->client, &notification) < 0) {
332 npt->cbfunc("", npt->user_data);
333 break;
334 }
355 if (notification) { 335 if (notification) {
356 npt->cbfunc(notification, npt->user_data); 336 npt->cbfunc(notification, npt->user_data);
357 free(notification); 337 free(notification);
@@ -366,25 +346,6 @@ gpointer np_notifier( gpointer arg )
366 return NULL; 346 return NULL;
367} 347}
368 348
369/**
370 * This function allows an application to define a callback function that will
371 * be called when a notification has been received.
372 * It will start a thread that polls for notifications and calls the callback
373 * function if a notification has been received.
374 *
375 * @param client the NP client
376 * @param notify_cb pointer to a callback function or NULL to de-register a
377 * previously set callback function.
378 * @param user_data Pointer that will be passed to the callback function as
379 * user data. If notify_cb is NULL, this parameter is ignored.
380 *
381 * @note Only one callback function can be registered at the same time;
382 * any previously set callback function will be removed automatically.
383 *
384 * @return NP_E_SUCCESS when the callback was successfully registered,
385 * NP_E_INVALID_ARG when client is NULL, or NP_E_UNKNOWN_ERROR when
386 * the callback thread could no be created.
387 */
388np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) 349np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data )
389{ 350{
390 if (!client) 351 if (!client)
@@ -394,11 +355,12 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
394 355
395 np_lock(client); 356 np_lock(client);
396 if (client->notifier) { 357 if (client->notifier) {
397 debug_info("callback already set, removing\n"); 358 debug_info("callback already set, removing");
398 property_list_service_client_t parent = client->parent; 359 property_list_service_client_t parent = client->parent;
399 client->parent = NULL; 360 client->parent = NULL;
400 g_thread_join(client->notifier); 361 thread_join(client->notifier);
401 client->notifier = NULL; 362 thread_free(client->notifier);
363 client->notifier = THREAD_T_NULL;
402 client->parent = parent; 364 client->parent = parent;
403 } 365 }
404 366
@@ -409,8 +371,7 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
409 npt->cbfunc = notify_cb; 371 npt->cbfunc = notify_cb;
410 npt->user_data = user_data; 372 npt->user_data = user_data;
411 373
412 client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL); 374 if (thread_new(&client->notifier, np_notifier, npt) == 0) {
413 if (client->notifier) {
414 res = NP_E_SUCCESS; 375 res = NP_E_SUCCESS;
415 } 376 }
416 } 377 }
diff --git a/src/notification_proxy.h b/src/notification_proxy.h
index 8d5cd24..595cb01 100644
--- a/src/notification_proxy.h
+++ b/src/notification_proxy.h
@@ -8,30 +8,31 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef INOTIFICATION_PROXY_H
22#define INOTIFICATION_PROXY_H
23 21
24#include <glib.h> 22#ifndef __NOTIFICATION_PROXY_H
23#define __NOTIFICATION_PROXY_H
25 24
25#include "idevice.h"
26#include "libimobiledevice/notification_proxy.h" 26#include "libimobiledevice/notification_proxy.h"
27#include "property_list_service.h" 27#include "property_list_service.h"
28#include <libimobiledevice-glue/thread.h>
28 29
29struct np_client_private { 30struct np_client_private {
30 property_list_service_client_t parent; 31 property_list_service_client_t parent;
31 GMutex *mutex; 32 mutex_t mutex;
32 GThread *notifier; 33 THREAD_T notifier;
33}; 34};
34 35
35gpointer np_notifier(gpointer arg); 36void* np_notifier(void* arg);
36 37
37#endif 38#endif
diff --git a/src/preboard.c b/src/preboard.c
new file mode 100644
index 0000000..c3eff02
--- /dev/null
+++ b/src/preboard.c
@@ -0,0 +1,256 @@
1/*
2 * preboard.c
3 * com.apple.preboardservice_v2 service implementation.
4 *
5 * Copyright (c) 2019 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include <plist/plist.h>
28
29#include "preboard.h"
30#include "lockdown.h"
31#include "common/debug.h"
32
33/**
34 * Convert a property_list_service_error_t value to a preboard_error_t value.
35 * Used internally to get correct error codes.
36 *
37 * @param err An property_list_service_error_t error code
38 *
39 * @return A matching preboard_error_t error code,
40 * PREBOARD_E_UNKNOWN_ERROR otherwise.
41 */
42static preboard_error_t preboard_error(property_list_service_error_t err)
43{
44 switch (err) {
45 case PROPERTY_LIST_SERVICE_E_SUCCESS:
46 return PREBOARD_E_SUCCESS;
47 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
48 return PREBOARD_E_INVALID_ARG;
49 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
50 return PREBOARD_E_PLIST_ERROR;
51 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
52 return PREBOARD_E_MUX_ERROR;
53 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
54 return PREBOARD_E_SSL_ERROR;
55 case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA:
56 return PREBOARD_E_NOT_ENOUGH_DATA;
57 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
58 return PREBOARD_E_TIMEOUT;
59 default:
60 break;
61 }
62 return PREBOARD_E_UNKNOWN_ERROR;
63}
64
65preboard_error_t preboard_client_new(idevice_t device, lockdownd_service_descriptor_t service, preboard_client_t * client)
66{
67 *client = NULL;
68
69 if (!device || !service || service->port == 0 || !client || *client) {
70 debug_info("Incorrect parameter passed to preboard_client_new.");
71 return PREBOARD_E_INVALID_ARG;
72 }
73
74 debug_info("Creating preboard_client, port = %d.", service->port);
75
76 property_list_service_client_t plclient = NULL;
77 preboard_error_t ret = preboard_error(property_list_service_client_new(device, service, &plclient));
78 if (ret != PREBOARD_E_SUCCESS) {
79 debug_info("Creating a property list client failed. Error: %i", ret);
80 return ret;
81 }
82
83 preboard_client_t client_loc = (preboard_client_t) malloc(sizeof(struct preboard_client_private));
84 client_loc->parent = plclient;
85 client_loc->receive_status_thread = THREAD_T_NULL;
86
87 *client = client_loc;
88
89 debug_info("preboard_client successfully created.");
90 return 0;
91}
92
93preboard_error_t preboard_client_start_service(idevice_t device, preboard_client_t * client, const char* label)
94{
95 preboard_error_t err = PREBOARD_E_UNKNOWN_ERROR;
96 service_client_factory_start_service(device, PREBOARD_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(preboard_client_new), &err);
97 return err;
98}
99
100preboard_error_t preboard_client_free(preboard_client_t client)
101{
102 if (!client)
103 return PREBOARD_E_INVALID_ARG;
104
105 property_list_service_client_t parent = client->parent;
106 client->parent = NULL;
107 if (client->receive_status_thread) {
108 debug_info("joining receive_status_thread");
109 thread_join(client->receive_status_thread);
110 thread_free(client->receive_status_thread);
111 client->receive_status_thread = THREAD_T_NULL;
112 }
113 preboard_error_t err = preboard_error(property_list_service_client_free(parent));
114 free(client);
115
116 return err;
117}
118
119preboard_error_t preboard_send(preboard_client_t client, plist_t plist)
120{
121 preboard_error_t res = PREBOARD_E_UNKNOWN_ERROR;
122 res = preboard_error(property_list_service_send_binary_plist(client->parent, plist));
123 if (res != PREBOARD_E_SUCCESS) {
124 debug_info("Sending plist failed with error %d", res);
125 return res;
126 }
127 return res;
128}
129
130preboard_error_t preboard_receive_with_timeout(preboard_client_t client, plist_t * plist, uint32_t timeout_ms)
131{
132 preboard_error_t res = PREBOARD_E_UNKNOWN_ERROR;
133 plist_t outplist = NULL;
134 res = preboard_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, timeout_ms));
135 if (res != PREBOARD_E_SUCCESS && res != PREBOARD_E_TIMEOUT) {
136 debug_info("Could not receive plist, error %d", res);
137 plist_free(outplist);
138 } else if (res == PREBOARD_E_SUCCESS) {
139 *plist = outplist;
140 }
141 return res;
142}
143
144preboard_error_t preboard_receive(preboard_client_t client, plist_t * plist)
145{
146 return preboard_receive_with_timeout(client, plist, 5000);
147}
148
149struct preboard_status_data {
150 preboard_client_t client;
151 preboard_status_cb_t cbfunc;
152 void *user_data;
153};
154
155static void* preboard_receive_status_loop_thread(void* arg)
156{
157 struct preboard_status_data *data = (struct preboard_status_data*)arg;
158
159 /* run until the service disconnects or an error occurs */
160 while (data->client && data->client->parent) {
161 plist_t pl = NULL;
162 preboard_error_t perr = preboard_receive_with_timeout(data->client, &pl, 1000);
163 if (perr == PREBOARD_E_TIMEOUT) {
164 continue;
165 }
166 if (perr == PREBOARD_E_SUCCESS) {
167 data->cbfunc(pl, data->user_data);
168 }
169 plist_free(pl);
170 if (perr != PREBOARD_E_SUCCESS) {
171 data->cbfunc(NULL, data->user_data);
172 break;
173 }
174 }
175
176 /* cleanup */
177 debug_info("done, cleaning up.");
178
179 if (data->client->receive_status_thread) {
180 thread_free(data->client->receive_status_thread);
181 data->client->receive_status_thread = THREAD_T_NULL;
182 }
183 free(data);
184
185 return NULL;
186}
187
188static preboard_error_t preboard_receive_status_loop_with_callback(preboard_client_t client, preboard_status_cb_t status_cb, void *user_data)
189{
190 if (!client || !client->parent) {
191 return PREBOARD_E_INVALID_ARG;
192 }
193
194 if (client->receive_status_thread) {
195 return PREBOARD_E_OP_IN_PROGRESS;
196 }
197
198 preboard_error_t res = PREBOARD_E_UNKNOWN_ERROR;
199 struct preboard_status_data *data = (struct preboard_status_data*)malloc(sizeof(struct preboard_status_data));
200 if (data) {
201 data->client = client;
202 data->cbfunc = status_cb;
203 data->user_data = user_data;
204 if (thread_new(&client->receive_status_thread, preboard_receive_status_loop_thread, data) == 0) {
205 res = PREBOARD_E_SUCCESS;
206 }
207 }
208
209 return res;
210}
211
212preboard_error_t preboard_create_stashbag(preboard_client_t client, plist_t manifest, preboard_status_cb_t status_cb, void *user_data)
213{
214 if (!client) {
215 return PREBOARD_E_INVALID_ARG;
216 }
217
218 plist_t dict = plist_new_dict();
219 plist_dict_set_item(dict, "Command", plist_new_string("CreateStashbag"));
220 if (manifest) {
221 plist_dict_set_item(dict, "Manifest", plist_copy(manifest));
222 }
223 preboard_error_t perr = preboard_send(client, dict);
224 plist_free(dict);
225 if (perr != PREBOARD_E_SUCCESS) {
226 return perr;
227 }
228 if (!status_cb) {
229 return PREBOARD_E_SUCCESS;
230 }
231
232 return preboard_receive_status_loop_with_callback(client, status_cb, user_data);
233}
234
235preboard_error_t preboard_commit_stashbag(preboard_client_t client, plist_t manifest, preboard_status_cb_t status_cb, void *user_data)
236{
237 if (!client) {
238 return PREBOARD_E_INVALID_ARG;
239 }
240
241 plist_t dict = plist_new_dict();
242 plist_dict_set_item(dict, "Command", plist_new_string("CommitStashbag"));
243 if (manifest) {
244 plist_dict_set_item(dict, "Manifest", plist_copy(manifest));
245 }
246 preboard_error_t perr = preboard_send(client, dict);
247 plist_free(dict);
248 if (perr != PREBOARD_E_SUCCESS) {
249 return perr;
250 }
251 if (!status_cb) {
252 return PREBOARD_E_SUCCESS;
253 }
254
255 return preboard_receive_status_loop_with_callback(client, status_cb, user_data);
256}
diff --git a/src/preboard.h b/src/preboard.h
new file mode 100644
index 0000000..f8164eb
--- /dev/null
+++ b/src/preboard.h
@@ -0,0 +1,35 @@
1/*
2 * preboard.h
3 * com.apple.preboard_v2 service header file.
4 *
5 * Copyright (c) 2019 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __PREBOARD_H
23#define __PREBOARD_H
24
25#include "idevice.h"
26#include "libimobiledevice/preboard.h"
27#include "property_list_service.h"
28#include <libimobiledevice-glue/thread.h>
29
30struct preboard_client_private {
31 property_list_service_client_t parent;
32 THREAD_T receive_status_thread;
33};
34
35#endif
diff --git a/src/property_list_service.c b/src/property_list_service.c
index 8af958e..2fca4e7 100644
--- a/src/property_list_service.c
+++ b/src/property_list_service.c
@@ -1,4 +1,4 @@
1/* 1/*
2 * property_list_service.c 2 * property_list_service.c
3 * PropertyList service implementation. 3 * PropertyList service implementation.
4 * 4 *
@@ -8,97 +8,86 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
21#include <stdlib.h> 24#include <stdlib.h>
22#include <string.h> 25#include <string.h>
23#include <glib.h>
24 26
25#include "property_list_service.h" 27#include "property_list_service.h"
26#include "idevice.h" 28#include "common/debug.h"
27#include "debug.h" 29#include "endianness.h"
28 30
29/** 31/**
30 * Convert an idevice_error_t value to an property_list_service_error_t value. 32 * Convert a service_error_t value to a property_list_service_error_t value.
31 * Used internally to get correct error codes. 33 * Used internally to get correct error codes.
32 * 34 *
33 * @param err An idevice_error_t error code 35 * @param err A service_error_t error code
34 * 36 *
35 * @return A matching property_list_service_error_t error code, 37 * @return A matching property_list_service_error_t error code,
36 * PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR otherwise. 38 * PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR otherwise.
37 */ 39 */
38static property_list_service_error_t idevice_to_property_list_service_error(idevice_error_t err) 40static property_list_service_error_t service_to_property_list_service_error(service_error_t err)
39{ 41{
40 switch (err) { 42 switch (err) {
41 case IDEVICE_E_SUCCESS: 43 case SERVICE_E_SUCCESS:
42 return PROPERTY_LIST_SERVICE_E_SUCCESS; 44 return PROPERTY_LIST_SERVICE_E_SUCCESS;
43 case IDEVICE_E_INVALID_ARG: 45 case SERVICE_E_INVALID_ARG:
44 return PROPERTY_LIST_SERVICE_E_INVALID_ARG; 46 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
45 case IDEVICE_E_SSL_ERROR: 47 case SERVICE_E_MUX_ERROR:
48 return PROPERTY_LIST_SERVICE_E_MUX_ERROR;
49 case SERVICE_E_SSL_ERROR:
46 return PROPERTY_LIST_SERVICE_E_SSL_ERROR; 50 return PROPERTY_LIST_SERVICE_E_SSL_ERROR;
51 case SERVICE_E_NOT_ENOUGH_DATA:
52 return PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA;
53 case SERVICE_E_TIMEOUT:
54 return PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT;
47 default: 55 default:
48 break; 56 break;
49 } 57 }
50 return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR; 58 return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR;
51} 59}
52 60
53/** 61property_list_service_error_t property_list_service_client_new(idevice_t device, lockdownd_service_descriptor_t service, property_list_service_client_t *client)
54 * Creates a new property list service for the specified port.
55 *
56 * @param device The device to connect to.
57 * @param port The port on the device to connect to, usually opened by a call to
58 * lockdownd_start_service.
59 * @param client Pointer that will be set to a newly allocated
60 * property_list_service_client_t upon successful return.
61 *
62 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
63 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when one of the arguments is invalid,
64 * or PROPERTY_LIST_SERVICE_E_MUX_ERROR when connecting to the device failed.
65 */
66property_list_service_error_t property_list_service_client_new(idevice_t device, uint16_t port, property_list_service_client_t *client)
67{ 62{
68 if (!device || port == 0 || !client || *client) 63 if (!device || !service || service->port == 0 || !client || *client)
69 return PROPERTY_LIST_SERVICE_E_INVALID_ARG; 64 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
70 65
71 /* Attempt connection */ 66 service_client_t parent = NULL;
72 idevice_connection_t connection = NULL; 67 service_error_t rerr = service_client_new(device, service, &parent);
73 if (idevice_connect(device, port, &connection) != IDEVICE_E_SUCCESS) { 68 if (rerr != SERVICE_E_SUCCESS) {
74 return PROPERTY_LIST_SERVICE_E_MUX_ERROR; 69 return service_to_property_list_service_error(rerr);
75 } 70 }
76 71
77 /* create client object */ 72 /* create client object */
78 property_list_service_client_t client_loc = (property_list_service_client_t)malloc(sizeof(struct property_list_service_client_private)); 73 property_list_service_client_t client_loc = (property_list_service_client_t)malloc(sizeof(struct property_list_service_client_private));
79 client_loc->connection = connection; 74 client_loc->parent = parent;
80 75
76 /* all done, return success */
81 *client = client_loc; 77 *client = client_loc;
82
83 return PROPERTY_LIST_SERVICE_E_SUCCESS; 78 return PROPERTY_LIST_SERVICE_E_SUCCESS;
84} 79}
85 80
86/**
87 * Frees a PropertyList service.
88 *
89 * @param client The property list service to free.
90 *
91 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
92 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client is invalid, or a
93 * PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when another error occured.
94 */
95property_list_service_error_t property_list_service_client_free(property_list_service_client_t client) 81property_list_service_error_t property_list_service_client_free(property_list_service_client_t client)
96{ 82{
97 if (!client) 83 if (!client)
98 return PROPERTY_LIST_SERVICE_E_INVALID_ARG; 84 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
99 85
100 property_list_service_error_t err = idevice_to_property_list_service_error(idevice_disconnect(client->connection)); 86 property_list_service_error_t err = service_to_property_list_service_error(service_client_free(client->parent));
87
101 free(client); 88 free(client);
89 client = NULL;
90
102 return err; 91 return err;
103} 92}
104 93
@@ -113,7 +102,8 @@ property_list_service_error_t property_list_service_client_free(property_list_se
113 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, 102 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
114 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when one or more parameters are 103 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when one or more parameters are
115 * invalid, PROPERTY_LIST_SERVICE_E_PLIST_ERROR when dict is not a valid 104 * invalid, PROPERTY_LIST_SERVICE_E_PLIST_ERROR when dict is not a valid
116 * plist, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when an unspecified 105 * plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a communication error
106 * occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when an unspecified
117 * error occurs. 107 * error occurs.
118 */ 108 */
119static property_list_service_error_t internal_plist_send(property_list_service_client_t client, plist_t plist, int binary) 109static property_list_service_error_t internal_plist_send(property_list_service_client_t client, plist_t plist, int binary)
@@ -122,9 +112,9 @@ static property_list_service_error_t internal_plist_send(property_list_service_c
122 char *content = NULL; 112 char *content = NULL;
123 uint32_t length = 0; 113 uint32_t length = 0;
124 uint32_t nlen = 0; 114 uint32_t nlen = 0;
125 int bytes = 0; 115 uint32_t bytes = 0;
126 116
127 if (!client || (client && !client->connection) || !plist) { 117 if (!client || (client && !client->parent) || !plist) {
128 return PROPERTY_LIST_SERVICE_E_INVALID_ARG; 118 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
129 } 119 }
130 120
@@ -138,15 +128,15 @@ static property_list_service_error_t internal_plist_send(property_list_service_c
138 return PROPERTY_LIST_SERVICE_E_PLIST_ERROR; 128 return PROPERTY_LIST_SERVICE_E_PLIST_ERROR;
139 } 129 }
140 130
141 nlen = GUINT32_TO_BE(length); 131 nlen = htobe32(length);
142 debug_info("sending %d bytes", length); 132 debug_info("sending %d bytes", length);
143 idevice_connection_send(client->connection, (const char*)&nlen, sizeof(nlen), (uint32_t*)&bytes); 133 service_send(client->parent, (const char*)&nlen, sizeof(nlen), &bytes);
144 if (bytes == sizeof(nlen)) { 134 if (bytes == sizeof(nlen)) {
145 idevice_connection_send(client->connection, content, length, (uint32_t*)&bytes); 135 service_send(client->parent, content, length, &bytes);
146 if (bytes > 0) { 136 if (bytes > 0) {
147 debug_info("sent %d bytes", bytes); 137 debug_info("sent %d bytes", bytes);
148 debug_plist(plist); 138 debug_plist(plist);
149 if ((uint32_t)bytes == length) { 139 if (bytes == length) {
150 res = PROPERTY_LIST_SERVICE_E_SUCCESS; 140 res = PROPERTY_LIST_SERVICE_E_SUCCESS;
151 } else { 141 } else {
152 debug_info("ERROR: Could not send all data (%d of %d)!", bytes, length); 142 debug_info("ERROR: Could not send all data (%d of %d)!", bytes, length);
@@ -155,40 +145,18 @@ static property_list_service_error_t internal_plist_send(property_list_service_c
155 } 145 }
156 if (bytes <= 0) { 146 if (bytes <= 0) {
157 debug_info("ERROR: sending to device failed."); 147 debug_info("ERROR: sending to device failed.");
148 res = PROPERTY_LIST_SERVICE_E_MUX_ERROR;
158 } 149 }
159 150
160 free(content); 151 free(content);
161
162 return res; 152 return res;
163} 153}
164 154
165/**
166 * Sends an XML plist.
167 *
168 * @param client The property list service client to use for sending.
169 * @param plist plist to send
170 *
171 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
172 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or plist is NULL,
173 * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when dict is not a valid plist,
174 * or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when an unspecified error occurs.
175 */
176property_list_service_error_t property_list_service_send_xml_plist(property_list_service_client_t client, plist_t plist) 155property_list_service_error_t property_list_service_send_xml_plist(property_list_service_client_t client, plist_t plist)
177{ 156{
178 return internal_plist_send(client, plist, 0); 157 return internal_plist_send(client, plist, 0);
179} 158}
180 159
181/**
182 * Sends a binary plist.
183 *
184 * @param client The property list service client to use for sending.
185 * @param plist plist to send
186 *
187 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
188 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or plist is NULL,
189 * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when dict is not a valid plist,
190 * or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when an unspecified error occurs.
191 */
192property_list_service_error_t property_list_service_send_binary_plist(property_list_service_client_t client, plist_t plist) 160property_list_service_error_t property_list_service_send_binary_plist(property_list_service_client_t client, plist_t plist)
193{ 161{
194 return internal_plist_send(client, plist, 1); 162 return internal_plist_send(client, plist, 1);
@@ -205,6 +173,8 @@ property_list_service_error_t property_list_service_send_binary_plist(property_l
205 * 173 *
206 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success, 174 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
207 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL, 175 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL,
176 * PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA when not enough data
177 * received, PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT when the connection times out,
208 * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be 178 * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be
209 * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a 179 * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a
210 * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR 180 * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR
@@ -216,135 +186,110 @@ static property_list_service_error_t internal_plist_receive_timeout(property_lis
216 uint32_t pktlen = 0; 186 uint32_t pktlen = 0;
217 uint32_t bytes = 0; 187 uint32_t bytes = 0;
218 188
219 if (!client || (client && !client->connection) || !plist) { 189 if (!client || (client && !client->parent) || !plist) {
220 return PROPERTY_LIST_SERVICE_E_INVALID_ARG; 190 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
221 } 191 }
222 192
223 idevice_connection_receive_timeout(client->connection, (char*)&pktlen, sizeof(pktlen), &bytes, timeout); 193 *plist = NULL;
224 debug_info("initial read=%i", bytes); 194 service_error_t serr = service_receive_with_timeout(client->parent, (char*)&pktlen, sizeof(pktlen), &bytes, timeout);
225 if (bytes < 4) { 195 if (serr != SERVICE_E_SUCCESS) {
226 debug_info("initial read failed!"); 196 debug_info("initial read failed!");
227 return PROPERTY_LIST_SERVICE_E_MUX_ERROR; 197 return service_to_property_list_service_error(serr);
228 } else { 198 }
229 pktlen = GUINT32_FROM_BE(pktlen); 199
230 if (pktlen < (1 << 24)) { /* prevent huge buffers */ 200 if (bytes == 0) {
231 uint32_t curlen = 0; 201 /* success but 0 bytes length, assume timeout */
232 char *content = NULL; 202 return PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT;
233 debug_info("%d bytes following", pktlen); 203 }
234 content = (char*)malloc(pktlen); 204
235 205 debug_info("initial read=%i", bytes);
236 while (curlen < pktlen) { 206
237 idevice_connection_receive(client->connection, content+curlen, pktlen-curlen, &bytes); 207 uint32_t curlen = 0;
238 if (bytes <= 0) { 208 char *content = NULL;
239 res = PROPERTY_LIST_SERVICE_E_MUX_ERROR; 209
240 break; 210 pktlen = be32toh(pktlen);
241 } 211 debug_info("%d bytes following", pktlen);
242 debug_info("received %d bytes", bytes); 212 content = (char*)malloc(pktlen);
243 curlen += bytes; 213 if (!content) {
244 } 214 debug_info("out of memory when allocating %d bytes", pktlen);
245 if (!memcmp(content, "bplist00", 8)) { 215 return PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR;
246 plist_from_bin(content, pktlen, plist); 216 }
247 } else { 217
248 /* iOS 4.3 hack: plist data might contain invalid null characters, thus we convert those to spaces */ 218 while (curlen < pktlen) {
249 for (bytes = 0; bytes < pktlen-1; bytes++) { 219 serr = service_receive(client->parent, content+curlen, pktlen-curlen, &bytes);
250 if (content[bytes] == 0x0) 220 if (serr != SERVICE_E_SUCCESS) {
251 content[bytes] = 0x20; 221 res = service_to_property_list_service_error(serr);
252 } 222 break;
253 plist_from_xml(content, pktlen, plist);
254 }
255 if (*plist) {
256 debug_plist(*plist);
257 res = PROPERTY_LIST_SERVICE_E_SUCCESS;
258 } else {
259 res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR;
260 }
261 free(content);
262 content = NULL;
263 } else {
264 res = PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR;
265 } 223 }
224 debug_info("received %d bytes", bytes);
225 curlen += bytes;
266 } 226 }
227
228 if (curlen < pktlen) {
229 debug_info("received incomplete packet (%d of %d bytes)", curlen, pktlen);
230 if (curlen > 0) {
231 debug_info("incomplete packet following:");
232 debug_buffer(content, curlen);
233 }
234 free(content);
235 return res;
236 }
237
238 if ((pktlen > 8) && !memcmp(content, "bplist00", 8)) {
239 plist_from_bin(content, pktlen, plist);
240 } else if ((pktlen > 5) && !memcmp(content, "<?xml", 5)) {
241 /* iOS 4.3+ hack: plist data might contain invalid characters, thus we convert those to spaces */
242 for (bytes = 0; bytes < pktlen-1; bytes++) {
243 if ((content[bytes] >= 0) && (content[bytes] < 0x20) && (content[bytes] != 0x09) && (content[bytes] != 0x0a) && (content[bytes] != 0x0d))
244 content[bytes] = 0x20;
245 }
246 plist_from_xml(content, pktlen, plist);
247 } else {
248 debug_info("WARNING: received unexpected non-plist content");
249 debug_buffer(content, pktlen);
250 }
251
252 if (*plist) {
253 debug_plist(*plist);
254 res = PROPERTY_LIST_SERVICE_E_SUCCESS;
255 } else {
256 res = PROPERTY_LIST_SERVICE_E_PLIST_ERROR;
257 }
258
259 free(content);
260 content = NULL;
261
267 return res; 262 return res;
268} 263}
269 264
270/**
271 * Receives a plist using the given property list service client with specified
272 * timeout.
273 * Binary or XML plists are automatically handled.
274 *
275 * @param client The property list service client to use for receiving
276 * @param plist pointer to a plist_t that will point to the received plist
277 * upon successful return
278 * @param timeout Maximum time in milliseconds to wait for data.
279 *
280 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
281 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when connection or *plist is NULL,
282 * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be
283 * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a
284 * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when
285 * an unspecified error occurs.
286 */
287property_list_service_error_t property_list_service_receive_plist_with_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) 265property_list_service_error_t property_list_service_receive_plist_with_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout)
288{ 266{
289 return internal_plist_receive_timeout(client, plist, timeout); 267 return internal_plist_receive_timeout(client, plist, timeout);
290} 268}
291 269
292/**
293 * Receives a plist using the given property list service client.
294 * Binary or XML plists are automatically handled.
295 *
296 * This function is like property_list_service_receive_plist_with_timeout
297 * using a timeout of 10 seconds.
298 * @see property_list_service_receive_plist_with_timeout
299 *
300 * @param client The property list service client to use for receiving
301 * @param plist pointer to a plist_t that will point to the received plist
302 * upon successful return
303 *
304 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
305 * PROPERTY_LIST_SERVICE_E_INVALID_ARG when client or *plist is NULL,
306 * PROPERTY_LIST_SERVICE_E_PLIST_ERROR when the received data cannot be
307 * converted to a plist, PROPERTY_LIST_SERVICE_E_MUX_ERROR when a
308 * communication error occurs, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR when
309 * an unspecified error occurs.
310 */
311property_list_service_error_t property_list_service_receive_plist(property_list_service_client_t client, plist_t *plist) 270property_list_service_error_t property_list_service_receive_plist(property_list_service_client_t client, plist_t *plist)
312{ 271{
313 return internal_plist_receive_timeout(client, plist, 10000); 272 return internal_plist_receive_timeout(client, plist, 30000);
314} 273}
315 274
316/**
317 * Enable SSL for the given property list service client.
318 *
319 * @param client The connected property list service client for which SSL
320 * should be enabled.
321 *
322 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
323 * PROPERTY_LIST_SERVICE_E_INVALID_ARG if client or client->connection is
324 * NULL, PROPERTY_LIST_SERVICE_E_SSL_ERROR when SSL could not be enabled,
325 * or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR otherwise.
326 */
327property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client) 275property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client)
328{ 276{
329 if (!client || !client->connection) 277 if (!client || !client->parent)
330 return PROPERTY_LIST_SERVICE_E_INVALID_ARG; 278 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
331 return idevice_to_property_list_service_error(idevice_connection_enable_ssl(client->connection)); 279 return service_to_property_list_service_error(service_enable_ssl(client->parent));
332} 280}
333 281
334/**
335 * Disable SSL for the given property list service client.
336 *
337 * @param client The connected property list service client for which SSL
338 * should be disabled.
339 *
340 * @return PROPERTY_LIST_SERVICE_E_SUCCESS on success,
341 * PROPERTY_LIST_SERVICE_E_INVALID_ARG if client or client->connection is
342 * NULL, or PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR otherwise.
343 */
344property_list_service_error_t property_list_service_disable_ssl(property_list_service_client_t client) 282property_list_service_error_t property_list_service_disable_ssl(property_list_service_client_t client)
345{ 283{
346 if (!client || !client->connection) 284 if (!client || !client->parent)
347 return PROPERTY_LIST_SERVICE_E_INVALID_ARG; 285 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
348 return idevice_to_property_list_service_error(idevice_connection_disable_ssl(client->connection)); 286 return service_to_property_list_service_error(service_disable_ssl(client->parent));
349} 287}
350 288
289property_list_service_error_t property_list_service_get_service_client(property_list_service_client_t client, service_client_t *service_client)
290{
291 if (!client || !client->parent || !service_client)
292 return PROPERTY_LIST_SERVICE_E_INVALID_ARG;
293 *service_client = client->parent;
294 return PROPERTY_LIST_SERVICE_E_SUCCESS;
295}
diff --git a/src/property_list_service.h b/src/property_list_service.h
index 037f9aa..0e9e948 100644
--- a/src/property_list_service.h
+++ b/src/property_list_service.h
@@ -1,59 +1,33 @@
1 /* 1/*
2 * property_list_service.h 2 * property_list_service.h
3 * Definitions for the PropertyList service 3 * Definitions for the PropertyList service
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. 5 * Copyright (c) 2010 Nikias Bassen, All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef PROPERTY_LIST_SERVICE_H
22#define PROPERTY_LIST_SERVICE_H
23 21
24#include "idevice.h" 22#ifndef __PROPERTY_LIST_SERVICE_H
25 23#define __PROPERTY_LIST_SERVICE_H
26/* Error Codes */
27#define PROPERTY_LIST_SERVICE_E_SUCCESS 0
28#define PROPERTY_LIST_SERVICE_E_INVALID_ARG -1
29#define PROPERTY_LIST_SERVICE_E_PLIST_ERROR -2
30#define PROPERTY_LIST_SERVICE_E_MUX_ERROR -3
31#define PROPERTY_LIST_SERVICE_E_SSL_ERROR -4
32 24
33#define PROPERTY_LIST_SERVICE_E_UNKNOWN_ERROR -256 25#include "idevice.h"
26#include "libimobiledevice/property_list_service.h"
27#include "service.h"
34 28
35struct property_list_service_client_private { 29struct property_list_service_client_private {
36 idevice_connection_t connection; 30 service_client_t parent;
37}; 31};
38 32
39typedef struct property_list_service_client_private *property_list_service_client_t;
40
41typedef int16_t property_list_service_error_t;
42
43/* creation and destruction */
44property_list_service_error_t property_list_service_client_new(idevice_t device, uint16_t port, property_list_service_client_t *client);
45property_list_service_error_t property_list_service_client_free(property_list_service_client_t client);
46
47/* sending */
48property_list_service_error_t property_list_service_send_xml_plist(property_list_service_client_t client, plist_t plist);
49property_list_service_error_t property_list_service_send_binary_plist(property_list_service_client_t client, plist_t plist);
50
51/* receiving */
52property_list_service_error_t property_list_service_receive_plist_with_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout);
53property_list_service_error_t property_list_service_receive_plist(property_list_service_client_t client, plist_t *plist);
54
55/* misc */
56property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client);
57property_list_service_error_t property_list_service_disable_ssl(property_list_service_client_t client);
58
59#endif 33#endif
diff --git a/src/restore.c b/src/restore.c
index fd23d85..d13a28a 100644
--- a/src/restore.c
+++ b/src/restore.c
@@ -19,17 +19,18 @@
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#include <arpa/inet.h> 22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
23#include <errno.h> 25#include <errno.h>
24#include <string.h> 26#include <string.h>
25#include <stdlib.h> 27#include <stdlib.h>
26#include <glib.h>
27#include <plist/plist.h> 28#include <plist/plist.h>
28 29
29#include "property_list_service.h" 30#include "property_list_service.h"
30#include "restore.h" 31#include "restore.h"
31#include "idevice.h" 32#include "idevice.h"
32#include "debug.h" 33#include "common/debug.h"
33 34
34#define RESULT_SUCCESS 0 35#define RESULT_SUCCESS 0
35#define RESULT_FAILURE 1 36#define RESULT_FAILURE 1
@@ -43,7 +44,7 @@
43 * 44 *
44 * @return RESULT_SUCCESS when the result is 'Success', 45 * @return RESULT_SUCCESS when the result is 'Success',
45 * RESULT_FAILURE when the result is 'Failure', 46 * RESULT_FAILURE when the result is 'Failure',
46 * or a negative value if an error occured during evaluation. 47 * or a negative value if an error occurred during evaluation.
47 */ 48 */
48static int restored_check_result(plist_t dict) 49static int restored_check_result(plist_t dict)
49{ 50{
@@ -87,40 +88,49 @@ static void plist_dict_add_label(plist_t plist, const char *label)
87{ 88{
88 if (plist && label) { 89 if (plist && label) {
89 if (plist_get_node_type(plist) == PLIST_DICT) 90 if (plist_get_node_type(plist) == PLIST_DICT)
90 plist_dict_insert_item(plist, "Label", plist_new_string(label)); 91 plist_dict_set_item(plist, "Label", plist_new_string(label));
91 } 92 }
92} 93}
93 94
94/** 95static restored_error_t restored_error(property_list_service_error_t err)
95 * Closes the restored client session if one is running and frees up the 96{
96 * restored_client struct. 97 switch (err) {
97 * 98 case PROPERTY_LIST_SERVICE_E_SUCCESS:
98 * @param client The restore client 99 return RESTORE_E_SUCCESS;
99 * 100 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
100 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL 101 return RESTORE_E_INVALID_ARG;
101 */ 102 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
103 return RESTORE_E_PLIST_ERROR;
104 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
105 return RESTORE_E_MUX_ERROR;
106 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
107 return RESTORE_E_RECEIVE_TIMEOUT;
108 default:
109 break;
110 }
111 return RESTORE_E_UNKNOWN_ERROR;
112}
113
102restored_error_t restored_client_free(restored_client_t client) 114restored_error_t restored_client_free(restored_client_t client)
103{ 115{
104 if (!client) 116 if (!client)
105 return RESTORE_E_INVALID_ARG; 117 return RESTORE_E_INVALID_ARG;
106 118
107 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR; 119 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR;
108 120
109 if (client->parent) { 121 if (client->parent) {
110 restored_goodbye(client); 122 restored_goodbye(client);
111 123
112 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { 124 ret = restored_error(property_list_service_client_free(client->parent));
113 ret = RESTORE_E_SUCCESS;
114 }
115 } 125 }
116 126
117 if (client->uuid) { 127 if (client->udid) {
118 free(client->uuid); 128 free(client->udid);
119 } 129 }
120 if (client->label) { 130 if (client->label) {
121 free(client->label); 131 free(client->label);
122 } 132 }
123 133
124 if (client->info) { 134 if (client->info) {
125 plist_free(client->info); 135 plist_free(client->info);
126 } 136 }
@@ -129,13 +139,6 @@ restored_error_t restored_client_free(restored_client_t client)
129 return ret; 139 return ret;
130} 140}
131 141
132/**
133 * Sets the label to send for requests to restored.
134 *
135 * @param client The restore client
136 * @param label The label to set or NULL to disable sending a label
137 *
138 */
139void restored_client_set_label(restored_client_t client, const char *label) 142void restored_client_set_label(restored_client_t client, const char *label)
140{ 143{
141 if (client) { 144 if (client) {
@@ -146,71 +149,22 @@ void restored_client_set_label(restored_client_t client, const char *label)
146 } 149 }
147} 150}
148 151
149/**
150 * Receives a plist from restored.
151 *
152 * @param client The restored client
153 * @param plist The plist to store the received data
154 *
155 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client or
156 * plist is NULL
157 */
158restored_error_t restored_receive(restored_client_t client, plist_t *plist) 152restored_error_t restored_receive(restored_client_t client, plist_t *plist)
159{ 153{
160 if (!client || !plist || (plist && *plist)) 154 if (!client || !plist || (plist && *plist))
161 return RESTORE_E_INVALID_ARG; 155 return RESTORE_E_INVALID_ARG;
162
163 restored_error_t ret = RESTORE_E_SUCCESS;
164 property_list_service_error_t err;
165
166 err = property_list_service_receive_plist(client->parent, plist);
167 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
168 ret = RESTORE_E_UNKNOWN_ERROR;
169 }
170 156
171 if (!*plist) 157 return restored_error(property_list_service_receive_plist(client->parent, plist));
172 ret = RESTORE_E_PLIST_ERROR;
173
174 return ret;
175} 158}
176 159
177/**
178 * Sends a plist to restored.
179 *
180 * @note This function is low-level and should only be used if you need to send
181 * a new type of message.
182 *
183 * @param client The restored client
184 * @param plist The plist to send
185 *
186 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client or
187 * plist is NULL
188 */
189restored_error_t restored_send(restored_client_t client, plist_t plist) 160restored_error_t restored_send(restored_client_t client, plist_t plist)
190{ 161{
191 if (!client || !plist) 162 if (!client || !plist)
192 return RESTORE_E_INVALID_ARG; 163 return RESTORE_E_INVALID_ARG;
193 164
194 restored_error_t ret = RESTORE_E_SUCCESS; 165 return restored_error(property_list_service_send_xml_plist(client->parent, plist));
195 idevice_error_t err;
196
197 err = property_list_service_send_xml_plist(client->parent, plist);
198 if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) {
199 ret = RESTORE_E_UNKNOWN_ERROR;
200 }
201 return ret;
202} 166}
203 167
204/**
205 * Query the type of the service daemon. Depending on whether the device is
206 * queried in normal mode or restore mode, different types will be returned.
207 *
208 * @param client The restored client
209 * @param type The type returned by the service daemon. Pass NULL to ignore.
210 * @param version The restore protocol version. Pass NULL to ignore.
211 *
212 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
213 */
214restored_error_t restored_query_type(restored_client_t client, char **type, uint64_t *version) 168restored_error_t restored_query_type(restored_client_t client, char **type, uint64_t *version)
215{ 169{
216 if (!client) 170 if (!client)
@@ -220,7 +174,7 @@ restored_error_t restored_query_type(restored_client_t client, char **type, uint
220 174
221 plist_t dict = plist_new_dict(); 175 plist_t dict = plist_new_dict();
222 plist_dict_add_label(dict, client->label); 176 plist_dict_add_label(dict, client->label);
223 plist_dict_insert_item(dict,"Request", plist_new_string("QueryType")); 177 plist_dict_set_item(dict,"Request", plist_new_string("QueryType"));
224 178
225 debug_info("called"); 179 debug_info("called");
226 ret = restored_send(client, dict); 180 ret = restored_send(client, dict);
@@ -229,25 +183,25 @@ restored_error_t restored_query_type(restored_client_t client, char **type, uint
229 dict = NULL; 183 dict = NULL;
230 184
231 ret = restored_receive(client, &dict); 185 ret = restored_receive(client, &dict);
232 186
233 if (RESTORE_E_SUCCESS != ret) 187 if (RESTORE_E_SUCCESS != ret)
234 return ret; 188 return ret;
235 189
236 ret = RESTORE_E_UNKNOWN_ERROR; 190 ret = RESTORE_E_UNKNOWN_ERROR;
237 if (restored_check_result(dict) == RESULT_SUCCESS) { 191 plist_t type_node = plist_dict_get_item(dict, "Type");
192 if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) {
193 char* typestr = NULL;
194
238 /* save our device information info */ 195 /* save our device information info */
239 client->info = dict; 196 client->info = dict;
240 197
198 plist_get_string_val(type_node, &typestr);
199 debug_info("success with type %s", typestr);
241 /* return the type if requested */ 200 /* return the type if requested */
242 if (type) { 201 if (type) {
243 plist_t type_node = plist_dict_get_item(dict, "Type"); 202 *type = typestr;
244 if (type_node && PLIST_STRING == plist_get_node_type(type_node)) { 203 } else {
245 plist_get_string_val(type_node, type); 204 free(typestr);
246 debug_info("success with type %s", *type);
247 ret = RESTORE_E_SUCCESS;
248 } else {
249 return RESTORE_E_UNKNOWN_ERROR;
250 }
251 } 205 }
252 206
253 /* fetch the restore protocol version */ 207 /* fetch the restore protocol version */
@@ -256,87 +210,121 @@ restored_error_t restored_query_type(restored_client_t client, char **type, uint
256 if (version_node && PLIST_UINT == plist_get_node_type(version_node)) { 210 if (version_node && PLIST_UINT == plist_get_node_type(version_node)) {
257 plist_get_uint_val(version_node, version); 211 plist_get_uint_val(version_node, version);
258 debug_info("restored protocol version %llu", *version); 212 debug_info("restored protocol version %llu", *version);
259 ret = RESTORE_E_SUCCESS;
260 } else { 213 } else {
261 return RESTORE_E_UNKNOWN_ERROR; 214 return RESTORE_E_UNKNOWN_ERROR;
262 } 215 }
263 } 216 }
264 ret = RESTORE_E_SUCCESS; 217 ret = RESTORE_E_SUCCESS;
218 } else {
219 debug_info("hmm. QueryType response does not contain a type?!");
220 debug_plist(dict);
221 plist_free(dict);
265 } 222 }
266 223
267 return ret; 224 return ret;
268} 225}
269 226
270/** 227restored_error_t restored_query_value(restored_client_t client, const char *key, plist_t *value)
271 * Retrieves a value from information plist specified by a key.
272 *
273 * @param client An initialized restored client.
274 * @param key The key name to request or NULL to query for all keys
275 * @param value A plist node representing the result value node
276 *
277 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, RESTORE_E_PLIST_ERROR if value for key can't be found
278 */
279restored_error_t restored_get_value(restored_client_t client, const char *key, plist_t *value)
280{ 228{
229 if (!client || !key)
230 return RESTORE_E_INVALID_ARG;
231
232 plist_t dict = NULL;
233 restored_error_t ret = RESTORE_E_UNKNOWN_ERROR;
234
235 /* setup request plist */
236 dict = plist_new_dict();
237 plist_dict_add_label(dict, client->label);
238 if (key) {
239 plist_dict_set_item(dict,"QueryKey", plist_new_string(key));
240 }
241 plist_dict_set_item(dict,"Request", plist_new_string("QueryValue"));
242
243 /* send to device */
244 ret = restored_send(client, dict);
245
246 plist_free(dict);
247 dict = NULL;
248
249 if (ret != RESTORE_E_SUCCESS)
250 return ret;
251
252 /* Now get device's answer */
253 ret = restored_receive(client, &dict);
254 if (ret != RESTORE_E_SUCCESS)
255 return ret;
256
257 plist_t value_node = plist_dict_get_item(dict, key);
258 if (value_node) {
259 debug_info("has a value");
260 *value = plist_copy(value_node);
261 } else {
262 ret = RESTORE_E_PLIST_ERROR;
263 }
264
265 plist_free(dict);
266 return ret;
267}
268
269restored_error_t restored_get_value(restored_client_t client, const char *key, plist_t *value)
270{
271 plist_t item;
272
281 if (!client || !value || (value && *value)) 273 if (!client || !value || (value && *value))
282 return RESTORE_E_INVALID_ARG; 274 return RESTORE_E_INVALID_ARG;
283 275
284 if (!client->info) 276 if (!client->info)
285 return RESTORE_E_NOT_ENOUGH_DATA; 277 return RESTORE_E_NOT_ENOUGH_DATA;
286 278
287 restored_error_t ret = RESTORE_E_SUCCESS;
288 plist_t item = NULL;
289
290 if (!key) { 279 if (!key) {
291 *value = plist_copy(client->info); 280 *value = plist_copy(client->info);
292 return RESTORE_E_SUCCESS; 281 return RESTORE_E_SUCCESS;
293 } else {
294 item = plist_dict_get_item(client->info, key);
295 } 282 }
296 283
297 if (item) { 284 item = plist_dict_get_item(client->info, key);
298 *value = plist_copy(item); 285 if (!item) {
299 } else { 286 return RESTORE_E_PLIST_ERROR;
300 ret = RESTORE_E_PLIST_ERROR;
301 } 287 }
302 288
303 return ret; 289 *value = plist_copy(item);
290
291 return RESTORE_E_SUCCESS;
304} 292}
305 293
306/**
307 * Creates a new restored client for the device.
308 *
309 * @param device The device to create a restored client for
310 * @param client The pointer to the location of the new restored_client
311 * @param label The label to use for communication. Usually the program name.
312 *
313 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL
314 */
315restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label) 294restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label)
316{ 295{
317 if (!client) 296 if (!client)
318 return RESTORE_E_INVALID_ARG; 297 return RESTORE_E_INVALID_ARG;
319 298
320 restored_error_t ret = RESTORE_E_SUCCESS; 299 restored_error_t ret = RESTORE_E_SUCCESS;
300 idevice_error_t idev_ret;
301
302 static struct lockdownd_service_descriptor service = {
303 .port = 0xf27e,
304 .ssl_enabled = 0
305 };
321 306
322 property_list_service_client_t plistclient = NULL; 307 property_list_service_client_t plistclient = NULL;
323 if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 308 ret = restored_error(property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient));
324 debug_info("could not connect to restored (device %s)", device->uuid); 309 if (ret != RESTORE_E_SUCCESS) {
325 return RESTORE_E_MUX_ERROR; 310 debug_info("could not connect to restored (device %s)", device->udid);
311 return ret;
326 } 312 }
327 313
328 restored_client_t client_loc = (restored_client_t) malloc(sizeof(struct restored_client_private)); 314 restored_client_t client_loc = (restored_client_t) malloc(sizeof(struct restored_client_private));
329 client_loc->parent = plistclient; 315 client_loc->parent = plistclient;
330 client_loc->uuid = NULL; 316 client_loc->udid = NULL;
331 client_loc->label = NULL; 317 client_loc->label = NULL;
318 client_loc->info = NULL;
332 if (label != NULL) 319 if (label != NULL)
333 client_loc->label = strdup(label); 320 client_loc->label = strdup(label);
334 321
335 ret = idevice_get_uuid(device, &client_loc->uuid); 322 idev_ret = idevice_get_udid(device, &client_loc->udid);
336 if (RESTORE_E_SUCCESS != ret) { 323 if (IDEVICE_E_SUCCESS != idev_ret) {
337 debug_info("failed to get device uuid."); 324 debug_info("failed to get device udid.");
325 ret = RESTORE_E_UNKNOWN_ERROR;
338 } 326 }
339 debug_info("device uuid: %s", client_loc->uuid); 327 debug_info("device udid: %s", client_loc->udid);
340 328
341 if (RESTORE_E_SUCCESS == ret) { 329 if (RESTORE_E_SUCCESS == ret) {
342 *client = client_loc; 330 *client = client_loc;
@@ -347,14 +335,6 @@ restored_error_t restored_client_new(idevice_t device, restored_client_t *client
347 return ret; 335 return ret;
348} 336}
349 337
350/**
351 * Sends the Goodbye request to restored signaling the end of communication.
352 *
353 * @param client The restore client
354 *
355 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL,
356 * RESTORE_E_PLIST_ERROR if the device did not acknowledge the request
357 */
358restored_error_t restored_goodbye(restored_client_t client) 338restored_error_t restored_goodbye(restored_client_t client)
359{ 339{
360 if (!client) 340 if (!client)
@@ -364,7 +344,7 @@ restored_error_t restored_goodbye(restored_client_t client)
364 344
365 plist_t dict = plist_new_dict(); 345 plist_t dict = plist_new_dict();
366 plist_dict_add_label(dict, client->label); 346 plist_dict_add_label(dict, client->label);
367 plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); 347 plist_dict_set_item(dict,"Request", plist_new_string("Goodbye"));
368 348
369 debug_info("called"); 349 debug_info("called");
370 350
@@ -387,15 +367,7 @@ restored_error_t restored_goodbye(restored_client_t client)
387 return ret; 367 return ret;
388} 368}
389 369
390/** 370restored_error_t restored_start_restore(restored_client_t client, plist_t options, uint64_t version)
391 * Requests to start a restore and retrieve it's port on success.
392 *
393 * @param client The restored client
394 *
395 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter
396 * is NULL, RESTORE_E_START_RESTORE_FAILED if the request fails
397 */
398restored_error_t restored_start_restore(restored_client_t client)
399{ 371{
400 if (!client) 372 if (!client)
401 return RESTORE_E_INVALID_ARG; 373 return RESTORE_E_INVALID_ARG;
@@ -405,8 +377,11 @@ restored_error_t restored_start_restore(restored_client_t client)
405 377
406 dict = plist_new_dict(); 378 dict = plist_new_dict();
407 plist_dict_add_label(dict, client->label); 379 plist_dict_add_label(dict, client->label);
408 plist_dict_insert_item(dict,"Request", plist_new_string("StartRestore")); 380 plist_dict_set_item(dict,"Request", plist_new_string("StartRestore"));
409 plist_dict_insert_item(dict,"RestoreProtocolVersion", plist_new_uint(2)); 381 if (options) {
382 plist_dict_set_item(dict, "RestoreOptions", plist_copy(options));
383 }
384 plist_dict_set_item(dict,"RestoreProtocolVersion", plist_new_uint(version));
410 385
411 /* send to device */ 386 /* send to device */
412 ret = restored_send(client, dict); 387 ret = restored_send(client, dict);
@@ -416,14 +391,6 @@ restored_error_t restored_start_restore(restored_client_t client)
416 return ret; 391 return ret;
417} 392}
418 393
419/**
420 * Requests device to reboot.
421 *
422 * @param client The restored client
423 *
424 * @return RESTORE_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter
425 * is NULL
426 */
427restored_error_t restored_reboot(restored_client_t client) 394restored_error_t restored_reboot(restored_client_t client)
428{ 395{
429 if (!client) 396 if (!client)
@@ -434,7 +401,7 @@ restored_error_t restored_reboot(restored_client_t client)
434 401
435 dict = plist_new_dict(); 402 dict = plist_new_dict();
436 plist_dict_add_label(dict, client->label); 403 plist_dict_add_label(dict, client->label);
437 plist_dict_insert_item(dict,"Request", plist_new_string("Reboot")); 404 plist_dict_set_item(dict,"Request", plist_new_string("Reboot"));
438 405
439 /* send to device */ 406 /* send to device */
440 ret = restored_send(client, dict); 407 ret = restored_send(client, dict);
@@ -455,4 +422,3 @@ restored_error_t restored_reboot(restored_client_t client)
455 dict = NULL; 422 dict = NULL;
456 return ret; 423 return ret;
457} 424}
458
diff --git a/src/restore.h b/src/restore.h
index d790d01..ec6fa04 100644
--- a/src/restore.h
+++ b/src/restore.h
@@ -19,17 +19,18 @@
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifndef RESTORED_H 22#ifndef __RESTORED_H
23#define RESTORED_H 23#define __RESTORED_H
24 24
25#include <string.h> 25#include <string.h>
26 26
27#include "idevice.h"
27#include "libimobiledevice/restore.h" 28#include "libimobiledevice/restore.h"
28#include "property_list_service.h" 29#include "property_list_service.h"
29 30
30struct restored_client_private { 31struct restored_client_private {
31 property_list_service_client_t parent; 32 property_list_service_client_t parent;
32 char *uuid; 33 char *udid;
33 char *label; 34 char *label;
34 plist_t info; 35 plist_t info;
35}; 36};
diff --git a/src/reverse_proxy.c b/src/reverse_proxy.c
new file mode 100644
index 0000000..2fcfdd1
--- /dev/null
+++ b/src/reverse_proxy.c
@@ -0,0 +1,810 @@
1/*
2 * reverse_proxy.c
3 * com.apple.PurpleReverseProxy service implementation.
4 *
5 * Copyright (c) 2021 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2014 BALATON Zoltan. All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26#include <string.h>
27#include <stdlib.h>
28#define _GNU_SOURCE 1
29#define __USE_GNU 1
30#include <stdio.h>
31#include <errno.h>
32
33#include <plist/plist.h>
34#include <libimobiledevice-glue/thread.h>
35#include <libimobiledevice-glue/socket.h>
36
37#include "reverse_proxy.h"
38#include "lockdown.h"
39#include "common/debug.h"
40#include "endianness.h"
41#include "asprintf.h"
42
43#ifndef ECONNRESET
44#define ECONNRESET 108
45#endif
46#ifndef ETIMEDOUT
47#define ETIMEDOUT 138
48#endif
49
50#define CTRL_PORT 1082
51#define CTRLCMD "BeginCtrl"
52#define HELLOCTRLCMD "HelloCtrl"
53#define HELLOCMD "HelloConn"
54
55#define RP_SYNC_MSG 0x1
56#define RP_PROXY_MSG 0x105
57#define RP_PLIST_MSG 0xbbaa
58
59/**
60 * Convert a service_error_t value to a reverse_proxy_error_t value.
61 * Used internally to get correct error codes.
62 *
63 * @param err A service_error_t error code
64 *
65 * @return A matching reverse_proxy_error_t error code,
66 * REVERSE_PROXY_E_UNKNOWN_ERROR otherwise.
67 */
68static reverse_proxy_error_t reverse_proxy_error(service_error_t err)
69{
70 switch (err) {
71 case SERVICE_E_SUCCESS:
72 return REVERSE_PROXY_E_SUCCESS;
73 case SERVICE_E_INVALID_ARG:
74 return REVERSE_PROXY_E_INVALID_ARG;
75 case SERVICE_E_MUX_ERROR:
76 return REVERSE_PROXY_E_MUX_ERROR;
77 case SERVICE_E_SSL_ERROR:
78 return REVERSE_PROXY_E_SSL_ERROR;
79 case SERVICE_E_NOT_ENOUGH_DATA:
80 return REVERSE_PROXY_E_NOT_ENOUGH_DATA;
81 case SERVICE_E_TIMEOUT:
82 return REVERSE_PROXY_E_TIMEOUT;
83 default:
84 break;
85 }
86 return REVERSE_PROXY_E_UNKNOWN_ERROR;
87}
88
89static void _reverse_proxy_log(reverse_proxy_client_t client, const char* format, ...)
90{
91 if (!client || !client->log_cb) {
92 return;
93 }
94 va_list args;
95 va_start(args, format);
96 char* buffer = NULL;
97 if(vasprintf(&buffer, format, args)<0){}
98 va_end(args);
99 client->log_cb(client, buffer, client->log_cb_user_data);
100 free(buffer);
101}
102
103static void _reverse_proxy_data(reverse_proxy_client_t client, int direction, char* buffer, uint32_t length)
104{
105 if (!client || !client->data_cb) {
106 return;
107 }
108 client->data_cb(client, direction, buffer, length, client->data_cb_user_data);
109}
110
111static void _reverse_proxy_status(reverse_proxy_client_t client, int status, const char* format, ...)
112{
113 if (!client || !client->status_cb) {
114 return;
115 }
116 va_list args;
117 va_start(args, format);
118 char* buffer = NULL;
119 if(vasprintf(&buffer, format, args)<0){}
120 va_end(args);
121 client->status_cb(client, status, buffer, client->status_cb_user_data);
122 free(buffer);
123}
124
125static int _reverse_proxy_handle_proxy_cmd(reverse_proxy_client_t client)
126{
127 reverse_proxy_error_t err = REVERSE_PROXY_E_SUCCESS;
128 char *buf = NULL;
129 size_t bufsize = 1048576;
130 uint32_t sent = 0, bytes = 0;
131 uint32_t sent_total = 0;
132 uint32_t recv_total = 0;
133 char *host = NULL;
134 uint16_t port = 0;
135
136 buf = malloc(bufsize);
137 if (!buf) {
138 _reverse_proxy_log(client, "ERROR: Failed to allocate buffer");
139 return -1;
140 }
141
142 err = reverse_proxy_receive(client, buf, bufsize, &bytes);
143 if (err != REVERSE_PROXY_E_SUCCESS) {
144 free(buf);
145 _reverse_proxy_log(client, "ERROR: Unable to read data for proxy command");
146 return -1;
147 }
148 _reverse_proxy_log(client, "Handling proxy command");
149
150 /* Just return success here unconditionally because we don't know
151 * anything else and we will eventually abort on failure anyway */
152 uint16_t ack = 5;
153 err = reverse_proxy_send(client, (char *)&ack, sizeof(ack), &sent);
154 if (err != REVERSE_PROXY_E_SUCCESS || sent != sizeof(ack)) {
155 free(buf);
156 _reverse_proxy_log(client, "ERROR: Unable to send ack. Sent %u of %u bytes.", sent, (uint32_t)sizeof(ack));
157 return -1;
158 }
159
160 if (bytes < 3) {
161 free(buf);
162 _reverse_proxy_log(client, "Proxy command data too short, retrying");
163 return 0;
164 }
165
166 /* ack command data too */
167 err = reverse_proxy_send(client, buf, bytes, &sent);
168 if (err != REVERSE_PROXY_E_SUCCESS || sent != bytes) {
169 free(buf);
170 _reverse_proxy_log(client, "ERROR: Unable to send data. Sent %u of %u bytes.", sent, bytes);
171 return -1;
172 }
173
174 /* Now try to handle actual messages */
175 /* Connect: 0 3 hostlen <host> <port> */
176 if (buf[0] == 0 && buf[1] == 3) {
177 uint16_t *p = (uint16_t *)&buf[bytes - 2];
178 port = be16toh(*p);
179 buf[bytes - 2] = '\0';
180 host = strdup(&buf[3]);
181 _reverse_proxy_log(client, "Connect request to %s:%u", host, port);
182 }
183
184 if (!host || !buf[2]) {
185 /* missing or zero length host name */
186 free(buf);
187 return 0;
188 }
189
190 /* else wait for messages and forward them */
191 int sockfd = socket_connect(host, port);
192 if (sockfd < 0) {
193 free(buf);
194 _reverse_proxy_log(client, "ERROR: Connection to %s:%u failed: %s", host, port, strerror(errno));
195 free(host);
196 return -1;
197 }
198
199 _reverse_proxy_status(client, RP_STATUS_CONNECTED, "Connected to %s:%u", host, port);
200
201 int res = 0, bytes_ret;
202 while (1) {
203 bytes = 0;
204 err = reverse_proxy_receive_with_timeout(client, buf, bufsize, &bytes, 100);
205 if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && !bytes)) {
206 /* just a timeout condition */
207 }
208 else if (err != REVERSE_PROXY_E_SUCCESS) {
209 _reverse_proxy_log(client, "Connection closed");
210 res = -1;
211 break;
212 }
213 if (bytes) {
214 _reverse_proxy_log(client, "Proxying %u bytes of data", bytes);
215 _reverse_proxy_data(client, RP_DATA_DIRECTION_OUT, buf, bytes);
216 sent = 0;
217 while (sent < bytes) {
218 int s = socket_send(sockfd, buf + sent, bytes - sent);
219 if (s < 0) {
220 break;
221 }
222 sent += s;
223 }
224 sent_total += sent;
225 if (sent != bytes) {
226 _reverse_proxy_log(client, "ERROR: Sending proxy payload failed: %s. Sent %u of %u bytes.", strerror(errno), sent, bytes);
227 socket_close(sockfd);
228 res = -1;
229 break;
230 }
231 }
232 bytes_ret = socket_receive_timeout(sockfd, buf, bufsize, 0, 100);
233 if (bytes_ret == -ETIMEDOUT) {
234 bytes_ret = 0;
235 } else if (bytes_ret == -ECONNRESET) {
236 res = 1;
237 break;
238 } else if (bytes_ret < 0) {
239 _reverse_proxy_log(client, "ERROR: Failed to receive from host: %s", strerror(-bytes_ret));
240 break;
241 }
242
243 bytes = bytes_ret;
244 if (bytes) {
245 _reverse_proxy_log(client, "Received %u bytes reply data, sending to device\n", bytes);
246 _reverse_proxy_data(client, RP_DATA_DIRECTION_IN, buf, bytes);
247 recv_total += bytes;
248 sent = 0;
249 while (sent < bytes) {
250 uint32_t s;
251 err = reverse_proxy_send(client, buf + sent, bytes - sent, &s);
252 if (err != REVERSE_PROXY_E_SUCCESS) {
253 break;
254 }
255 sent += s;
256 }
257 if (err != REVERSE_PROXY_E_SUCCESS || bytes != sent) {
258 _reverse_proxy_log(client, "ERROR: Unable to send data (%d). Sent %u of %u bytes.", err, sent, bytes);
259 res = -1;
260 break;
261 }
262 }
263 }
264 socket_close(sockfd);
265 free(host);
266 free(buf);
267
268 _reverse_proxy_status(client, RP_STATUS_DISCONNECTED, "Disconnected (out: %u / in: %u)", sent_total, recv_total);
269
270 return res;
271}
272
273static int _reverse_proxy_handle_plist_cmd(reverse_proxy_client_t client)
274{
275 plist_t dict;
276 reverse_proxy_error_t err;
277
278 err = reverse_proxy_receive_plist(client, &dict);
279 if (err != REVERSE_PROXY_E_SUCCESS) {
280 _reverse_proxy_log(client, "ERROR: Unable to receive plist command, error", err);
281 return -1;
282 }
283 plist_t node = plist_dict_get_item(dict, "Command");
284 if (!node || (plist_get_node_type(node) != PLIST_STRING)) {
285 _reverse_proxy_log(client, "ERROR: No 'Command' in reply", err);
286 plist_free(dict);
287 return -1;
288 }
289 char *command = NULL;
290 plist_get_string_val(node, &command);
291 plist_free(dict);
292
293 if (!command) {
294 _reverse_proxy_log(client, "ERROR: Empty 'Command' string");
295 return -1;
296 }
297
298 if (!strcmp(command, "Ping")) {
299 _reverse_proxy_log(client, "Received Ping command, replying with Pong");
300 dict = plist_new_dict();
301 plist_dict_set_item(dict, "Pong", plist_new_bool(1));
302 err = reverse_proxy_send_plist(client, dict);
303 plist_free(dict);
304 if (err) {
305 _reverse_proxy_log(client, "ERROR: Unable to send Ping command reply");
306 free(command);
307 return -1;
308 }
309 } else {
310 _reverse_proxy_log(client, "WARNING: Received unhandled plist command '%s'", command);
311 free(command);
312 return -1;
313 }
314
315 free(command);
316 /* reverse proxy connection will be terminated remotely. Next receive will get nothing, error and terminate this worker thread. */
317 return 0;
318}
319
320static reverse_proxy_error_t reverse_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, reverse_proxy_client_t * client)
321{
322 *client = NULL;
323
324 if (!device || !service || service->port == 0 || !client || *client) {
325 return REVERSE_PROXY_E_INVALID_ARG;
326 }
327
328 debug_info("Creating reverse_proxy_client, port = %d.", service->port);
329
330 service_client_t sclient = NULL;
331 reverse_proxy_error_t ret = reverse_proxy_error(service_client_new(device, service, &sclient));
332 if (ret != REVERSE_PROXY_E_SUCCESS) {
333 debug_info("Creating service client failed. Error: %i", ret);
334 return ret;
335 }
336
337 reverse_proxy_client_t client_loc = (reverse_proxy_client_t) calloc(1, sizeof(struct reverse_proxy_client_private));
338 client_loc->parent = sclient;
339 client_loc->th_ctrl = THREAD_T_NULL;
340 *client = client_loc;
341
342 return 0;
343}
344
345static void* _reverse_proxy_connection_thread(void *cdata)
346{
347 reverse_proxy_client_t client = (reverse_proxy_client_t)cdata;
348 uint32_t bytes = 0;
349 reverse_proxy_client_t conn_client = NULL;
350 reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR;
351
352 if (client->conn_port == 0) {
353 service_client_factory_start_service(client->parent->connection->device, "com.apple.PurpleReverseProxy.Conn", (void**)&conn_client, client->label, SERVICE_CONSTRUCTOR(reverse_proxy_client_new), &err);
354 if (!conn_client) {
355 _reverse_proxy_log(client, "ERROR: Failed to start proxy connection service, error %d", err);
356 }
357 } else {
358 struct lockdownd_service_descriptor svc;
359 svc.port = client->conn_port;
360 svc.ssl_enabled = 0;
361 svc.identifier = NULL;
362 err = reverse_proxy_client_new(client->parent->connection->device, &svc, &conn_client);
363 if (!conn_client) {
364 _reverse_proxy_log(client, "ERROR: Failed to connect to proxy connection port %u, error %d", client->conn_port, err);
365 }
366 }
367 if (!conn_client) {
368 goto leave;
369 }
370 conn_client->type = RP_TYPE_CONN;
371 conn_client->protoversion = client->protoversion;
372 conn_client->log_cb = client->log_cb;
373 conn_client->log_cb_user_data = client->log_cb_user_data;
374 conn_client->status_cb = client->status_cb;
375 conn_client->status_cb_user_data = client->status_cb_user_data;
376
377 err = reverse_proxy_send(conn_client, HELLOCMD, sizeof(HELLOCMD), &bytes);
378 if (err != REVERSE_PROXY_E_SUCCESS || bytes != sizeof(HELLOCMD)) {
379 _reverse_proxy_log(conn_client, "ERROR: Unable to send " HELLOCMD " (sent %u/%u bytes)", bytes, sizeof(HELLOCMD));
380 goto leave;
381 }
382
383 if (conn_client->protoversion == 2) {
384 plist_t reply = NULL;
385 err = reverse_proxy_receive_plist(conn_client, &reply);
386 if (err != REVERSE_PROXY_E_SUCCESS) {
387 _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " reply, error %d", err);
388 goto leave;
389 }
390 char* identifier = NULL;
391 char* cmd = NULL;
392 plist_t node = NULL;
393 node = plist_dict_get_item(reply, "Command");
394 if (node) {
395 plist_get_string_val(node, &cmd);
396 }
397 node = plist_dict_get_item(reply, "Identifier");
398 if (node) {
399 plist_get_string_val(node, &identifier);
400 }
401 plist_free(reply);
402
403 if (!cmd || (strcmp(cmd, HELLOCMD) != 0)) {
404 free(cmd);
405 free(identifier);
406 _reverse_proxy_log(conn_client, "ERROR: Unexpected reply to " HELLOCMD " received");
407 goto leave;
408 }
409 free(cmd);
410
411 if (identifier) {
412 _reverse_proxy_log(conn_client, "Got device identifier %s", identifier);
413 free(identifier);
414 }
415 } else {
416 char buf[16];
417 memset(buf, '\0', sizeof(buf));
418 bytes = 0;
419 err = reverse_proxy_receive(conn_client, buf, sizeof(HELLOCMD), &bytes);
420 if (err != REVERSE_PROXY_E_SUCCESS) {
421 _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " reply, error %d", err);
422 goto leave;
423 }
424 if (memcmp(buf, HELLOCMD, sizeof(HELLOCMD)) != 0) {
425 _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " as reply, but %.*s", (int)bytes, buf);
426 goto leave;
427 }
428 }
429
430 _reverse_proxy_status(conn_client, RP_STATUS_READY, "Ready");
431
432 int running = 1;
433 while (client->th_ctrl != THREAD_T_NULL && conn_client && running) {
434 uint16_t cmd = 0;
435 bytes = 0;
436 err = reverse_proxy_receive_with_timeout(conn_client, (char*)&cmd, sizeof(cmd), &bytes, 1000);
437 if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && bytes != sizeof(cmd))) {
438 continue;
439 } else if (err != REVERSE_PROXY_E_SUCCESS) {
440 _reverse_proxy_log(conn_client, "Connection closed");
441 break;
442 }
443 cmd = le16toh(cmd);
444 switch (cmd) {
445 case 0xBBAA:
446 /* plist command */
447 if (_reverse_proxy_handle_plist_cmd(conn_client) < 0) {
448 running = 0;
449 }
450 break;
451 case 0x105:
452 /* proxy command */
453 if (_reverse_proxy_handle_proxy_cmd(conn_client) < 0) {
454 running = 0;
455 }
456 break;
457 default:
458 /* unknown */
459 debug_info("ERROR: Unknown request 0x%x", cmd);
460 _reverse_proxy_log(conn_client, "ERROR: Unknown request 0x%x", cmd);
461 running = 0;
462 break;
463 }
464 }
465
466leave:
467 _reverse_proxy_status(conn_client, RP_STATUS_TERMINATE, "Terminated");
468 if (conn_client) {
469 reverse_proxy_client_free(conn_client);
470 }
471
472 return NULL;
473}
474
475static void* _reverse_proxy_control_thread(void *cdata)
476{
477 reverse_proxy_client_t client = (reverse_proxy_client_t)cdata;
478 THREAD_T th_conn = THREAD_T_NULL;
479 int running = 1;
480 _reverse_proxy_status(client, RP_STATUS_READY, "Ready");
481 while (client && client->parent && running) {
482 uint32_t cmd = 0;
483 uint32_t bytes = 0;
484 reverse_proxy_error_t err = reverse_proxy_receive_with_timeout(client, (char*)&cmd, sizeof(cmd), &bytes, 1000);
485 if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && bytes != sizeof(cmd))) {
486 continue;
487 } else if (err != REVERSE_PROXY_E_SUCCESS) {
488 _reverse_proxy_log(client, "Connection closed");
489 break;
490 }
491 cmd = le32toh(cmd);
492 switch (cmd) {
493 case 1:
494 /* connection request */
495 debug_info("ReverseProxy<%p> got connect request", client);
496 _reverse_proxy_status(client, RP_STATUS_CONNECT_REQ, "Connect Request");
497 if (thread_new(&th_conn, _reverse_proxy_connection_thread, client) != 0) {
498 debug_info("ERROR: Failed to start connection thread");
499 th_conn = THREAD_T_NULL;
500 running = 0;
501 }
502 break;
503 case 2:
504 /* shutdown request */
505 debug_info("ReverseProxy<%p> got shutdown request", client);
506 _reverse_proxy_status(client, RP_STATUS_SHUTDOWN_REQ, "Shutdown Request");
507 running = 0;
508 break;
509 default:
510 /* unknown */
511 debug_info("ERROR: Unknown request 0x%x", cmd);
512 _reverse_proxy_log(client, "ERROR: Unknown request 0x%x", cmd);
513 running = 0;
514 break;
515 }
516 }
517 _reverse_proxy_log(client, "Terminating");
518
519 client->th_ctrl = THREAD_T_NULL;
520 if (th_conn) {
521 debug_info("joining connection thread");
522 thread_join(th_conn);
523 thread_free(th_conn);
524 }
525
526 _reverse_proxy_status(client, RP_STATUS_TERMINATE, "Terminated");
527
528 return NULL;
529}
530
531reverse_proxy_error_t reverse_proxy_client_start_proxy(reverse_proxy_client_t client, int control_protocol_version)
532{
533 char buf[16] = {0, };
534 uint32_t bytes = 0;
535 reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR;
536
537 if (!client) {
538 return REVERSE_PROXY_E_INVALID_ARG;
539 }
540 if (control_protocol_version < 1 || control_protocol_version > 2) {
541 debug_info("invalid protocol version %d, must be 1 or 2", control_protocol_version);
542 return REVERSE_PROXY_E_INVALID_ARG;
543 }
544
545 if (control_protocol_version == 2) {
546 err = reverse_proxy_send(client, CTRLCMD, sizeof(CTRLCMD), &bytes);
547 if (err != REVERSE_PROXY_E_SUCCESS) {
548 _reverse_proxy_log(client, "ERROR: Failed to send " CTRLCMD " to device, error %d", err);
549 return err;
550 }
551 plist_t dict = plist_new_dict();
552 plist_dict_set_item(dict, "Command", plist_new_string(CTRLCMD));
553 plist_dict_set_item(dict, "CtrlProtoVersion", plist_new_uint(client->protoversion));
554 err = reverse_proxy_send_plist(client, dict);
555 plist_free(dict);
556 if (err != REVERSE_PROXY_E_SUCCESS) {
557 _reverse_proxy_log(client, "ERROR: Could not send " CTRLCMD " plist command, error %d", err);
558 return err;
559 }
560 dict = NULL;
561 err = reverse_proxy_receive_plist(client, &dict);
562 if (err != REVERSE_PROXY_E_SUCCESS) {
563 _reverse_proxy_log(client, "ERROR: Could not receive " CTRLCMD " plist reply, error %d", err);
564 return err;
565 }
566 plist_t node = plist_dict_get_item(dict, "ConnPort");
567 if (node && plist_get_node_type(node) == PLIST_UINT) {
568 uint64_t u64val = 0;
569 plist_get_uint_val(node, &u64val);
570 client->conn_port = (uint16_t)u64val;
571 } else {
572 _reverse_proxy_log(client, "ERROR: Could not get ConnPort value");
573 return REVERSE_PROXY_E_UNKNOWN_ERROR;
574 }
575 client->protoversion = 2;
576 } else {
577 err = reverse_proxy_send(client, HELLOCTRLCMD, sizeof(HELLOCTRLCMD), &bytes);
578 if (err != REVERSE_PROXY_E_SUCCESS) {
579 _reverse_proxy_log(client, "ERROR: Failed to send " HELLOCTRLCMD " to device, error %d", err);
580 return err;
581 }
582
583 bytes = 0;
584 err = reverse_proxy_receive(client, buf, sizeof(HELLOCTRLCMD)-1, &bytes);
585 if (err != REVERSE_PROXY_E_SUCCESS) {
586 _reverse_proxy_log(client, "ERROR: Could not receive " HELLOCTRLCMD " reply, error %d", err);
587 return err;
588 }
589
590 uint16_t cport = 0;
591 bytes = 0;
592 err = reverse_proxy_receive(client, (char*)&cport, 2, &bytes);
593 if (err != REVERSE_PROXY_E_SUCCESS) {
594 _reverse_proxy_log(client, "ERROR: Failed to receive connection port, error %d", err);
595 return err;
596 }
597 client->conn_port = le16toh(cport);
598 client->protoversion = 1;
599 }
600
601 if (thread_new(&(client->th_ctrl), _reverse_proxy_control_thread, client) != 0) {
602 _reverse_proxy_log(client, "ERROR: Failed to start control thread");
603 client->th_ctrl = THREAD_T_NULL; /* undefined after failure */
604 err = REVERSE_PROXY_E_UNKNOWN_ERROR;
605 }
606
607 return err;
608}
609
610reverse_proxy_error_t reverse_proxy_client_create_with_service(idevice_t device, reverse_proxy_client_t* client, const char* label)
611{
612 reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR;
613 service_client_factory_start_service(device, "com.apple.PurpleReverseProxy.Ctrl", (void**)client, label, SERVICE_CONSTRUCTOR(reverse_proxy_client_new), &err);
614 if (!*client) {
615 return err;
616 }
617 (*client)->label = strdup(label);
618 (*client)->type = RP_TYPE_CTRL;
619
620 return REVERSE_PROXY_E_SUCCESS;
621}
622
623reverse_proxy_error_t reverse_proxy_client_create_with_port(idevice_t device, reverse_proxy_client_t* client, uint16_t device_port)
624{
625 reverse_proxy_client_t client_loc = NULL;
626 reverse_proxy_error_t err;
627
628 struct lockdownd_service_descriptor svc;
629 svc.port = device_port;
630 svc.ssl_enabled = 0;
631 svc.identifier = NULL;
632
633 err = reverse_proxy_client_new(device, &svc, &client_loc);
634 if (err != REVERSE_PROXY_E_SUCCESS) {
635 return err;
636 }
637
638 client_loc->type = RP_TYPE_CTRL;
639 *client = client_loc;
640
641 return REVERSE_PROXY_E_SUCCESS;
642}
643
644reverse_proxy_error_t reverse_proxy_client_free(reverse_proxy_client_t client)
645{
646 if (!client)
647 return REVERSE_PROXY_E_INVALID_ARG;
648 service_client_t parent = client->parent;
649 client->parent = NULL;
650 if (client->th_ctrl) {
651 debug_info("joining control thread");
652 thread_join(client->th_ctrl);
653 thread_free(client->th_ctrl);
654 client->th_ctrl = THREAD_T_NULL;
655 }
656 reverse_proxy_error_t err = reverse_proxy_error(service_client_free(parent));
657 free(client->label);
658 free(client);
659
660 return err;
661}
662
663reverse_proxy_client_type_t reverse_proxy_get_type(reverse_proxy_client_t client)
664{
665 if (!client)
666 return 0;
667 return client->type;
668}
669
670void reverse_proxy_client_set_status_callback(reverse_proxy_client_t client, reverse_proxy_status_cb_t status_callback, void* user_data)
671{
672 if (!client) {
673 return;
674 }
675 client->status_cb = status_callback;
676 client->status_cb_user_data = user_data;
677}
678
679void reverse_proxy_client_set_log_callback(reverse_proxy_client_t client, reverse_proxy_log_cb_t log_callback, void* user_data)
680{
681 if (!client) {
682 return;
683 }
684 client->log_cb = log_callback;
685 client->log_cb_user_data = user_data;
686}
687
688void reverse_proxy_client_set_data_callback(reverse_proxy_client_t client, reverse_proxy_data_cb_t data_callback, void* user_data)
689{
690 if (!client) {
691 return;
692 }
693 client->data_cb = data_callback;
694 client->data_cb_user_data = user_data;
695}
696
697reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent)
698{
699 reverse_proxy_error_t err = reverse_proxy_error(service_send(client->parent, data, len, sent));
700 return err;
701}
702
703reverse_proxy_error_t reverse_proxy_receive_with_timeout(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received, unsigned int timeout)
704{
705 if (!client)
706 return REVERSE_PROXY_E_INVALID_ARG;
707 return reverse_proxy_error(service_receive_with_timeout(client->parent, buffer, len, received, timeout));
708}
709
710reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received)
711{
712 return reverse_proxy_receive_with_timeout(client, buffer, len, received, 20000);
713}
714
715reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist)
716{
717 reverse_proxy_error_t err;
718 uint32_t len = 0;
719 char* buf = NULL;
720 uint32_t bytes = 0;
721
722 plist_to_bin(plist, &buf, &len);
723
724 if (!buf) {
725 return REVERSE_PROXY_E_INVALID_ARG;
726 }
727
728 debug_info("Sending %u bytes", len);
729
730 uint32_t slen = htole32(len);
731 err = reverse_proxy_send(client, (char*)&slen, sizeof(slen), &bytes);
732 if (err != REVERSE_PROXY_E_SUCCESS) {
733 free(buf);
734 debug_info("ERROR: Unable to send data length, error %d. Sent %u/%u bytes.", err, bytes, (uint32_t)sizeof(slen));
735 return err;
736 }
737 uint32_t done = 0;
738 do {
739 bytes = 0;
740 err = reverse_proxy_send(client, buf+done, len-done, &bytes);
741 if (err != REVERSE_PROXY_E_SUCCESS) {
742 break;
743 }
744 done += bytes;
745 } while (done < len);
746 free(buf);
747 if (err != REVERSE_PROXY_E_SUCCESS || done != len) {
748 debug_info("ERROR: Unable to send data, error %d. Sent %u/%u bytes.", err, done, len);
749 return err;
750 }
751
752 debug_info("Sent %u bytes", len);
753
754 return REVERSE_PROXY_E_SUCCESS;
755}
756
757reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist)
758{
759 return reverse_proxy_receive_plist_with_timeout(client, plist, 20000);
760}
761
762reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms)
763{
764 uint32_t len;
765 uint32_t bytes;
766 reverse_proxy_error_t err;
767
768 err = reverse_proxy_receive_with_timeout(client, (char*)&len, sizeof(len), &bytes, timeout_ms);
769 if (err != REVERSE_PROXY_E_SUCCESS) {
770 if (err != REVERSE_PROXY_E_TIMEOUT) {
771 debug_info("ERROR: Unable to receive packet length, error %d\n", err);
772 }
773 return err;
774 }
775
776 len = le32toh(len);
777 char* buf = calloc(1, len);
778 if (!buf) {
779 debug_info("ERROR: Out of memory");
780 return REVERSE_PROXY_E_UNKNOWN_ERROR;
781 }
782
783 uint32_t done = 0;
784 do {
785 bytes = 0;
786 err = reverse_proxy_receive_with_timeout(client, buf+done, len-done, &bytes, timeout_ms);
787 if (err != REVERSE_PROXY_E_SUCCESS) {
788 break;
789 }
790 done += bytes;
791 } while (done < len);
792
793 if (err != REVERSE_PROXY_E_SUCCESS || done != len) {
794 free(buf);
795 debug_info("ERROR: Unable to receive data, error %d. Received %u/%u bytes.", err, done, len);
796 return err;
797 }
798
799 debug_info("Received %u bytes", len);
800
801 plist_from_bin(buf, len, plist);
802 free(buf);
803
804 if (!(*plist)) {
805 debug_info("ERROR: Failed to convert buffer to plist");
806 return REVERSE_PROXY_E_PLIST_ERROR;
807 }
808
809 return REVERSE_PROXY_E_SUCCESS;
810}
diff --git a/src/reverse_proxy.h b/src/reverse_proxy.h
new file mode 100644
index 0000000..7f441bd
--- /dev/null
+++ b/src/reverse_proxy.h
@@ -0,0 +1,51 @@
1/*
2 * reverse_proxy.h
3 * com.apple.PurpleReverseProxy service header file.
4 *
5 * Copyright (c) 2021 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __REVERSE_PROXY_H
23#define __REVERSE_PROXY_H
24
25#include "idevice.h"
26#include "libimobiledevice/reverse_proxy.h"
27#include "service.h"
28
29struct reverse_proxy_client_private {
30 service_client_t parent;
31 char* label;
32 int type;
33 int protoversion;
34 THREAD_T th_ctrl;
35 uint16_t conn_port;
36 reverse_proxy_log_cb_t log_cb;
37 void* log_cb_user_data;
38 reverse_proxy_data_cb_t data_cb;
39 void* data_cb_user_data;
40 reverse_proxy_status_cb_t status_cb;
41 void* status_cb_user_data;
42};
43
44reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent);
45reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received);
46reverse_proxy_error_t reverse_proxy_receive_with_timeout(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received, unsigned int timeout);
47reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist);
48reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist);
49reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms);
50
51#endif
diff --git a/src/sbservices.c b/src/sbservices.c
index 3596cbd..365e130 100644
--- a/src/sbservices.c
+++ b/src/sbservices.c
@@ -8,17 +8,20 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
22#include <string.h> 25#include <string.h>
23#include <stdlib.h> 26#include <stdlib.h>
24#include <unistd.h> 27#include <unistd.h>
@@ -26,28 +29,28 @@
26 29
27#include "sbservices.h" 30#include "sbservices.h"
28#include "property_list_service.h" 31#include "property_list_service.h"
29#include "debug.h" 32#include "common/debug.h"
30 33
31/** 34/**
32 * Locks an sbservices client, used for thread safety. 35 * Locks an sbservices client, used for thread safety.
33 * 36 *
34 * @param client sbservices client to lock. 37 * @param client sbservices client to lock.
35 */ 38 */
36static void sbs_lock(sbservices_client_t client) 39static void sbservices_lock(sbservices_client_t client)
37{ 40{
38 debug_info("SBServices: Locked"); 41 debug_info("Locked");
39 g_mutex_lock(client->mutex); 42 mutex_lock(&client->mutex);
40} 43}
41 44
42/** 45/**
43 * Unlocks an sbservices client, used for thread safety. 46 * Unlocks an sbservices client, used for thread safety.
44 * 47 *
45 * @param client sbservices client to unlock 48 * @param client sbservices client to unlock
46 */ 49 */
47static void sbs_unlock(sbservices_client_t client) 50static void sbservices_unlock(sbservices_client_t client)
48{ 51{
49 debug_info("SBServices: Unlocked"); 52 debug_info("Unlocked");
50 g_mutex_unlock(client->mutex); 53 mutex_unlock(&client->mutex);
51} 54}
52 55
53/** 56/**
@@ -61,64 +64,44 @@ static void sbs_unlock(sbservices_client_t client)
61 */ 64 */
62static sbservices_error_t sbservices_error(property_list_service_error_t err) 65static sbservices_error_t sbservices_error(property_list_service_error_t err)
63{ 66{
64 switch (err) { 67 switch (err) {
65 case PROPERTY_LIST_SERVICE_E_SUCCESS: 68 case PROPERTY_LIST_SERVICE_E_SUCCESS:
66 return SBSERVICES_E_SUCCESS; 69 return SBSERVICES_E_SUCCESS;
67 case PROPERTY_LIST_SERVICE_E_INVALID_ARG: 70 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
68 return SBSERVICES_E_INVALID_ARG; 71 return SBSERVICES_E_INVALID_ARG;
69 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: 72 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
70 return SBSERVICES_E_PLIST_ERROR; 73 return SBSERVICES_E_PLIST_ERROR;
71 case PROPERTY_LIST_SERVICE_E_MUX_ERROR: 74 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
72 return SBSERVICES_E_CONN_FAILED; 75 return SBSERVICES_E_CONN_FAILED;
73 default: 76 default:
74 break; 77 break;
75 } 78 }
76 return SBSERVICES_E_UNKNOWN_ERROR; 79 return SBSERVICES_E_UNKNOWN_ERROR;
77} 80}
78 81
79/** 82sbservices_error_t sbservices_client_new(idevice_t device, lockdownd_service_descriptor_t service, sbservices_client_t *client)
80 * Connects to the springboardservices service on the specified device.
81 *
82 * @param device The device to connect to.
83 * @param port Destination port (usually given by lockdownd_start_service).
84 * @param client Pointer that will point to a newly allocated
85 * sbservices_client_t upon successful return.
86 *
87 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when
88 * client is NULL, or an SBSERVICES_E_* error code otherwise.
89 */
90sbservices_error_t sbservices_client_new(idevice_t device, uint16_t port, sbservices_client_t *client)
91{ 83{
92 /* makes sure thread environment is available */
93 if (!g_thread_supported())
94 g_thread_init(NULL);
95
96 if (!device)
97 return SBSERVICES_E_INVALID_ARG;
98
99 property_list_service_client_t plistclient = NULL; 84 property_list_service_client_t plistclient = NULL;
100 sbservices_error_t err = sbservices_error(property_list_service_client_new(device, port, &plistclient)); 85 sbservices_error_t err = sbservices_error(property_list_service_client_new(device, service, &plistclient));
101 if (err != SBSERVICES_E_SUCCESS) { 86 if (err != SBSERVICES_E_SUCCESS) {
102 return err; 87 return err;
103 } 88 }
104 89
105 sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private)); 90 sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private));
106 client_loc->parent = plistclient; 91 client_loc->parent = plistclient;
107 client_loc->mutex = g_mutex_new(); 92 mutex_init(&client_loc->mutex);
108 93
109 *client = client_loc; 94 *client = client_loc;
110 return SBSERVICES_E_SUCCESS; 95 return SBSERVICES_E_SUCCESS;
111} 96}
112 97
113/** 98sbservices_error_t sbservices_client_start_service(idevice_t device, sbservices_client_t * client, const char* label)
114 * Disconnects an sbservices client from the device and frees up the 99{
115 * sbservices client data. 100 sbservices_error_t err = SBSERVICES_E_UNKNOWN_ERROR;
116 * 101 service_client_factory_start_service(device, SBSERVICES_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(sbservices_client_new), &err);
117 * @param client The sbservices client to disconnect and free. 102 return err;
118 * 103}
119 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when 104
120 * client is NULL, or an SBSERVICES_E_* error code otherwise.
121 */
122sbservices_error_t sbservices_client_free(sbservices_client_t client) 105sbservices_error_t sbservices_client_free(sbservices_client_t client)
123{ 106{
124 if (!client) 107 if (!client)
@@ -126,28 +109,12 @@ sbservices_error_t sbservices_client_free(sbservices_client_t client)
126 109
127 sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent)); 110 sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent));
128 client->parent = NULL; 111 client->parent = NULL;
129 if (client->mutex) { 112 mutex_destroy(&client->mutex);
130 g_mutex_free(client->mutex);
131 }
132 free(client); 113 free(client);
133 114
134 return err; 115 return err;
135} 116}
136 117
137/**
138 * Gets the icon state of the connected device.
139 *
140 * @param client The connected sbservices client to use.
141 * @param state Pointer that will point to a newly allocated plist containing
142 * the current icon state. It is up to the caller to free the memory.
143 * @param format_version A string to be passed as formatVersion along with
144 * the request, or NULL if no formatVersion should be passed. This is only
145 * supported since iOS 4.0 so for older firmware versions this must be set
146 * to NULL.
147 *
148 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when
149 * client or state is invalid, or an SBSERVICES_E_* error code otherwise.
150 */
151sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version) 118sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version)
152{ 119{
153 if (!client || !client->parent || !state) 120 if (!client || !client->parent || !state)
@@ -156,12 +123,12 @@ sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t
156 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 123 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
157 124
158 plist_t dict = plist_new_dict(); 125 plist_t dict = plist_new_dict();
159 plist_dict_insert_item(dict, "command", plist_new_string("getIconState")); 126 plist_dict_set_item(dict, "command", plist_new_string("getIconState"));
160 if (format_version) { 127 if (format_version) {
161 plist_dict_insert_item(dict, "formatVersion", plist_new_string(format_version)); 128 plist_dict_set_item(dict, "formatVersion", plist_new_string(format_version));
162 } 129 }
163 130
164 sbs_lock(client); 131 sbservices_lock(client);
165 132
166 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 133 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
167 if (res != SBSERVICES_E_SUCCESS) { 134 if (res != SBSERVICES_E_SUCCESS) {
@@ -184,19 +151,10 @@ leave_unlock:
184 if (dict) { 151 if (dict) {
185 plist_free(dict); 152 plist_free(dict);
186 } 153 }
187 sbs_unlock(client); 154 sbservices_unlock(client);
188 return res; 155 return res;
189} 156}
190 157
191/**
192 * Sets the icon state of the connected device.
193 *
194 * @param client The connected sbservices client to use.
195 * @param newstate A plist containing the new iconstate.
196 *
197 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when
198 * client or newstate is NULL, or an SBSERVICES_E_* error code otherwise.
199 */
200sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) 158sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate)
201{ 159{
202 if (!client || !client->parent || !newstate) 160 if (!client || !client->parent || !newstate)
@@ -205,39 +163,27 @@ sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t
205 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 163 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
206 164
207 plist_t dict = plist_new_dict(); 165 plist_t dict = plist_new_dict();
208 plist_dict_insert_item(dict, "command", plist_new_string("setIconState")); 166 plist_dict_set_item(dict, "command", plist_new_string("setIconState"));
209 plist_dict_insert_item(dict, "iconState", plist_copy(newstate)); 167 plist_dict_set_item(dict, "iconState", plist_copy(newstate));
210 168
211 sbs_lock(client); 169 sbservices_lock(client);
212 170
213 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 171 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
214 if (res != SBSERVICES_E_SUCCESS) { 172 if (res != SBSERVICES_E_SUCCESS) {
215 debug_info("could not send plist, error %d", res); 173 debug_info("could not send plist, error %d", res);
216 } 174 }
217 /* NO RESPONSE */ 175
176 uint32_t bytes = 0;
177 service_receive_with_timeout(client->parent->parent, malloc(4), 4, &bytes, 2000);
178 debug_info("setIconState response: %u", bytes);
218 179
219 if (dict) { 180 if (dict) {
220 plist_free(dict); 181 plist_free(dict);
221 } 182 }
222 sbs_unlock(client); 183 sbservices_unlock(client);
223 return res; 184 return res;
224} 185}
225 186
226/**
227 * Get the icon of the specified app as PNG data.
228 *
229 * @param client The connected sbservices client to use.
230 * @param bundleId The bundle identifier of the app to retrieve the icon for.
231 * @param pngdata Pointer that will point to a newly allocated buffer
232 * containing the PNG data upon successful return. It is up to the caller
233 * to free the memory.
234 * @param pngsize Pointer to a uint64_t that will be set to the size of the
235 * buffer pngdata points to upon successful return.
236 *
237 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when
238 * client, bundleId, or pngdata are invalid, or an SBSERVICES_E_* error
239 * code otherwise.
240 */
241sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) 187sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize)
242{ 188{
243 if (!client || !client->parent || !bundleId || !pngdata) 189 if (!client || !client->parent || !bundleId || !pngdata)
@@ -246,10 +192,10 @@ sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const
246 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 192 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
247 193
248 plist_t dict = plist_new_dict(); 194 plist_t dict = plist_new_dict();
249 plist_dict_insert_item(dict, "command", plist_new_string("getIconPNGData")); 195 plist_dict_set_item(dict, "command", plist_new_string("getIconPNGData"));
250 plist_dict_insert_item(dict, "bundleId", plist_new_string(bundleId)); 196 plist_dict_set_item(dict, "bundleId", plist_new_string(bundleId));
251 197
252 sbs_lock(client); 198 sbservices_lock(client);
253 199
254 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 200 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
255 if (res != SBSERVICES_E_SUCCESS) { 201 if (res != SBSERVICES_E_SUCCESS) {
@@ -271,25 +217,48 @@ leave_unlock:
271 if (dict) { 217 if (dict) {
272 plist_free(dict); 218 plist_free(dict);
273 } 219 }
274 sbs_unlock(client); 220 sbservices_unlock(client);
275 return res; 221 return res;
222}
223
224sbservices_error_t sbservices_get_interface_orientation(sbservices_client_t client, sbservices_interface_orientation_t* interface_orientation)
225{
226 if (!client || !client->parent || !interface_orientation)
227 return SBSERVICES_E_INVALID_ARG;
228
229 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
230
231 plist_t dict = plist_new_dict();
232 plist_dict_set_item(dict, "command", plist_new_string("getInterfaceOrientation"));
233
234 sbservices_lock(client);
235
236 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
237 if (res != SBSERVICES_E_SUCCESS) {
238 debug_info("could not send plist, error %d", res);
239 goto leave_unlock;
240 }
241 plist_free(dict);
242 dict = NULL;
243
244 res = sbservices_error(property_list_service_receive_plist(client->parent, &dict));
245 if (res == SBSERVICES_E_SUCCESS) {
246 plist_t node = plist_dict_get_item(dict, "interfaceOrientation");
247 if (node) {
248 uint64_t value = SBSERVICES_INTERFACE_ORIENTATION_UNKNOWN;
249 plist_get_uint_val(node, &value);
250 *interface_orientation = (sbservices_interface_orientation_t)value;
251 }
252 }
276 253
254leave_unlock:
255 if (dict) {
256 plist_free(dict);
257 }
258 sbservices_unlock(client);
259 return res;
277} 260}
278 261
279/**
280 * Get the home screen wallpaper as PNG data.
281 *
282 * @param client The connected sbservices client to use.
283 * @param pngdata Pointer that will point to a newly allocated buffer
284 * containing the PNG data upon successful return. It is up to the caller
285 * to free the memory.
286 * @param pngsize Pointer to a uint64_t that will be set to the size of the
287 * buffer pngdata points to upon successful return.
288 *
289 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when
290 * client or pngdata are invalid, or an SBSERVICES_E_* error
291 * code otherwise.
292 */
293sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize) 262sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize)
294{ 263{
295 if (!client || !client->parent || !pngdata) 264 if (!client || !client->parent || !pngdata)
@@ -298,9 +267,9 @@ sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_clien
298 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 267 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
299 268
300 plist_t dict = plist_new_dict(); 269 plist_t dict = plist_new_dict();
301 plist_dict_insert_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData")); 270 plist_dict_set_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData"));
302 271
303 sbs_lock(client); 272 sbservices_lock(client);
304 273
305 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 274 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
306 if (res != SBSERVICES_E_SUCCESS) { 275 if (res != SBSERVICES_E_SUCCESS) {
@@ -322,6 +291,6 @@ leave_unlock:
322 if (dict) { 291 if (dict) {
323 plist_free(dict); 292 plist_free(dict);
324 } 293 }
325 sbs_unlock(client); 294 sbservices_unlock(client);
326 return res; 295 return res;
327} 296}
diff --git a/src/sbservices.h b/src/sbservices.h
index 3a4120f..b67281e 100644
--- a/src/sbservices.h
+++ b/src/sbservices.h
@@ -8,27 +8,28 @@
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef ISBSERVICES_H
22#define ISBSERVICES_H
23 21
24#include <glib.h> 22#ifndef __SBSERVICES_H
23#define __SBSERVICES_H
25 24
25#include "idevice.h"
26#include "libimobiledevice/sbservices.h" 26#include "libimobiledevice/sbservices.h"
27#include "property_list_service.h" 27#include "property_list_service.h"
28#include <libimobiledevice-glue/thread.h>
28 29
29struct sbservices_client_private { 30struct sbservices_client_private {
30 property_list_service_client_t parent; 31 property_list_service_client_t parent;
31 GMutex *mutex; 32 mutex_t mutex;
32}; 33};
33 34
34#endif 35#endif
diff --git a/src/screenshotr.c b/src/screenshotr.c
index 0c4c9b2..c3cc9ba 100644
--- a/src/screenshotr.c
+++ b/src/screenshotr.c
@@ -1,33 +1,36 @@
1/* 1/*
2 * screenshotr.c 2 * screenshotr.c
3 * com.apple.mobile.screenshotr service implementation. 3 * com.apple.mobile.screenshotr service implementation.
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen All Rights Reserved. 5 * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21 21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
22#include <plist/plist.h> 25#include <plist/plist.h>
23#include <string.h> 26#include <string.h>
24#include <stdlib.h> 27#include <stdlib.h>
25 28
26#include "screenshotr.h" 29#include "screenshotr.h"
27#include "device_link_service.h" 30#include "device_link_service.h"
28#include "debug.h" 31#include "common/debug.h"
29 32
30#define SCREENSHOTR_VERSION_INT1 100 33#define SCREENSHOTR_VERSION_INT1 400
31#define SCREENSHOTR_VERSION_INT2 0 34#define SCREENSHOTR_VERSION_INT2 0
32 35
33/** 36/**
@@ -50,6 +53,10 @@ static screenshotr_error_t screenshotr_error(device_link_service_error_t err)
50 return SCREENSHOTR_E_PLIST_ERROR; 53 return SCREENSHOTR_E_PLIST_ERROR;
51 case DEVICE_LINK_SERVICE_E_MUX_ERROR: 54 case DEVICE_LINK_SERVICE_E_MUX_ERROR:
52 return SCREENSHOTR_E_MUX_ERROR; 55 return SCREENSHOTR_E_MUX_ERROR;
56 case DEVICE_LINK_SERVICE_E_SSL_ERROR:
57 return SCREENSHOTR_E_SSL_ERROR;
58 case DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT:
59 return SCREENSHOTR_E_RECEIVE_TIMEOUT;
53 case DEVICE_LINK_SERVICE_E_BAD_VERSION: 60 case DEVICE_LINK_SERVICE_E_BAD_VERSION:
54 return SCREENSHOTR_E_BAD_VERSION; 61 return SCREENSHOTR_E_BAD_VERSION;
55 default: 62 default:
@@ -58,29 +65,14 @@ static screenshotr_error_t screenshotr_error(device_link_service_error_t err)
58 return SCREENSHOTR_E_UNKNOWN_ERROR; 65 return SCREENSHOTR_E_UNKNOWN_ERROR;
59} 66}
60 67
61/** 68screenshotr_error_t screenshotr_client_new(idevice_t device, lockdownd_service_descriptor_t service,
62 * Connects to the screenshotr service on the specified device.
63 *
64 * @param device The device to connect to.
65 * @param port Destination port (usually given by lockdownd_start_service).
66 * @param client Pointer that will be set to a newly allocated
67 * screenshotr_client_t upon successful return.
68 *
69 * @note This service is only available if a developer disk image has been
70 * mounted.
71 *
72 * @return SCREENSHOTR_E_SUCCESS on success, SCREENSHOTR_E_INVALID ARG if one
73 * or more parameters are invalid, or SCREENSHOTR_E_CONN_FAILED if the
74 * connection to the device could not be established.
75 */
76screenshotr_error_t screenshotr_client_new(idevice_t device, uint16_t port,
77 screenshotr_client_t * client) 69 screenshotr_client_t * client)
78{ 70{
79 if (!device || port == 0 || !client || *client) 71 if (!device || !service || service->port == 0 || !client || *client)
80 return SCREENSHOTR_E_INVALID_ARG; 72 return SCREENSHOTR_E_INVALID_ARG;
81 73
82 device_link_service_client_t dlclient = NULL; 74 device_link_service_client_t dlclient = NULL;
83 screenshotr_error_t ret = screenshotr_error(device_link_service_client_new(device, port, &dlclient)); 75 screenshotr_error_t ret = screenshotr_error(device_link_service_client_new(device, service, &dlclient));
84 if (ret != SCREENSHOTR_E_SUCCESS) { 76 if (ret != SCREENSHOTR_E_SUCCESS) {
85 return ret; 77 return ret;
86 } 78 }
@@ -101,39 +93,23 @@ screenshotr_error_t screenshotr_client_new(idevice_t device, uint16_t port,
101 return ret; 93 return ret;
102} 94}
103 95
104/** 96screenshotr_error_t screenshotr_client_start_service(idevice_t device, screenshotr_client_t * client, const char* label)
105 * Disconnects a screenshotr client from the device and frees up the 97{
106 * screenshotr client data. 98 screenshotr_error_t err = SCREENSHOTR_E_UNKNOWN_ERROR;
107 * 99 service_client_factory_start_service(device, SCREENSHOTR_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(screenshotr_client_new), &err);
108 * @param client The screenshotr client to disconnect and free. 100 return err;
109 * 101}
110 * @return SCREENSHOTR_E_SUCCESS on success, or SCREENSHOTR_E_INVALID_ARG 102
111 * if client is NULL.
112 */
113screenshotr_error_t screenshotr_client_free(screenshotr_client_t client) 103screenshotr_error_t screenshotr_client_free(screenshotr_client_t client)
114{ 104{
115 if (!client) 105 if (!client)
116 return SCREENSHOTR_E_INVALID_ARG; 106 return SCREENSHOTR_E_INVALID_ARG;
117 device_link_service_disconnect(client->parent); 107 device_link_service_disconnect(client->parent, NULL);
118 screenshotr_error_t err = screenshotr_error(device_link_service_client_free(client->parent)); 108 screenshotr_error_t err = screenshotr_error(device_link_service_client_free(client->parent));
119 free(client); 109 free(client);
120 return err; 110 return err;
121} 111}
122 112
123/**
124 * Get a screen shot from the connected device.
125 *
126 * @param client The connection screenshotr service client.
127 * @param imgdata Pointer that will point to a newly allocated buffer
128 * containing TIFF image data upon successful return. It is up to the
129 * caller to free the memory.
130 * @param imgsize Pointer to a uint64_t that will be set to the size of the
131 * buffer imgdata points to upon successful return.
132 *
133 * @return SCREENSHOTR_E_SUCCESS on success, SCREENSHOTR_E_INVALID_ARG if
134 * one or more parameters are invalid, or another error code if an
135 * error occured.
136 */
137screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, char **imgdata, uint64_t *imgsize) 113screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, char **imgdata, uint64_t *imgsize)
138{ 114{
139 if (!client || !client->parent || !imgdata) 115 if (!client || !client->parent || !imgdata)
@@ -142,7 +118,7 @@ screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, cha
142 screenshotr_error_t res = SCREENSHOTR_E_UNKNOWN_ERROR; 118 screenshotr_error_t res = SCREENSHOTR_E_UNKNOWN_ERROR;
143 119
144 plist_t dict = plist_new_dict(); 120 plist_t dict = plist_new_dict();
145 plist_dict_insert_item(dict, "MessageType", plist_new_string("ScreenShotRequest")); 121 plist_dict_set_item(dict, "MessageType", plist_new_string("ScreenShotRequest"));
146 122
147 res = screenshotr_error(device_link_service_send_process_message(client->parent, dict)); 123 res = screenshotr_error(device_link_service_send_process_message(client->parent, dict));
148 plist_free(dict); 124 plist_free(dict);
@@ -166,7 +142,7 @@ screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, cha
166 plist_t node = plist_dict_get_item(dict, "MessageType"); 142 plist_t node = plist_dict_get_item(dict, "MessageType");
167 char *strval = NULL; 143 char *strval = NULL;
168 plist_get_string_val(node, &strval); 144 plist_get_string_val(node, &strval);
169 if (!strval || strcmp(strval, "ScreenShotReply")) { 145 if (!strval || strcmp(strval, "ScreenShotReply") != 0) {
170 debug_info("invalid screenshot data received!"); 146 debug_info("invalid screenshot data received!");
171 res = SCREENSHOTR_E_PLIST_ERROR; 147 res = SCREENSHOTR_E_PLIST_ERROR;
172 goto leave; 148 goto leave;
diff --git a/src/screenshotr.h b/src/screenshotr.h
index df6774a..1319ec0 100644
--- a/src/screenshotr.h
+++ b/src/screenshotr.h
@@ -1,26 +1,28 @@
1/* 1/*
2 * screenshotr.h 2 * screenshotr.h
3 * com.apple.mobile.screenshotr service header file. 3 * com.apple.mobile.screenshotr service header file.
4 * 4 *
5 * Copyright (c) 2010 Nikias Bassen All Rights Reserved. 5 * Copyright (c) 2010 Nikias Bassen All Rights Reserved.
6 * 6 *
7 * This library is free software; you can redistribute it and/or 7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public 8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either 9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version. 10 * version 2.1 of the License, or (at your option) any later version.
11 * 11 *
12 * This library is distributed in the hope that it will be useful, 12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details. 15 * Lesser General Public License for more details.
16 * 16 *
17 * You should have received a copy of the GNU Lesser General Public 17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software 18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */ 20 */
21#ifndef SCREENSHOTR_H
22#define SCREENSHOTR_H
23 21
22#ifndef __SCREENSHOTR_H
23#define __SCREENSHOTR_H
24
25#include "idevice.h"
24#include "libimobiledevice/screenshotr.h" 26#include "libimobiledevice/screenshotr.h"
25#include "device_link_service.h" 27#include "device_link_service.h"
26 28
diff --git a/src/service.c b/src/service.c
new file mode 100644
index 0000000..9474021
--- /dev/null
+++ b/src/service.c
@@ -0,0 +1,207 @@
1/*
2 * service.c
3 * generic service implementation.
4 *
5 * Copyright (c) 2013 Nikias Bassen. All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24#include <stdlib.h>
25#include <string.h>
26
27#include "service.h"
28#include "idevice.h"
29#include "common/debug.h"
30
31/**
32 * Convert an idevice_error_t value to an service_error_t value.
33 * Used internally to get correct error codes.
34 *
35 * @param err An idevice_error_t error code
36 *
37 * @return A matching service_error_t error code,
38 * SERVICE_E_UNKNOWN_ERROR otherwise.
39 */
40static service_error_t idevice_to_service_error(idevice_error_t err)
41{
42 switch (err) {
43 case IDEVICE_E_SUCCESS:
44 return SERVICE_E_SUCCESS;
45 case IDEVICE_E_INVALID_ARG:
46 return SERVICE_E_INVALID_ARG;
47 case IDEVICE_E_SSL_ERROR:
48 return SERVICE_E_SSL_ERROR;
49 case IDEVICE_E_NOT_ENOUGH_DATA:
50 return SERVICE_E_NOT_ENOUGH_DATA;
51 case IDEVICE_E_TIMEOUT:
52 return SERVICE_E_TIMEOUT;
53 default:
54 break;
55 }
56 return SERVICE_E_UNKNOWN_ERROR;
57}
58
59service_error_t service_client_new(idevice_t device, lockdownd_service_descriptor_t service, service_client_t *client)
60{
61 if (!device || !service || service->port == 0 || !client || *client)
62 return SERVICE_E_INVALID_ARG;
63
64 /* Attempt connection */
65 idevice_connection_t connection = NULL;
66 if (idevice_connect(device, service->port, &connection) != IDEVICE_E_SUCCESS) {
67 return SERVICE_E_MUX_ERROR;
68 }
69
70 /* create client object */
71 service_client_t client_loc = (service_client_t)malloc(sizeof(struct service_client_private));
72 client_loc->connection = connection;
73
74 /* enable SSL if requested */
75 if (service->ssl_enabled == 1)
76 service_enable_ssl(client_loc);
77
78 /* all done, return success */
79 *client = client_loc;
80 return SERVICE_E_SUCCESS;
81}
82
83service_error_t service_client_factory_start_service(idevice_t device, const char* service_name, void **client, const char* label, int32_t (*constructor_func)(idevice_t, lockdownd_service_descriptor_t, void**), int32_t *error_code)
84{
85 *client = NULL;
86
87 lockdownd_client_t lckd = NULL;
88 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, label)) {
89 debug_info("Could not create a lockdown client.");
90 return SERVICE_E_START_SERVICE_ERROR;
91 }
92
93 lockdownd_service_descriptor_t service = NULL;
94 lockdownd_error_t lerr = lockdownd_start_service(lckd, service_name, &service);
95 lockdownd_client_free(lckd);
96
97 if (lerr != LOCKDOWN_E_SUCCESS) {
98 debug_info("Could not start service %s: %s", service_name, lockdownd_strerror(lerr));
99 return SERVICE_E_START_SERVICE_ERROR;
100 }
101
102 int32_t ec;
103 if (constructor_func) {
104 ec = (int32_t)constructor_func(device, service, client);
105 } else {
106 ec = service_client_new(device, service, (service_client_t*)client);
107 }
108 if (error_code) {
109 *error_code = ec;
110 }
111
112 if (ec != SERVICE_E_SUCCESS) {
113 debug_info("Could not connect to service %s! Port: %i, error: %i", service_name, service->port, ec);
114 }
115
116 lockdownd_service_descriptor_free(service);
117 service = NULL;
118
119 return (ec == SERVICE_E_SUCCESS) ? SERVICE_E_SUCCESS : SERVICE_E_START_SERVICE_ERROR;
120}
121
122service_error_t service_client_free(service_client_t client)
123{
124 if (!client)
125 return SERVICE_E_INVALID_ARG;
126
127 service_error_t err = idevice_to_service_error(idevice_disconnect(client->connection));
128
129 free(client);
130 client = NULL;
131
132 return err;
133}
134
135service_error_t service_send(service_client_t client, const char* data, uint32_t size, uint32_t *sent)
136{
137 service_error_t res = SERVICE_E_UNKNOWN_ERROR;
138 uint32_t bytes = 0;
139
140 if (!client || (client && !client->connection) || !data || (size == 0)) {
141 return SERVICE_E_INVALID_ARG;
142 }
143
144 debug_info("sending %d bytes", size);
145 res = idevice_to_service_error(idevice_connection_send(client->connection, data, size, &bytes));
146 if (res != SERVICE_E_SUCCESS) {
147 debug_info("ERROR: sending to device failed.");
148 }
149 if (sent) {
150 *sent = bytes;
151 }
152
153 return res;
154}
155
156service_error_t service_receive_with_timeout(service_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
157{
158 service_error_t res = SERVICE_E_UNKNOWN_ERROR;
159 uint32_t bytes = 0;
160
161 if (!client || (client && !client->connection) || !data || (size == 0)) {
162 return SERVICE_E_INVALID_ARG;
163 }
164
165 res = idevice_to_service_error(idevice_connection_receive_timeout(client->connection, data, size, &bytes, timeout));
166 if (res != SERVICE_E_SUCCESS && res != SERVICE_E_TIMEOUT) {
167 debug_info("could not read data");
168 return res;
169 }
170 if (received) {
171 *received = bytes;
172 }
173
174 return res;
175}
176
177service_error_t service_receive(service_client_t client, char* data, uint32_t size, uint32_t *received)
178{
179 return service_receive_with_timeout(client, data, size, received, 30000);
180}
181
182service_error_t service_enable_ssl(service_client_t client)
183{
184 if (!client || !client->connection)
185 return SERVICE_E_INVALID_ARG;
186 return idevice_to_service_error(idevice_connection_enable_ssl(client->connection));
187}
188
189service_error_t service_disable_ssl(service_client_t client)
190{
191 return service_disable_bypass_ssl(client, 0);
192}
193
194service_error_t service_disable_bypass_ssl(service_client_t client, uint8_t sslBypass)
195{
196 if (!client || !client->connection)
197 return SERVICE_E_INVALID_ARG;
198 return idevice_to_service_error(idevice_connection_disable_bypass_ssl(client->connection, sslBypass));
199}
200
201service_error_t service_get_connection(service_client_t client, idevice_connection_t *connection)
202{
203 if (!client || !client->connection || !connection)
204 return SERVICE_E_INVALID_ARG;
205 *connection = client->connection;
206 return SERVICE_E_SUCCESS;
207}
diff --git a/src/service.h b/src/service.h
new file mode 100644
index 0000000..071fe3f
--- /dev/null
+++ b/src/service.h
@@ -0,0 +1,32 @@
1/*
2 * service.h
3 * Definitions for the generic service implementation
4 *
5 * Copyright (c) 2013 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21#ifndef SERVICE_H
22#define SERVICE_H
23
24#include "idevice.h"
25#include "libimobiledevice/service.h"
26#include "libimobiledevice/lockdown.h"
27
28struct service_client_private {
29 idevice_connection_t connection;
30};
31
32#endif
diff --git a/src/syslog_relay.c b/src/syslog_relay.c
new file mode 100644
index 0000000..9f4296e
--- /dev/null
+++ b/src/syslog_relay.c
@@ -0,0 +1,248 @@
1/*
2 * syslog_relay.c
3 * com.apple.syslog_relay service implementation.
4 *
5 * Copyright (c) 2019-2020 Nikias Bassen, All Rights Reserved.
6 * Copyright (c) 2013-2015 Martin Szulecki, All Rights Reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26#include <string.h>
27#include <stdlib.h>
28
29#include "syslog_relay.h"
30#include "lockdown.h"
31#include "common/debug.h"
32
33struct syslog_relay_worker_thread {
34 syslog_relay_client_t client;
35 syslog_relay_receive_cb_t cbfunc;
36 void *user_data;
37 int is_raw;
38};
39
40/**
41 * Convert a service_error_t value to a syslog_relay_error_t value.
42 * Used internally to get correct error codes.
43 *
44 * @param err An service_error_t error code
45 *
46 * @return A matching syslog_relay_error_t error code,
47 * SYSLOG_RELAY_E_UNKNOWN_ERROR otherwise.
48 */
49static syslog_relay_error_t syslog_relay_error(service_error_t err)
50{
51 switch (err) {
52 case SERVICE_E_SUCCESS:
53 return SYSLOG_RELAY_E_SUCCESS;
54 case SERVICE_E_INVALID_ARG:
55 return SYSLOG_RELAY_E_INVALID_ARG;
56 case SERVICE_E_MUX_ERROR:
57 return SYSLOG_RELAY_E_MUX_ERROR;
58 case SERVICE_E_SSL_ERROR:
59 return SYSLOG_RELAY_E_SSL_ERROR;
60 case SERVICE_E_NOT_ENOUGH_DATA:
61 return SYSLOG_RELAY_E_NOT_ENOUGH_DATA;
62 case SERVICE_E_TIMEOUT:
63 return SYSLOG_RELAY_E_TIMEOUT;
64 default:
65 break;
66 }
67 return SYSLOG_RELAY_E_UNKNOWN_ERROR;
68}
69
70syslog_relay_error_t syslog_relay_client_new(idevice_t device, lockdownd_service_descriptor_t service, syslog_relay_client_t * client)
71{
72 *client = NULL;
73
74 if (!device || !service || service->port == 0 || !client || *client) {
75 debug_info("Incorrect parameter passed to syslog_relay_client_new.");
76 return SYSLOG_RELAY_E_INVALID_ARG;
77 }
78
79 debug_info("Creating syslog_relay_client, port = %d.", service->port);
80
81 service_client_t parent = NULL;
82 syslog_relay_error_t ret = syslog_relay_error(service_client_new(device, service, &parent));
83 if (ret != SYSLOG_RELAY_E_SUCCESS) {
84 debug_info("Creating base service client failed. Error: %i", ret);
85 return ret;
86 }
87
88 syslog_relay_client_t client_loc = (syslog_relay_client_t) malloc(sizeof(struct syslog_relay_client_private));
89 client_loc->parent = parent;
90 client_loc->worker = THREAD_T_NULL;
91
92 *client = client_loc;
93
94 debug_info("syslog_relay_client successfully created.");
95 return 0;
96}
97
98syslog_relay_error_t syslog_relay_client_start_service(idevice_t device, syslog_relay_client_t * client, const char* label)
99{
100 syslog_relay_error_t err = SYSLOG_RELAY_E_UNKNOWN_ERROR;
101 service_client_factory_start_service(device, SYSLOG_RELAY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(syslog_relay_client_new), &err);
102 return err;
103}
104
105syslog_relay_error_t syslog_relay_client_free(syslog_relay_client_t client)
106{
107 if (!client)
108 return SYSLOG_RELAY_E_INVALID_ARG;
109 syslog_relay_stop_capture(client);
110 syslog_relay_error_t err = syslog_relay_error(service_client_free(client->parent));
111 free(client);
112
113 return err;
114}
115
116syslog_relay_error_t syslog_relay_receive(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received)
117{
118 return syslog_relay_receive_with_timeout(client, data, size, received, 1000);
119}
120
121syslog_relay_error_t syslog_relay_receive_with_timeout(syslog_relay_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout)
122{
123 syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR;
124 int bytes = 0;
125
126 if (!client || !data || (size == 0)) {
127 return SYSLOG_RELAY_E_INVALID_ARG;
128 }
129
130 res = syslog_relay_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout));
131 if (res != SYSLOG_RELAY_E_SUCCESS && res != SYSLOG_RELAY_E_TIMEOUT && res != SYSLOG_RELAY_E_NOT_ENOUGH_DATA) {
132 debug_info("Could not read data, error %d", res);
133 }
134 if (received) {
135 *received = (uint32_t)bytes;
136 }
137
138 return res;
139}
140
141void *syslog_relay_worker(void *arg)
142{
143 syslog_relay_error_t ret = SYSLOG_RELAY_E_UNKNOWN_ERROR;
144 struct syslog_relay_worker_thread *srwt = (struct syslog_relay_worker_thread*)arg;
145
146 if (!srwt)
147 return NULL;
148
149 debug_info("Running");
150
151 while (srwt->client->parent) {
152 char c;
153 uint32_t bytes = 0;
154 ret = syslog_relay_receive_with_timeout(srwt->client, &c, 1, &bytes, 100);
155 if (ret == SYSLOG_RELAY_E_TIMEOUT || ret == SYSLOG_RELAY_E_NOT_ENOUGH_DATA || ((bytes == 0) && (ret == SYSLOG_RELAY_E_SUCCESS))) {
156 continue;
157 }
158 if (ret < 0) {
159 debug_info("Connection to syslog relay interrupted");
160 break;
161 }
162 if (srwt->is_raw) {
163 srwt->cbfunc(c, srwt->user_data);
164 } else if (c != 0) {
165 srwt->cbfunc(c, srwt->user_data);
166 }
167 }
168
169 if (srwt) {
170 free(srwt);
171 }
172
173 debug_info("Exiting");
174
175 return NULL;
176}
177
178syslog_relay_error_t syslog_relay_start_capture(syslog_relay_client_t client, syslog_relay_receive_cb_t callback, void* user_data)
179{
180 if (!client || !callback)
181 return SYSLOG_RELAY_E_INVALID_ARG;
182
183 syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR;
184
185 if (client->worker) {
186 debug_info("Another syslog capture thread appears to be running already.");
187 return res;
188 }
189
190 /* start worker thread */
191 struct syslog_relay_worker_thread *srwt = (struct syslog_relay_worker_thread*)malloc(sizeof(struct syslog_relay_worker_thread));
192 if (srwt) {
193 srwt->client = client;
194 srwt->cbfunc = callback;
195 srwt->user_data = user_data;
196 srwt->is_raw = 0;
197
198 if (thread_new(&client->worker, syslog_relay_worker, srwt) == 0) {
199 res = SYSLOG_RELAY_E_SUCCESS;
200 }
201 }
202
203 return res;
204}
205
206syslog_relay_error_t syslog_relay_start_capture_raw(syslog_relay_client_t client, syslog_relay_receive_cb_t callback, void* user_data)
207{
208 if (!client || !callback)
209 return SYSLOG_RELAY_E_INVALID_ARG;
210
211 syslog_relay_error_t res = SYSLOG_RELAY_E_UNKNOWN_ERROR;
212
213 if (client->worker) {
214 debug_info("Another syslog capture thread appears to be running already.");
215 return res;
216 }
217
218 /* start worker thread */
219 struct syslog_relay_worker_thread *srwt = (struct syslog_relay_worker_thread*)malloc(sizeof(struct syslog_relay_worker_thread));
220 if (srwt) {
221 srwt->client = client;
222 srwt->cbfunc = callback;
223 srwt->user_data = user_data;
224 srwt->is_raw = 1;
225
226 if (thread_new(&client->worker, syslog_relay_worker, srwt) == 0) {
227 res = SYSLOG_RELAY_E_SUCCESS;
228 }
229 }
230
231 return res;
232}
233
234syslog_relay_error_t syslog_relay_stop_capture(syslog_relay_client_t client)
235{
236 if (client->worker) {
237 /* notify thread to finish */
238 service_client_t parent = client->parent;
239 client->parent = NULL;
240 /* join thread to make it exit */
241 thread_join(client->worker);
242 thread_free(client->worker);
243 client->worker = THREAD_T_NULL;
244 client->parent = parent;
245 }
246
247 return SYSLOG_RELAY_E_SUCCESS;
248}
diff --git a/src/syslog_relay.h b/src/syslog_relay.h
new file mode 100644
index 0000000..d5263e2
--- /dev/null
+++ b/src/syslog_relay.h
@@ -0,0 +1,37 @@
1/*
2 * syslog_relay.h
3 * com.apple.syslog_relay service header file.
4 *
5 * Copyright (c) 2013 Martin Szulecki All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef _SYSLOG_RELAY_H
23#define _SYSLOG_RELAY_H
24
25#include "idevice.h"
26#include "libimobiledevice/syslog_relay.h"
27#include "service.h"
28#include <libimobiledevice-glue/thread.h>
29
30struct syslog_relay_client_private {
31 service_client_t parent;
32 THREAD_T worker;
33};
34
35void *syslog_relay_worker(void *arg);
36
37#endif
diff --git a/src/userpref.c b/src/userpref.c
deleted file mode 100644
index 6e62000..0000000
--- a/src/userpref.c
+++ /dev/null
@@ -1,605 +0,0 @@
1/*
2 * userpref.c
3 * contains methods to access user specific certificates IDs and more.
4 *
5 * Copyright (c) 2008 Jonathan Beck All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <glib.h>
23#include <glib/gstdio.h>
24#include <glib/gprintf.h>
25#include <stdio.h>
26#include <stdint.h>
27#include <stdlib.h>
28#include <string.h>
29#include <gnutls/gnutls.h>
30#include <gnutls/x509.h>
31#include <gcrypt.h>
32
33#include "userpref.h"
34#include "debug.h"
35
36#define LIBIMOBILEDEVICE_CONF_DIR "libimobiledevice"
37#define LIBIMOBILEDEVICE_CONF_FILE "libimobiledevicerc"
38
39#define LIBIMOBILEDEVICE_ROOT_PRIVKEY "RootPrivateKey.pem"
40#define LIBIMOBILEDEVICE_HOST_PRIVKEY "HostPrivateKey.pem"
41#define LIBIMOBILEDEVICE_ROOT_CERTIF "RootCertificate.pem"
42#define LIBIMOBILEDEVICE_HOST_CERTIF "HostCertificate.pem"
43
44
45/**
46 * Creates a freedesktop compatible configuration directory.
47 */
48static void userpref_create_config_dir(void)
49{
50 gchar *config_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, NULL);
51
52 if (!g_file_test(config_dir, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
53 g_mkdir_with_parents(config_dir, 0755);
54
55 g_free(config_dir);
56}
57
58static int get_rand(int min, int max)
59{
60 int retval = (rand() % (max - min)) + min;
61 return retval;
62}
63
64/**
65 * Generates a valid HostID (which is actually a UUID).
66 *
67 * @return A null terminated string containing a valid HostID.
68 */
69static char *userpref_generate_host_id()
70{
71 /* HostID's are just UUID's, and UUID's are 36 characters long */
72 char *hostid = (char *) malloc(sizeof(char) * 37);
73 const char *chars = "ABCDEF0123456789";
74 srand(time(NULL));
75 int i = 0;
76
77 for (i = 0; i < 36; i++) {
78 if (i == 8 || i == 13 || i == 18 || i == 23) {
79 hostid[i] = '-';
80 continue;
81 } else {
82 hostid[i] = chars[get_rand(0, 16)];
83 }
84 }
85 /* make it a real string */
86 hostid[36] = '\0';
87 return hostid;
88}
89
90/**
91 * Store HostID in config file.
92 *
93 * @param host_id A null terminated string containing a valid HostID.
94 */
95static int userpref_set_host_id(const char *host_id)
96{
97 GKeyFile *key_file;
98 gsize length;
99 gchar *buf, *config_file;
100 GIOChannel *file;
101
102 if (!host_id)
103 return 0;
104
105 /* Make sure config directory exists */
106 userpref_create_config_dir();
107
108 /* Now parse file to get the HostID */
109 key_file = g_key_file_new();
110
111 /* Store in config file */
112 debug_info("setting hostID to %s", host_id);
113 g_key_file_set_value(key_file, "Global", "HostID", host_id);
114
115 /* Write config file on disk */
116 buf = g_key_file_to_data(key_file, &length, NULL);
117 config_file =
118 g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_CONF_FILE, NULL);
119 file = g_io_channel_new_file(config_file, "w", NULL);
120 g_free(config_file);
121 g_io_channel_write_chars(file, buf, length, NULL, NULL);
122 g_io_channel_shutdown(file, TRUE, NULL);
123 g_io_channel_unref(file);
124
125 g_key_file_free(key_file);
126 return 1;
127}
128
129/**
130 * Reads the HostID from a previously generated configuration file.
131 *
132 * @note It is the responsibility of the calling function to free the returned host_id
133 *
134 * @return The string containing the HostID or NULL
135 */
136void userpref_get_host_id(char **host_id)
137{
138 gchar *config_file;
139 GKeyFile *key_file;
140 gchar *loc_host_id;
141
142 config_file =
143 g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_CONF_FILE, NULL);
144
145 /* now parse file to get the HostID */
146 key_file = g_key_file_new();
147 if (g_key_file_load_from_file(key_file, config_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
148 loc_host_id = g_key_file_get_value(key_file, "Global", "HostID", NULL);
149 if (loc_host_id)
150 *host_id = strdup((char *) loc_host_id);
151 g_free(loc_host_id);
152 }
153 g_key_file_free(key_file);
154 g_free(config_file);
155
156 if (!*host_id) {
157 /* no config, generate host_id */
158 *host_id = userpref_generate_host_id();
159 userpref_set_host_id(*host_id);
160 }
161
162 debug_info("Using %s as HostID", *host_id);
163}
164
165/**
166 * Determines whether this device has been connected to this system before.
167 *
168 * @param uid The device uid as given by the device.
169 *
170 * @return 1 if the device has been connected previously to this configuration
171 * or 0 otherwise.
172 */
173int userpref_has_device_public_key(const char *uuid)
174{
175 int ret = 0;
176 gchar *config_file;
177
178 /* first get config file */
179 gchar *device_file = g_strconcat(uuid, ".pem", NULL);
180 config_file = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, device_file, NULL);
181 if (g_file_test(config_file, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)))
182 ret = 1;
183 g_free(config_file);
184 g_free(device_file);
185 return ret;
186}
187
188/**
189 * Fills a list with UUIDs of devices that have been connected to this
190 * system before, i.e. for which a public key file exists.
191 *
192 * @param list A pointer to a char** initially pointing to NULL that will
193 * hold a newly allocated list of UUIDs upon successful return.
194 * The caller is responsible for freeing the memory. Note that if
195 * no public key file was found the list has to be freed too as it
196 * points to a terminating NULL element.
197 * @param count The number of UUIDs found. This parameter can be NULL if it
198 * is not required.
199 *
200 * @return USERPREF_E_SUCCESS on success, or USERPREF_E_INVALID_ARG if the
201 * list parameter is not pointing to NULL.
202 */
203userpref_error_t userpref_get_paired_uuids(char ***list, unsigned int *count)
204{
205 GDir *config_dir;
206 gchar *config_path;
207 const gchar *dir_file;
208 GList *uuids = NULL;
209 unsigned int i;
210 unsigned int found = 0;
211
212 if (!list || (list && *list)) {
213 debug_info("ERROR: The list parameter needs to point to NULL!");
214 return USERPREF_E_INVALID_ARG;
215 }
216
217 if (count) {
218 *count = 0;
219 }
220
221 config_path = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, NULL);
222
223 config_dir = g_dir_open(config_path,0,NULL);
224 if (config_dir) {
225 while ((dir_file = g_dir_read_name(config_dir))) {
226 if (g_str_has_suffix(dir_file, ".pem") && (strlen(dir_file) == 44)) {
227 uuids = g_list_append(uuids, g_strndup(dir_file, strlen(dir_file)-4));
228 found++;
229 }
230 }
231 g_dir_close(config_dir);
232 }
233 *list = (char**)malloc(sizeof(char*) * (found+1));
234 for (i = 0; i < found; i++) {
235 (*list)[i] = g_list_nth_data(uuids, i);
236 }
237 (*list)[i] = NULL;
238
239 if (count) {
240 *count = found;
241 }
242 g_list_free(uuids);
243 g_free(config_path);
244
245 return USERPREF_E_SUCCESS;
246}
247
248/**
249 * Mark the device (as represented by the key) as having connected to this
250 * configuration.
251 *
252 * @param public_key The public key given by the device
253 *
254 * @return 1 on success and 0 if no public key is given or if it has already
255 * been marked as connected previously.
256 */
257userpref_error_t userpref_set_device_public_key(const char *uuid, gnutls_datum_t public_key)
258{
259 if (NULL == public_key.data)
260 return USERPREF_E_INVALID_ARG;
261
262 if (userpref_has_device_public_key(uuid))
263 return USERPREF_E_SUCCESS;
264
265 /* ensure config directory exists */
266 userpref_create_config_dir();
267
268 /* build file path */
269 gchar *device_file = g_strconcat(uuid, ".pem", NULL);
270 gchar *pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, device_file, NULL);
271
272 /* store file */
273 FILE *pFile = fopen(pem, "wb");
274 fwrite(public_key.data, 1, public_key.size, pFile);
275 fclose(pFile);
276 g_free(pem);
277 g_free(device_file);
278
279 return USERPREF_E_SUCCESS;
280}
281
282/**
283 * Remove the public key stored for the device with uuid from this host.
284 *
285 * @param uuid The uuid of the device
286 *
287 * @return USERPREF_E_SUCCESS on success.
288 */
289userpref_error_t userpref_remove_device_public_key(const char *uuid)
290{
291 if (!userpref_has_device_public_key(uuid))
292 return USERPREF_E_SUCCESS;
293
294 /* build file path */
295 gchar *device_file = g_strconcat(uuid, ".pem", NULL);
296 gchar *pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, device_file, NULL);
297
298 /* remove file */
299 g_remove(pem);
300
301 g_free(pem);
302 g_free(device_file);
303
304 return USERPREF_E_SUCCESS;
305}
306
307/**
308 * Private function which reads the given file into a gnutls structure.
309 *
310 * @param file The filename of the file to read
311 * @param data The pointer at which to store the data.
312 *
313 * @return 1 if the file contents where read successfully and 0 otherwise.
314 */
315static int userpref_get_file_contents(const char *file, gnutls_datum_t * data)
316{
317 gboolean success;
318 gsize size;
319 char *content;
320 gchar *filepath;
321
322 if (NULL == file || NULL == data)
323 return 0;
324
325 /* Read file */
326 filepath = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, file, NULL);
327 success = g_file_get_contents(filepath, &content, &size, NULL);
328 g_free(filepath);
329
330 /* Add it to the gnutls_datnum_t structure */
331 data->data = (uint8_t*) content;
332 data->size = size;
333
334 return success;
335}
336
337/**
338 * Private function which generate private keys and certificates.
339 *
340 * @return 1 if keys were successfully generated, 0 otherwise
341 */
342static userpref_error_t userpref_gen_keys_and_cert(void)
343{
344 userpref_error_t ret = USERPREF_E_SSL_ERROR;
345
346 gnutls_x509_privkey_t root_privkey;
347 gnutls_x509_crt_t root_cert;
348 gnutls_x509_privkey_t host_privkey;
349 gnutls_x509_crt_t host_cert;
350
351 gnutls_global_deinit();
352 gnutls_global_init();
353
354 //use less secure random to speed up key generation
355 gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM);
356
357 gnutls_x509_privkey_init(&root_privkey);
358 gnutls_x509_privkey_init(&host_privkey);
359
360 gnutls_x509_crt_init(&root_cert);
361 gnutls_x509_crt_init(&host_cert);
362
363 /* generate root key */
364 gnutls_x509_privkey_generate(root_privkey, GNUTLS_PK_RSA, 2048, 0);
365 gnutls_x509_privkey_generate(host_privkey, GNUTLS_PK_RSA, 2048, 0);
366
367 /* generate certificates */
368 gnutls_x509_crt_set_key(root_cert, root_privkey);
369 gnutls_x509_crt_set_serial(root_cert, "\x00", 1);
370 gnutls_x509_crt_set_version(root_cert, 3);
371 gnutls_x509_crt_set_ca_status(root_cert, 1);
372 gnutls_x509_crt_set_activation_time(root_cert, time(NULL));
373 gnutls_x509_crt_set_expiration_time(root_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
374 gnutls_x509_crt_sign(root_cert, root_cert, root_privkey);
375
376 gnutls_x509_crt_set_key(host_cert, host_privkey);
377 gnutls_x509_crt_set_serial(host_cert, "\x00", 1);
378 gnutls_x509_crt_set_version(host_cert, 3);
379 gnutls_x509_crt_set_ca_status(host_cert, 0);
380 gnutls_x509_crt_set_key_usage(host_cert, GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_DIGITAL_SIGNATURE);
381 gnutls_x509_crt_set_activation_time(host_cert, time(NULL));
382 gnutls_x509_crt_set_expiration_time(host_cert, time(NULL) + (60 * 60 * 24 * 365 * 10));
383 gnutls_x509_crt_sign(host_cert, root_cert, root_privkey);
384
385 /* export to PEM format */
386 size_t root_key_export_size = 0;
387 size_t host_key_export_size = 0;
388 gnutls_datum_t root_key_pem = { NULL, 0 };
389 gnutls_datum_t host_key_pem = { NULL, 0 };
390
391 gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, NULL, &root_key_export_size);
392 gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, NULL, &host_key_export_size);
393
394 root_key_pem.data = gnutls_malloc(root_key_export_size);
395 host_key_pem.data = gnutls_malloc(host_key_export_size);
396
397 gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, root_key_pem.data, &root_key_export_size);
398 root_key_pem.size = root_key_export_size;
399 gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, host_key_pem.data, &host_key_export_size);
400 host_key_pem.size = host_key_export_size;
401
402 size_t root_cert_export_size = 0;
403 size_t host_cert_export_size = 0;
404 gnutls_datum_t root_cert_pem = { NULL, 0 };
405 gnutls_datum_t host_cert_pem = { NULL, 0 };
406
407 gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, NULL, &root_cert_export_size);
408 gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, NULL, &host_cert_export_size);
409
410 root_cert_pem.data = gnutls_malloc(root_cert_export_size);
411 host_cert_pem.data = gnutls_malloc(host_cert_export_size);
412
413 gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, root_cert_pem.data, &root_cert_export_size);
414 root_cert_pem.size = root_cert_export_size;
415 gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, host_cert_pem.data, &host_cert_export_size);
416 host_cert_pem.size = host_cert_export_size;
417
418 if (NULL != root_cert_pem.data && 0 != root_cert_pem.size &&
419 NULL != host_cert_pem.data && 0 != host_cert_pem.size)
420 ret = USERPREF_E_SUCCESS;
421
422 /* store values in config file */
423 userpref_set_keys_and_certs( &root_key_pem, &root_cert_pem, &host_key_pem, &host_cert_pem);
424
425 gnutls_free(root_key_pem.data);
426 gnutls_free(root_cert_pem.data);
427 gnutls_free(host_key_pem.data);
428 gnutls_free(host_cert_pem.data);
429
430 //restore gnutls env
431 gnutls_global_deinit();
432 gnutls_global_init();
433
434 return ret;
435}
436
437/**
438 * Private function which import the given key into a gnutls structure.
439 *
440 * @param key_name The filename of the private key to import.
441 * @param key the gnutls key structure.
442 *
443 * @return 1 if the key was successfully imported.
444 */
445static userpref_error_t userpref_import_key(const char* key_name, gnutls_x509_privkey_t key)
446{
447 userpref_error_t ret = USERPREF_E_INVALID_CONF;
448 gnutls_datum_t pem_key = { NULL, 0 };
449
450 if (userpref_get_file_contents(key_name, &pem_key)) {
451 if (GNUTLS_E_SUCCESS == gnutls_x509_privkey_import(key, &pem_key, GNUTLS_X509_FMT_PEM))
452 ret = USERPREF_E_SUCCESS;
453 else
454 ret = USERPREF_E_SSL_ERROR;
455 }
456 gnutls_free(pem_key.data);
457 return ret;
458}
459
460/**
461 * Private function which import the given certificate into a gnutls structure.
462 *
463 * @param crt_name The filename of the certificate to import.
464 * @param cert the gnutls certificate structure.
465 *
466 * @return IDEVICE_E_SUCCESS if the certificate was successfully imported.
467 */
468static userpref_error_t userpref_import_crt(const char* crt_name, gnutls_x509_crt_t cert)
469{
470 userpref_error_t ret = USERPREF_E_INVALID_CONF;
471 gnutls_datum_t pem_cert = { NULL, 0 };
472
473 if (userpref_get_file_contents(crt_name, &pem_cert)) {
474 if (GNUTLS_E_SUCCESS == gnutls_x509_crt_import(cert, &pem_cert, GNUTLS_X509_FMT_PEM))
475 ret = USERPREF_E_SUCCESS;
476 else
477 ret = USERPREF_E_SSL_ERROR;
478 }
479 gnutls_free(pem_cert.data);
480 return ret;
481}
482
483/**
484 * Function to retrieve host keys and certificates.
485 * This function trigger key generation if they do not exists yet or are invalid.
486 *
487 * @note This function can take few seconds to complete (typically 5 seconds)
488 *
489 * @param root_privkey The root private key.
490 * @param root_crt The root certificate.
491 * @param host_privkey The host private key.
492 * @param host_crt The host certificate.
493 *
494 * @return 1 if the keys and certificates were successfully retrieved, 0 otherwise
495 */
496userpref_error_t userpref_get_keys_and_certs(gnutls_x509_privkey_t root_privkey, gnutls_x509_crt_t root_crt, gnutls_x509_privkey_t host_privkey, gnutls_x509_crt_t host_crt)
497{
498 userpref_error_t ret = USERPREF_E_SUCCESS;
499
500 if (ret == USERPREF_E_SUCCESS)
501 ret = userpref_import_key(LIBIMOBILEDEVICE_ROOT_PRIVKEY, root_privkey);
502
503 if (ret == USERPREF_E_SUCCESS)
504 ret = userpref_import_key(LIBIMOBILEDEVICE_HOST_PRIVKEY, host_privkey);
505
506 if (ret == USERPREF_E_SUCCESS)
507 ret = userpref_import_crt(LIBIMOBILEDEVICE_ROOT_CERTIF, root_crt);
508
509 if (ret == USERPREF_E_SUCCESS)
510 ret = userpref_import_crt(LIBIMOBILEDEVICE_HOST_CERTIF, host_crt);
511
512
513 if (USERPREF_E_SUCCESS != ret) {
514 //we had problem reading or importing root cert
515 //try with a new ones.
516 ret = userpref_gen_keys_and_cert();
517
518 if (ret == USERPREF_E_SUCCESS)
519 ret = userpref_import_key(LIBIMOBILEDEVICE_ROOT_PRIVKEY, root_privkey);
520
521 if (ret == USERPREF_E_SUCCESS)
522 ret = userpref_import_key(LIBIMOBILEDEVICE_HOST_PRIVKEY, host_privkey);
523
524 if (ret == USERPREF_E_SUCCESS)
525 ret = userpref_import_crt(LIBIMOBILEDEVICE_ROOT_CERTIF, root_crt);
526
527 if (ret == USERPREF_E_SUCCESS)
528 ret = userpref_import_crt(LIBIMOBILEDEVICE_HOST_CERTIF, host_crt);
529 }
530
531 return ret;
532}
533
534/**
535 * Function to retrieve certificates encoded in PEM format.
536 *
537 * @param pem_root_cert The root certificate.
538 * @param pem_host_cert The host certificate.
539 *
540 * @return 1 if the certificates were successfully retrieved, 0 otherwise
541 */
542userpref_error_t userpref_get_certs_as_pem(gnutls_datum_t *pem_root_cert, gnutls_datum_t *pem_host_cert)
543{
544 if (!pem_root_cert || !pem_host_cert)
545 return USERPREF_E_INVALID_ARG;
546
547 if (userpref_get_file_contents(LIBIMOBILEDEVICE_ROOT_CERTIF, pem_root_cert) && userpref_get_file_contents(LIBIMOBILEDEVICE_HOST_CERTIF, pem_host_cert))
548 return USERPREF_E_SUCCESS;
549 else {
550 g_free(pem_root_cert->data);
551 g_free(pem_host_cert->data);
552 }
553 return USERPREF_E_INVALID_CONF;
554}
555
556/**
557 * Create and save a configuration file containing the given data.
558 *
559 * @note: All fields must specified and be non-null
560 *
561 * @param root_key The root key
562 * @param root_cert The root certificate
563 * @param host_key The host key
564 * @param host_cert The host certificate
565 *
566 * @return 1 on success and 0 otherwise.
567 */
568userpref_error_t userpref_set_keys_and_certs(gnutls_datum_t * root_key, gnutls_datum_t * root_cert, gnutls_datum_t * host_key, gnutls_datum_t * host_cert)
569{
570 FILE *pFile;
571 gchar *pem;
572
573 if (!root_key || !host_key || !root_cert || !host_cert)
574 return USERPREF_E_INVALID_ARG;
575
576 /* Make sure config directory exists */
577 userpref_create_config_dir();
578
579 /* Now write keys and certificates to disk */
580 pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_ROOT_PRIVKEY, NULL);
581 pFile = fopen(pem, "wb");
582 fwrite(root_key->data, 1, root_key->size, pFile);
583 fclose(pFile);
584 g_free(pem);
585
586 pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_HOST_PRIVKEY, NULL);
587 pFile = fopen(pem, "wb");
588 fwrite(host_key->data, 1, host_key->size, pFile);
589 fclose(pFile);
590 g_free(pem);
591
592 pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_ROOT_CERTIF, NULL);
593 pFile = fopen(pem, "wb");
594 fwrite(root_cert->data, 1, root_cert->size, pFile);
595 fclose(pFile);
596 g_free(pem);
597
598 pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIMOBILEDEVICE_CONF_DIR, LIBIMOBILEDEVICE_HOST_CERTIF, NULL);
599 pFile = fopen(pem, "wb");
600 fwrite(host_cert->data, 1, host_cert->size, pFile);
601 fclose(pFile);
602 g_free(pem);
603
604 return USERPREF_E_SUCCESS;
605}
diff --git a/src/userpref.h b/src/userpref.h
deleted file mode 100644
index f9d3913..0000000
--- a/src/userpref.h
+++ /dev/null
@@ -1,46 +0,0 @@
1/*
2 * userpref.h
3 * contains methods to access user specific certificates IDs and more.
4 *
5 * Copyright (c) 2008 Jonathan Beck All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef USERPREF_H
23#define USERPREF_H
24
25#include <gnutls/gnutls.h>
26#include <glib.h>
27
28#define USERPREF_E_SUCCESS 0
29#define USERPREF_E_INVALID_ARG -1
30#define USERPREF_E_INVALID_CONF -2
31#define USERPREF_E_SSL_ERROR -3
32
33#define USERPREF_E_UNKNOWN_ERROR -256
34
35typedef int16_t userpref_error_t;
36
37G_GNUC_INTERNAL userpref_error_t userpref_get_keys_and_certs(gnutls_x509_privkey_t root_privkey, gnutls_x509_crt_t root_crt, gnutls_x509_privkey_t host_privkey, gnutls_x509_crt_t host_crt);
38G_GNUC_INTERNAL userpref_error_t userpref_set_keys_and_certs(gnutls_datum_t * root_key, gnutls_datum_t * root_cert, gnutls_datum_t * host_key, gnutls_datum_t * host_cert);
39G_GNUC_INTERNAL userpref_error_t userpref_get_certs_as_pem(gnutls_datum_t *pem_root_cert, gnutls_datum_t *pem_host_cert);
40G_GNUC_INTERNAL userpref_error_t userpref_set_device_public_key(const char *uuid, gnutls_datum_t public_key);
41userpref_error_t userpref_remove_device_public_key(const char *uuid);
42G_GNUC_INTERNAL int userpref_has_device_public_key(const char *uuid);
43userpref_error_t userpref_get_paired_uuids(char ***list, unsigned int *count);
44void userpref_get_host_id(char **host_id);
45
46#endif
diff --git a/src/webinspector.c b/src/webinspector.c
new file mode 100644
index 0000000..f960fcc
--- /dev/null
+++ b/src/webinspector.c
@@ -0,0 +1,263 @@
1/*
2 * webinspector.c
3 * com.apple.webinspector service implementation.
4 *
5 * Copyright (c) 2013 Yury Melnichek All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include <plist/plist.h>
28
29#include "webinspector.h"
30#include "lockdown.h"
31#include "common/debug.h"
32
33/**
34 * Convert a property_list_service_error_t value to a webinspector_error_t value.
35 * Used internally to get correct error codes.
36 *
37 * @param err An property_list_service_error_t error code
38 *
39 * @return A matching webinspector_error_t error code,
40 * WEBINSPECTOR_E_UNKNOWN_ERROR otherwise.
41 */
42static webinspector_error_t webinspector_error(property_list_service_error_t err)
43{
44 switch (err) {
45 case PROPERTY_LIST_SERVICE_E_SUCCESS:
46 return WEBINSPECTOR_E_SUCCESS;
47 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
48 return WEBINSPECTOR_E_INVALID_ARG;
49 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
50 return WEBINSPECTOR_E_PLIST_ERROR;
51 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
52 return WEBINSPECTOR_E_MUX_ERROR;
53 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
54 return WEBINSPECTOR_E_SSL_ERROR;
55 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
56 return WEBINSPECTOR_E_RECEIVE_TIMEOUT;
57 case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA:
58 return WEBINSPECTOR_E_NOT_ENOUGH_DATA;
59 default:
60 break;
61 }
62 return WEBINSPECTOR_E_UNKNOWN_ERROR;
63}
64
65webinspector_error_t webinspector_client_new(idevice_t device, lockdownd_service_descriptor_t service, webinspector_client_t * client)
66{
67 *client = NULL;
68
69 if (!device || !service || service->port == 0 || !client || *client) {
70 debug_info("Incorrect parameter passed to webinspector_client_new.");
71 return WEBINSPECTOR_E_INVALID_ARG;
72 }
73
74 debug_info("Creating webinspector_client, port = %d.", service->port);
75
76 property_list_service_client_t plclient = NULL;
77 webinspector_error_t ret = webinspector_error(property_list_service_client_new(device, service, &plclient));
78 if (ret != WEBINSPECTOR_E_SUCCESS) {
79 debug_info("Creating a property list client failed. Error: %i", ret);
80 return ret;
81 }
82
83 webinspector_client_t client_loc = (webinspector_client_t) malloc(sizeof(struct webinspector_client_private));
84 client_loc->parent = plclient;
85
86 *client = client_loc;
87
88 debug_info("webinspector_client successfully created.");
89 return 0;
90}
91
92webinspector_error_t webinspector_client_start_service(idevice_t device, webinspector_client_t * client, const char* label)
93{
94 webinspector_error_t err = WEBINSPECTOR_E_UNKNOWN_ERROR;
95 service_client_factory_start_service(device, WEBINSPECTOR_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(webinspector_client_new), &err);
96 return err;
97}
98
99webinspector_error_t webinspector_client_free(webinspector_client_t client)
100{
101 if (!client)
102 return WEBINSPECTOR_E_INVALID_ARG;
103
104 webinspector_error_t err = webinspector_error(property_list_service_client_free(client->parent));
105 free(client);
106
107 return err;
108}
109
110webinspector_error_t webinspector_send(webinspector_client_t client, plist_t plist)
111{
112 webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR;
113
114 uint32_t offset = 0;
115 int is_final_message = 0;
116
117 char *packet = NULL;
118 uint32_t packet_length = 0;
119
120 debug_info("Sending webinspector message...");
121 debug_plist(plist);
122
123 /* convert plist to packet */
124 plist_to_bin(plist, &packet, &packet_length);
125 if (!packet || packet_length == 0) {
126 debug_info("Error converting plist to binary.");
127 return res;
128 }
129
130 do {
131 /* determine if we need to send partial messages */
132 if (packet_length < WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE) {
133 is_final_message = 1;
134 } else {
135 /* send partial packet */
136 is_final_message = 0;
137 }
138
139 plist_t outplist = plist_new_dict();
140 if (!is_final_message) {
141 /* split packet into partial chunks */
142 plist_dict_set_item(outplist, "WIRPartialMessageKey", plist_new_data(packet + offset, WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE));
143 offset += WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE;
144 packet_length -= WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE;
145 } else {
146 /* send final chunk */
147 plist_dict_set_item(outplist, "WIRFinalMessageKey", plist_new_data(packet + offset, packet_length));
148 offset += packet_length;
149 packet_length -= packet_length;
150 }
151
152 res = webinspector_error(property_list_service_send_binary_plist(client->parent, outplist));
153 plist_free(outplist);
154 outplist = NULL;
155 if (res != WEBINSPECTOR_E_SUCCESS) {
156 debug_info("Sending plist failed with error %d", res);
157 return res;
158 }
159 } while(packet_length > 0);
160
161 free(packet);
162 packet = NULL;
163
164 return res;
165}
166
167webinspector_error_t webinspector_receive(webinspector_client_t client, plist_t * plist)
168{
169 return webinspector_receive_with_timeout(client, plist, 5000);
170}
171
172webinspector_error_t webinspector_receive_with_timeout(webinspector_client_t client, plist_t * plist, uint32_t timeout_ms)
173{
174 webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR;
175 plist_t message = NULL;
176 plist_t key = NULL;
177
178 int is_final_message = 1;
179
180 char* buffer = NULL;
181 uint64_t length = 0;
182
183 char* packet = NULL;
184 char* newpacket = NULL;
185 uint64_t packet_length = 0;
186
187 debug_info("Receiving webinspector message...");
188
189 do {
190 /* receive message */
191 res = webinspector_error(property_list_service_receive_plist_with_timeout(client->parent, &message, timeout_ms));
192 if (res != WEBINSPECTOR_E_SUCCESS || !message) {
193 debug_info("Could not receive message, error %d", res);
194 plist_free(message);
195 return WEBINSPECTOR_E_MUX_ERROR;
196 }
197
198 /* get message key */
199 key = plist_dict_get_item(message, "WIRFinalMessageKey");
200 if (!key) {
201 key = plist_dict_get_item(message, "WIRPartialMessageKey");
202 if (!key) {
203 debug_info("ERROR: Unable to read message key.");
204 plist_free(message);
205 return WEBINSPECTOR_E_PLIST_ERROR;
206 }
207 is_final_message = 0;
208 } else {
209 is_final_message = 1;
210 }
211
212 /* read partial data */
213 plist_get_data_val(key, &buffer, &length);
214 if (!buffer || length == 0 || length > 0xFFFFFFFF) {
215 debug_info("ERROR: Unable to get the inner plist binary data.");
216 free(packet);
217 free(buffer);
218 return WEBINSPECTOR_E_PLIST_ERROR;
219 }
220
221 /* (re)allocate packet data */
222 if (!packet) {
223 packet = (char*)malloc(length * sizeof(char));
224 } else {
225 newpacket = (char*)realloc(packet, (packet_length + length) * sizeof(char));
226 packet = newpacket;
227 }
228
229 /* copy partial data into final packet data */
230 memcpy(packet + packet_length, buffer, length);
231
232 /* cleanup buffer */
233 free(buffer);
234 buffer = NULL;
235
236 if (message) {
237 plist_free(message);
238 message = NULL;
239 }
240
241 /* adjust packet length */
242 packet_length += length;
243 length = 0;
244 } while(!is_final_message);
245
246 /* read final message */
247 if (packet_length) {
248 plist_from_bin(packet, (uint32_t)packet_length, plist);
249 if (!*plist) {
250 debug_info("Error restoring the final plist.");
251 free(packet);
252 return WEBINSPECTOR_E_PLIST_ERROR;
253 }
254
255 debug_plist(*plist);
256 }
257
258 if (packet) {
259 free(packet);
260 }
261
262 return res;
263}
diff --git a/src/webinspector.h b/src/webinspector.h
new file mode 100644
index 0000000..d249c58
--- /dev/null
+++ b/src/webinspector.h
@@ -0,0 +1,35 @@
1/*
2 * webinspector.h
3 * com.apple.webinspector service header file.
4 *
5 * Copyright (c) 2013 Yury Melnichek All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef __WEBINSPECTOR_H
23#define __WEBINSPECTOR_H
24
25#include "idevice.h"
26#include "libimobiledevice/webinspector.h"
27#include "property_list_service.h"
28
29#define WEBINSPECTOR_PARTIAL_PACKET_CHUNK_SIZE 8096
30
31struct webinspector_client_private {
32 property_list_service_client_t parent;
33};
34
35#endif