summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am90
-rw-r--r--src/afc.c1280
-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.c183
-rw-r--r--src/house_arrest.h12
-rw-r--r--src/idevice.c1470
-rw-r--r--src/idevice.h72
-rw-r--r--src/installation_proxy.c1236
-rw-r--r--src/installation_proxy.h20
-rw-r--r--src/libimobiledevice-1.0.pc.in12
-rw-r--r--src/lockdown-cu.c1197
-rw-r--r--src/lockdown.c1455
-rw-r--r--src/lockdown.h20
-rw-r--r--src/misagent.c294
-rw-r--r--src/misagent.h34
-rw-r--r--src/mobile_image_mounter.c518
-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.c258
-rw-r--r--src/notification_proxy.h19
-rw-r--r--src/ostrace.c436
-rw-r--r--src/ostrace.h37
-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.c223
-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
65 files changed, 11587 insertions, 4687 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 70dc895..1ee9be8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,24 +1,70 @@
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 ostrace.c ostrace.h \
62 bt_packet_logger.c bt_packet_logger.h
63
64if WIN32
65libimobiledevice_1_0_la_LDFLAGS += -avoid-version -static-libgcc
66libimobiledevice_1_0_la_LIBADD += -lole32 -lws2_32 -lgdi32
67endif
68
69pkgconfigdir = $(libdir)/pkgconfig
70pkgconfig_DATA = libimobiledevice-1.0.pc
diff --git a/src/afc.c b/src/afc.c
index 989d0ec..c7eed5c 100644
--- a/src/afc.c
+++ b/src/afc.c
@@ -1,308 +1,246 @@
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>
25#include <string.h> 29#include <string.h>
26 30
27#include "afc.h" 31#ifndef _MSC_VER
28#include "idevice.h" 32#include <unistd.h>
29#include "debug.h" 33#endif
30 34
31/** The maximum size an AFC data packet can be */ 35#include "idevice.h"
32static const int MAXIMUM_PACKET_SIZE = (2 << 15); 36#include "afc.h"
37#include "common/debug.h"
38#include "endianness.h"
33 39
34/** 40/**
35 * Locks an AFC client, done for thread safety stuff 41 * Locks an AFC client, done for thread safety stuff
36 * 42 *
37 * @param client The AFC client connection to lock 43 * @param client The AFC client connection to lock
38 */ 44 */
39static void afc_lock(afc_client_t client) 45static void afc_lock(afc_client_t client)
40{ 46{
41 debug_info("Locked"); 47 debug_info("Locked");
42 g_mutex_lock(client->mutex); 48 mutex_lock(&client->mutex);
43} 49}
44 50
45/** 51/**
46 * Unlocks an AFC client, done for thread safety stuff. 52 * Unlocks an AFC client, done for thread safety stuff.
47 * 53 *
48 * @param client The AFC 54 * @param client The AFC
49 */ 55 */
50static void afc_unlock(afc_client_t client) 56static void afc_unlock(afc_client_t client)
51{ 57{
52 debug_info("Unlocked"); 58 debug_info("Unlocked");
53 g_mutex_unlock(client->mutex); 59 mutex_unlock(&client->mutex);
54} 60}
55 61
56/** 62/**
57 * Makes a connection to the AFC service on the device using the given 63 * Makes a connection to the AFC service on the device using the given
58 * connection. 64 * connection.
59 * 65 *
60 * @param connection An idevice_connection_t that must have been previously 66 * @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 67 * @param client Pointer that will be set to a newly allocated afc_client_t
64 * upon successful return. 68 * upon successful return.
65 * 69 *
66 * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if connection is 70 * @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. 71 * invalid, or AFC_E_NO_MEM if there is a memory allocation problem.
68 */ 72 */
69 73
70afc_error_t afc_client_new_from_connection(idevice_connection_t connection, afc_client_t *client) 74afc_error_t afc_client_new_with_service_client(service_client_t service_client, afc_client_t *client)
71{ 75{
72 /* makes sure thread environment is available */ 76 if (!service_client)
73 if (!g_thread_supported())
74 g_thread_init(NULL);
75
76 if (!connection)
77 return AFC_E_INVALID_ARG; 77 return AFC_E_INVALID_ARG;
78 78
79 afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private)); 79 afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private));
80 client_loc->connection = connection; 80 client_loc->parent = service_client;
81 client_loc->own_connection = 0; 81 client_loc->free_parent = 0;
82 82
83 /* allocate a packet */ 83 /* allocate a packet */
84 client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); 84 client_loc->packet_extra = 1024;
85 client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket) + client_loc->packet_extra);
85 if (!client_loc->afc_packet) { 86 if (!client_loc->afc_packet) {
86 free(client_loc); 87 free(client_loc);
87 return AFC_E_NO_MEM; 88 return AFC_E_NO_MEM;
88 } 89 }
89
90 client_loc->afc_packet->packet_num = 0; 90 client_loc->afc_packet->packet_num = 0;
91 client_loc->afc_packet->entire_length = 0; 91 client_loc->afc_packet->entire_length = 0;
92 client_loc->afc_packet->this_length = 0; 92 client_loc->afc_packet->this_length = 0;
93 memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN); 93 memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN);
94 client_loc->file_handle = 0; 94 mutex_init(&client_loc->mutex);
95 client_loc->lock = 0;
96 client_loc->mutex = g_mutex_new();
97 95
98 *client = client_loc; 96 *client = client_loc;
99 return AFC_E_SUCCESS; 97 return AFC_E_SUCCESS;
100} 98}
101 99
102/** 100afc_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{ 101{
120 /* makes sure thread environment is available */ 102 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; 103 return AFC_E_INVALID_ARG;
126 104
127 /* attempt connection */ 105 service_client_t parent = NULL;
128 idevice_connection_t connection = NULL; 106 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; 107 return AFC_E_MUX_ERROR;
131 } 108 }
132 109
133 afc_error_t err = afc_client_new_from_connection(connection, client); 110 afc_error_t err = afc_client_new_with_service_client(parent, client);
134 if (err != AFC_E_SUCCESS) { 111 if (err != AFC_E_SUCCESS) {
135 idevice_disconnect(connection); 112 service_client_free(parent);
136 } else { 113 } else {
137 (*client)->own_connection = 1; 114 (*client)->free_parent = 1;
138 } 115 }
139 return err; 116 return err;
140} 117}
141 118
142/** 119afc_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 120{
144 * client itself, the connection will be closed. 121 int32_t err = AFC_E_UNKNOWN_ERROR;
145 * 122 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. 123 return err;
147 */ 124}
125
148afc_error_t afc_client_free(afc_client_t client) 126afc_error_t afc_client_free(afc_client_t client)
149{ 127{
150 if (!client || !client->afc_packet) 128 if (!client || !client->afc_packet)
151 return AFC_E_INVALID_ARG; 129 return AFC_E_INVALID_ARG;
152 130
153 if (client->own_connection && client->connection) { 131 if (client->free_parent && client->parent) {
154 idevice_disconnect(client->connection); 132 service_client_free(client->parent);
155 client->connection = NULL; 133 client->parent = NULL;
156 } 134 }
157 free(client->afc_packet); 135 free(client->afc_packet);
158 if (client->mutex) { 136 mutex_destroy(&client->mutex);
159 g_mutex_free(client->mutex);
160 }
161 free(client); 137 free(client);
162 return AFC_E_SUCCESS; 138 return AFC_E_SUCCESS;
163} 139}
164 140
165/** 141/**
166 * Dispatches an AFC packet over a client. 142 * Dispatches an AFC packet over a client.
167 * 143 *
168 * @param client The client to send data through. 144 * @param client The client to send data through.
169 * @param data The data to send. 145 * @param operation The operation to perform.
170 * @param length The length to send. 146 * @param data The data to send together with the header.
171 * @param bytes_sent The number of bytes actually sent. 147 * @param data_length The length of the data to send with the header.
148 * @param payload The data to send after the header has been sent.
149 * @param payload_length The length of data to send after the header.
150 * @param bytes_sent The total number of bytes actually sent.
172 * 151 *
173 * @return AFC_E_SUCCESS on success or an AFC_E_* error value. 152 * @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 */ 153 */
180static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, uint32_t length, uint32_t *bytes_sent) 154static 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{ 155{
182 uint32_t offset = 0;
183 uint32_t sent = 0; 156 uint32_t sent = 0;
184 157
185 if (!client || !client->connection || !client->afc_packet) 158 if (!client || !client->parent || !client->afc_packet)
186 return AFC_E_INVALID_ARG; 159 return AFC_E_INVALID_ARG;
187 160
188 *bytes_sent = 0; 161 *bytes_sent = 0;
189 162
190 if (!data || !length) 163 if (!payload || !payload_length)
191 length = 0; 164 payload_length = 0;
192 165
193 client->afc_packet->packet_num++; 166 client->afc_packet->packet_num++;
194 if (!client->afc_packet->entire_length) { 167 client->afc_packet->operation = operation;
195 client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length : sizeof(AFCPacket); 168 client->afc_packet->entire_length = sizeof(AFCPacket) + data_length + payload_length;
196 client->afc_packet->this_length = client->afc_packet->entire_length; 169 client->afc_packet->this_length = sizeof(AFCPacket) + data_length;
197 } 170
198 if (!client->afc_packet->this_length) { 171 debug_info("packet length = %i", client->afc_packet->this_length);
199 client->afc_packet->this_length = sizeof(AFCPacket); 172
200 } 173 /* send AFC packet header and data */
201 /* We want to send two segments; buffer+sizeof(AFCPacket) to this_length 174 AFCPacket_to_LE(client->afc_packet);
202 is the parameters and everything beyond that is the next packet. 175 debug_buffer((char*)client->afc_packet, sizeof(AFCPacket) + data_length);
203 (for writing) */ 176 sent = 0;
204 if (client->afc_packet->this_length != client->afc_packet->entire_length) { 177 service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket) + data_length, &sent);
205 offset = client->afc_packet->this_length - sizeof(AFCPacket); 178 AFCPacket_from_LE(client->afc_packet);
206 179 *bytes_sent += sent;
207 debug_info("Offset: %i", offset); 180 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; 181 return AFC_E_SUCCESS;
244 } else { 182 }
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 183
250 /* send AFC packet header */ 184 sent = 0;
251 AFCPacket_to_LE(client->afc_packet); 185 if (payload_length > 0) {
252 sent = 0; 186 if (payload_length > 256) {
253 idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); 187 debug_info("packet payload follows (256/%u)", payload_length);
254 AFCPacket_from_LE(client->afc_packet); 188 debug_buffer(payload, 256);
255 if (sent == 0) { 189 } else {
256 return AFC_E_SUCCESS; 190 debug_info("packet payload follows");
257 } 191 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 } 192 }
193 service_send(client->parent, payload, payload_length, &sent);
194 }
195 *bytes_sent += sent;
196 if (sent < payload_length) {
267 return AFC_E_SUCCESS; 197 return AFC_E_SUCCESS;
268 } 198 }
269 return AFC_E_INTERNAL_ERROR; 199
200 return AFC_E_SUCCESS;
270} 201}
271 202
272/** 203/**
273 * Receives data through an AFC client and sets a variable to the received data. 204 * Receives data through an AFC client and sets a variable to the received data.
274 * 205 *
275 * @param client The client to receive data on. 206 * @param client The client to receive data on.
276 * @param dump_here The char* to point to the newly-received data. 207 * @param bytes The char* to point to the newly-received data.
277 * @param bytes_recv How much data was received. 208 * @param bytes_recv How much data was received.
278 * 209 *
279 * @return AFC_E_SUCCESS on success or an AFC_E_* error value. 210 * @return AFC_E_SUCCESS on success or an AFC_E_* error value.
280 */ 211 */
281static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint32_t *bytes_recv) 212static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t *bytes_recv)
282{ 213{
283 AFCPacket header; 214 AFCPacket header;
284 uint32_t entire_len = 0; 215 uint32_t entire_len = 0;
285 uint32_t this_len = 0; 216 uint32_t this_len = 0;
286 uint32_t current_count = 0; 217 uint32_t current_count = 0;
287 uint64_t param1 = -1; 218 uint64_t param1 = -1;
219 char *buf = NULL;
220 uint32_t recv_len = 0;
288 221
289 *bytes_recv = 0; 222 if (bytes_recv) {
223 *bytes_recv = 0;
224 }
225 if (bytes) {
226 *bytes = NULL;
227 }
290 228
291 /* first, read the AFC header */ 229 /* first, read the AFC header */
292 idevice_connection_receive(client->connection, (char*)&header, sizeof(AFCPacket), bytes_recv); 230 service_receive(client->parent, (char*)&header, sizeof(AFCPacket), &recv_len);
293 AFCPacket_from_LE(&header); 231 AFCPacket_from_LE(&header);
294 if (*bytes_recv == 0) { 232 if (recv_len == 0) {
295 debug_info("Just didn't get enough."); 233 debug_info("Just didn't get enough.");
296 *dump_here = NULL;
297 return AFC_E_MUX_ERROR; 234 return AFC_E_MUX_ERROR;
298 } else if (*bytes_recv < sizeof(AFCPacket)) { 235 }
236
237 if (recv_len < sizeof(AFCPacket)) {
299 debug_info("Did not even get the AFCPacket header"); 238 debug_info("Did not even get the AFCPacket header");
300 *dump_here = NULL;
301 return AFC_E_MUX_ERROR; 239 return AFC_E_MUX_ERROR;
302 } 240 }
303 241
304 /* check if it's a valid AFC header */ 242 /* check if it's a valid AFC header */
305 if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN)) { 243 if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN) != 0) {
306 debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!"); 244 debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!");
307 } 245 }
308 246
@@ -310,25 +248,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) { 248 if (header.packet_num != client->afc_packet->packet_num) {
311 /* otherwise print a warning but do not abort */ 249 /* 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); 250 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; 251 return AFC_E_OP_HEADER_INVALID;
315 } 252 }
316 253
317 /* then, read the attached packet */ 254 /* then, read the attached packet */
318 if (header.this_length < sizeof(AFCPacket)) { 255 if (header.this_length < sizeof(AFCPacket)) {
319 debug_info("Invalid AFCPacket header received!"); 256 debug_info("Invalid AFCPacket header received!");
320 *dump_here = NULL;
321 return AFC_E_OP_HEADER_INVALID; 257 return AFC_E_OP_HEADER_INVALID;
322 } else if ((header.this_length == header.entire_length) 258 }
323 && header.entire_length == sizeof(AFCPacket)) { 259 if ((header.this_length == header.entire_length)
260 && header.entire_length == sizeof(AFCPacket)) {
324 debug_info("Empty AFCPacket received!"); 261 debug_info("Empty AFCPacket received!");
325 *dump_here = NULL;
326 *bytes_recv = 0;
327 if (header.operation == AFC_OP_DATA) { 262 if (header.operation == AFC_OP_DATA) {
328 return AFC_E_SUCCESS; 263 return AFC_E_SUCCESS;
329 } else {
330 return AFC_E_IO_ERROR;
331 } 264 }
265 return AFC_E_IO_ERROR;
332 } 266 }
333 267
334 debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation); 268 debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation);
@@ -336,22 +270,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); 270 entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket);
337 this_len = (uint32_t)header.this_length - sizeof(AFCPacket); 271 this_len = (uint32_t)header.this_length - sizeof(AFCPacket);
338 272
339 /* this is here as a check (perhaps a different upper limit is good?) */ 273 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) { 274 if (this_len > 0) {
346 idevice_connection_receive(client->connection, *dump_here, this_len, bytes_recv); 275 recv_len = 0;
347 if (*bytes_recv <= 0) { 276 service_receive(client->parent, buf, this_len, &recv_len);
348 free(*dump_here); 277 if (recv_len <= 0) {
349 *dump_here = NULL; 278 free(buf);
350 debug_info("Did not get packet contents!"); 279 debug_info("Did not get packet contents!");
351 return AFC_E_NOT_ENOUGH_DATA; 280 return AFC_E_NOT_ENOUGH_DATA;
352 } else if (*bytes_recv < this_len) { 281 }
353 free(*dump_here); 282 if (recv_len < this_len) {
354 *dump_here = NULL; 283 free(buf);
355 debug_info("Could not receive this_len=%d bytes", this_len); 284 debug_info("Could not receive this_len=%d bytes", this_len);
356 return AFC_E_NOT_ENOUGH_DATA; 285 return AFC_E_NOT_ENOUGH_DATA;
357 } 286 }
@@ -361,12 +290,13 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
361 290
362 if (entire_len > this_len) { 291 if (entire_len > this_len) {
363 while (current_count < entire_len) { 292 while (current_count < entire_len) {
364 idevice_connection_receive(client->connection, (*dump_here)+current_count, entire_len - current_count, bytes_recv); 293 recv_len = 0;
365 if (*bytes_recv <= 0) { 294 service_receive(client->parent, buf+current_count, entire_len - current_count, &recv_len);
366 debug_info("Error receiving data (recv returned %d)", *bytes_recv); 295 if (recv_len <= 0) {
296 debug_info("Error receiving data (recv returned %d)", recv_len);
367 break; 297 break;
368 } 298 }
369 current_count += *bytes_recv; 299 current_count += recv_len;
370 } 300 }
371 if (current_count < entire_len) { 301 if (current_count < entire_len) {
372 debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len); 302 debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len);
@@ -374,12 +304,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
374 } 304 }
375 305
376 if (current_count >= sizeof(uint64_t)) { 306 if (current_count >= sizeof(uint64_t)) {
377 param1 = GUINT64_FROM_LE(*(uint64_t*)(*dump_here)); 307 param1 = le64toh(*(uint64_t*)(buf));
378 } 308 }
379 309
380 debug_info("packet data size = %i", current_count); 310 debug_info("packet data size = %i", current_count);
381 debug_info("packet data follows"); 311 if (current_count > 256) {
382 debug_buffer(*dump_here, current_count); 312 debug_info("packet data follows (256/%u)", current_count);
313 debug_buffer(buf, 256);
314 } else {
315 debug_info("packet data follows");
316 debug_buffer(buf, current_count);
317 }
383 318
384 /* check operation types */ 319 /* check operation types */
385 if (header.operation == AFC_OP_STATUS) { 320 if (header.operation == AFC_OP_STATUS) {
@@ -389,8 +324,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
389 if (param1 != AFC_E_SUCCESS) { 324 if (param1 != AFC_E_SUCCESS) {
390 /* error status */ 325 /* error status */
391 /* free buffer */ 326 /* free buffer */
392 free(*dump_here); 327 free(buf);
393 *dump_here = NULL;
394 return (afc_error_t)param1; 328 return (afc_error_t)param1;
395 } 329 }
396 } else if (header.operation == AFC_OP_DATA) { 330 } else if (header.operation == AFC_OP_DATA) {
@@ -404,16 +338,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); 338 debug_info("got a tell response, position=%lld", param1);
405 } else { 339 } else {
406 /* unknown operation code received */ 340 /* unknown operation code received */
407 free(*dump_here); 341 free(buf);
408 *dump_here = NULL;
409 *bytes_recv = 0;
410 342
411 debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); 343 debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1);
344#ifndef _WIN32
412 fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1); 345 fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1);
346#endif
413 347
414 return AFC_E_OP_NOT_SUPPORTED; 348 return AFC_E_OP_NOT_SUPPORTED;
415 } 349 }
416 350
351 if (bytes) {
352 *bytes = buf;
353 } else {
354 free(buf);
355 }
356
417 *bytes_recv = current_count; 357 *bytes_recv = current_count;
418 return AFC_E_SUCCESS; 358 return AFC_E_SUCCESS;
419} 359}
@@ -421,7 +361,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3
421/** 361/**
422 * Returns counts of null characters within a string. 362 * Returns counts of null characters within a string.
423 */ 363 */
424static uint32_t count_nullspaces(char *string, uint32_t number) 364static uint32_t count_nullspaces(const char *string, uint32_t number)
425{ 365{
426 uint32_t i = 0, nulls = 0; 366 uint32_t i = 0, nulls = 0;
427 367
@@ -462,32 +402,86 @@ static char **make_strings_list(char *tokens, uint32_t length)
462 return list; 402 return list;
463} 403}
464 404
465/** 405static plist_t *make_dictionary(char *tokens, size_t length)
466 * Gets a directory listing of the directory requested. 406{
467 * 407 size_t j = 0;
468 * @param client The client to get a directory listing from. 408 plist_t dict = NULL;
469 * @param dir The directory to list. (must be a fully-qualified path) 409
470 * @param list A char list of files in that directory, terminated by an empty 410 if (!tokens || !length)
471 * string or NULL if there was an error. 411 return NULL;
472 * 412
473 * @return AFC_E_SUCCESS on success or an AFC_E_* error value. 413 dict = plist_new_dict();
474 */ 414
475afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***list) 415 while (j < length) {
416 size_t key_len = strnlen(tokens + j, length - j);
417 if (j + key_len >= length) {
418 plist_free(dict);
419 return NULL;
420 }
421 char* key = tokens + j;
422 j += key_len + 1;
423
424 if (j >= length) {
425 plist_free(dict);
426 return NULL;
427 }
428
429 size_t val_len = strnlen(tokens + j, length - j);
430 if (j + val_len >= length) {
431 plist_free(dict);
432 return NULL;
433 }
434 char* val = tokens + j;
435 j += val_len + 1;
436
437 char* endp = NULL;
438 unsigned long long u64val = strtoull(val, &endp, 10);
439 if (endp && *endp == '\0') {
440 plist_dict_set_item(dict, key, plist_new_uint(u64val));
441 } else {
442 plist_dict_set_item(dict, key, plist_new_string(val));
443 }
444 }
445
446 return dict;
447}
448
449static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len)
450{
451 if (data_len > client->packet_extra) {
452 client->packet_extra = (data_len & ~8) + 8;
453 AFCPacket* newpkt = (AFCPacket*)realloc(client->afc_packet, sizeof(AFCPacket) + client->packet_extra);
454 if (!newpkt) {
455 return -1;
456 }
457 client->afc_packet = newpkt;
458 }
459 return 0;
460}
461
462#define AFC_PACKET_DATA_PTR ((char*)client->afc_packet + sizeof(AFCPacket))
463
464afc_error_t afc_read_directory(afc_client_t client, const char *path, char ***directory_information)
476{ 465{
477 uint32_t bytes = 0; 466 uint32_t bytes = 0;
478 char *data = NULL, **list_loc = NULL; 467 char *data = NULL, **list_loc = NULL;
479 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 468 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
480 469
481 if (!client || !dir || !list || (list && *list)) 470 if (!client || !path || !directory_information || (directory_information && *directory_information))
482 return AFC_E_INVALID_ARG; 471 return AFC_E_INVALID_ARG;
483 472
484 afc_lock(client); 473 afc_lock(client);
485 474
475 uint32_t data_len = (uint32_t)strlen(path)+1;
476 if (_afc_check_packet_buffer(client, data_len) < 0) {
477 afc_unlock(client);
478 debug_info("Failed to realloc packet buffer");
479 return AFC_E_NO_MEM;
480 }
481
486 /* Send the command */ 482 /* Send the command */
487 client->afc_packet->operation = AFC_OP_READ_DIR; 483 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
488 client->afc_packet->entire_length = 0; 484 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) { 485 if (ret != AFC_E_SUCCESS) {
492 afc_unlock(client); 486 afc_unlock(client);
493 return AFC_E_NOT_ENOUGH_DATA; 487 return AFC_E_NOT_ENOUGH_DATA;
@@ -495,6 +489,8 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis
495 /* Receive the data */ 489 /* Receive the data */
496 ret = afc_receive_data(client, &data, &bytes); 490 ret = afc_receive_data(client, &data, &bytes);
497 if (ret != AFC_E_SUCCESS) { 491 if (ret != AFC_E_SUCCESS) {
492 if (data)
493 free(data);
498 afc_unlock(client); 494 afc_unlock(client);
499 return ret; 495 return ret;
500 } 496 }
@@ -504,37 +500,24 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis
504 free(data); 500 free(data);
505 501
506 afc_unlock(client); 502 afc_unlock(client);
507 *list = list_loc; 503 *directory_information = list_loc;
508 504
509 return ret; 505 return ret;
510} 506}
511 507
512/** 508afc_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{ 509{
525 uint32_t bytes = 0; 510 uint32_t bytes = 0;
526 char *data = NULL, **list = NULL; 511 char *data = NULL, **list = NULL;
527 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 512 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
528 513
529 if (!client || !infos) 514 if (!client || !device_information)
530 return AFC_E_INVALID_ARG; 515 return AFC_E_INVALID_ARG;
531 516
532 afc_lock(client); 517 afc_lock(client);
533 518
534 /* Send the command */ 519 /* Send the command */
535 client->afc_packet->operation = AFC_OP_GET_DEVINFO; 520 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) { 521 if (ret != AFC_E_SUCCESS) {
539 afc_unlock(client); 522 afc_unlock(client);
540 return AFC_E_NOT_ENOUGH_DATA; 523 return AFC_E_NOT_ENOUGH_DATA;
@@ -542,6 +525,8 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos)
542 /* Receive the data */ 525 /* Receive the data */
543 ret = afc_receive_data(client, &data, &bytes); 526 ret = afc_receive_data(client, &data, &bytes);
544 if (ret != AFC_E_SUCCESS) { 527 if (ret != AFC_E_SUCCESS) {
528 if (data)
529 free(data);
545 afc_unlock(client); 530 afc_unlock(client);
546 return ret; 531 return ret;
547 } 532 }
@@ -552,22 +537,45 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos)
552 537
553 afc_unlock(client); 538 afc_unlock(client);
554 539
555 *infos = list; 540 *device_information = list;
541
542 return ret;
543}
544
545afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information)
546{
547 uint32_t bytes = 0;
548 char *data = NULL;
549 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
550
551 if (!client || !device_information)
552 return AFC_E_INVALID_ARG;
553
554 afc_lock(client);
555
556 /* Send the command */
557 ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes);
558 if (ret != AFC_E_SUCCESS) {
559 afc_unlock(client);
560 return AFC_E_NOT_ENOUGH_DATA;
561 }
562 /* Receive the data */
563 ret = afc_receive_data(client, &data, &bytes);
564 if (ret != AFC_E_SUCCESS) {
565 if (data)
566 free(data);
567 afc_unlock(client);
568 return ret;
569 }
570 /* Parse the data */
571 *device_information = make_dictionary(data, bytes);
572 free(data);
573
574 afc_unlock(client);
556 575
557 return ret; 576 return ret;
558} 577}
559 578
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) 579afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value)
572{ 580{
573 afc_error_t ret = AFC_E_INTERNAL_ERROR; 581 afc_error_t ret = AFC_E_INTERNAL_ERROR;
@@ -587,43 +595,40 @@ afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char *
587 break; 595 break;
588 } 596 }
589 } 597 }
590 598 for (ptr = kvps; *ptr; ptr++) {
591 g_strfreev(kvps); 599 free(*ptr);
600 }
601 free(kvps);
592 602
593 return ret; 603 return ret;
594} 604}
595 605
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) 606afc_error_t afc_remove_path(afc_client_t client, const char *path)
605{ 607{
606 char *response = NULL;
607 uint32_t bytes = 0; 608 uint32_t bytes = 0;
608 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 609 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
609 610
610 if (!client || !path || !client->afc_packet || !client->connection) 611 if (!client || !path || !client->afc_packet || !client->parent)
611 return AFC_E_INVALID_ARG; 612 return AFC_E_INVALID_ARG;
612 613
613 afc_lock(client); 614 afc_lock(client);
614 615
616 uint32_t data_len = (uint32_t)strlen(path)+1;
617 if (_afc_check_packet_buffer(client, data_len) < 0) {
618 afc_unlock(client);
619 debug_info("Failed to realloc packet buffer");
620 return AFC_E_NO_MEM;
621 }
622
615 /* Send command */ 623 /* Send command */
616 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 624 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
617 client->afc_packet->operation = AFC_OP_REMOVE_PATH; 625 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) { 626 if (ret != AFC_E_SUCCESS) {
620 afc_unlock(client); 627 afc_unlock(client);
621 return AFC_E_NOT_ENOUGH_DATA; 628 return AFC_E_NOT_ENOUGH_DATA;
622 } 629 }
623 /* Receive response */ 630 /* Receive response */
624 ret = afc_receive_data(client, &response, &bytes); 631 ret = afc_receive_data(client, NULL, &bytes);
625 if (response)
626 free(response);
627 632
628 /* special case; unknown error actually means directory not empty */ 633 /* special case; unknown error actually means directory not empty */
629 if (ret == AFC_E_UNKNOWN_ERROR) 634 if (ret == AFC_E_UNKNOWN_ERROR)
@@ -634,61 +639,45 @@ afc_error_t afc_remove_path(afc_client_t client, const char *path)
634 return ret; 639 return ret;
635} 640}
636 641
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) 642afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to)
647{ 643{
648 char *response = NULL; 644 if (!client || !from || !to || !client->afc_packet || !client->parent)
649 char *send = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t))); 645 return AFC_E_INVALID_ARG;
646
650 uint32_t bytes = 0; 647 uint32_t bytes = 0;
651 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 648 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
652 649
653 if (!client || !from || !to || !client->afc_packet || !client->connection) 650 size_t from_len = strlen(from);
654 return AFC_E_INVALID_ARG; 651 size_t to_len = strlen(to);
655 652
656 afc_lock(client); 653 afc_lock(client);
657 654
655 uint32_t data_len = (uint32_t)(from_len+1 + to_len+1);
656 if (_afc_check_packet_buffer(client, data_len) < 0) {
657 afc_unlock(client);
658 debug_info("Failed to realloc packet buffer");
659 return AFC_E_NO_MEM;
660 }
661
658 /* Send command */ 662 /* Send command */
659 memcpy(send, from, strlen(from) + 1); 663 memcpy(AFC_PACKET_DATA_PTR, from, from_len+1);
660 memcpy(send + strlen(from) + 1, to, strlen(to) + 1); 664 memcpy(AFC_PACKET_DATA_PTR + from_len+1, to, to_len+1);
661 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 665 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) { 666 if (ret != AFC_E_SUCCESS) {
666 afc_unlock(client); 667 afc_unlock(client);
667 return AFC_E_NOT_ENOUGH_DATA; 668 return AFC_E_NOT_ENOUGH_DATA;
668 } 669 }
669 /* Receive response */ 670 /* Receive response */
670 ret = afc_receive_data(client, &response, &bytes); 671 ret = afc_receive_data(client, NULL, &bytes);
671 if (response)
672 free(response);
673 672
674 afc_unlock(client); 673 afc_unlock(client);
675 674
676 return ret; 675 return ret;
677} 676}
678 677
679/** 678afc_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{ 679{
690 uint32_t bytes = 0; 680 uint32_t bytes = 0;
691 char *response = NULL;
692 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 681 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
693 682
694 if (!client) 683 if (!client)
@@ -696,50 +685,49 @@ afc_error_t afc_make_directory(afc_client_t client, const char *dir)
696 685
697 afc_lock(client); 686 afc_lock(client);
698 687
688 uint32_t data_len = (uint32_t)strlen(path)+1;
689 if (_afc_check_packet_buffer(client, data_len) < 0) {
690 afc_unlock(client);
691 debug_info("Failed to realloc packet buffer");
692 return AFC_E_NO_MEM;
693 }
694
699 /* Send command */ 695 /* Send command */
700 client->afc_packet->operation = AFC_OP_MAKE_DIR; 696 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
701 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 697 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) { 698 if (ret != AFC_E_SUCCESS) {
704 afc_unlock(client); 699 afc_unlock(client);
705 return AFC_E_NOT_ENOUGH_DATA; 700 return AFC_E_NOT_ENOUGH_DATA;
706 } 701 }
707 /* Receive response */ 702 /* Receive response */
708 ret = afc_receive_data(client, &response, &bytes); 703 ret = afc_receive_data(client, NULL, &bytes);
709 if (response)
710 free(response);
711 704
712 afc_unlock(client); 705 afc_unlock(client);
713 706
714 return ret; 707 return ret;
715} 708}
716 709
717/** 710afc_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{ 711{
730 char *received = NULL; 712 char *received = NULL;
731 uint32_t bytes = 0; 713 uint32_t bytes = 0;
732 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 714 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
733 715
734 if (!client || !path || !infolist) 716 if (!client || !path || !file_information)
735 return AFC_E_INVALID_ARG; 717 return AFC_E_INVALID_ARG;
736 718
737 afc_lock(client); 719 afc_lock(client);
738 720
721 uint32_t data_len = (uint32_t)strlen(path)+1;
722 if (_afc_check_packet_buffer(client, data_len) < 0) {
723 afc_unlock(client);
724 debug_info("Failed to realloc packet buffer");
725 return AFC_E_NO_MEM;
726 }
727
739 /* Send command */ 728 /* Send command */
740 client->afc_packet->operation = AFC_OP_GET_FILE_INFO; 729 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
741 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 730 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) { 731 if (ret != AFC_E_SUCCESS) {
744 afc_unlock(client); 732 afc_unlock(client);
745 return AFC_E_NOT_ENOUGH_DATA; 733 return AFC_E_NOT_ENOUGH_DATA;
@@ -748,7 +736,7 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf
748 /* Receive data */ 736 /* Receive data */
749 ret = afc_receive_data(client, &received, &bytes); 737 ret = afc_receive_data(client, &received, &bytes);
750 if (received) { 738 if (received) {
751 *infolist = make_strings_list(received, bytes); 739 *file_information = make_strings_list(received, bytes);
752 free(received); 740 free(received);
753 } 741 }
754 742
@@ -757,51 +745,77 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf
757 return ret; 745 return ret;
758} 746}
759 747
760/** 748afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information)
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{ 749{
777 uint64_t file_mode_loc = GUINT64_TO_LE(file_mode); 750 char *received = NULL;
778 uint32_t bytes = 0; 751 uint32_t bytes = 0;
779 char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1));
780 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 752 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
781 753
782 /* set handle to 0 so in case an error occurs, the handle is invalid */ 754 if (!client || !path || !file_information)
783 *handle = 0;
784
785 if (!client || !client->connection || !client->afc_packet)
786 return AFC_E_INVALID_ARG; 755 return AFC_E_INVALID_ARG;
787 756
788 afc_lock(client); 757 afc_lock(client);
789 758
759 uint32_t data_len = (uint32_t)strlen(path)+1;
760 if (_afc_check_packet_buffer(client, data_len) < 0) {
761 afc_unlock(client);
762 debug_info("Failed to realloc packet buffer");
763 return AFC_E_NO_MEM;
764 }
765
790 /* Send command */ 766 /* Send command */
791 memcpy(data, &file_mode_loc, 8); 767 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
792 memcpy(data + 8, filename, strlen(filename)); 768 ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes);
793 data[8 + strlen(filename)] = '\0'; 769 if (ret != AFC_E_SUCCESS) {
794 client->afc_packet->operation = AFC_OP_FILE_OPEN; 770 afc_unlock(client);
795 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 771 return AFC_E_NOT_ENOUGH_DATA;
796 ret = afc_dispatch_packet(client, data, 8 + strlen(filename) + 1, &bytes); 772 }
797 free(data); 773
774 /* Receive data */
775 ret = afc_receive_data(client, &received, &bytes);
776 if (received) {
777 *file_information = make_dictionary(received, bytes);
778 free(received);
779 }
780
781 afc_unlock(client);
782
783 return ret;
784}
785
786afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle)
787{
788 if (!client || !client->parent || !client->afc_packet)
789 return AFC_E_INVALID_ARG;
790
791 //uint64_t file_mode_loc = htole64(file_mode);
792 uint32_t bytes = 0;
793 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
794
795 /* set handle to 0 so in case an error occurs, the handle is invalid */
796 *handle = 0;
797
798 afc_lock(client);
799
800 uint32_t data_len = (uint32_t)(strlen(filename)+1 + 8);
801 if (_afc_check_packet_buffer(client, data_len) < 0) {
802 afc_unlock(client);
803 debug_info("Failed to realloc packet buffer");
804 return AFC_E_NO_MEM;
805 }
798 806
807 /* Send command */
808 //memcpy(AFC_PACKET_DATA_PTR, &file_mode_loc, 8);
809 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(file_mode);
810 memcpy(AFC_PACKET_DATA_PTR + 8, filename, data_len-8);
811 ret = afc_dispatch_packet(client, AFC_OP_FILE_OPEN, data_len, NULL, 0, &bytes);
799 if (ret != AFC_E_SUCCESS) { 812 if (ret != AFC_E_SUCCESS) {
800 debug_info("Didn't receive a response to the command"); 813 debug_info("Didn't receive a response to the command");
801 afc_unlock(client); 814 afc_unlock(client);
802 return AFC_E_NOT_ENOUGH_DATA; 815 return AFC_E_NOT_ENOUGH_DATA;
803 } 816 }
804 /* Receive the data */ 817 /* Receive the data */
818 char* data = NULL;
805 ret = afc_receive_data(client, &data, &bytes); 819 ret = afc_receive_data(client, &data, &bytes);
806 if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { 820 if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) {
807 afc_unlock(client); 821 afc_unlock(client);
@@ -811,6 +825,8 @@ afc_file_open(afc_client_t client, const char *filename,
811 free(data); 825 free(data);
812 return ret; 826 return ret;
813 } 827 }
828 /* in case memory was allocated but no data received or an error occurred */
829 free(data);
814 830
815 debug_info("Didn't get any further data"); 831 debug_info("Didn't get any further data");
816 832
@@ -819,156 +835,81 @@ afc_file_open(afc_client_t client, const char *filename,
819 return ret; 835 return ret;
820} 836}
821 837
822/** 838afc_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{ 839{
836 char *input = NULL; 840 char *input = NULL;
837 uint32_t current_count = 0, bytes_loc = 0; 841 uint32_t current_count = 0, bytes_loc = 0;
838 const uint32_t MAXIMUM_READ_SIZE = 1 << 16; 842 struct readinfo {
843 uint64_t handle;
844 uint64_t size;
845 };
839 afc_error_t ret = AFC_E_SUCCESS; 846 afc_error_t ret = AFC_E_SUCCESS;
840 847
841 if (!client || !client->afc_packet || !client->connection || handle == 0) 848 if (!client || !client->afc_packet || !client->parent || handle == 0)
842 return AFC_E_INVALID_ARG; 849 return AFC_E_INVALID_ARG;
843 debug_info("called for length %i", length); 850 debug_info("called for length %i", length);
844 851
852 //uint32_t data_len = 8 + 8;
853
845 afc_lock(client); 854 afc_lock(client);
846 855
847 /* Looping here to get around the maximum amount of data that 856 /* Send the read command */
848 afc_receive_data can handle */ 857 struct readinfo* readinfo = (struct readinfo*)(AFC_PACKET_DATA_PTR);
849 while (current_count < length) { 858 readinfo->handle = handle;
850 debug_info("current count is %i but length is %i", current_count, length); 859 readinfo->size = htole64(length);
851 860 ret = afc_dispatch_packet(client, AFC_OP_FILE_READ, sizeof(struct readinfo), NULL, 0, &bytes_loc);
852 /* Send the read command */ 861 if (ret != AFC_E_SUCCESS) {
853 AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); 862 afc_unlock(client);
854 packet->filehandle = handle; 863 return AFC_E_NOT_ENOUGH_DATA;
855 packet->size = GUINT64_TO_LE(((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE); 864 }
856 client->afc_packet->operation = AFC_OP_READ; 865 /* Receive the data */
857 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 866 ret = afc_receive_data(client, &input, &bytes_loc);
858 ret = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket), &bytes_loc); 867 debug_info("afc_receive_data returned error: %d", ret);
859 free(packet); 868 debug_info("bytes returned: %i", bytes_loc);
860 869 if (ret != AFC_E_SUCCESS) {
861 if (ret != AFC_E_SUCCESS) { 870 afc_unlock(client);
862 afc_unlock(client); 871 return ret;
863 return AFC_E_NOT_ENOUGH_DATA; 872 }
864 } 873 if (bytes_loc == 0) {
865 /* Receive the data */ 874 if (input)
866 ret = afc_receive_data(client, &input, &bytes_loc); 875 free(input);
867 debug_info("afc_receive_data returned error: %d", ret); 876 afc_unlock(client);
868 debug_info("bytes returned: %i", bytes_loc); 877 *bytes_read = current_count;
869 if (ret != AFC_E_SUCCESS) { 878 /* FIXME: check that's actually a success */
870 afc_unlock(client); 879 return ret;
871 return ret; 880 }
872 } else if (bytes_loc == 0) { 881 if (input) {
873 if (input) 882 debug_info("%d", bytes_loc);
874 free(input); 883 memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc);
875 afc_unlock(client); 884 free(input);
876 *bytes_read = current_count; 885 input = NULL;
877 /* FIXME: check that's actually a success */ 886 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 } 887 }
889 debug_info("returning current_count as %i", current_count);
890 888
891 afc_unlock(client); 889 afc_unlock(client);
892 *bytes_read = current_count; 890 *bytes_read = current_count;
893 return ret; 891 return ret;
894} 892}
895 893
896/** 894afc_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{ 895{
910 char *acknowledgement = NULL; 896 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; 897 uint32_t bytes_loc = 0;
915 char *out_buffer = NULL;
916 afc_error_t ret = AFC_E_SUCCESS; 898 afc_error_t ret = AFC_E_SUCCESS;
917 899
918 if (!client || !client->afc_packet || !client->connection || !bytes_written || (handle == 0)) 900 if (!client || !client->afc_packet || !client->parent || !bytes_written || (handle == 0))
919 return AFC_E_INVALID_ARG; 901 return AFC_E_INVALID_ARG;
920 902
903 uint32_t data_len = 8;
904
921 afc_lock(client); 905 afc_lock(client);
922 906
923 debug_info("Write length: %i", length); 907 debug_info("Write length: %i", length);
924 908
925 /* Divide the file into segments. */ 909 *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle;
926 for (i = 0; i < segments; i++) { 910 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 911
952 /* By this point, we should be at the end. i.e. the last segment that didn't 912 current_count += bytes_loc - (sizeof(AFCPacket) + 8);
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
971 current_count += bytes_loc;
972 913
973 if (ret != AFC_E_SUCCESS) { 914 if (ret != AFC_E_SUCCESS) {
974 afc_unlock(client); 915 afc_unlock(client);
@@ -976,43 +917,32 @@ afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t
976 return AFC_E_SUCCESS; 917 return AFC_E_SUCCESS;
977 } 918 }
978 919
979 ret = afc_receive_data(client, &acknowledgement, &bytes_loc); 920 ret = afc_receive_data(client, NULL, &bytes_loc);
980 afc_unlock(client); 921 afc_unlock(client);
981 if (ret != AFC_E_SUCCESS) { 922 if (ret != AFC_E_SUCCESS) {
982 debug_info("uh oh?"); 923 debug_info("Failed to receive reply (%d)", ret);
983 } else {
984 free(acknowledgement);
985 } 924 }
986 *bytes_written = current_count; 925 *bytes_written = current_count;
987 return ret; 926 return ret;
988} 927}
989 928
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) 929afc_error_t afc_file_close(afc_client_t client, uint64_t handle)
997{ 930{
998 char *buffer = malloc(sizeof(char) * 8);
999 uint32_t bytes = 0; 931 uint32_t bytes = 0;
1000 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 932 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1001 933
1002 if (!client || (handle == 0)) 934 if (!client || (handle == 0))
1003 return AFC_E_INVALID_ARG; 935 return AFC_E_INVALID_ARG;
1004 936
937 uint32_t data_len = 8;
938
1005 afc_lock(client); 939 afc_lock(client);
1006 940
1007 debug_info("File handle %i", handle); 941 debug_info("File handle %i", handle);
1008 942
1009 /* Send command */ 943 /* Send command */
1010 memcpy(buffer, &handle, sizeof(uint64_t)); 944 *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle;
1011 client->afc_packet->operation = AFC_OP_FILE_CLOSE; 945 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 946
1017 if (ret != AFC_E_SUCCESS) { 947 if (ret != AFC_E_SUCCESS) {
1018 afc_unlock(client); 948 afc_unlock(client);
@@ -1020,32 +950,20 @@ afc_error_t afc_file_close(afc_client_t client, uint64_t handle)
1020 } 950 }
1021 951
1022 /* Receive the response */ 952 /* Receive the response */
1023 ret = afc_receive_data(client, &buffer, &bytes); 953 ret = afc_receive_data(client, NULL, &bytes);
1024 if (buffer)
1025 free(buffer);
1026 954
1027 afc_unlock(client); 955 afc_unlock(client);
1028 956
1029 return ret; 957 return ret;
1030} 958}
1031 959
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) 960afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation)
1045{ 961{
1046 char *buffer = malloc(16);
1047 uint32_t bytes = 0; 962 uint32_t bytes = 0;
1048 uint64_t op = GUINT64_TO_LE(operation); 963 struct lockinfo {
964 uint64_t handle;
965 uint64_t op;
966 };
1049 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 967 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1050 968
1051 if (!client || (handle == 0)) 969 if (!client || (handle == 0))
@@ -1056,47 +974,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); 974 debug_info("file handle %i", handle);
1057 975
1058 /* Send command */ 976 /* Send command */
1059 memcpy(buffer, &handle, sizeof(uint64_t)); 977 struct lockinfo* lockinfo = (struct lockinfo*)(AFC_PACKET_DATA_PTR);
1060 memcpy(buffer + 8, &op, 8); 978 lockinfo->handle = handle;
1061 979 lockinfo->op = htole64(operation);
1062 client->afc_packet->operation = AFC_OP_FILE_LOCK; 980 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) { 981 if (ret != AFC_E_SUCCESS) {
1069 afc_unlock(client); 982 afc_unlock(client);
1070 debug_info("could not send lock command"); 983 debug_info("could not send lock command");
1071 return AFC_E_UNKNOWN_ERROR; 984 return AFC_E_UNKNOWN_ERROR;
1072 } 985 }
1073 /* Receive the response */ 986 /* Receive the response */
1074 ret = afc_receive_data(client, &buffer, &bytes); 987 ret = afc_receive_data(client, NULL, &bytes);
1075 if (buffer) { 988
1076 debug_buffer(buffer, bytes);
1077 free(buffer);
1078 }
1079 afc_unlock(client); 989 afc_unlock(client);
1080 990
1081 return ret; 991 return ret;
1082} 992}
1083 993
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) 994afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence)
1095{ 995{
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; 996 uint32_t bytes = 0;
997 struct seekinfo {
998 uint64_t handle;
999 uint64_t whence;
1000 int64_t offset;
1001 };
1100 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1002 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1101 1003
1102 if (!client || (handle == 0)) 1004 if (!client || (handle == 0))
@@ -1105,57 +1007,40 @@ afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset,
1105 afc_lock(client); 1007 afc_lock(client);
1106 1008
1107 /* Send the command */ 1009 /* Send the command */
1108 memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ 1010 struct seekinfo* seekinfo = (struct seekinfo*)(AFC_PACKET_DATA_PTR);
1109 memcpy(buffer + 8, &whence_loc, sizeof(uint64_t)); /* fromwhere */ 1011 seekinfo->handle = handle;
1110 memcpy(buffer + 16, &offset_loc, sizeof(uint64_t)); /* offset */ 1012 seekinfo->whence = htole64(whence);
1111 client->afc_packet->operation = AFC_OP_FILE_SEEK; 1013 seekinfo->offset = (int64_t)htole64(offset);
1112 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 1014 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 1015
1117 if (ret != AFC_E_SUCCESS) { 1016 if (ret != AFC_E_SUCCESS) {
1118 afc_unlock(client); 1017 afc_unlock(client);
1119 return AFC_E_NOT_ENOUGH_DATA; 1018 return AFC_E_NOT_ENOUGH_DATA;
1120 } 1019 }
1121 /* Receive response */ 1020 /* Receive response */
1122 ret = afc_receive_data(client, &buffer, &bytes); 1021 ret = afc_receive_data(client, NULL, &bytes);
1123 if (buffer)
1124 free(buffer);
1125 1022
1126 afc_unlock(client); 1023 afc_unlock(client);
1127 1024
1128 return ret; 1025 return ret;
1129} 1026}
1130 1027
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) 1028afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position)
1141{ 1029{
1142 char *buffer = (char *) malloc(sizeof(char) * 8); 1030 char *buffer = NULL;
1143 uint32_t bytes = 0; 1031 uint32_t bytes = 0;
1144 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1032 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1145 1033
1146 if (!client || (handle == 0)) 1034 if (!client || (handle == 0))
1147 return AFC_E_INVALID_ARG; 1035 return AFC_E_INVALID_ARG;
1148 1036
1037 uint32_t data_len = 8;
1038
1149 afc_lock(client); 1039 afc_lock(client);
1150 1040
1151 /* Send the command */ 1041 /* Send the command */
1152 memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ 1042 *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle;
1153 client->afc_packet->operation = AFC_OP_FILE_TELL; 1043 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) { 1044 if (ret != AFC_E_SUCCESS) {
1160 afc_unlock(client); 1045 afc_unlock(client);
1161 return AFC_E_NOT_ENOUGH_DATA; 1046 return AFC_E_NOT_ENOUGH_DATA;
@@ -1166,33 +1051,22 @@ afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *positi
1166 if (bytes > 0 && buffer) { 1051 if (bytes > 0 && buffer) {
1167 /* Get the position */ 1052 /* Get the position */
1168 memcpy(position, buffer, sizeof(uint64_t)); 1053 memcpy(position, buffer, sizeof(uint64_t));
1169 *position = GUINT64_FROM_LE(*position); 1054 *position = le64toh(*position);
1170 } 1055 }
1171 if (buffer) 1056 free(buffer);
1172 free(buffer);
1173 1057
1174 afc_unlock(client); 1058 afc_unlock(client);
1175 1059
1176 return ret; 1060 return ret;
1177} 1061}
1178 1062
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) 1063afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize)
1192{ 1064{
1193 char *buffer = (char *) malloc(sizeof(char) * 16);
1194 uint32_t bytes = 0; 1065 uint32_t bytes = 0;
1195 uint64_t newsize_loc = GUINT64_TO_LE(newsize); 1066 struct truncinfo {
1067 uint64_t handle;
1068 uint64_t newsize;
1069 };
1196 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1070 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1197 1071
1198 if (!client || (handle == 0)) 1072 if (!client || (handle == 0))
@@ -1201,160 +1075,240 @@ afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t new
1201 afc_lock(client); 1075 afc_lock(client);
1202 1076
1203 /* Send command */ 1077 /* Send command */
1204 memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ 1078 struct truncinfo* truncinfo = (struct truncinfo*)(AFC_PACKET_DATA_PTR);
1205 memcpy(buffer + 8, &newsize_loc, sizeof(uint64_t)); /* newsize */ 1079 truncinfo->handle = handle;
1206 client->afc_packet->operation = AFC_OP_FILE_SET_SIZE; 1080 truncinfo->newsize = htole64(newsize);
1207 client->afc_packet->this_length = client->afc_packet->entire_length = 0; 1081 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 1082
1212 if (ret != AFC_E_SUCCESS) { 1083 if (ret != AFC_E_SUCCESS) {
1213 afc_unlock(client); 1084 afc_unlock(client);
1214 return AFC_E_NOT_ENOUGH_DATA; 1085 return AFC_E_NOT_ENOUGH_DATA;
1215 } 1086 }
1216 /* Receive response */ 1087 /* Receive response */
1217 ret = afc_receive_data(client, &buffer, &bytes); 1088 ret = afc_receive_data(client, NULL, &bytes);
1218 if (buffer)
1219 free(buffer);
1220 1089
1221 afc_unlock(client); 1090 afc_unlock(client);
1222 1091
1223 return ret; 1092 return ret;
1224} 1093}
1225 1094
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) 1095afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize)
1236{ 1096{
1237 char *response = NULL; 1097 if (!client || !path || !client->afc_packet || !client->parent)
1238 char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); 1098 return AFC_E_INVALID_ARG;
1099
1239 uint32_t bytes = 0; 1100 uint32_t bytes = 0;
1240 uint64_t size_requested = GUINT64_TO_LE(newsize);
1241 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1101 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1242 1102
1243 if (!client || !path || !client->afc_packet || !client->connection)
1244 return AFC_E_INVALID_ARG;
1245
1246 afc_lock(client); 1103 afc_lock(client);
1247 1104
1105 uint32_t data_len = 8 + (uint32_t)(strlen(path)+1);
1106 if (_afc_check_packet_buffer(client, data_len) < 0) {
1107 afc_unlock(client);
1108 debug_info("Failed to realloc packet buffer");
1109 return AFC_E_NO_MEM;
1110 }
1111
1248 /* Send command */ 1112 /* Send command */
1249 memcpy(send, &size_requested, 8); 1113 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(newsize);
1250 memcpy(send + 8, path, strlen(path) + 1); 1114 memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8);
1251 client->afc_packet->entire_length = client->afc_packet->this_length = 0; 1115 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) { 1116 if (ret != AFC_E_SUCCESS) {
1256 afc_unlock(client); 1117 afc_unlock(client);
1257 return AFC_E_NOT_ENOUGH_DATA; 1118 return AFC_E_NOT_ENOUGH_DATA;
1258 } 1119 }
1259 /* Receive response */ 1120 /* Receive response */
1260 ret = afc_receive_data(client, &response, &bytes); 1121 ret = afc_receive_data(client, NULL, &bytes);
1261 if (response)
1262 free(response);
1263 1122
1264 afc_unlock(client); 1123 afc_unlock(client);
1265 1124
1266 return ret; 1125 return ret;
1267} 1126}
1268 1127
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) 1128afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname)
1280{ 1129{
1281 char *response = NULL; 1130 if (!client || !target || !linkname || !client->afc_packet || !client->parent)
1282 char *send = (char *) malloc(sizeof(char) * (strlen(target)+1 + strlen(linkname)+1 + 8)); 1131 return AFC_E_INVALID_ARG;
1132
1283 uint32_t bytes = 0; 1133 uint32_t bytes = 0;
1284 uint64_t type = GUINT64_TO_LE(linktype);
1285 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1134 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1286 1135
1287 if (!client || !target || !linkname || !client->afc_packet || !client->connection) 1136 size_t target_len = strlen(target);
1288 return AFC_E_INVALID_ARG; 1137 size_t link_len = strlen(linkname);
1289 1138
1290 afc_lock(client); 1139 afc_lock(client);
1291 1140
1292 debug_info("link type: %lld", type); 1141 uint32_t data_len = 8 + target_len + 1 + link_len + 1;
1293 debug_info("target: %s, length:%d", target, strlen(target)); 1142 if (_afc_check_packet_buffer(client, data_len) < 0) {
1294 debug_info("linkname: %s, length:%d", linkname, strlen(linkname)); 1143 afc_unlock(client);
1144 debug_info("Failed to realloc packet buffer");
1145 return AFC_E_NO_MEM;
1146 }
1147
1148 debug_info("link type: %lld", htole64(linktype));
1149 debug_info("target: %s, length:%d", target, target_len);
1150 debug_info("linkname: %s, length:%d", linkname, link_len);
1295 1151
1296 /* Send command */ 1152 /* Send command */
1297 memcpy(send, &type, 8); 1153 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(linktype);
1298 memcpy(send + 8, target, strlen(target) + 1); 1154 memcpy(AFC_PACKET_DATA_PTR + 8, target, target_len + 1);
1299 memcpy(send + 8 + strlen(target) + 1, linkname, strlen(linkname) + 1); 1155 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; 1156 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) { 1157 if (ret != AFC_E_SUCCESS) {
1305 afc_unlock(client); 1158 afc_unlock(client);
1306 return AFC_E_NOT_ENOUGH_DATA; 1159 return AFC_E_NOT_ENOUGH_DATA;
1307 } 1160 }
1308 /* Receive response */ 1161 /* Receive response */
1309 ret = afc_receive_data(client, &response, &bytes); 1162 ret = afc_receive_data(client, NULL, &bytes);
1310 if (response)
1311 free(response);
1312 1163
1313 afc_unlock(client); 1164 afc_unlock(client);
1314 1165
1315 return ret; 1166 return ret;
1316} 1167}
1317 1168
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) 1169afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime)
1328{ 1170{
1329 char *response = NULL; 1171 if (!client || !path || !client->afc_packet || !client->parent)
1330 char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); 1172 return AFC_E_INVALID_ARG;
1173
1331 uint32_t bytes = 0; 1174 uint32_t bytes = 0;
1332 uint64_t mtime_loc = GUINT64_TO_LE(mtime);
1333 afc_error_t ret = AFC_E_UNKNOWN_ERROR; 1175 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1334 1176
1335 if (!client || !path || !client->afc_packet || !client->connection) 1177 afc_lock(client);
1178
1179 uint32_t data_len = 8 + strlen(path) + 1;
1180 if (_afc_check_packet_buffer(client, data_len) < 0) {
1181 afc_unlock(client);
1182 debug_info("Failed to realloc packet buffer");
1183 return AFC_E_NO_MEM;
1184 }
1185
1186 /* Send command */
1187 *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(mtime);
1188 memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8);
1189 ret = afc_dispatch_packet(client, AFC_OP_SET_FILE_MOD_TIME, data_len, NULL, 0, &bytes);
1190 if (ret != AFC_E_SUCCESS) {
1191 afc_unlock(client);
1192 return AFC_E_NOT_ENOUGH_DATA;
1193 }
1194 /* Receive response */
1195 ret = afc_receive_data(client, NULL, &bytes);
1196
1197 afc_unlock(client);
1198
1199 return ret;
1200}
1201
1202afc_error_t afc_remove_path_and_contents(afc_client_t client, const char *path)
1203{
1204 uint32_t bytes = 0;
1205 afc_error_t ret = AFC_E_UNKNOWN_ERROR;
1206
1207 if (!client || !path || !client->afc_packet || !client->parent)
1336 return AFC_E_INVALID_ARG; 1208 return AFC_E_INVALID_ARG;
1337 1209
1338 afc_lock(client); 1210 afc_lock(client);
1339 1211
1212 uint32_t data_len = strlen(path) + 1;
1213 if (_afc_check_packet_buffer(client, data_len) < 0) {
1214 afc_unlock(client);
1215 debug_info("Failed to realloc packet buffer");
1216 return AFC_E_NO_MEM;
1217 }
1218
1340 /* Send command */ 1219 /* Send command */
1341 memcpy(send, &mtime_loc, 8); 1220 memcpy(AFC_PACKET_DATA_PTR, path, data_len);
1342 memcpy(send + 8, path, strlen(path) + 1); 1221 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) { 1222 if (ret != AFC_E_SUCCESS) {
1348 afc_unlock(client); 1223 afc_unlock(client);
1349 return AFC_E_NOT_ENOUGH_DATA; 1224 return AFC_E_NOT_ENOUGH_DATA;
1350 } 1225 }
1351 /* Receive response */ 1226 /* Receive response */
1352 ret = afc_receive_data(client, &response, &bytes); 1227 ret = afc_receive_data(client, NULL, &bytes);
1353 if (response)
1354 free(response);
1355 1228
1356 afc_unlock(client); 1229 afc_unlock(client);
1357 1230
1358 return ret; 1231 return ret;
1359} 1232}
1360 1233
1234afc_error_t afc_dictionary_free(char **dictionary)
1235{
1236 int i = 0;
1237
1238 if (!dictionary)
1239 return AFC_E_INVALID_ARG;
1240
1241 for (i = 0; dictionary[i]; i++) {
1242 free(dictionary[i]);
1243 }
1244 free(dictionary);
1245
1246 return AFC_E_SUCCESS;
1247}
1248
1249const char* afc_strerror(afc_error_t err)
1250{
1251 switch (err) {
1252 case AFC_E_SUCCESS:
1253 return "Success";
1254 case AFC_E_UNKNOWN_ERROR:
1255 return "Unknown Error";
1256 case AFC_E_OP_HEADER_INVALID:
1257 return "Operation header invalid";
1258 case AFC_E_NO_RESOURCES:
1259 return "No resources";
1260 case AFC_E_READ_ERROR:
1261 return "Read error";
1262 case AFC_E_WRITE_ERROR:
1263 return "Write error";
1264 case AFC_E_UNKNOWN_PACKET_TYPE:
1265 return "Unknown packet type";
1266 case AFC_E_INVALID_ARG:
1267 return "Invalid argument";
1268 case AFC_E_OBJECT_NOT_FOUND:
1269 return "Not found";
1270 case AFC_E_OBJECT_IS_DIR:
1271 return "Object is a directory";
1272 case AFC_E_PERM_DENIED:
1273 return "Permission denied";
1274 case AFC_E_SERVICE_NOT_CONNECTED:
1275 return "Service not connected";
1276 case AFC_E_OP_TIMEOUT:
1277 return "Timeout";
1278 case AFC_E_TOO_MUCH_DATA:
1279 return "Too much data";
1280 case AFC_E_END_OF_DATA:
1281 return "End of data";
1282 case AFC_E_OP_NOT_SUPPORTED:
1283 return "Operation not supported";
1284 case AFC_E_OBJECT_EXISTS:
1285 return "Object exists";
1286 case AFC_E_OBJECT_BUSY:
1287 return "Object busy";
1288 case AFC_E_NO_SPACE_LEFT:
1289 return "No space left on device";
1290 case AFC_E_OP_WOULD_BLOCK:
1291 return "Operation would block";
1292 case AFC_E_IO_ERROR:
1293 return "I/O error";
1294 case AFC_E_OP_INTERRUPTED:
1295 return "Operation interrupted";
1296 case AFC_E_OP_IN_PROGRESS:
1297 return "Operation on progress";
1298 case AFC_E_INTERNAL_ERROR:
1299 return "Internal error";
1300 case AFC_E_MUX_ERROR:
1301 return "MUX error";
1302 case AFC_E_NO_MEM:
1303 return "Out of memory";
1304 case AFC_E_NOT_ENOUGH_DATA:
1305 return "Not enough data";
1306 case AFC_E_DIR_NOT_EMPTY:
1307 return "Directory not empty";
1308 case AFC_E_FORCE_SIGNED_TYPE:
1309 return "Force signed type";
1310 default:
1311 break;
1312 }
1313 return "Unknown Error";
1314}
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..06068c6 100644
--- a/src/house_arrest.c
+++ b/src/house_arrest.c
@@ -8,26 +8,33 @@
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>
27
28#ifndef _MSC_VER
24#include <unistd.h> 29#include <unistd.h>
30#endif
31
25#include <plist/plist.h> 32#include <plist/plist.h>
26 33
27#include "house_arrest.h" 34#include "house_arrest.h"
28#include "property_list_service.h" 35#include "property_list_service.h"
29#include "afc.h" 36#include "afc.h"
30#include "debug.h" 37#include "common/debug.h"
31 38
32/** 39/**
33 * Convert a property_list_service_error_t value to a house_arrest_error_t 40 * Convert a property_list_service_error_t value to a house_arrest_error_t
@@ -40,39 +47,25 @@
40 */ 47 */
41static house_arrest_error_t house_arrest_error(property_list_service_error_t err) 48static house_arrest_error_t house_arrest_error(property_list_service_error_t err)
42{ 49{
43 switch (err) { 50 switch (err) {
44 case PROPERTY_LIST_SERVICE_E_SUCCESS: 51 case PROPERTY_LIST_SERVICE_E_SUCCESS:
45 return HOUSE_ARREST_E_SUCCESS; 52 return HOUSE_ARREST_E_SUCCESS;
46 case PROPERTY_LIST_SERVICE_E_INVALID_ARG: 53 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
47 return HOUSE_ARREST_E_INVALID_ARG; 54 return HOUSE_ARREST_E_INVALID_ARG;
48 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: 55 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
49 return HOUSE_ARREST_E_PLIST_ERROR; 56 return HOUSE_ARREST_E_PLIST_ERROR;
50 case PROPERTY_LIST_SERVICE_E_MUX_ERROR: 57 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
51 return HOUSE_ARREST_E_CONN_FAILED; 58 return HOUSE_ARREST_E_CONN_FAILED;
52 default: 59 default:
53 break; 60 break;
54 } 61 }
55 return HOUSE_ARREST_E_UNKNOWN_ERROR; 62 return HOUSE_ARREST_E_UNKNOWN_ERROR;
56} 63}
57 64
58/** 65house_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{ 66{
71 if (!device)
72 return HOUSE_ARREST_E_INVALID_ARG;
73
74 property_list_service_client_t plistclient = NULL; 67 property_list_service_client_t plistclient = NULL;
75 house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, port, &plistclient)); 68 house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, service, &plistclient));
76 if (err != HOUSE_ARREST_E_SUCCESS) { 69 if (err != HOUSE_ARREST_E_SUCCESS) {
77 return err; 70 return err;
78 } 71 }
@@ -85,27 +78,20 @@ house_arrest_error_t house_arrest_client_new(idevice_t device, uint16_t port, ho
85 return HOUSE_ARREST_E_SUCCESS; 78 return HOUSE_ARREST_E_SUCCESS;
86} 79}
87 80
88/** 81house_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 82{
90 * house_arrest client data. 83 house_arrest_error_t err = HOUSE_ARREST_E_UNKNOWN_ERROR;
91 * 84 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 85 return err;
93 * you call afc_client_free() before calling this function to ensure 86}
94 * a proper cleanup. Do not call this function if you still need to 87
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) 88house_arrest_error_t house_arrest_client_free(house_arrest_client_t client)
103{ 89{
104 if (!client) 90 if (!client)
105 return HOUSE_ARREST_E_INVALID_ARG; 91 return HOUSE_ARREST_E_INVALID_ARG;
106 92
107 house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS; 93 house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS;
108 if (client->parent && client->parent->connection) { 94 if (client->parent && client->parent->parent->connection) {
109 house_arrest_error(property_list_service_client_free(client->parent)); 95 house_arrest_error(property_list_service_client_free(client->parent));
110 } 96 }
111 client->parent = NULL; 97 client->parent = NULL;
@@ -114,70 +100,34 @@ house_arrest_error_t house_arrest_client_free(house_arrest_client_t client)
114 return err; 100 return err;
115} 101}
116 102
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) 103house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict)
135{ 104{
136 if (!client || !client->parent || !dict) 105 if (!client || !client->parent || !dict)
137 return HOUSE_ARREST_E_INVALID_ARG; 106 return HOUSE_ARREST_E_INVALID_ARG;
138 if (plist_get_node_type(dict) != PLIST_DICT) 107 if (plist_get_node_type(dict) != PLIST_DICT)
139 return HOUSE_ARREST_E_PLIST_ERROR; 108 return HOUSE_ARREST_E_PLIST_ERROR;
140 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) 109 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL)
141 return HOUSE_ARREST_E_INVALID_MODE; 110 return HOUSE_ARREST_E_INVALID_MODE;
142 111
143 house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict)); 112 house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict));
144 if (res != HOUSE_ARREST_E_SUCCESS) { 113 if (res != HOUSE_ARREST_E_SUCCESS) {
145 debug_info("could not send plist, error %d", res); 114 debug_info("could not send plist, error %d", res);
146 } 115 }
147 return res; 116 return res;
148} 117}
149 118
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) 119house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid)
170{ 120{
171 if (!client || !client->parent || !command || !appid) 121 if (!client || !client->parent || !command || !appid)
172 return HOUSE_ARREST_E_INVALID_ARG; 122 return HOUSE_ARREST_E_INVALID_ARG;
173 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) 123 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL)
174 return HOUSE_ARREST_E_INVALID_MODE; 124 return HOUSE_ARREST_E_INVALID_MODE;
175 125
176 house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR; 126 house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR;
177 127
178 plist_t dict = plist_new_dict(); 128 plist_t dict = plist_new_dict();
179 plist_dict_insert_item(dict, "Command", plist_new_string(command)); 129 plist_dict_set_item(dict, "Command", plist_new_string(command));
180 plist_dict_insert_item(dict, "Identifier", plist_new_string(appid)); 130 plist_dict_set_item(dict, "Identifier", plist_new_string(appid));
181 131
182 res = house_arrest_send_request(client, dict); 132 res = house_arrest_send_request(client, dict);
183 133
@@ -186,63 +136,30 @@ house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, con
186 return res; 136 return res;
187} 137}
188 138
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) 139house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict)
204{ 140{
205 if (!client || !client->parent) 141 if (!client || !client->parent)
206 return HOUSE_ARREST_E_INVALID_ARG; 142 return HOUSE_ARREST_E_INVALID_ARG;
207 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) 143 if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL)
208 return HOUSE_ARREST_E_INVALID_MODE; 144 return HOUSE_ARREST_E_INVALID_MODE;
209 145
210 house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict)); 146 house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict));
211 if (res != HOUSE_ARREST_E_SUCCESS) { 147 if (res != HOUSE_ARREST_E_SUCCESS) {
212 debug_info("could not get result, error %d", res); 148 debug_info("could not get result, error %d", res);
213 if (*dict) { 149 if (*dict) {
214 plist_free(*dict); 150 plist_free(*dict);
215 *dict = NULL; 151 *dict = NULL;
216 } 152 }
217 } 153 }
218 return res; 154 return res;
219} 155}
220 156
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) 157afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client)
241{ 158{
242 if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { 159 if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) {
243 return AFC_E_INVALID_ARG; 160 return AFC_E_INVALID_ARG;
244 } 161 }
245 afc_error_t err = afc_client_new_from_connection(client->parent->connection, afc_client); 162 afc_error_t err = afc_client_new_with_service_client(client->parent->parent, afc_client);
246 if (err == AFC_E_SUCCESS) { 163 if (err == AFC_E_SUCCESS) {
247 client->mode = HOUSE_ARREST_CLIENT_MODE_AFC; 164 client->mode = HOUSE_ARREST_CLIENT_MODE_AFC;
248 } 165 }
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..0af27fd 100644
--- a/src/idevice.c
+++ b/src/idevice.c
@@ -1,98 +1,377 @@
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
127// Reference: https://stackoverflow.com/a/2390626/1806760
128// Initializer/finalizer sample for MSVC and GCC/Clang.
129// 2010-2016 Joe Lowe. Released into the public domain.
130
131#ifdef __cplusplus
132 #define INITIALIZER(f) \
133 static void f(void); \
134 struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
135 static void f(void)
136#elif defined(_MSC_VER)
137 #pragma section(".CRT$XCU",read)
138 #define INITIALIZER2_(f,p) \
139 static void f(void); \
140 __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
141 __pragma(comment(linker,"/include:" p #f "_")) \
142 static void f(void)
143 #ifdef _WIN64
144 #define INITIALIZER(f) INITIALIZER2_(f,"")
145 #else
146 #define INITIALIZER(f) INITIALIZER2_(f,"_")
147 #endif
148#else
149 #define INITIALIZER(f) \
150 static void f(void) __attribute__((__constructor__)); \
151 static void f(void)
152#endif
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
184INITIALIZER(internal_idevice_init)
185{
186#if defined(HAVE_OPENSSL)
187#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
188 int i;
189 SSL_library_init();
190
191 mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t));
192 if (!mutex_buf)
193 return;
194 for (i = 0; i < CRYPTO_num_locks(); i++)
195 mutex_init(&mutex_buf[i]);
196
197#if OPENSSL_VERSION_NUMBER < 0x10000000L
198 CRYPTO_set_id_callback(id_function);
199#else
200 CRYPTO_THREADID_set_callback(id_function);
201#endif
202 CRYPTO_set_locking_callback(locking_function);
203#endif
204#elif defined(HAVE_GNUTLS)
205 gnutls_global_init();
206#elif defined(HAVE_MBEDTLS)
207 // NO-OP
208#endif
209 atexit(internal_idevice_deinit);
210}
211
212const char* libimobiledevice_version()
213{
214#ifndef PACKAGE_VERSION
215#error PACKAGE_VERSION is not defined!
216#endif
217 return PACKAGE_VERSION;
218}
219
220struct idevice_subscription_context {
221 idevice_event_cb_t callback;
222 void *user_data;
223 usbmuxd_subscription_context_t ctx;
224};
32 225
33static idevice_event_cb_t event_cb = NULL; 226static idevice_subscription_context_t event_ctx = NULL;
34 227
35static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) 228static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data)
36{ 229{
230 idevice_subscription_context_t context = (idevice_subscription_context_t)user_data;
37 idevice_event_t ev; 231 idevice_event_t ev;
38 232
39 ev.event = event->event; 233 ev.event = event->event;
40 ev.uuid = event->device.uuid; 234 ev.udid = event->device.udid;
41 ev.conn_type = CONNECTION_USBMUXD; 235 ev.conn_type = 0;
236 if (event->device.conn_type == CONNECTION_TYPE_USB) {
237 ev.conn_type = CONNECTION_USBMUXD;
238 } else if (event->device.conn_type == CONNECTION_TYPE_NETWORK) {
239 ev.conn_type = CONNECTION_NETWORK;
240 } else {
241 debug_info("Unknown connection type %d", event->device.conn_type);
242 }
42 243
43 if (event_cb) { 244 if (context->callback) {
44 event_cb(&ev, user_data); 245 context->callback(&ev, context->user_data);
45 } 246 }
46} 247}
47 248
48/** 249idevice_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{ 250{
60 event_cb = callback; 251 if (!context || !callback) {
61 int res = usbmuxd_subscribe(usbmux_event_cb, user_data); 252 return IDEVICE_E_INVALID_ARG;
62 if (res != 0) { 253 }
63 event_cb = NULL; 254 *context = malloc(sizeof(struct idevice_subscription_context));
64 debug_info("Error %d when subscribing usbmux event callback!", res); 255 if (!*context) {
256 debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__);
257 return IDEVICE_E_UNKNOWN_ERROR;
258 }
259 (*context)->callback = callback;
260 (*context)->user_data = user_data;
261 int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context);
262 if (res != 0) {
263 free(*context);
264 *context = NULL;
265 debug_info("ERROR: usbmuxd_subscribe() returned %d!", res);
65 return IDEVICE_E_UNKNOWN_ERROR; 266 return IDEVICE_E_UNKNOWN_ERROR;
66 } 267 }
67 return IDEVICE_E_SUCCESS; 268 return IDEVICE_E_SUCCESS;
68} 269}
69 270
70/** 271idevice_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{ 272{
78 event_cb = NULL; 273 if (!context) {
79 int res = usbmuxd_unsubscribe(); 274 return IDEVICE_E_INVALID_ARG;
275 }
276 int res = usbmuxd_events_unsubscribe(context->ctx);
80 if (res != 0) { 277 if (res != 0) {
81 debug_info("Error %d when unsubscribing usbmux event callback!", res); 278 debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res);
82 return IDEVICE_E_UNKNOWN_ERROR; 279 return IDEVICE_E_UNKNOWN_ERROR;
83 } 280 }
281 if (context == event_ctx) {
282 event_ctx = NULL;
283 }
284 free(context);
285 return IDEVICE_E_SUCCESS;
286}
287
288idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data)
289{
290 if (event_ctx) {
291 idevice_events_unsubscribe(event_ctx);
292 }
293 return idevice_events_subscribe(&event_ctx, callback, user_data);
294}
295
296idevice_error_t idevice_event_unsubscribe(void)
297{
298 if (!event_ctx) {
299 return IDEVICE_E_SUCCESS;
300 }
301 event_ctx->callback = NULL;
302 return idevice_events_unsubscribe(event_ctx);
303}
304
305idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count)
306{
307 usbmuxd_device_info_t *dev_list;
308
309 *devices = NULL;
310 *count = 0;
311
312 if (usbmuxd_get_device_list(&dev_list) < 0) {
313 debug_info("ERROR: usbmuxd is not running!", __func__);
314 return IDEVICE_E_NO_DEVICE;
315 }
316
317 idevice_info_t *newlist = NULL;
318 int i, newcount = 0;
319
320 for (i = 0; dev_list[i].handle > 0; i++) {
321 newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1));
322 newlist[newcount] = malloc(sizeof(struct idevice_info));
323 newlist[newcount]->udid = strdup(dev_list[i].udid);
324 if (dev_list[i].conn_type == CONNECTION_TYPE_USB) {
325 newlist[newcount]->conn_type = CONNECTION_USBMUXD;
326 newlist[newcount]->conn_data = NULL;
327 } else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) {
328 newlist[newcount]->conn_type = CONNECTION_NETWORK;
329 struct sockaddr* saddr = (struct sockaddr*)(dev_list[i].conn_data);
330 size_t addrlen = 0;
331 switch (saddr->sa_family) {
332 case AF_INET:
333 addrlen = sizeof(struct sockaddr_in);
334 break;
335#ifdef AF_INET6
336 case AF_INET6:
337 addrlen = sizeof(struct sockaddr_in6);
338 break;
339#endif
340 default:
341 debug_info("Unsupported address family 0x%02x\n", saddr->sa_family);
342 continue;
343 }
344 newlist[newcount]->conn_data = malloc(addrlen);
345 memcpy(newlist[newcount]->conn_data, dev_list[i].conn_data, addrlen);
346 }
347 newcount++;
348 *devices = newlist;
349 }
350 usbmuxd_device_list_free(&dev_list);
351
352 *count = newcount;
353 newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1));
354 newlist[newcount] = NULL;
355 *devices = newlist;
356
357 return IDEVICE_E_SUCCESS;
358}
359
360idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices)
361{
362 if (devices) {
363 int i = 0;
364 while (devices[i]) {
365 free(devices[i]->udid);
366 free(devices[i]->conn_data);
367 free(devices[i]);
368 i++;
369 }
370 free(devices);
371 }
84 return IDEVICE_E_SUCCESS; 372 return IDEVICE_E_SUCCESS;
85} 373}
86 374
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) 375idevice_error_t idevice_get_device_list(char ***devices, int *count)
97{ 376{
98 usbmuxd_device_info_t *dev_list; 377 usbmuxd_device_info_t *dev_list;
@@ -101,7 +380,7 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count)
101 *count = 0; 380 *count = 0;
102 381
103 if (usbmuxd_get_device_list(&dev_list) < 0) { 382 if (usbmuxd_get_device_list(&dev_list) < 0) {
104 debug_info("ERROR: usbmuxd is not running!\n", __func__); 383 debug_info("ERROR: usbmuxd is not running!", __func__);
105 return IDEVICE_E_NO_DEVICE; 384 return IDEVICE_E_NO_DEVICE;
106 } 385 }
107 386
@@ -109,9 +388,11 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count)
109 int i, newcount = 0; 388 int i, newcount = 0;
110 389
111 for (i = 0; dev_list[i].handle > 0; i++) { 390 for (i = 0; dev_list[i].handle > 0; i++) {
112 newlist = realloc(*devices, sizeof(char*) * (newcount+1)); 391 if (dev_list[i].conn_type == CONNECTION_TYPE_USB) {
113 newlist[newcount++] = strdup(dev_list[i].uuid); 392 newlist = realloc(*devices, sizeof(char*) * (newcount+1));
114 *devices = newlist; 393 newlist[newcount++] = strdup(dev_list[i].udid);
394 *devices = newlist;
395 }
115 } 396 }
116 usbmuxd_device_list_free(&dev_list); 397 usbmuxd_device_list_free(&dev_list);
117 398
@@ -123,62 +404,101 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count)
123 return IDEVICE_E_SUCCESS; 404 return IDEVICE_E_SUCCESS;
124} 405}
125 406
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) 407idevice_error_t idevice_device_list_free(char **devices)
134{ 408{
135 if (devices) { 409 if (devices) {
136 int i = 0; 410 int i = 0;
137 while (devices[i++]) { 411 while (devices[i]) {
138 free(devices[i]); 412 free(devices[i]);
413 i++;
139 } 414 }
140 free(devices); 415 free(devices);
141 } 416 }
142 return IDEVICE_E_SUCCESS; 417 return IDEVICE_E_SUCCESS;
143} 418}
144 419
145/** 420void idevice_set_debug_level(int level)
146 * Creates an idevice_t structure for the device specified by uuid, 421{
147 * if the device is available. 422 internal_set_debug_level(level);
148 * 423}
149 * @note The resulting idevice_t structure has to be freed with 424
150 * idevice_free() if it is no longer used. 425static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev)
151 * 426{
152 * @param device Upon calling this function, a pointer to a location of type 427 if (!muxdev)
153 * idevice_t. On successful return, this location will be populated. 428 return NULL;
154 * @param uuid The UUID to match. 429
155 * 430 idevice_t device = (idevice_t)malloc(sizeof(struct idevice_private));
156 * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. 431 if (!device)
157 */ 432 return NULL;
158idevice_error_t idevice_new(idevice_t * device, const char *uuid) 433
434 device->udid = strdup(muxdev->udid);
435 device->mux_id = muxdev->handle;
436 device->version = 0;
437 device->device_class = 0;
438 switch (muxdev->conn_type) {
439 case CONNECTION_TYPE_USB:
440 device->conn_type = CONNECTION_USBMUXD;
441 device->conn_data = NULL;
442 break;
443 case CONNECTION_TYPE_NETWORK:
444 device->conn_type = CONNECTION_NETWORK;
445 struct sockaddr* saddr = (struct sockaddr*)(muxdev->conn_data);
446 size_t addrlen = 0;
447 switch (saddr->sa_family) {
448 case AF_INET:
449 addrlen = sizeof(struct sockaddr_in);
450 break;
451#ifdef AF_INET6
452 case AF_INET6:
453 addrlen = sizeof(struct sockaddr_in6);
454 break;
455#endif
456 default:
457 debug_info("Unsupported address family 0x%02x\n", saddr->sa_family);
458 free(device->udid);
459 free(device);
460 return NULL;
461 }
462 device->conn_data = malloc(addrlen);
463 memcpy(device->conn_data, muxdev->conn_data, addrlen);
464 break;
465 default:
466 device->conn_type = 0;
467 device->conn_data = NULL;
468 break;
469 }
470 return device;
471}
472
473idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options)
159{ 474{
160 usbmuxd_device_info_t muxdev; 475 usbmuxd_device_info_t muxdev;
161 int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); 476 int usbmux_options = 0;
477 if (options & IDEVICE_LOOKUP_USBMUX) {
478 usbmux_options |= DEVICE_LOOKUP_USBMUX;
479 }
480 if (options & IDEVICE_LOOKUP_NETWORK) {
481 usbmux_options |= DEVICE_LOOKUP_NETWORK;
482 }
483 if (options & IDEVICE_LOOKUP_PREFER_NETWORK) {
484 usbmux_options |= DEVICE_LOOKUP_PREFER_NETWORK;
485 }
486 int res = usbmuxd_get_device(udid, &muxdev, usbmux_options);
162 if (res > 0) { 487 if (res > 0) {
163 idevice_t phone = (idevice_t) malloc(sizeof(struct idevice_private)); 488 *device = idevice_from_mux_device(&muxdev);
164 phone->uuid = strdup(muxdev.uuid); 489 if (!*device) {
165 phone->conn_type = CONNECTION_USBMUXD; 490 return IDEVICE_E_UNKNOWN_ERROR;
166 phone->conn_data = (void*)(long)muxdev.handle; 491 }
167 *device = phone;
168 return IDEVICE_E_SUCCESS; 492 return IDEVICE_E_SUCCESS;
169 } 493 }
170 /* other connection types could follow here */
171
172 return IDEVICE_E_NO_DEVICE; 494 return IDEVICE_E_NO_DEVICE;
173} 495}
174 496
175/** 497idevice_error_t idevice_new(idevice_t * device, const char *udid)
176 * Cleans up an idevice structure, then frees the structure itself. 498{
177 * This is a library-level function; deals directly with the device to tear 499 return idevice_new_with_options(device, udid, 0);
178 * down relations, but otherwise is mostly internal. 500}
179 * 501
180 * @param device idevice_t to free.
181 */
182idevice_error_t idevice_free(idevice_t device) 502idevice_error_t idevice_free(idevice_t device)
183{ 503{
184 if (!device) 504 if (!device)
@@ -187,11 +507,8 @@ idevice_error_t idevice_free(idevice_t device)
187 507
188 ret = IDEVICE_E_SUCCESS; 508 ret = IDEVICE_E_SUCCESS;
189 509
190 free(device->uuid); 510 free(device->udid);
191 511
192 if (device->conn_type == CONNECTION_USBMUXD) {
193 device->conn_data = 0;
194 }
195 if (device->conn_data) { 512 if (device->conn_data) {
196 free(device->conn_data); 513 free(device->conn_data);
197 } 514 }
@@ -199,16 +516,6 @@ idevice_error_t idevice_free(idevice_t device)
199 return ret; 516 return ret;
200} 517}
201 518
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) 519idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection)
213{ 520{
214 if (!device) { 521 if (!device) {
@@ -216,31 +523,80 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect
216 } 523 }
217 524
218 if (device->conn_type == CONNECTION_USBMUXD) { 525 if (device->conn_type == CONNECTION_USBMUXD) {
219 int sfd = usbmuxd_connect((uint32_t)(long)device->conn_data, port); 526 int sfd = usbmuxd_connect(device->mux_id, port);
220 if (sfd < 0) { 527 if (sfd < 0) {
221 debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); 528 debug_info("ERROR: Connecting to usbmux device failed: %d (%s)", sfd, strerror(-sfd));
529 switch (-sfd) {
530 case ECONNREFUSED:
531 return IDEVICE_E_CONNREFUSED;
532 case ENODEV:
533 return IDEVICE_E_NO_DEVICE;
534 default:
535 break;
536 }
222 return IDEVICE_E_UNKNOWN_ERROR; 537 return IDEVICE_E_UNKNOWN_ERROR;
223 } 538 }
224 idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); 539 idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private));
225 new_connection->type = CONNECTION_USBMUXD; 540 new_connection->type = CONNECTION_USBMUXD;
226 new_connection->data = (void*)(long)sfd; 541 new_connection->data = (void*)(uintptr_t)sfd;
227 new_connection->ssl_data = NULL; 542 new_connection->ssl_data = NULL;
543 new_connection->device = device;
544 new_connection->ssl_recv_timeout = (unsigned int)-1;
545 new_connection->status = IDEVICE_E_SUCCESS;
228 *connection = new_connection; 546 *connection = new_connection;
229 return IDEVICE_E_SUCCESS; 547 return IDEVICE_E_SUCCESS;
230 } else { 548 }
231 debug_info("Unknown connection type %d", device->conn_type); 549 if (device->conn_type == CONNECTION_NETWORK) {
550 struct sockaddr* saddr = (struct sockaddr*)(device->conn_data);
551 switch (saddr->sa_family) {
552 case AF_INET:
553#ifdef AF_INET6
554 case AF_INET6:
555#endif
556 break;
557 default:
558 debug_info("Unsupported address family 0x%02x", saddr->sa_family);
559 return IDEVICE_E_UNKNOWN_ERROR;
560 }
561
562 char addrtxt[48];
563 addrtxt[0] = '\0';
564
565 if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) {
566 debug_info("Failed to convert network address: %d (%s)", errno, strerror(errno));
567 }
568
569 debug_info("Connecting to %s port %d...", addrtxt, port);
570
571 int sfd = socket_connect_addr(saddr, port);
572 if (sfd < 0) {
573 int result = errno;
574 debug_info("ERROR: Connecting to network device failed: %d (%s)", result, strerror(result));
575 switch (result) {
576 case ECONNREFUSED:
577 return IDEVICE_E_CONNREFUSED;
578 default:
579 break;
580 }
581 return IDEVICE_E_NO_DEVICE;
582 }
583
584 idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private));
585 new_connection->type = CONNECTION_NETWORK;
586 new_connection->data = (void*)(uintptr_t)sfd;
587 new_connection->ssl_data = NULL;
588 new_connection->device = device;
589 new_connection->ssl_recv_timeout = (unsigned int)-1;
590
591 *connection = new_connection;
592
593 return IDEVICE_E_SUCCESS;
232 } 594 }
233 595
596 debug_info("Unknown connection type %d", device->conn_type);
234 return IDEVICE_E_UNKNOWN_ERROR; 597 return IDEVICE_E_UNKNOWN_ERROR;
235} 598}
236 599
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) 600idevice_error_t idevice_disconnect(idevice_connection_t connection)
245{ 601{
246 if (!connection) { 602 if (!connection) {
@@ -252,12 +608,20 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection)
252 } 608 }
253 idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; 609 idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR;
254 if (connection->type == CONNECTION_USBMUXD) { 610 if (connection->type == CONNECTION_USBMUXD) {
255 usbmuxd_disconnect((int)(long)connection->data); 611 usbmuxd_disconnect((int)(uintptr_t)connection->data);
612 connection->data = NULL;
613 result = IDEVICE_E_SUCCESS;
614 } else if (connection->type == CONNECTION_NETWORK) {
615 socket_close((int)(uintptr_t)connection->data);
616 connection->data = NULL;
256 result = IDEVICE_E_SUCCESS; 617 result = IDEVICE_E_SUCCESS;
257 } else { 618 } else {
258 debug_info("Unknown connection type %d", connection->type); 619 debug_info("Unknown connection type %d", connection->type);
259 } 620 }
621
260 free(connection); 622 free(connection);
623 connection = NULL;
624
261 return result; 625 return result;
262} 626}
263 627
@@ -271,46 +635,111 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection,
271 } 635 }
272 636
273 if (connection->type == CONNECTION_USBMUXD) { 637 if (connection->type == CONNECTION_USBMUXD) {
274 int res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); 638 int res;
639 do {
640 res = usbmuxd_send((int)(uintptr_t)connection->data, data, len, sent_bytes);
641 } while (res == -EAGAIN);
275 if (res < 0) { 642 if (res < 0) {
276 debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); 643 debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res));
277 return IDEVICE_E_UNKNOWN_ERROR; 644 return IDEVICE_E_UNKNOWN_ERROR;
278 } 645 }
279 return IDEVICE_E_SUCCESS; 646 return IDEVICE_E_SUCCESS;
280 } else {
281 debug_info("Unknown connection type %d", connection->type);
282 } 647 }
648 if (connection->type == CONNECTION_NETWORK) {
649 int s = socket_send((int)(uintptr_t)connection->data, (void*)data, len);
650 if (s < 0) {
651 *sent_bytes = 0;
652 return IDEVICE_E_UNKNOWN_ERROR;
653 }
654 *sent_bytes = s;
655 return IDEVICE_E_SUCCESS;
656 }
657
658 debug_info("Unknown connection type %d", connection->type);
283 return IDEVICE_E_UNKNOWN_ERROR; 659 return IDEVICE_E_UNKNOWN_ERROR;
284 660
285} 661}
286 662
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) 663idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes)
299{ 664{
300 if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { 665 if (!connection || !data
666#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
667 || (connection->ssl_data && !connection->ssl_data->session)
668#endif
669 ) {
301 return IDEVICE_E_INVALID_ARG; 670 return IDEVICE_E_INVALID_ARG;
302 } 671 }
303 672
304 if (connection->ssl_data) { 673 if (connection->ssl_data) {
305 ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); 674 connection->status = IDEVICE_E_SUCCESS;
306 if ((uint32_t)sent == (uint32_t)len) { 675 uint32_t sent = 0;
307 *sent_bytes = sent; 676 while (sent < len) {
308 return IDEVICE_E_SUCCESS; 677#if defined(HAVE_OPENSSL)
678 int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent));
679 if (s <= 0) {
680 int sslerr = SSL_get_error(connection->ssl_data->session, s);
681 if (sslerr == SSL_ERROR_WANT_WRITE) {
682 continue;
683 }
684 break;
685 }
686#elif defined(HAVE_GNUTLS)
687 ssize_t s = gnutls_record_send(connection->ssl_data->session, (void*)(data+sent), (size_t)(len-sent));
688#elif defined(HAVE_MBEDTLS)
689 int s = mbedtls_ssl_write(&connection->ssl_data->ctx, (const unsigned char*)(data+sent), (size_t)(len-sent));
690#endif
691 if (s < 0) {
692 break;
693 }
694 sent += s;
695 }
696 debug_info("SSL_write %d, sent %d", len, sent);
697 if (sent < len) {
698 *sent_bytes = 0;
699 return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status;
700 }
701 *sent_bytes = sent;
702 return IDEVICE_E_SUCCESS;
703 }
704 uint32_t sent = 0;
705 while (sent < len) {
706 uint32_t bytes = 0;
707 int s = internal_connection_send(connection, data+sent, len-sent, &bytes);
708 if (s < 0) {
709 break;
710 }
711 sent += bytes;
712 }
713 debug_info("internal_connection_send %d, sent %d", len, sent);
714 if (sent < len) {
715 *sent_bytes = sent;
716 if (sent == 0) {
717 return IDEVICE_E_UNKNOWN_ERROR;
718 }
719 return IDEVICE_E_NOT_ENOUGH_DATA;
720 }
721 *sent_bytes = sent;
722 return IDEVICE_E_SUCCESS;
723}
724
725static inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received)
726{
727 if (conn_error < 0) {
728 switch (conn_error) {
729 case -EAGAIN:
730 if (len) {
731 debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error));
732 } else {
733 debug_info("ERROR: received partial data (%s)", strerror(-conn_error));
734 }
735 return IDEVICE_E_NOT_ENOUGH_DATA;
736 case -ETIMEDOUT:
737 return IDEVICE_E_TIMEOUT;
738 default:
739 return IDEVICE_E_UNKNOWN_ERROR;
309 } 740 }
310 *sent_bytes = 0;
311 return IDEVICE_E_SSL_ERROR;
312 } 741 }
313 return internal_connection_send(connection, data, len, sent_bytes); 742 return IDEVICE_E_SUCCESS;
314} 743}
315 744
316/** 745/**
@@ -324,47 +753,92 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t
324 } 753 }
325 754
326 if (connection->type == CONNECTION_USBMUXD) { 755 if (connection->type == CONNECTION_USBMUXD) {
327 int res = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); 756 int conn_error = usbmuxd_recv_timeout((int)(uintptr_t)connection->data, data, len, recv_bytes, timeout);
328 if (res < 0) { 757 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)); 758 if (error == IDEVICE_E_UNKNOWN_ERROR) {
330 return IDEVICE_E_UNKNOWN_ERROR; 759 debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error));
331 } 760 }
332 return IDEVICE_E_SUCCESS; 761 return error;
333 } else { 762 }
334 debug_info("Unknown connection type %d", connection->type); 763 if (connection->type == CONNECTION_NETWORK) {
764 int res = socket_receive_timeout((int)(uintptr_t)connection->data, data, len, 0, timeout);
765 idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0);
766 if (error == IDEVICE_E_SUCCESS) {
767 *recv_bytes = (uint32_t)res;
768 } else if (error == IDEVICE_E_UNKNOWN_ERROR) {
769 debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res));
770 }
771 return error;
335 } 772 }
773
774 debug_info("Unknown connection type %d", connection->type);
336 return IDEVICE_E_UNKNOWN_ERROR; 775 return IDEVICE_E_UNKNOWN_ERROR;
337} 776}
338 777
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) 778idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout)
355{ 779{
356 if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { 780 if (!connection
781#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
782 || (connection->ssl_data && !connection->ssl_data->session)
783#endif
784 || len == 0
785 ) {
357 return IDEVICE_E_INVALID_ARG; 786 return IDEVICE_E_INVALID_ARG;
358 } 787 }
359 788
360 if (connection->ssl_data) { 789 if (connection->ssl_data) {
361 ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); 790 uint32_t received = 0;
362 if (received > 0) { 791
792 if (connection->ssl_recv_timeout != (unsigned int)-1) {
793 debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout");
794 }
795
796 // this should be reset after the SSL_read call on all codepaths, as
797 // the supplied timeout should only apply to the current read.
798 connection->ssl_recv_timeout = timeout;
799 connection->status = IDEVICE_E_SUCCESS;
800 while (received < len) {
801#if defined(HAVE_OPENSSL)
802 int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received);
803 if (r > 0) {
804 received += r;
805 } else {
806 int sslerr = SSL_get_error(connection->ssl_data->session, r);
807 if (sslerr == SSL_ERROR_WANT_READ) {
808 continue;
809 } else if (sslerr == SSL_ERROR_ZERO_RETURN) {
810 if (connection->status == IDEVICE_E_TIMEOUT) {
811 SSL_set_shutdown(connection->ssl_data->session, 0);
812 }
813 }
814 break;
815 }
816#elif defined(HAVE_GNUTLS)
817 ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received);
818 if (r > 0) {
819 received += r;
820 } else {
821 break;
822 }
823#elif defined(HAVE_MBEDTLS)
824 int r = mbedtls_ssl_read(&connection->ssl_data->ctx, (void*)(data+received), (size_t)len-received);
825 if (r > 0) {
826 received += r;
827 } else {
828 break;
829 }
830#endif
831 }
832 connection->ssl_recv_timeout = (unsigned int)-1;
833
834 debug_info("SSL_read %d, received %d", len, received);
835 if (received < len) {
363 *recv_bytes = received; 836 *recv_bytes = received;
364 return IDEVICE_E_SUCCESS; 837 return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status;
365 } 838 }
366 *recv_bytes = 0; 839
367 return IDEVICE_E_SSL_ERROR; 840 *recv_bytes = received;
841 return IDEVICE_E_SUCCESS;
368 } 842 }
369 return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); 843 return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout);
370} 844}
@@ -379,40 +853,50 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti
379 } 853 }
380 854
381 if (connection->type == CONNECTION_USBMUXD) { 855 if (connection->type == CONNECTION_USBMUXD) {
382 int res = usbmuxd_recv((int)(long)connection->data, data, len, recv_bytes); 856 int res = usbmuxd_recv((int)(uintptr_t)connection->data, data, len, recv_bytes);
383 if (res < 0) { 857 if (res < 0) {
384 debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); 858 debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res));
385 return IDEVICE_E_UNKNOWN_ERROR; 859 return IDEVICE_E_UNKNOWN_ERROR;
386 } 860 }
387
388 return IDEVICE_E_SUCCESS; 861 return IDEVICE_E_SUCCESS;
389 } else {
390 debug_info("Unknown connection type %d", connection->type);
391 } 862 }
863 if (connection->type == CONNECTION_NETWORK) {
864 int res = socket_receive((int)(uintptr_t)connection->data, data, len);
865 if (res < 0) {
866 debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res));
867 return IDEVICE_E_UNKNOWN_ERROR;
868 }
869 *recv_bytes = (uint32_t)res;
870 return IDEVICE_E_SUCCESS;
871 }
872
873 debug_info("Unknown connection type %d", connection->type);
392 return IDEVICE_E_UNKNOWN_ERROR; 874 return IDEVICE_E_UNKNOWN_ERROR;
393} 875}
394 876
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) 877idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes)
409{ 878{
410 if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { 879 if (!connection
880#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
881 || (connection->ssl_data && !connection->ssl_data->session)
882#endif
883 ) {
411 return IDEVICE_E_INVALID_ARG; 884 return IDEVICE_E_INVALID_ARG;
412 } 885 }
413 886
414 if (connection->ssl_data) { 887 if (connection->ssl_data) {
888 if (connection->ssl_recv_timeout != (unsigned int)-1) {
889 debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout");
890 connection->ssl_recv_timeout = (unsigned int)-1;
891 }
892#if defined(HAVE_OPENSSL)
893 int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len);
894 debug_info("SSL_read %d, received %d", len, received);
895#elif defined(HAVE_GNUTLS)
415 ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); 896 ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len);
897#elif defined(HAVE_MBEDTLS)
898 int received = mbedtls_ssl_read(&connection->ssl_data->ctx, (unsigned char*)data, (size_t)len);
899#endif
416 if (received > 0) { 900 if (received > 0) {
417 *recv_bytes = received; 901 *recv_bytes = received;
418 return IDEVICE_E_SUCCESS; 902 return IDEVICE_E_SUCCESS;
@@ -423,89 +907,119 @@ idevice_error_t idevice_connection_receive(idevice_connection_t connection, char
423 return internal_connection_receive(connection, data, len, recv_bytes); 907 return internal_connection_receive(connection, data, len, recv_bytes);
424} 908}
425 909
426/** 910idevice_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{ 911{
431 if (!device) 912 if (!connection || !fd) {
432 return IDEVICE_E_INVALID_ARG; 913 return IDEVICE_E_INVALID_ARG;
914 }
433 915
434 if (device->conn_type == CONNECTION_USBMUXD) { 916 if (connection->type == CONNECTION_USBMUXD) {
435 *handle = (uint32_t)(long)device->conn_data; 917 *fd = (int)(uintptr_t)connection->data;
436 return IDEVICE_E_SUCCESS; 918 return IDEVICE_E_SUCCESS;
437 } else {
438 debug_info("Unknown connection type %d", device->conn_type);
439 } 919 }
920 if (connection->type == CONNECTION_NETWORK) {
921 *fd = (int)(uintptr_t)connection->data;
922 return IDEVICE_E_SUCCESS;
923 }
924
925 debug_info("Unknown connection type %d", connection->type);
440 return IDEVICE_E_UNKNOWN_ERROR; 926 return IDEVICE_E_UNKNOWN_ERROR;
441} 927}
442 928
443/** 929idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle)
444 * Gets the unique id for the device. 930{
445 */ 931 if (!device || !handle)
446idevice_error_t idevice_get_uuid(idevice_t device, char **uuid) 932 return IDEVICE_E_INVALID_ARG;
933
934 *handle = device->mux_id;
935 return IDEVICE_E_SUCCESS;
936}
937
938idevice_error_t idevice_get_udid(idevice_t device, char **udid)
447{ 939{
448 if (!device || !uuid) 940 if (!device || !udid)
449 return IDEVICE_E_INVALID_ARG; 941 return IDEVICE_E_INVALID_ARG;
450 942
451 *uuid = strdup(device->uuid); 943 if (device->udid) {
944 *udid = strdup(device->udid);
945 }
452 return IDEVICE_E_SUCCESS; 946 return IDEVICE_E_SUCCESS;
453} 947}
454 948
949unsigned int idevice_get_device_version(idevice_t device)
950{
951 if (!device) {
952 return 0;
953 }
954 if (!device->version) {
955 lockdownd_client_t lockdown = NULL;
956 lockdownd_client_new(device, &lockdown, NULL);
957 // we don't handle any errors here. We should have the product version cached now.
958 lockdownd_client_free(lockdown);
959 }
960 return device->version;
961}
962
963#if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
964typedef ssize_t ssl_cb_ret_type_t;
965#elif defined(HAVE_MBEDTLS)
966typedef int ssl_cb_ret_type_t;
967#endif
968
455/** 969/**
456 * Internally used gnutls callback function for receiving encrypted data. 970 * Internally used SSL callback function for receiving encrypted data.
457 */ 971 */
458static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) 972static ssl_cb_ret_type_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length)
459{ 973{
460 int bytes = 0, pos_start_fill = 0; 974 uint32_t bytes = 0;
461 size_t tbytes = 0; 975 uint32_t pos = 0;
462 int this_len = length;
463 idevice_error_t res; 976 idevice_error_t res;
464 idevice_connection_t connection = (idevice_connection_t)transport; 977 unsigned int timeout = connection->ssl_recv_timeout;
465 char *recv_buffer;
466 978
467 debug_info("pre-read client wants %zi bytes", length); 979 debug_info("pre-read length = %zi bytes", length);
468
469 recv_buffer = (char *) malloc(sizeof(char) * this_len);
470 980
471 /* repeat until we have the full data or an error occurs */ 981 /* repeat until we have the full data or an error occurs */
472 do { 982 do {
473 if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { 983 bytes = 0;
474 debug_info("ERROR: idevice_connection_receive returned %d", res); 984 if (timeout == (unsigned int)-1) {
475 return res; 985 res = internal_connection_receive(connection, buffer + pos, (uint32_t)length - pos, &bytes);
986 } else {
987 res = internal_connection_receive_timeout(connection, buffer + pos, (uint32_t)length - pos, &bytes, (unsigned int)timeout);
988 }
989 if (res != IDEVICE_E_SUCCESS) {
990 if (res != IDEVICE_E_TIMEOUT) {
991 debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res);
992 }
993 connection->status = res;
994 return -1;
476 } 995 }
477 debug_info("post-read we got %i bytes", bytes); 996 debug_info("read %i bytes", bytes);
478 997
479 /* increase read count */ 998 /* increase read count */
480 tbytes += bytes; 999 pos += bytes;
481 1000 if (pos < (uint32_t)length) {
482 /* fill the buffer with what we got right now */ 1001 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 } 1002 }
1003 } while (pos < (uint32_t)length);
489 1004
490 this_len = length - tbytes; 1005 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 1006
494 if (recv_buffer) { 1007 return pos;
495 free(recv_buffer);
496 }
497 return tbytes;
498} 1008}
499 1009
500/** 1010/**
501 * Internally used gnutls callback function for sending encrypted data. 1011 * Internally used SSL callback function for sending encrypted data.
502 */ 1012 */
503static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) 1013static ssl_cb_ret_type_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length)
504{ 1014{
505 uint32_t bytes = 0; 1015 uint32_t bytes = 0;
506 idevice_connection_t connection = (idevice_connection_t)transport; 1016 idevice_error_t res;
507 debug_info("pre-send length = %zi", length); 1017 debug_info("pre-send length = %zi bytes", length);
508 internal_connection_send(connection, buffer, length, &bytes); 1018 if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) {
1019 debug_info("ERROR: internal_connection_send returned %d", res);
1020 connection->status = res;
1021 return -1;
1022 }
509 debug_info("post-send sent %i bytes", bytes); 1023 debug_info("post-send sent %i bytes", bytes);
510 return bytes; 1024 return bytes;
511} 1025}
@@ -518,6 +1032,14 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data)
518 if (!ssl_data) 1032 if (!ssl_data)
519 return; 1033 return;
520 1034
1035#if defined(HAVE_OPENSSL)
1036 if (ssl_data->session) {
1037 SSL_free(ssl_data->session);
1038 }
1039 if (ssl_data->ctx) {
1040 SSL_CTX_free(ssl_data->ctx);
1041 }
1042#elif defined(HAVE_GNUTLS)
521 if (ssl_data->session) { 1043 if (ssl_data->session) {
522 gnutls_deinit(ssl_data->session); 1044 gnutls_deinit(ssl_data->session);
523 } 1045 }
@@ -536,20 +1058,121 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data)
536 if (ssl_data->host_privkey) { 1058 if (ssl_data->host_privkey) {
537 gnutls_x509_privkey_deinit(ssl_data->host_privkey); 1059 gnutls_x509_privkey_deinit(ssl_data->host_privkey);
538 } 1060 }
1061#elif defined(HAVE_MBEDTLS)
1062 mbedtls_pk_free(&ssl_data->root_privkey);
1063 mbedtls_x509_crt_free(&ssl_data->certificate);
1064 mbedtls_entropy_free(&ssl_data->entropy);
1065 mbedtls_ctr_drbg_free(&ssl_data->ctr_drbg);
1066 mbedtls_ssl_config_free(&ssl_data->config);
1067 mbedtls_ssl_free(&ssl_data->ctx);
1068#endif
1069}
1070
1071#ifdef HAVE_OPENSSL
1072#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1073static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, size_t len, int argi, long argl, int retvalue, size_t *processed)
1074#else
1075static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue)
1076#endif
1077{
1078 ssize_t bytes = 0;
1079 idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b);
1080#if OPENSSL_VERSION_NUMBER < 0x30000000L
1081 size_t len = (size_t)argi;
1082#endif
1083 switch (oper) {
1084 case (BIO_CB_READ|BIO_CB_RETURN):
1085 if (argp) {
1086 bytes = internal_ssl_read(conn, (char *)argp, len);
1087#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1088 *processed = (size_t)(bytes < 0) ? 0 : bytes;
1089#endif
1090 return (long)bytes;
1091 }
1092 return 0;
1093 case (BIO_CB_PUTS|BIO_CB_RETURN):
1094 len = strlen(argp);
1095 // fallthrough
1096 case (BIO_CB_WRITE|BIO_CB_RETURN):
1097 bytes = internal_ssl_write(conn, argp, len);
1098#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1099 *processed = (size_t)(bytes < 0) ? 0 : bytes;
1100#endif
1101 return (long)bytes;
1102 default:
1103 return retvalue;
1104 }
1105}
1106
1107static BIO *ssl_idevice_bio_new(idevice_connection_t conn)
1108{
1109 BIO *b = BIO_new(BIO_s_null());
1110 if (!b) return NULL;
1111 BIO_set_callback_arg(b, (char *)conn);
1112#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1113 BIO_set_callback_ex(b, ssl_idevice_bio_callback);
1114#else
1115 BIO_set_callback(b, ssl_idevice_bio_callback);
1116#endif
1117 return b;
1118}
1119
1120static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx)
1121{
1122 return 1;
1123}
1124
1125#ifndef STRIP_DEBUG_CODE
1126static const char *ssl_error_to_string(int e)
1127{
1128 switch(e) {
1129 case SSL_ERROR_NONE:
1130 return "SSL_ERROR_NONE";
1131 case SSL_ERROR_SSL:
1132 return ERR_error_string(ERR_get_error(), NULL);
1133 case SSL_ERROR_WANT_READ:
1134 return "SSL_ERROR_WANT_READ";
1135 case SSL_ERROR_WANT_WRITE:
1136 return "SSL_ERROR_WANT_WRITE";
1137 case SSL_ERROR_WANT_X509_LOOKUP:
1138 return "SSL_ERROR_WANT_X509_LOOKUP";
1139 case SSL_ERROR_SYSCALL:
1140 return "SSL_ERROR_SYSCALL";
1141 case SSL_ERROR_ZERO_RETURN:
1142 return "SSL_ERROR_ZERO_RETURN";
1143 case SSL_ERROR_WANT_CONNECT:
1144 return "SSL_ERROR_WANT_CONNECT";
1145 case SSL_ERROR_WANT_ACCEPT:
1146 return "SSL_ERROR_WANT_ACCEPT";
1147 default:
1148 return "UNKOWN_ERROR_VALUE";
1149 }
539} 1150}
1151#endif
1152#endif
540 1153
1154#if defined(HAVE_GNUTLS)
541/** 1155/**
542 * Internally used gnutls callback function that gets called during handshake. 1156 * Internally used gnutls callback function that gets called during handshake.
543 */ 1157 */
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) 1158#if GNUTLS_VERSION_NUMBER >= 0x020b07
1159static 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)
1160#else
1161static 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)
1162#endif
545{ 1163{
546 int res = -1; 1164 int res = -1;
547 gnutls_certificate_type_t type = gnutls_certificate_type_get (session); 1165 gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
548 if (type == GNUTLS_CRT_X509) { 1166 if (type == GNUTLS_CRT_X509) {
549 ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr (session); 1167 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) { 1168 if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) {
551 debug_info("Passing certificate"); 1169 debug_info("Passing certificate");
1170#if GNUTLS_VERSION_NUMBER >= 0x020b07
1171 st->cert_type = type;
1172 st->key_type = GNUTLS_PRIVKEY_X509;
1173#else
552 st->type = type; 1174 st->type = type;
1175#endif
553 st->ncerts = 1; 1176 st->ncerts = 1;
554 st->cert.x509 = &ssl_data->host_cert; 1177 st->cert.x509 = &ssl_data->host_cert;
555 st->key.x509 = ssl_data->host_privkey; 1178 st->key.x509 = ssl_data->host_privkey;
@@ -559,46 +1182,204 @@ static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_
559 } 1182 }
560 return res; 1183 return res;
561} 1184}
1185#elif defined(HAVE_MBEDTLS)
1186static void _mbedtls_log_cb(void* ctx, int level, const char* filename, int line, const char* message)
1187{
1188 fprintf(stderr, "[mbedtls][%d] %s:%d => %s", level, filename, line, message);
1189}
1190
1191static int cert_verify_cb(void* ctx, mbedtls_x509_crt* cert, int depth, uint32_t *flags)
1192{
1193 *flags = 0;
1194 return 0;
1195}
1196
1197static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len)
1198{
1199 memset(buf, 4, len);
1200 return 0;
1201}
1202#endif
562 1203
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) 1204idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
573{ 1205{
574 if (!connection || connection->ssl_data) 1206 if (!connection || connection->ssl_data)
575 return IDEVICE_E_INVALID_ARG; 1207 return IDEVICE_E_INVALID_ARG;
576 1208
577 idevice_error_t ret = IDEVICE_E_SSL_ERROR; 1209 idevice_error_t ret = IDEVICE_E_SSL_ERROR;
578 uint32_t return_me = 0; 1210 plist_t pair_record = NULL;
1211
1212 userpref_error_t uerr = userpref_read_pair_record(connection->device->udid, &pair_record);
1213 if (uerr != USERPREF_E_SUCCESS) {
1214 debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s (%d)", connection->device->udid, uerr);
1215 return ret;
1216 }
1217
1218#if defined(HAVE_OPENSSL)
1219 key_data_t root_cert = { NULL, 0 };
1220 key_data_t root_privkey = { NULL, 0 };
1221
1222 pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert);
1223 pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey);
1224
1225 if (pair_record)
1226 plist_free(pair_record);
1227
1228 BIO *ssl_bio = ssl_idevice_bio_new(connection);
1229 if (!ssl_bio) {
1230 debug_info("ERROR: Could not create SSL bio.");
1231 return ret;
1232 }
1233
1234 SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
1235 if (ssl_ctx == NULL) {
1236 debug_info("ERROR: Could not create SSL context.");
1237 BIO_free(ssl_bio);
1238 return ret;
1239 }
1240
1241#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || \
1242 (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3060000fL))
1243 SSL_CTX_set_security_level(ssl_ctx, 0);
1244#endif
1245
1246#if OPENSSL_VERSION_NUMBER < 0x10100002L || \
1247 (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL))
1248 /* force use of TLSv1 for older devices */
1249 if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) {
1250#ifdef SSL_OP_NO_TLSv1_1
1251 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
1252#endif
1253#ifdef SSL_OP_NO_TLSv1_2
1254 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
1255#endif
1256#ifdef SSL_OP_NO_TLSv1_3
1257 SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3);
1258#endif
1259 }
1260#else
1261 SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
1262 if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) {
1263 SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION);
1264 if (connection->device->version == 0) {
1265 /*
1266 iOS 1 doesn't understand TLS1_VERSION, it can only speak SSL3_VERSION.
1267 However, modern OpenSSL is usually compiled without SSLv3 support.
1268 So if we set min_proto_version to SSL3_VERSION on an OpenSSL instance which doesn't support it,
1269 it will just ignore min_proto_version altogether and fall back to an even higher version.
1270 To avoid accidentally breaking iOS 2.0+, we set min version to 0 instead.
1271 Here is what documentation says:
1272 Setting the minimum or maximum version to 0,
1273 will enable protocol versions down to the lowest version,
1274 or up to the highest version supported by the library, respectively.
1275 */
1276 SSL_CTX_set_min_proto_version(ssl_ctx, 0);
1277 }
1278 }
1279#endif
1280#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1281#if defined(SSL_OP_IGNORE_UNEXPECTED_EOF)
1282 /*
1283 * For OpenSSL 3 and later, mark close_notify alerts as optional.
1284 * For prior versions of OpenSSL we check for SSL_ERROR_SYSCALL when
1285 * reading instead (this error changes to SSL_ERROR_SSL in OpenSSL 3).
1286 */
1287 SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
1288#endif
1289#if defined(SSL_OP_LEGACY_SERVER_CONNECT)
1290 /*
1291 * Without setting SSL_OP_LEGACY_SERVER_CONNECT, OpenSSL 3 fails with
1292 * error "unsafe legacy renegotiation disabled" when talking to iOS 5
1293 */
1294 SSL_CTX_set_options(ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT);
1295#endif
1296#endif
1297
1298 BIO* membp;
1299 X509* rootCert = NULL;
1300 membp = BIO_new_mem_buf(root_cert.data, root_cert.size);
1301 PEM_read_bio_X509(membp, &rootCert, NULL, NULL);
1302 BIO_free(membp);
1303 if (SSL_CTX_use_certificate(ssl_ctx, rootCert) != 1) {
1304 debug_info("WARNING: Could not load RootCertificate");
1305 }
1306 X509_free(rootCert);
1307 free(root_cert.data);
1308
1309#if OPENSSL_VERSION_NUMBER >= 0x30000000L
1310 EVP_PKEY* rootPrivKey = NULL;
1311 membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size);
1312 PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL);
1313 BIO_free(membp);
1314 if (SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey) != 1) {
1315 debug_info("WARNING: Could not load RootPrivateKey");
1316 }
1317 EVP_PKEY_free(rootPrivKey);
1318#else
1319 RSA* rootPrivKey = NULL;
1320 membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size);
1321 PEM_read_bio_RSAPrivateKey(membp, &rootPrivKey, NULL, NULL);
1322 BIO_free(membp);
1323 if (SSL_CTX_use_RSAPrivateKey(ssl_ctx, rootPrivKey) != 1) {
1324 debug_info("WARNING: Could not load RootPrivateKey");
1325 }
1326 RSA_free(rootPrivKey);
1327#endif
1328 free(root_privkey.data);
1329
1330 SSL *ssl = SSL_new(ssl_ctx);
1331 if (!ssl) {
1332 debug_info("ERROR: Could not create SSL object");
1333 BIO_free(ssl_bio);
1334 SSL_CTX_free(ssl_ctx);
1335 return ret;
1336 }
1337 SSL_set_connect_state(ssl);
1338 SSL_set_verify(ssl, 0, ssl_verify_callback);
1339 SSL_set_bio(ssl, ssl_bio, ssl_bio);
579 1340
1341 debug_info("Performing SSL handshake");
1342 int ssl_error = 0;
1343 do {
1344 ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl));
1345 if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) {
1346 break;
1347 }
1348#ifdef _WIN32
1349 Sleep(100);
1350#else
1351 struct timespec ts = { 0, 100000000 };
1352 nanosleep(&ts, NULL);
1353#endif
1354 } while (1);
1355 if (ssl_error != 0) {
1356 debug_info("ERROR during SSL handshake: %s", ssl_error_to_string(ssl_error));
1357 SSL_free(ssl);
1358 SSL_CTX_free(ssl_ctx);
1359 } else {
1360 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private));
1361 ssl_data_loc->session = ssl;
1362 ssl_data_loc->ctx = ssl_ctx;
1363 connection->ssl_data = ssl_data_loc;
1364 ret = IDEVICE_E_SUCCESS;
1365 debug_info("SSL mode enabled, %s, cipher: %s", SSL_get_version(ssl), SSL_get_cipher(ssl));
1366 }
1367 /* required for proper multi-thread clean up to prevent leaks */
1368 openssl_remove_thread_state();
1369#elif defined(HAVE_GNUTLS)
580 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); 1370 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private));
581 1371
582 /* Set up GnuTLS... */ 1372 /* Set up GnuTLS... */
583 debug_info("enabling SSL mode"); 1373 debug_info("enabling SSL mode");
584 errno = 0; 1374 errno = 0;
585 gnutls_global_init();
586 gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); 1375 gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate);
587 gnutls_certificate_client_set_retrieve_function (ssl_data_loc->certificate, internal_cert_callback); 1376#if GNUTLS_VERSION_NUMBER >= 0x020b07
1377 gnutls_certificate_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback);
1378#else
1379 gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback);
1380#endif
588 gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); 1381 gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT);
589 { 1382 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); 1383 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); 1384 gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc);
604 1385
@@ -607,10 +1388,13 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
607 gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); 1388 gnutls_x509_privkey_init(&ssl_data_loc->root_privkey);
608 gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); 1389 gnutls_x509_privkey_init(&ssl_data_loc->host_privkey);
609 1390
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); 1391 pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, ssl_data_loc->root_cert);
611 if (uerr != USERPREF_E_SUCCESS) { 1392 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); 1393 pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, ssl_data_loc->root_privkey);
613 } 1394 pair_record_import_key_with_name(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, ssl_data_loc->host_privkey);
1395
1396 if (pair_record)
1397 plist_free(pair_record);
614 1398
615 debug_info("GnuTLS step 1..."); 1399 debug_info("GnuTLS step 1...");
616 gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); 1400 gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection);
@@ -619,46 +1403,146 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection)
619 debug_info("GnuTLS step 3..."); 1403 debug_info("GnuTLS step 3...");
620 gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); 1404 gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read);
621 debug_info("GnuTLS step 4 -- now handshaking..."); 1405 debug_info("GnuTLS step 4 -- now handshaking...");
622 if (errno) 1406 if (errno) {
623 debug_info("WARN: errno says %s before handshake!", strerror(errno)); 1407 debug_info("WARNING: errno says %s before handshake!", strerror(errno));
624 return_me = gnutls_handshake(ssl_data_loc->session); 1408 }
1409
1410 int return_me = 0;
1411 do {
1412 return_me = gnutls_handshake(ssl_data_loc->session);
1413 } while(return_me == GNUTLS_E_AGAIN || return_me == GNUTLS_E_INTERRUPTED);
1414
625 debug_info("GnuTLS handshake done..."); 1415 debug_info("GnuTLS handshake done...");
626 1416
627 if (return_me != GNUTLS_E_SUCCESS) { 1417 if (return_me != GNUTLS_E_SUCCESS) {
628 internal_ssl_cleanup(ssl_data_loc); 1418 internal_ssl_cleanup(ssl_data_loc);
629 free(ssl_data_loc); 1419 free(ssl_data_loc);
630 debug_info("GnuTLS reported something wrong."); 1420 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)); 1421 debug_info("oh.. errno says %s", strerror(errno));
633 } else { 1422 } else {
634 connection->ssl_data = ssl_data_loc; 1423 connection->ssl_data = ssl_data_loc;
635 ret = IDEVICE_E_SUCCESS; 1424 ret = IDEVICE_E_SUCCESS;
636 debug_info("SSL mode enabled"); 1425 debug_info("SSL mode enabled");
637 } 1426 }
1427#elif defined(HAVE_MBEDTLS)
1428 key_data_t root_cert = { NULL, 0 };
1429 key_data_t root_privkey = { NULL, 0 };
1430
1431 pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert);
1432 pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey);
1433
1434 plist_free(pair_record);
1435
1436 ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private));
1437
1438 mbedtls_ssl_init(&ssl_data_loc->ctx);
1439 mbedtls_ssl_config_init(&ssl_data_loc->config);
1440 mbedtls_entropy_init(&ssl_data_loc->entropy);
1441 mbedtls_ctr_drbg_init(&ssl_data_loc->ctr_drbg);
1442
1443 int r = mbedtls_ctr_drbg_seed(&ssl_data_loc->ctr_drbg, mbedtls_entropy_func, &ssl_data_loc->entropy, NULL, 0);
1444 if (r != 0) {
1445 debug_info("ERROR: [mbedtls] mbedtls_ctr_drbg_seed failed: %d", r);
1446 return ret;
1447 }
1448
1449 if (mbedtls_ssl_config_defaults(&ssl_data_loc->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
1450 debug_info("ERROR: [mbedtls] Failed to set config defaults");
1451 return ret;
1452 }
1453
1454 mbedtls_ssl_conf_rng(&ssl_data_loc->config, mbedtls_ctr_drbg_random, &ssl_data_loc->ctr_drbg);
1455
1456 mbedtls_ssl_conf_dbg(&ssl_data_loc->config, _mbedtls_log_cb, NULL);
1457
1458 mbedtls_ssl_conf_verify(&ssl_data_loc->config, cert_verify_cb, NULL);
1459
1460 mbedtls_ssl_setup(&ssl_data_loc->ctx, &ssl_data_loc->config);
1461
1462 mbedtls_ssl_set_bio(&ssl_data_loc->ctx, connection, (mbedtls_ssl_send_t*)&internal_ssl_write, (mbedtls_ssl_recv_t*)&internal_ssl_read, NULL);
1463
1464 mbedtls_x509_crt_init(&ssl_data_loc->certificate);
1465
1466 int crterr = mbedtls_x509_crt_parse(&ssl_data_loc->certificate, root_cert.data, root_cert.size);
1467 if (crterr < 0) {
1468 debug_info("ERROR: [mbedtls] parsing root cert failed: %d", crterr);
1469 return ret;
1470 }
1471
1472 mbedtls_ssl_conf_ca_chain(&ssl_data_loc->config, &ssl_data_loc->certificate, NULL);
1473
1474 mbedtls_pk_init(&ssl_data_loc->root_privkey);
1475
1476#if MBEDTLS_VERSION_NUMBER >= 0x03000000
1477 int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0, &_mbedtls_f_rng, NULL);
1478#else
1479 int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0);
1480#endif
1481 if (pkerr < 0) {
1482 debug_info("ERROR: [mbedtls] parsing private key failed: %d (size=%d)", pkerr, root_privkey.size);
1483 return ret;
1484 }
1485
1486 mbedtls_ssl_conf_own_cert(&ssl_data_loc->config, &ssl_data_loc->certificate, &ssl_data_loc->root_privkey);
1487
1488 int return_me = 0;
1489 do {
1490 return_me = mbedtls_ssl_handshake(&ssl_data_loc->ctx);
1491 } while (return_me == MBEDTLS_ERR_SSL_WANT_READ || return_me == MBEDTLS_ERR_SSL_WANT_WRITE);
1492
1493 if (return_me != 0) {
1494 debug_info("ERROR during SSL handshake: %d", return_me);
1495 internal_ssl_cleanup(ssl_data_loc);
1496 free(ssl_data_loc);
1497 } else {
1498 connection->ssl_data = ssl_data_loc;
1499 ret = IDEVICE_E_SUCCESS;
1500 debug_info("SSL mode enabled, %s, cipher: %s", mbedtls_ssl_get_version(&ssl_data_loc->ctx), mbedtls_ssl_get_ciphersuite(&ssl_data_loc->ctx));
1501 debug_info("SSL mode enabled");
1502 }
1503#endif
638 return ret; 1504 return ret;
639} 1505}
640 1506
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) 1507idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection)
651{ 1508{
1509 return idevice_connection_disable_bypass_ssl(connection, 0);
1510}
1511
1512idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass)
1513{
652 if (!connection) 1514 if (!connection)
653 return IDEVICE_E_INVALID_ARG; 1515 return IDEVICE_E_INVALID_ARG;
654 if (!connection->ssl_data) { 1516 if (!connection->ssl_data) {
655 /* ignore if ssl is not enabled */ 1517 /* ignore if ssl is not enabled */
656 return IDEVICE_E_SUCCESS; 1518 return IDEVICE_E_SUCCESS;
657 } 1519 }
658 1520
659 if (connection->ssl_data->session) { 1521 // some services require plain text communication after SSL handshake
660 gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); 1522 // sending out SSL_shutdown will cause bytes
1523 if (!sslBypass) {
1524#if defined(HAVE_OPENSSL)
1525 if (connection->ssl_data->session) {
1526 /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */
1527 if (SSL_shutdown(connection->ssl_data->session) == 0) {
1528 /* Only try bidirectional shutdown if we know it can complete */
1529 int ssl_error;
1530 if ((ssl_error = SSL_get_error(connection->ssl_data->session, 0)) == SSL_ERROR_NONE) {
1531 SSL_shutdown(connection->ssl_data->session);
1532 } else {
1533 debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error);
1534 }
1535 }
1536 }
1537#elif defined(HAVE_GNUTLS)
1538 if (connection->ssl_data->session) {
1539 gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR);
1540 }
1541#elif defined(HAVE_MBEDTLS)
1542 mbedtls_ssl_close_notify(&connection->ssl_data->ctx);
1543#endif
661 } 1544 }
1545
662 internal_ssl_cleanup(connection->ssl_data); 1546 internal_ssl_cleanup(connection->ssl_data);
663 free(connection->ssl_data); 1547 free(connection->ssl_data);
664 connection->ssl_data = NULL; 1548 connection->ssl_data = NULL;
@@ -668,3 +1552,27 @@ idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection)
668 return IDEVICE_E_SUCCESS; 1552 return IDEVICE_E_SUCCESS;
669} 1553}
670 1554
1555const char* idevice_strerror(idevice_error_t err)
1556{
1557 switch (err) {
1558 case IDEVICE_E_SUCCESS:
1559 return "Success";
1560 case IDEVICE_E_INVALID_ARG:
1561 return "Invalid argument";
1562 case IDEVICE_E_UNKNOWN_ERROR:
1563 return "Unknown Error";
1564 case IDEVICE_E_NO_DEVICE:
1565 return "No device";
1566 case IDEVICE_E_NOT_ENOUGH_DATA:
1567 return "Not enough data";
1568 case IDEVICE_E_CONNREFUSED:
1569 return "Connection refused";
1570 case IDEVICE_E_SSL_ERROR:
1571 return "SSL error";
1572 case IDEVICE_E_TIMEOUT:
1573 return "Timeout";
1574 default:
1575 break;
1576 }
1577 return "Unknown Error";
1578}
diff --git a/src/idevice.h b/src/idevice.h
index 231b3ab..e05338e 100644
--- a/src/idevice.h
+++ b/src/idevice.h
@@ -8,51 +8,95 @@
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_CLASS_IPHONE 1
30 CONNECTION_USBMUXD = 1 56#define DEVICE_CLASS_IPAD 2
31}; 57#define DEVICE_CLASS_IPOD 3
58#define DEVICE_CLASS_APPLETV 4
59#define DEVICE_CLASS_WATCH 5
60#define DEVICE_CLASS_UNKNOWN 255
32 61
33struct ssl_data_private { 62struct ssl_data_private {
63#if defined(HAVE_OPENSSL)
64 SSL *session;
65 SSL_CTX *ctx;
66#elif defined(HAVE_GNUTLS)
34 gnutls_certificate_credentials_t certificate; 67 gnutls_certificate_credentials_t certificate;
35 gnutls_session_t session; 68 gnutls_session_t session;
36 gnutls_x509_privkey_t root_privkey; 69 gnutls_x509_privkey_t root_privkey;
37 gnutls_x509_crt_t root_cert; 70 gnutls_x509_crt_t root_cert;
38 gnutls_x509_privkey_t host_privkey; 71 gnutls_x509_privkey_t host_privkey;
39 gnutls_x509_crt_t host_cert; 72 gnutls_x509_crt_t host_cert;
73#elif defined(HAVE_MBEDTLS)
74 mbedtls_ssl_context ctx;
75 mbedtls_ssl_config config;
76 mbedtls_entropy_context entropy;
77 mbedtls_ctr_drbg_context ctr_drbg;
78 mbedtls_x509_crt certificate;
79 mbedtls_pk_context root_privkey;
80#endif
40}; 81};
41typedef struct ssl_data_private *ssl_data_t; 82typedef struct ssl_data_private *ssl_data_t;
42 83
43struct idevice_connection_private { 84struct idevice_connection_private {
44 enum connection_type type; 85 idevice_t device;
86 enum idevice_connection_type type;
45 void *data; 87 void *data;
46 ssl_data_t ssl_data; 88 ssl_data_t ssl_data;
89 unsigned int ssl_recv_timeout;
90 idevice_error_t status;
47}; 91};
48 92
49struct idevice_private { 93struct idevice_private {
50 char *uuid; 94 char *udid;
51 enum connection_type conn_type; 95 uint32_t mux_id;
96 enum idevice_connection_type conn_type;
52 void *conn_data; 97 void *conn_data;
98 int version;
99 int device_class;
53}; 100};
54 101
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 102#endif
diff --git a/src/installation_proxy.c b/src/installation_proxy.c
index 4a76dd2..bb6ef01 100644
--- a/src/installation_proxy.c
+++ b/src/installation_proxy.c
@@ -2,63 +2,214 @@
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>
29
30#ifndef _MSC_VER
24#include <unistd.h> 31#include <unistd.h>
32#endif
33
25#include <plist/plist.h> 34#include <plist/plist.h>
26 35
27#include "installation_proxy.h" 36#include "installation_proxy.h"
28#include "property_list_service.h" 37#include "property_list_service.h"
29#include "debug.h" 38#include "common/debug.h"
39
40typedef enum {
41 INSTPROXY_COMMAND_TYPE_ASYNC,
42 INSTPROXY_COMMAND_TYPE_SYNC
43} instproxy_command_type_t;
30 44
31struct instproxy_status_data { 45struct instproxy_status_data {
32 instproxy_client_t client; 46 instproxy_client_t client;
47 plist_t command;
33 instproxy_status_cb_t cbfunc; 48 instproxy_status_cb_t cbfunc;
34 char *operation;
35 void *user_data; 49 void *user_data;
36}; 50};
37 51
38/** 52/**
53 * Converts an error string identifier to a instproxy_error_t value.
54 * Used internally to get correct error codes from a response.
55 *
56 * @param name The error name to convert.
57 * @param error_detail Pointer to store error detail text if available. The
58 * caller is reponsible for freeing the allocated buffer after use. If NULL
59 * is passed no error detail will be returned.
60 *
61 * @return A matching instproxy_error_t error code or
62 * INSTPROXY_E_UNKNOWN_ERROR otherwise.
63 */
64static instproxy_error_t instproxy_strtoerr(const char* name)
65{
66 instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR;
67
68 if (strcmp(name, "AlreadyArchived") == 0) {
69 err = INSTPROXY_E_ALREADY_ARCHIVED;
70 } else if (strcmp(name, "APIInternalError") == 0) {
71 err = INSTPROXY_E_API_INTERNAL_ERROR;
72 } else if (strcmp(name, "ApplicationAlreadyInstalled") == 0) {
73 err = INSTPROXY_E_APPLICATION_ALREADY_INSTALLED;
74 } else if (strcmp(name, "ApplicationMoveFailed") == 0) {
75 err = INSTPROXY_E_APPLICATION_MOVE_FAILED;
76 } else if (strcmp(name, "ApplicationSINFCaptureFailed") == 0) {
77 err = INSTPROXY_E_APPLICATION_SINF_CAPTURE_FAILED;
78 } else if (strcmp(name, "ApplicationSandboxFailed") == 0) {
79 err = INSTPROXY_E_APPLICATION_SANDBOX_FAILED;
80 } else if (strcmp(name, "ApplicationVerificationFailed") == 0) {
81 err = INSTPROXY_E_APPLICATION_VERIFICATION_FAILED;
82 } else if (strcmp(name, "ArchiveDestructionFailed") == 0) {
83 err = INSTPROXY_E_ARCHIVE_DESTRUCTION_FAILED;
84 } else if (strcmp(name, "BundleVerificationFailed") == 0) {
85 err = INSTPROXY_E_BUNDLE_VERIFICATION_FAILED;
86 } else if (strcmp(name, "CarrierBundleCopyFailed") == 0) {
87 err = INSTPROXY_E_CARRIER_BUNDLE_COPY_FAILED;
88 } else if (strcmp(name, "CarrierBundleDirectoryCreationFailed") == 0) {
89 err = INSTPROXY_E_CARRIER_BUNDLE_DIRECTORY_CREATION_FAILED;
90 } else if (strcmp(name, "CarrierBundleMissingSupportedSIMs") == 0) {
91 err = INSTPROXY_E_CARRIER_BUNDLE_MISSING_SUPPORTED_SIMS;
92 } else if (strcmp(name, "CommCenterNotificationFailed") == 0) {
93 err = INSTPROXY_E_COMM_CENTER_NOTIFICATION_FAILED;
94 } else if (strcmp(name, "ContainerCreationFailed") == 0) {
95 err = INSTPROXY_E_CONTAINER_CREATION_FAILED;
96 } else if (strcmp(name, "ContainerP0wnFailed") == 0) {
97 err = INSTPROXY_E_CONTAINER_P0WN_FAILED;
98 } else if (strcmp(name, "ContainerRemovalFailed") == 0) {
99 err = INSTPROXY_E_CONTAINER_REMOVAL_FAILED;
100 } else if (strcmp(name, "EmbeddedProfileInstallFailed") == 0) {
101 err = INSTPROXY_E_EMBEDDED_PROFILE_INSTALL_FAILED;
102 } else if (strcmp(name, "ExecutableTwiddleFailed") == 0) {
103 err = INSTPROXY_E_EXECUTABLE_TWIDDLE_FAILED;
104 } else if (strcmp(name, "ExistenceCheckFailed") == 0) {
105 err = INSTPROXY_E_EXISTENCE_CHECK_FAILED;
106 } else if (strcmp(name, "InstallMapUpdateFailed") == 0) {
107 err = INSTPROXY_E_INSTALL_MAP_UPDATE_FAILED;
108 } else if (strcmp(name, "ManifestCaptureFailed") == 0) {
109 err = INSTPROXY_E_MANIFEST_CAPTURE_FAILED;
110 } else if (strcmp(name, "MapGenerationFailed") == 0) {
111 err = INSTPROXY_E_MAP_GENERATION_FAILED;
112 } else if (strcmp(name, "MissingBundleExecutable") == 0) {
113 err = INSTPROXY_E_MISSING_BUNDLE_EXECUTABLE;
114 } else if (strcmp(name, "MissingBundleIdentifier") == 0) {
115 err = INSTPROXY_E_MISSING_BUNDLE_IDENTIFIER;
116 } else if (strcmp(name, "MissingBundlePath") == 0) {
117 err = INSTPROXY_E_MISSING_BUNDLE_PATH;
118 } else if (strcmp(name, "MissingContainer") == 0) {
119 err = INSTPROXY_E_MISSING_CONTAINER;
120 } else if (strcmp(name, "NotificationFailed") == 0) {
121 err = INSTPROXY_E_NOTIFICATION_FAILED;
122 } else if (strcmp(name, "PackageExtractionFailed") == 0) {
123 err = INSTPROXY_E_PACKAGE_EXTRACTION_FAILED;
124 } else if (strcmp(name, "PackageInspectionFailed") == 0) {
125 err = INSTPROXY_E_PACKAGE_INSPECTION_FAILED;
126 } else if (strcmp(name, "PackageMoveFailed") == 0) {
127 err = INSTPROXY_E_PACKAGE_MOVE_FAILED;
128 } else if (strcmp(name, "PathConversionFailed") == 0) {
129 err = INSTPROXY_E_PATH_CONVERSION_FAILED;
130 } else if (strcmp(name, "RestoreContainerFailed") == 0) {
131 err = INSTPROXY_E_RESTORE_CONTAINER_FAILED;
132 } else if (strcmp(name, "SeatbeltProfileRemovalFailed") == 0) {
133 err = INSTPROXY_E_SEATBELT_PROFILE_REMOVAL_FAILED;
134 } else if (strcmp(name, "StageCreationFailed") == 0) {
135 err = INSTPROXY_E_STAGE_CREATION_FAILED;
136 } else if (strcmp(name, "SymlinkFailed") == 0) {
137 err = INSTPROXY_E_SYMLINK_FAILED;
138 } else if (strcmp(name, "UnknownCommand") == 0) {
139 err = INSTPROXY_E_UNKNOWN_COMMAND;
140 } else if (strcmp(name, "iTunesArtworkCaptureFailed") == 0) {
141 err = INSTPROXY_E_ITUNES_ARTWORK_CAPTURE_FAILED;
142 } else if (strcmp(name, "iTunesMetadataCaptureFailed") == 0) {
143 err = INSTPROXY_E_ITUNES_METADATA_CAPTURE_FAILED;
144 } else if (strcmp(name, "DeviceOSVersionTooLow") == 0) {
145 err = INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW;
146 } else if (strcmp(name, "DeviceFamilyNotSupported") == 0) {
147 err = INSTPROXY_E_DEVICE_FAMILY_NOT_SUPPORTED;
148 } else if (strcmp(name, "PackagePatchFailed") == 0) {
149 err = INSTPROXY_E_PACKAGE_PATCH_FAILED;
150 } else if (strcmp(name, "IncorrectArchitecture") == 0) {
151 err = INSTPROXY_E_INCORRECT_ARCHITECTURE;
152 } else if (strcmp(name, "PluginCopyFailed") == 0) {
153 err = INSTPROXY_E_PLUGIN_COPY_FAILED;
154 } else if (strcmp(name, "BreadcrumbFailed") == 0) {
155 err = INSTPROXY_E_BREADCRUMB_FAILED;
156 } else if (strcmp(name, "BreadcrumbUnlockFailed") == 0) {
157 err = INSTPROXY_E_BREADCRUMB_UNLOCK_FAILED;
158 } else if (strcmp(name, "GeoJSONCaptureFailed") == 0) {
159 err = INSTPROXY_E_GEOJSON_CAPTURE_FAILED;
160 } else if (strcmp(name, "NewsstandArtworkCaptureFailed") == 0) {
161 err = INSTPROXY_E_NEWSSTAND_ARTWORK_CAPTURE_FAILED;
162 } else if (strcmp(name, "MissingCommand") == 0) {
163 err = INSTPROXY_E_MISSING_COMMAND;
164 } else if (strcmp(name, "NotEntitled") == 0) {
165 err = INSTPROXY_E_NOT_ENTITLED;
166 } else if (strcmp(name, "MissingPackagePath") == 0) {
167 err = INSTPROXY_E_MISSING_PACKAGE_PATH;
168 } else if (strcmp(name, "MissingContainerPath") == 0) {
169 err = INSTPROXY_E_MISSING_CONTAINER_PATH;
170 } else if (strcmp(name, "MissingApplicationIdentifier") == 0) {
171 err = INSTPROXY_E_MISSING_APPLICATION_IDENTIFIER;
172 } else if (strcmp(name, "MissingAttributeValue") == 0) {
173 err = INSTPROXY_E_MISSING_ATTRIBUTE_VALUE;
174 } else if (strcmp(name, "LookupFailed") == 0) {
175 err = INSTPROXY_E_LOOKUP_FAILED;
176 } else if (strcmp(name, "DictCreationFailed") == 0) {
177 err = INSTPROXY_E_DICT_CREATION_FAILED;
178 } else if (strcmp(name, "InstallProhibited") == 0) {
179 err = INSTPROXY_E_INSTALL_PROHIBITED;
180 } else if (strcmp(name, "UninstallProhibited") == 0) {
181 err = INSTPROXY_E_UNINSTALL_PROHIBITED;
182 } else if (strcmp(name, "MissingBundleVersion") == 0) {
183 err = INSTPROXY_E_MISSING_BUNDLE_VERSION;
184 }
185
186 return err;
187}
188
189/**
39 * Locks an installation_proxy client, used for thread safety. 190 * Locks an installation_proxy client, used for thread safety.
40 * 191 *
41 * @param client The installation_proxy client to lock 192 * @param client The installation_proxy client to lock
42 */ 193 */
43static void instproxy_lock(instproxy_client_t client) 194static void instproxy_lock(instproxy_client_t client)
44{ 195{
45 debug_info("InstallationProxy: Locked"); 196 debug_info("Locked");
46 g_mutex_lock(client->mutex); 197 mutex_lock(&client->mutex);
47} 198}
48 199
49/** 200/**
50 * Unlocks an installation_proxy client, used for thread safety. 201 * Unlocks an installation_proxy client, used for thread safety.
51 * 202 *
52 * @param client The installation_proxy client to lock 203 * @param client The installation_proxy client to lock
53 */ 204 */
54static void instproxy_unlock(instproxy_client_t client) 205static void instproxy_unlock(instproxy_client_t client)
55{ 206{
56 debug_info("InstallationProxy: Unlocked"); 207 debug_info("Unlocked");
57 g_mutex_unlock(client->mutex); 208 mutex_unlock(&client->mutex);
58} 209}
59 210
60/** 211/**
61 * Convert a property_list_service_error_t value to an instproxy_error_t value. 212 * Converts a property_list_service_error_t value to an instproxy_error_t value.
62 * Used internally to get correct error codes. 213 * Used internally to get correct error codes.
63 * 214 *
64 * @param err A property_list_service_error_t error code 215 * @param err A property_list_service_error_t error code
@@ -77,186 +228,80 @@ static instproxy_error_t instproxy_error(property_list_service_error_t err)
77 return INSTPROXY_E_PLIST_ERROR; 228 return INSTPROXY_E_PLIST_ERROR;
78 case PROPERTY_LIST_SERVICE_E_MUX_ERROR: 229 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
79 return INSTPROXY_E_CONN_FAILED; 230 return INSTPROXY_E_CONN_FAILED;
231 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
232 return INSTPROXY_E_RECEIVE_TIMEOUT;
80 default: 233 default:
81 break; 234 break;
82 } 235 }
83 return INSTPROXY_E_UNKNOWN_ERROR; 236 return INSTPROXY_E_UNKNOWN_ERROR;
84} 237}
85 238
86/** 239instproxy_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{ 240{
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; 241 property_list_service_client_t plistclient = NULL;
107 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 242 instproxy_error_t err = instproxy_error(property_list_service_client_new(device, service, &plistclient));
108 return INSTPROXY_E_CONN_FAILED; 243 if (err != INSTPROXY_E_SUCCESS) {
244 return err;
109 } 245 }
110 246
111 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); 247 instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private));
112 client_loc->parent = plistclient; 248 client_loc->parent = plistclient;
113 client_loc->mutex = g_mutex_new(); 249 mutex_init(&client_loc->mutex);
114 client_loc->status_updater = NULL; 250 client_loc->receive_status_thread = THREAD_T_NULL;
115 251
116 *client = client_loc; 252 *client = client_loc;
117 return INSTPROXY_E_SUCCESS; 253 return INSTPROXY_E_SUCCESS;
118} 254}
119 255
120/** 256instproxy_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 257{
122 * installation_proxy client data. 258 int32_t err = INSTPROXY_E_UNKNOWN_ERROR;
123 * 259 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. 260 return err;
125 * 261}
126 * @return INSTPROXY_E_SUCCESS on success 262
127 * or INSTPROXY_E_INVALID_ARG if client is NULL.
128 */
129instproxy_error_t instproxy_client_free(instproxy_client_t client) 263instproxy_error_t instproxy_client_free(instproxy_client_t client)
130{ 264{
131 if (!client) 265 if (!client)
132 return INSTPROXY_E_INVALID_ARG; 266 return INSTPROXY_E_INVALID_ARG;
133 267
134 property_list_service_client_free(client->parent); 268 property_list_service_client_t parent = client->parent;
135 client->parent = NULL; 269 client->parent = NULL;
136 if (client->status_updater) { 270 if (client->receive_status_thread) {
137 debug_info("joining status_updater"); 271 debug_info("joining receive_status_thread");
138 g_thread_join(client->status_updater); 272 thread_join(client->receive_status_thread);
139 } 273 thread_free(client->receive_status_thread);
140 if (client->mutex) { 274 client->receive_status_thread = THREAD_T_NULL;
141 g_mutex_free(client->mutex);
142 } 275 }
276 property_list_service_client_free(parent);
277 mutex_destroy(&client->mutex);
143 free(client); 278 free(client);
144 279
145 return INSTPROXY_E_SUCCESS; 280 return INSTPROXY_E_SUCCESS;
146} 281}
147 282
148/** 283/**
149 * Send a command with specified options to the device. 284 * Sends a command to the device.
150 * Only used internally. 285 * Only used internally.
151 * 286 *
152 * @param client The connected installation_proxy client. 287 * @param client The connected installation_proxy client.
153 * @param command The command to execute. Required. 288 * @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 * 289 *
158 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if 290 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
159 * an error occured. 291 * an error occurred.
160 */ 292 */
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) 293static instproxy_error_t instproxy_send_command(instproxy_client_t client, plist_t command)
162{
163 if (!client || !command || (client_options && (plist_get_node_type(client_options) != PLIST_DICT)))
164 return INSTPROXY_E_INVALID_ARG;
165
166 plist_t dict = plist_new_dict();
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
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{ 294{
199 if (!client || !client->parent || !result) 295 if (!client || !command)
200 return INSTPROXY_E_INVALID_ARG; 296 return INSTPROXY_E_INVALID_ARG;
201 297
202 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 298 instproxy_error_t res = instproxy_error(property_list_service_send_xml_plist(client->parent, command));
203 299
204 instproxy_lock(client);
205 res = instproxy_send_command(client, "Browse", client_options, NULL, NULL);
206 if (res != INSTPROXY_E_SUCCESS) { 300 if (res != INSTPROXY_E_SUCCESS) {
207 debug_info("could not send plist"); 301 debug_info("could not send command plist, error %d", res);
208 goto leave_unlock; 302 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 } 303 }
257 304
258leave_unlock:
259 instproxy_unlock(client);
260 return res; 305 return res;
261} 306}
262 307
@@ -269,78 +314,99 @@ leave_unlock:
269 * 314 *
270 * @param client The connected installation proxy client 315 * @param client The connected installation proxy client
271 * @param status_cb Pointer to a callback function or NULL 316 * @param status_cb Pointer to a callback function or NULL
272 * @param operation Operation name. Will be passed to the callback function 317 * @param command Operation specificiation in plist. Will be passed to the
273 * in async mode or shown in debug messages in sync mode. 318 * status_cb callback.
274 * @param user_data Callback data passed to status_cb. 319 * @param user_data Callback data passed to status_cb.
275 */ 320 */
276static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) 321static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client, plist_t command, instproxy_status_cb_t status_cb, void *user_data)
277{ 322{
278 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 323 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
279 int ok = 1; 324 int complete = 0;
280 plist_t dict = NULL; 325 plist_t node = NULL;
326 char* command_name = NULL;
327 char* status_name = NULL;
328 char* error_name = NULL;
329 char* error_description = NULL;
330 uint64_t error_code = 0;
331#ifndef STRIP_DEBUG_CODE
332 int percent_complete = 0;
333#endif
334
335 instproxy_command_get_name(command, &command_name);
281 336
282 do { 337 do {
338 /* receive status response */
283 instproxy_lock(client); 339 instproxy_lock(client);
284 res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 30000)); 340 res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000));
285 instproxy_unlock(client); 341 instproxy_unlock(client);
286 if (res != INSTPROXY_E_SUCCESS) { 342
343 /* break out if we have a communication problem */
344 if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) {
287 debug_info("could not receive plist, error %d", res); 345 debug_info("could not receive plist, error %d", res);
288 break; 346 break;
289 } 347 }
290 if (dict) { 348
291 /* invoke callback function */ 349 /* parse status response */
292 if (status_cb) { 350 if (node) {
293 status_cb(operation, dict, user_data); 351 /* check status for possible error to allow reporting it and aborting it gracefully */
352 res = instproxy_status_get_error(node, &error_name, &error_description, &error_code);
353 if (res != INSTPROXY_E_SUCCESS) {
354 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");
355 complete = 1;
294 } 356 }
295 /* check for 'Error', so we can abort cleanly */ 357
296 plist_t err = plist_dict_get_item(dict, "Error"); 358 if (error_name) {
297 if (err) { 359 free(error_name);
360 error_name = NULL;
361 }
362
363 if (error_description) {
364 free(error_description);
365 error_description = NULL;
366 }
367
368 /* check status from response */
369 instproxy_status_get_name(node, &status_name);
370 if (!status_name) {
371 debug_info("ignoring message without Status key:");
372 debug_plist(node);
373 } else {
374 if (!strcmp(status_name, "Complete")) {
375 complete = 1;
376 } else {
377 res = INSTPROXY_E_OP_IN_PROGRESS;
378 }
298#ifndef STRIP_DEBUG_CODE 379#ifndef STRIP_DEBUG_CODE
299 char *err_msg = NULL; 380 percent_complete = -1;
300 plist_get_string_val(err, &err_msg); 381 instproxy_status_get_percent_complete(node, &percent_complete);
301 if (err_msg) { 382 if (percent_complete >= 0) {
302 debug_info("(%s): ERROR: %s", operation, err_msg); 383 debug_info("command: %s, status: %s, percent (%d%%)", command_name, status_name, percent_complete);
303 free(err_msg); 384 } else {
385 debug_info("command: %s, status: %s", command_name, status_name);
304 } 386 }
305#endif 387#endif
306 ok = 0; 388 free(status_name);
307 res = INSTPROXY_E_OP_FAILED; 389 status_name = NULL;
308 } 390 }
309 /* get 'Status' */ 391
310 plist_t status = plist_dict_get_item(dict, "Status"); 392 /* invoke status callback function */
311 if (status) { 393 if (status_cb) {
312 char *status_msg = NULL; 394 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 } 395 }
334 plist_free(dict); 396
335 dict = NULL; 397 plist_free(node);
398 node = NULL;
336 } 399 }
337 } while (ok && client->parent); 400 } while (!complete && client->parent);
401
402 if (command_name)
403 free(command_name);
338 404
339 return res; 405 return res;
340} 406}
341 407
342/** 408/**
343 * Internally used status updater thread function that will call the specified 409 * Internally used "receive status" thread function that will call the specified
344 * callback function when status update messages (or error messages) are 410 * callback function when status update messages (or error messages) are
345 * received. 411 * received.
346 * 412 *
@@ -349,20 +415,27 @@ static instproxy_error_t instproxy_perform_operation(instproxy_client_t client,
349 * 415 *
350 * @return Always NULL. 416 * @return Always NULL.
351 */ 417 */
352static gpointer instproxy_status_updater(gpointer arg) 418static void* instproxy_receive_status_loop_thread(void* arg)
353{ 419{
354 struct instproxy_status_data *data = (struct instproxy_status_data*)arg; 420 struct instproxy_status_data *data = (struct instproxy_status_data*)arg;
355 421
356 /* run until the operation is complete or an error occurs */ 422 /* run until the command is complete or an error occurs */
357 (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation, data->user_data); 423 (void)instproxy_receive_status_loop(data->client, data->command, data->cbfunc, data->user_data);
358 424
359 /* cleanup */ 425 /* cleanup */
360 instproxy_lock(data->client); 426 instproxy_lock(data->client);
427
361 debug_info("done, cleaning up."); 428 debug_info("done, cleaning up.");
362 if (data->operation) { 429
363 free(data->operation); 430 if (data->command) {
431 plist_free(data->command);
364 } 432 }
365 data->client->status_updater = NULL; 433
434 if (data->client->receive_status_thread) {
435 thread_free(data->client->receive_status_thread);
436 data->client->receive_status_thread = THREAD_T_NULL;
437 }
438
366 instproxy_unlock(data->client); 439 instproxy_unlock(data->client);
367 free(data); 440 free(data);
368 441
@@ -370,379 +443,493 @@ static gpointer instproxy_status_updater(gpointer arg)
370} 443}
371 444
372/** 445/**
373 * Internally used helper function that creates a status updater thread which 446 * Internally used helper function that creates a "receive status" thread which
374 * will call the passed callback function when status updates occur. 447 * 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 448 *
376 * run synchronously until it completes or an error occurs. 449 * If async is 0 no thread will be created and the command will run
450 * synchronously until it completes or an error occurs.
377 * 451 *
378 * @param client The connected installation proxy client 452 * @param client The connected installation proxy client
379 * @param status_cb Pointer to a callback function or NULL 453 * @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. 454 * in async mode or shown in debug messages in sync mode.
455 * @param async A boolean indicating if receive loop should be run
456 * asynchronously or block.
457 * @param status_cb Pointer to a callback function or NULL.
382 * @param user_data Callback data passed to status_cb. 458 * @param user_data Callback data passed to status_cb.
383 * 459 *
384 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or 460 * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or
385 * when the operation completed successfully (sync). 461 * when the command completed successfully (sync).
386 * An INSTPROXY_E_* error value is returned if an error occured. 462 * An INSTPROXY_E_* error value is returned if an error occurred.
387 */ 463 */
388static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) 464static 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{ 465{
466 if (!client || !client->parent || !command) {
467 return INSTPROXY_E_INVALID_ARG;
468 }
469
470 if (client->receive_status_thread) {
471 return INSTPROXY_E_OP_IN_PROGRESS;
472 }
473
390 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 474 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
391 if (status_cb) { 475 if (async == INSTPROXY_COMMAND_TYPE_ASYNC) {
392 /* async mode */ 476 /* async mode */
393 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); 477 struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data));
394 if (data) { 478 if (data) {
395 data->client = client; 479 data->client = client;
480 data->command = plist_copy(command);
396 data->cbfunc = status_cb; 481 data->cbfunc = status_cb;
397 data->operation = strdup(operation);
398 data->user_data = user_data; 482 data->user_data = user_data;
399 483
400 client->status_updater = g_thread_create(instproxy_status_updater, data, TRUE, NULL); 484 if (thread_new(&client->receive_status_thread, instproxy_receive_status_loop_thread, data) == 0) {
401 if (client->status_updater) {
402 res = INSTPROXY_E_SUCCESS; 485 res = INSTPROXY_E_SUCCESS;
403 } 486 }
404 } 487 }
405 } else { 488 } else {
406 /* sync mode */ 489 /* sync mode as a fallback */
407 res = instproxy_perform_operation(client, NULL, operation, NULL); 490 res = instproxy_receive_status_loop(client, command, status_cb, user_data);
408 } 491 }
492
409 return res; 493 return res;
410} 494}
411 495
412
413/** 496/**
414 * Internal function used by instproxy_install and instproxy_upgrade. 497 * Internal core function to send a command and process the response.
415 * 498 *
416 * @param client The connected installation_proxy client 499 * @param client The connected installation_proxy client
417 * @param pkg_path Path of the installation package (inside the AFC jail) 500 * @param command The command specification dictionary.
418 * @param client_options The client options to use, as PLIST_DICT, or NULL. 501 * @param async A boolean indicating whether the receive loop should be run
419 * @param status_cb Callback function for progress and status information. If 502 * asynchronously or block until completing the command.
420 * NULL is passed, this function will run synchronously. 503 * @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. 504 * @param user_data Callback data passed to status_cb.
423 * 505 *
424 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if 506 * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if
425 * an error occured. 507 * an error occurred.
426 */ 508 */
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) 509static 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{ 510{
429 if (!client || !client->parent || !pkg_path) { 511 if (!client || !client->parent || !command) {
430 return INSTPROXY_E_INVALID_ARG; 512 return INSTPROXY_E_INVALID_ARG;
431 } 513 }
432 if (client->status_updater) { 514
515 if (client->receive_status_thread) {
433 return INSTPROXY_E_OP_IN_PROGRESS; 516 return INSTPROXY_E_OP_IN_PROGRESS;
434 } 517 }
435 518
519 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
520
521 /* send command */
436 instproxy_lock(client); 522 instproxy_lock(client);
437 instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path); 523 res = instproxy_send_command(client, command);
438 instproxy_unlock(client); 524 instproxy_unlock(client);
439 525
440 if (res != INSTPROXY_E_SUCCESS) { 526 /* loop until status or error is received */
441 debug_info("could not send plist, error %d", res); 527 res = instproxy_receive_status_loop_with_callback(client, command, async, status_cb, user_data);
442 return res;
443 }
444 528
445 return instproxy_create_status_updater(client, status_cb, command, user_data); 529 return res;
446} 530}
447 531
448/** 532instproxy_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{ 533{
474 return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Install", user_data); 534 if (!client || !client->parent || !status_cb)
535 return INSTPROXY_E_INVALID_ARG;
536
537 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
538
539 plist_t command = plist_new_dict();
540 plist_dict_set_item(command, "Command", plist_new_string("Browse"));
541 if (client_options)
542 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
543
544 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, (void*)user_data);
545
546 plist_free(command);
547
548 return res;
475} 549}
476 550
477/** 551static 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{ 552{
505 return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Upgrade", user_data); 553 plist_t *result_array = (plist_t*)user_data;
554 uint64_t current_amount = 0;
555 plist_t current_list = NULL;
556 uint64_t i;
557
558 instproxy_status_get_current_list(status, NULL, NULL, &current_amount, &current_list);
559
560 debug_info("current_amount: %d", current_amount);
561
562 if (current_amount > 0) {
563 for (i = 0; current_list && (i < current_amount); i++) {
564 plist_t item = plist_array_get_item(current_list, i);
565 plist_array_append_item(*result_array, plist_copy(item));
566 }
567 }
568
569 if (current_list)
570 plist_free(current_list);
506} 571}
507 572
508/** 573instproxy_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{ 574{
529 if (!client || !client->parent || !appid) { 575 if (!client || !client->parent || !result)
530 return INSTPROXY_E_INVALID_ARG; 576 return INSTPROXY_E_INVALID_ARG;
531 }
532
533 if (client->status_updater) {
534 return INSTPROXY_E_OP_IN_PROGRESS;
535 }
536 577
537 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; 578 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 579
542 instproxy_lock(client); 580 plist_t result_array = plist_new_array();
543 res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL);
544 instproxy_unlock(client);
545 581
546 plist_free(dict); 582 plist_t command = plist_new_dict();
583 plist_dict_set_item(command, "Command", plist_new_string("Browse"));
584 if (client_options)
585 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
547 586
548 if (res != INSTPROXY_E_SUCCESS) { 587 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); 588
550 return res; 589 if (res == INSTPROXY_E_SUCCESS) {
590 *result = result_array;
591 } else {
592 plist_free(result_array);
551 } 593 }
552 594
553 return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data); 595 plist_free(command);
596
597 return res;
554} 598}
555 599
556/** 600static void instproxy_copy_lookup_result_cb(plist_t command, plist_t status, void *user_data)
557 * List archived applications. This function runs synchronously.
558 *
559 * @see instproxy_archive
560 *
561 * @param client The connected installation_proxy client
562 * @param client_options The client options to use, as PLIST_DICT, or NULL.
563 * Currently there are no known client options, so pass NULL here.
564 * @param result Pointer that will be set to a plist containing a PLIST_DICT
565 * holding information about the archived applications found.
566 *
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{ 601{
602 plist_t* result = (plist_t*)user_data;
603
604 plist_t node = plist_dict_get_item(status, "LookupResult");
605 if (node) {
606 *result = plist_copy(node);
607 }
608}
609
610instproxy_error_t instproxy_lookup(instproxy_client_t client, const char** appids, plist_t client_options, plist_t *result)
611{
612 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
613 int i = 0;
614 plist_t lookup_result = NULL;
615 plist_t command = NULL;
616 plist_t appid_array = NULL;
617 plist_t node = NULL;
618
572 if (!client || !client->parent || !result) 619 if (!client || !client->parent || !result)
573 return INSTPROXY_E_INVALID_ARG; 620 return INSTPROXY_E_INVALID_ARG;
574 621
575 instproxy_lock(client); 622 command = plist_new_dict();
576 instproxy_error_t res = instproxy_send_command(client, "LookupArchives", client_options, NULL, NULL); 623 plist_dict_set_item(command, "Command", plist_new_string("Lookup"));
624 if (client_options) {
625 node = plist_copy(client_options);
626 } else if (appids) {
627 node = plist_new_dict();
628 }
577 629
578 if (res != INSTPROXY_E_SUCCESS) { 630 /* add bundle identifiers to client options */
579 debug_info("could not send plist, error %d", res); 631 if (appids) {
580 goto leave_unlock; 632 appid_array = plist_new_array();
633 while (appids[i]) {
634 plist_array_append_item(appid_array, plist_new_string(appids[i]));
635 i++;
636 }
637 plist_dict_set_item(node, "BundleIDs", appid_array);
581 } 638 }
582 639
583 res = instproxy_error(property_list_service_receive_plist(client->parent, result)); 640 if (node) {
584 if (res != INSTPROXY_E_SUCCESS) { 641 plist_dict_set_item(command, "ClientOptions", node);
585 debug_info("could not receive plist, error %d", res);
586 goto leave_unlock;
587 } 642 }
588 643
589 res = INSTPROXY_E_SUCCESS; 644 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result);
645
646 if (res == INSTPROXY_E_SUCCESS) {
647 *result = lookup_result;
648 } else {
649 plist_free(lookup_result);
650 }
651
652 plist_free(command);
653
654 return res;
655}
656
657instproxy_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)
658{
659 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
660
661 plist_t command = plist_new_dict();
662 plist_dict_set_item(command, "Command", plist_new_string("Install"));
663 if (client_options)
664 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
665 plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
666
667 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
668
669 plist_free(command);
670
671 return res;
672}
673
674instproxy_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)
675{
676 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
677
678 plist_t command = plist_new_dict();
679 plist_dict_set_item(command, "Command", plist_new_string("Upgrade"));
680 if (client_options)
681 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
682 plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path));
683
684 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
685
686 plist_free(command);
687
688 return res;
689}
690
691instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
692{
693 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
694
695 plist_t command = plist_new_dict();
696 plist_dict_set_item(command, "Command", plist_new_string("Uninstall"));
697 if (client_options)
698 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
699 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
700
701 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
702
703 plist_free(command);
704
705 return res;
706}
707
708instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result)
709{
710 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
711
712 plist_t command = plist_new_dict();
713 plist_dict_set_item(command, "Command", plist_new_string("LookupArchives"));
714 if (client_options)
715 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
716
717 res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)result);
718
719 plist_free(command);
590 720
591leave_unlock:
592 instproxy_unlock(client);
593 return res; 721 return res;
594} 722}
595 723
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) 724instproxy_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{ 725{
622 if (!client || !client->parent || !appid) 726 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
727
728 plist_t command = plist_new_dict();
729 plist_dict_set_item(command, "Command", plist_new_string("Archive"));
730 if (client_options)
731 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
732 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
733
734 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
735
736 plist_free(command);
737
738 return res;
739}
740
741instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data)
742{
743 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
744
745 plist_t command = plist_new_dict();
746 plist_dict_set_item(command, "Command", plist_new_string("Restore"));
747 if (client_options)
748 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
749 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
750
751 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
752
753 plist_free(command);
754
755 return res;
756}
757
758instproxy_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)
759{
760 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
761
762 plist_t command = plist_new_dict();
763 plist_dict_set_item(command, "Command", plist_new_string("RemoveArchive"));
764 if (client_options)
765 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
766 plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid));
767
768 res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data);
769
770 plist_free(command);
771
772 return res;
773}
774
775instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, const char** capabilities, plist_t client_options, plist_t *result)
776{
777 if (!client || !capabilities || !result)
623 return INSTPROXY_E_INVALID_ARG; 778 return INSTPROXY_E_INVALID_ARG;
624 779
625 if (client->status_updater) { 780 plist_t lookup_result = NULL;
626 return INSTPROXY_E_OP_IN_PROGRESS; 781
782 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
783
784 plist_t command = plist_new_dict();
785 plist_dict_set_item(command, "Command", plist_new_string("CheckCapabilitiesMatch"));
786 if (client_options)
787 plist_dict_set_item(command, "ClientOptions", plist_copy(client_options));
788
789 if (capabilities) {
790 int i = 0;
791 plist_t capabilities_array = plist_new_array();
792 while (capabilities[i]) {
793 plist_array_append_item(capabilities_array, plist_new_string(capabilities[i]));
794 i++;
795 }
796 plist_dict_set_item(command, "Capabilities", capabilities_array);
627 } 797 }
628 798
629 instproxy_lock(client); 799 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 800
633 if (res != INSTPROXY_E_SUCCESS) { 801 if (res == INSTPROXY_E_SUCCESS) {
634 debug_info("could not send plist, error %d", res); 802 *result = lookup_result;
635 return res; 803 } else {
804 plist_free(lookup_result);
636 } 805 }
637 return instproxy_create_status_updater(client, status_cb, "Archive", user_data); 806
807 plist_free(command);
808
809 return res;
638} 810}
639 811
640/** 812instproxy_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{ 813{
663 if (!client || !client->parent || !appid) 814 instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR;
815
816 if (!status || !name)
664 return INSTPROXY_E_INVALID_ARG; 817 return INSTPROXY_E_INVALID_ARG;
665 818
666 if (client->status_updater) { 819 plist_t node = plist_dict_get_item(status, "Error");
667 return INSTPROXY_E_OP_IN_PROGRESS; 820 if (node) {
821 plist_get_string_val(node, name);
822 } else {
823 /* no error here */
824 res = INSTPROXY_E_SUCCESS;
668 } 825 }
669 826
670 instproxy_lock(client); 827 if (code != NULL) {
671 instproxy_error_t res = instproxy_send_command(client, "Restore", client_options, appid, NULL); 828 *code = 0;
672 instproxy_unlock(client); 829 node = plist_dict_get_item(status, "ErrorDetail");
830 if (node) {
831 plist_get_uint_val(node, code);
832 *code &= 0xffffffff;
833 }
834 }
673 835
674 if (res != INSTPROXY_E_SUCCESS) { 836 if (description != NULL) {
675 debug_info("could not send plist, error %d", res); 837 node = plist_dict_get_item(status, "ErrorDescription");
676 return res; 838 if (node) {
839 plist_get_string_val(node, description);
840 }
841 }
842
843 if (*name) {
844 res = instproxy_strtoerr(*name);
677 } 845 }
678 return instproxy_create_status_updater(client, status_cb, "Restore", user_data); 846
847 return res;
679} 848}
680 849
681/** 850void 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{ 851{
704 if (!client || !client->parent || !appid) 852 if (name) {
705 return INSTPROXY_E_INVALID_ARG; 853 plist_t node = plist_dict_get_item(status, "Status");
854 if (node) {
855 plist_get_string_val(node, name);
856 } else {
857 *name = NULL;
858 }
859 }
860}
706 861
707 if (client->status_updater) { 862void instproxy_status_get_percent_complete(plist_t status, int *percent)
708 return INSTPROXY_E_OP_IN_PROGRESS; 863{
864 uint64_t val = 0;
865 if (percent) {
866 plist_t node = plist_dict_get_item(status, "PercentComplete");
867 if (node) {
868 plist_get_uint_val(node, &val);
869 *percent = val;
870 }
709 } 871 }
872}
710 873
711 instproxy_lock(client); 874void 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); 875{
713 instproxy_unlock(client); 876 plist_t node = NULL;
877
878 if (status && plist_get_node_type(status) == PLIST_DICT) {
879 /* command specific logic: parse browsed list */
880 if (list != NULL) {
881 node = plist_dict_get_item(status, "CurrentList");
882 if (node) {
883 *current_amount = plist_array_get_size(node);
884 *list = plist_copy(node);
885 }
886 }
714 887
715 if (res != INSTPROXY_E_SUCCESS) { 888 if (total != NULL) {
716 debug_info("could not send plist, error %d", res); 889 node = plist_dict_get_item(status, "Total");
717 return res; 890 if (node) {
891 plist_get_uint_val(node, total);
892 }
893 }
894
895 if (current_amount != NULL) {
896 node = plist_dict_get_item(status, "CurrentAmount");
897 if (node) {
898 plist_get_uint_val(node, current_amount);
899 }
900 }
901
902 if (current_index != NULL) {
903 node = plist_dict_get_item(status, "CurrentIndex");
904 if (node) {
905 plist_get_uint_val(node, current_index);
906 }
907 }
718 } 908 }
719 return instproxy_create_status_updater(client, status_cb, "RemoveArchive", user_data);
720} 909}
721 910
722/** 911void instproxy_command_get_name(plist_t command, char** name)
723 * Create a new client_options plist. 912{
724 * 913 if (name) {
725 * @return A new plist_t of type PLIST_DICT. 914 plist_t node = plist_dict_get_item(command, "Command");
726 */ 915 if (node) {
727plist_t instproxy_client_options_new() 916 plist_get_string_val(node, name);
917 } else {
918 *name = NULL;
919 }
920 }
921}
922
923plist_t instproxy_client_options_new(void)
728{ 924{
729 return plist_new_dict(); 925 return plist_new_dict();
730} 926}
731 927
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, ...) 928void instproxy_client_options_add(plist_t client_options, ...)
743{ 929{
744 if (!client_options) 930 if (!client_options)
745 return; 931 return;
932
746 va_list args; 933 va_list args;
747 va_start(args, client_options); 934 va_start(args, client_options);
748 char *arg = va_arg(args, char*); 935 char *arg = va_arg(args, char*);
@@ -750,21 +937,21 @@ void instproxy_client_options_add(plist_t client_options, ...)
750 char *key = strdup(arg); 937 char *key = strdup(arg);
751 if (!strcmp(key, "SkipUninstall")) { 938 if (!strcmp(key, "SkipUninstall")) {
752 int intval = va_arg(args, int); 939 int intval = va_arg(args, int);
753 plist_dict_insert_item(client_options, key, plist_new_bool(intval)); 940 plist_dict_set_item(client_options, key, plist_new_bool(intval));
754 } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata")) { 941 } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata") || !strcmp(key, "ReturnAttributes") || !strcmp(key, "BundleIDs")) {
755 plist_t plistval = va_arg(args, plist_t); 942 plist_t plistval = va_arg(args, plist_t);
756 if (!plistval) { 943 if (!plistval) {
757 free(key); 944 free(key);
758 break; 945 break;
759 } 946 }
760 plist_dict_insert_item(client_options, key, plist_copy(plistval)); 947 plist_dict_set_item(client_options, key, plist_copy(plistval));
761 } else { 948 } else {
762 char *strval = va_arg(args, char*); 949 char *strval = va_arg(args, char*);
763 if (!strval) { 950 if (!strval) {
764 free(key); 951 free(key);
765 break; 952 break;
766 } 953 }
767 plist_dict_insert_item(client_options, key, plist_new_string(strval)); 954 plist_dict_set_item(client_options, key, plist_new_string(strval));
768 } 955 }
769 free(key); 956 free(key);
770 arg = va_arg(args, char*); 957 arg = va_arg(args, char*);
@@ -772,15 +959,106 @@ void instproxy_client_options_add(plist_t client_options, ...)
772 va_end(args); 959 va_end(args);
773} 960}
774 961
775/** 962void instproxy_client_options_set_return_attributes(plist_t client_options, ...)
776 * Free client_options plist. 963{
777 * 964 if (!client_options)
778 * @param client_options The client options plist to free. Does nothing if NULL 965 return;
779 * is passed. 966
780 */ 967 plist_t return_attributes = plist_new_array();
968
969 va_list args;
970 va_start(args, client_options);
971 char *arg = va_arg(args, char*);
972 while (arg) {
973 char *attribute = strdup(arg);
974 plist_array_append_item(return_attributes, plist_new_string(attribute));
975 free(attribute);
976 arg = va_arg(args, char*);
977 }
978 va_end(args);
979
980 plist_dict_set_item(client_options, "ReturnAttributes", return_attributes);
981}
982
781void instproxy_client_options_free(plist_t client_options) 983void instproxy_client_options_free(plist_t client_options)
782{ 984{
783 if (client_options) { 985 if (client_options) {
784 plist_free(client_options); 986 plist_free(client_options);
785 } 987 }
786} 988}
989
990instproxy_error_t instproxy_client_get_path_for_bundle_identifier(instproxy_client_t client, const char* bundle_id, char** path)
991{
992 if (!client || !client->parent || !bundle_id)
993 return INSTPROXY_E_INVALID_ARG;
994
995 plist_t apps = NULL;
996
997 // create client options for any application types
998 plist_t client_opts = instproxy_client_options_new();
999 instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL);
1000
1001 // only return attributes we need
1002 instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "CFBundleExecutable", "Path", NULL);
1003
1004 // only query for specific appid
1005 const char* appids[] = {bundle_id, NULL};
1006
1007 // query device for list of apps
1008 instproxy_error_t ierr = instproxy_lookup(client, appids, client_opts, &apps);
1009
1010 instproxy_client_options_free(client_opts);
1011
1012 if (ierr != INSTPROXY_E_SUCCESS) {
1013 return ierr;
1014 }
1015
1016 plist_t app_found = plist_access_path(apps, 1, bundle_id);
1017 if (!app_found) {
1018 if (apps)
1019 plist_free(apps);
1020 *path = NULL;
1021 return INSTPROXY_E_OP_FAILED;
1022 }
1023
1024 char* path_str = NULL;
1025 plist_t path_p = plist_dict_get_item(app_found, "Path");
1026 if (path_p) {
1027 plist_get_string_val(path_p, &path_str);
1028 }
1029
1030 char* exec_str = NULL;
1031 plist_t exec_p = plist_dict_get_item(app_found, "CFBundleExecutable");
1032 if (exec_p) {
1033 plist_get_string_val(exec_p, &exec_str);
1034 }
1035
1036 if (!path_str) {
1037 debug_info("app path not found");
1038 return INSTPROXY_E_OP_FAILED;
1039 }
1040
1041 if (!exec_str) {
1042 debug_info("bundle executable not found");
1043 return INSTPROXY_E_OP_FAILED;
1044 }
1045
1046 plist_free(apps);
1047
1048 char* ret = (char*)malloc(strlen(path_str) + 1 + strlen(exec_str) + 1);
1049 strcpy(ret, path_str);
1050 strcat(ret, "/");
1051 strcat(ret, exec_str);
1052
1053 *path = ret;
1054
1055 if (path_str) {
1056 free(path_str);
1057 }
1058
1059 if (exec_str) {
1060 free(exec_str);
1061 }
1062
1063 return INSTPROXY_E_SUCCESS;
1064}
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..c457cb2
--- /dev/null
+++ b/src/lockdown-cu.c
@@ -0,0 +1,1197 @@
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
33#ifndef _MSC_VER
34#include <unistd.h>
35#endif
36
37#include <plist/plist.h>
38
39#include "idevice.h"
40#include "lockdown.h"
41#include "common/debug.h"
42
43#ifdef HAVE_WIRELESS_PAIRING
44
45#include <libimobiledevice-glue/utils.h>
46#include <libimobiledevice-glue/socket.h>
47#include <libimobiledevice-glue/opack.h>
48#include <libimobiledevice-glue/tlv.h>
49
50#if defined(HAVE_OPENSSL)
51#include <openssl/hmac.h>
52#include <openssl/evp.h>
53#include <openssl/rand.h>
54#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL)
55#include <openssl/chacha.h>
56#include <openssl/poly1305.h>
57#endif
58#elif defined(HAVE_GCRYPT)
59#include <gcrypt.h>
60#elif defined(HAVE_MBEDTLS)
61#include <mbedtls/md.h>
62#include <mbedtls/chachapoly.h>
63#endif
64
65#ifdef __APPLE__
66#include <sys/sysctl.h>
67#include <SystemConfiguration/SystemConfiguration.h>
68#include <CoreFoundation/CoreFoundation.h>
69#include <TargetConditionals.h>
70#endif
71
72#include "property_list_service.h"
73#include "common/userpref.h"
74
75#include "endianness.h"
76
77#include "srp.h"
78#include "ed25519.h"
79
80/* {{{ SRP6a parameters */
81static const unsigned char kSRPModulus3072[384] = {
82 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
83 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
84 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
85 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
86 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
87 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
88 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6,
89 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05,
90 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f,
91 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb,
92 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04,
93 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b,
94 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f,
95 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18,
96 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10,
97 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33,
98 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a,
99 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7,
100 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d,
101 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64,
102 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c,
103 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2,
104 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e,
105 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
106};
107
108static const unsigned char kSRPGenerator5 = 5;
109/* }}} */
110
111/* {{{ HKDF */
112#if defined(HAVE_OPENSSL)
113#define MD_ALGO_SHA512 EVP_sha512()
114typedef const EVP_MD* MD_ALGO_TYPE_T;
115#define MD_ALGO_DIGEST_SIZE EVP_MD_size
116#define MD_MAX_DIGEST_SIZE EVP_MAX_MD_SIZE
117
118#elif defined(HAVE_GCRYPT)
119#define MD_ALGO_SHA512 GCRY_MD_SHA512
120typedef int MD_ALGO_TYPE_T;
121#define MD_ALGO_DIGEST_SIZE gcry_md_get_algo_dlen
122#define MD_MAX_DIGEST_SIZE 64
123
124static 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)
125{
126 gcry_md_hd_t hd;
127 if (gcry_md_open(&hd, md, GCRY_MD_FLAG_HMAC)) {
128 debug_info("gcry_md_open() failed");
129 return;
130 }
131 if (gcry_md_setkey(hd, key, key_len)) {
132 gcry_md_close (hd);
133 debug_info("gcry_md_setkey() failed");
134 return;
135 }
136 gcry_md_write(hd, data, data_len);
137
138 unsigned char* digest = gcry_md_read(hd, md);
139 if (!digest) {
140 gcry_md_close(hd);
141 debug_info("gcry_md_read() failed");
142 return;
143 }
144
145 *out_len = gcry_md_get_algo_dlen(md);
146 memcpy(out, digest, *out_len);
147 gcry_md_close(hd);
148}
149#elif defined(HAVE_MBEDTLS)
150#define MD_ALGO_SHA512 MBEDTLS_MD_SHA512
151typedef mbedtls_md_type_t MD_ALGO_TYPE_T;
152#define MD_ALGO_DIGEST_SIZE(x) mbedtls_md_get_size(mbedtls_md_info_from_type(x))
153#define MD_MAX_DIGEST_SIZE MBEDTLS_MD_MAX_SIZE
154
155static 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)
156{
157 mbedtls_md_context_t mdctx;
158 mbedtls_md_init(&mdctx);
159 int mr = mbedtls_md_setup(&mdctx, mbedtls_md_info_from_type(md), 1);
160 if (mr != 0) {
161 debug_info("mbedtls_md_setup() failed: %d", mr);
162 return;
163 }
164
165 mr = mbedtls_md_hmac_starts(&mdctx, key, key_len);
166 if (mr != 0) {
167 mbedtls_md_free(&mdctx);
168 debug_info("mbedtls_md_hmac_starts() failed: %d", mr);
169 return;
170 }
171
172 mbedtls_md_hmac_update(&mdctx, data, data_len);
173
174 mr = mbedtls_md_hmac_finish(&mdctx, out);
175 if (mr == 0) {
176 *out_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md));
177 } else {
178 debug_info("mbedtls_md_hmac_finish() failed: %d", mr);
179 }
180 mbedtls_md_free(&mdctx);
181}
182#endif
183
184static 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)
185{
186 unsigned char empty_salt[MD_MAX_DIGEST_SIZE];
187 if (!md || !out || !out_len || !*out_len) return;
188 if (salt_len == 0) {
189 salt_len = MD_ALGO_DIGEST_SIZE(md);
190 salt = (unsigned char*)empty_salt;
191 }
192 HMAC(md, salt, salt_len, input_key_material, input_key_material_len, out, out_len);
193}
194
195static 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)
196{
197 if (!md || !out || !out_len || !*out_len) return;
198 unsigned int md_size = MD_ALGO_DIGEST_SIZE(md);
199 if (*out_len > 255 * md_size) {
200 *out_len = 0;
201 return;
202 }
203 int blocks_needed = (*out_len) / md_size;
204 if (((*out_len) % md_size) != 0) blocks_needed++;
205 unsigned int okm_len = 0;
206 unsigned char okm_block[MD_MAX_DIGEST_SIZE];
207 unsigned int okm_block_len = 0;
208 int i;
209 for (i = 0; i < blocks_needed; i++) {
210 unsigned int output_block_len = okm_block_len + info_len + 1;
211 unsigned char* output_block = malloc(output_block_len);
212 if (okm_block_len > 0) {
213 memcpy(output_block, okm_block, okm_block_len);
214 }
215 memcpy(output_block + okm_block_len, info, info_len);
216 output_block[okm_block_len + info_len] = (uint8_t)(i+1);
217
218 HMAC(md, prk, prk_len, output_block, output_block_len, okm_block, &okm_block_len);
219 if (okm_len < *out_len) {
220 memcpy(out + okm_len, okm_block, (okm_len + okm_block_len > *out_len) ? *out_len - okm_len : okm_block_len);
221 }
222 okm_len += okm_block_len;
223 free(output_block);
224 }
225}
226
227static 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)
228{
229 if (!md || !initial_key_material || !out || !out_len || !*out_len) return;
230
231 unsigned char prk[MD_MAX_DIGEST_SIZE];
232 unsigned int prk_len = MD_ALGO_DIGEST_SIZE(md);
233
234 hkdf_md_extract(md, salt, salt_len, initial_key_material, initial_key_material_size, prk, &prk_len);
235 if (prk_len > 0) {
236 hkdf_md_expand(md, prk, prk_len, info, info_len, out, out_len);
237 } else {
238 *out_len = 0;
239 }
240}
241/* }}} */
242
243/* {{{ chacha20 poly1305 encryption/decryption */
244#if defined(HAVE_OPENSSL) && defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL)
245/* {{{ From: OpenBSD's e_chacha20poly1305.c */
246/*
247 * Copyright (c) 2015 Reyk Floter <reyk@openbsd.org>
248 * Copyright (c) 2014, Google Inc.
249 *
250 * Permission to use, copy, modify, and/or distribute this software for any
251 * purpose with or without fee is hereby granted, provided that the above
252 * copyright notice and this permission notice appear in all copies.
253 *
254 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
255 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
256 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
257 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
258 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
259 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
260 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
261 */
262static void
263poly1305_update_with_length(poly1305_state *poly1305,
264 const unsigned char *data, size_t data_len)
265{
266 size_t j = data_len;
267 unsigned char length_bytes[8];
268 unsigned i;
269
270 for (i = 0; i < sizeof(length_bytes); i++) {
271 length_bytes[i] = j;
272 j >>= 8;
273 }
274
275 if (data != NULL)
276 CRYPTO_poly1305_update(poly1305, data, data_len);
277 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes));
278}
279
280static void
281poly1305_update_with_pad16(poly1305_state *poly1305,
282 const unsigned char *data, size_t data_len)
283{
284 static const unsigned char zero_pad16[16];
285 size_t pad_len;
286
287 CRYPTO_poly1305_update(poly1305, data, data_len);
288
289 /* pad16() is defined in RFC 7539 2.8.1. */
290 if ((pad_len = data_len % 16) == 0)
291 return;
292
293 CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len);
294}
295/* }}} */
296#endif
297
298static 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)
299{
300#if defined(HAVE_OPENSSL)
301#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL)
302#if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL)
303 const EVP_AEAD *aead = EVP_aead_chacha20_poly1305();
304 EVP_AEAD_CTX ctx;
305 EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL);
306 EVP_AEAD_CTX_seal(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len);
307#else
308 unsigned char poly1305_key[32];
309 poly1305_state poly1305;
310 uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32;
311 const unsigned char* iv = nonce + 4;
312
313 memset(poly1305_key, 0, sizeof(poly1305_key));
314 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr);
315
316 CRYPTO_poly1305_init(&poly1305, poly1305_key);
317 poly1305_update_with_pad16(&poly1305, ad, ad_len);
318 CRYPTO_chacha_20(out, in, in_len, key, iv, ctr + 1);
319 poly1305_update_with_pad16(&poly1305, out, in_len);
320 poly1305_update_with_length(&poly1305, NULL, ad_len);
321 poly1305_update_with_length(&poly1305, NULL, in_len);
322
323 CRYPTO_poly1305_finish(&poly1305, out + in_len);
324
325 *out_len = in_len + 16;
326#endif
327#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
328 int outl = 0;
329 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
330 EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce);
331 EVP_EncryptUpdate(ctx, out, &outl, in, in_len);
332 *out_len = outl;
333 outl = 0;
334 EVP_EncryptFinal_ex(ctx, out + *out_len, &outl);
335 *out_len += outl;
336 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, out + *out_len);
337 EVP_CIPHER_CTX_free(ctx);
338 *out_len += 16;
339#else
340#error Please use a newer version of OpenSSL (>= 1.1.0)
341#endif
342#elif defined(HAVE_GCRYPT)
343#if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700)
344 gcry_cipher_hd_t hd;
345 if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) {
346 debug_info("gcry_cipher_open() failed");
347 return;
348 }
349 gcry_cipher_setkey(hd, key, 32);
350 gcry_cipher_setiv(hd, nonce, 12);
351 gcry_cipher_authenticate(hd, ad, ad_len);
352 *out_len = in_len + 16;
353 if (gcry_cipher_encrypt(hd, out, *out_len, in, in_len)) {
354 *out_len = 0;
355 }
356 gcry_cipher_gettag(hd, out+in_len, 16);
357 gcry_cipher_close(hd);
358#else
359#error Please use a newer version of libgcrypt (>= 1.7.0)
360#endif
361#elif defined (HAVE_MBEDTLS)
362 mbedtls_chachapoly_context ctx;
363 mbedtls_chachapoly_init(&ctx);
364 mbedtls_chachapoly_setkey(&ctx, key);
365 if (mbedtls_chachapoly_encrypt_and_tag(&ctx, in_len, nonce, ad, ad_len, in, out, out+in_len) != 0) {
366 *out_len = 0;
367 }
368 mbedtls_chachapoly_free(&ctx);
369#else
370#error chacha20_poly1305_encrypt_96 is not implemented
371#endif
372}
373
374static 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)
375{
376 unsigned char _nonce[12];
377 *(uint32_t*)(&_nonce[0]) = 0;
378 memcpy(&_nonce[4], nonce, 8);
379 chacha20_poly1305_encrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len);
380}
381
382static 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)
383{
384#if defined(HAVE_OPENSSL)
385#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL)
386#if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL)
387 const EVP_AEAD *aead = EVP_aead_chacha20_poly1305();
388 EVP_AEAD_CTX ctx;
389 EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL);
390 EVP_AEAD_CTX_open(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len);
391#else
392 unsigned char mac[16];
393 unsigned char poly1305_key[32];
394 poly1305_state poly1305;
395 size_t plaintext_len = in_len - 16;
396 uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32;
397 const unsigned char *iv = nonce + 4;
398
399 memset(poly1305_key, 0, sizeof(poly1305_key));
400 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr);
401
402 CRYPTO_poly1305_init(&poly1305, poly1305_key);
403 poly1305_update_with_pad16(&poly1305, ad, ad_len);
404 poly1305_update_with_pad16(&poly1305, in, plaintext_len);
405 poly1305_update_with_length(&poly1305, NULL, ad_len);
406 poly1305_update_with_length(&poly1305, NULL, plaintext_len);
407
408 CRYPTO_poly1305_finish(&poly1305, mac);
409
410 if (memcmp(mac, in + plaintext_len, 16) != 0) {
411 *out_len = 0;
412 return;
413 }
414
415 CRYPTO_chacha_20(out, in, plaintext_len, key, iv, ctr + 1);
416 *out_len = plaintext_len;
417#endif
418#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
419 int outl = 0;
420 size_t plaintext_len = in_len - 16;
421 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
422 EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce);
423 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, in + plaintext_len);
424 EVP_DecryptUpdate(ctx, out, &outl, in, plaintext_len);
425 *out_len = outl;
426 outl = 0;
427 if (EVP_DecryptFinal_ex(ctx, out + *out_len, &outl) == 1) {
428 *out_len += outl;
429 } else {
430 *out_len = 0;
431 }
432 EVP_CIPHER_CTX_free(ctx);
433#else
434#error Please use a newer version of OpenSSL (>= 1.1.0)
435#endif
436#elif defined(HAVE_GCRYPT)
437#if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700)
438 gcry_cipher_hd_t hd;
439 if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) {
440 debug_info("gcry_cipher_open() failed");
441 return;
442 }
443 gcry_cipher_setkey(hd, key, 32);
444 gcry_cipher_setiv(hd, nonce, 12);
445 gcry_cipher_authenticate(hd, ad, ad_len);
446 unsigned int plaintext_len = in_len - 16;
447 gcry_cipher_decrypt(hd, out, *out_len, in, plaintext_len);
448 if (gcry_cipher_checktag(hd, in + plaintext_len, 16) == 0) {
449 *out_len = plaintext_len;
450 } else {
451 *out_len = 0;
452 }
453 gcry_cipher_close(hd);
454#else
455#error Please use a newer version of libgcrypt (>= 1.7.0)
456#endif
457#elif defined(HAVE_MBEDTLS)
458 mbedtls_chachapoly_context ctx;
459 mbedtls_chachapoly_init(&ctx);
460 mbedtls_chachapoly_setkey(&ctx, key);
461 unsigned int plaintext_len = in_len - 16;
462 if (mbedtls_chachapoly_auth_decrypt(&ctx, plaintext_len, nonce, ad, ad_len, in + plaintext_len, in, out) == 0) {
463 *out_len = plaintext_len;
464 } else {
465 *out_len = 0;
466 }
467 mbedtls_chachapoly_free(&ctx);
468#else
469#error chacha20_poly1305_decrypt_96 is not implemented
470#endif
471}
472
473static 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)
474{
475 unsigned char _nonce[12];
476 *(uint32_t*)(&_nonce[0]) = 0;
477 memcpy(&_nonce[4], nonce, 8);
478 chacha20_poly1305_decrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len);
479}
480/* }}} */
481
482#define PAIRING_ERROR(x) \
483 debug_info(x); \
484 if (pairing_callback) { \
485 pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, (char*)x, NULL); \
486 }
487
488#define PAIRING_ERROR_FMT(...) \
489 sprintf(tmp, __VA_ARGS__); \
490 debug_info(tmp); \
491 if (pairing_callback) { \
492 pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, tmp, NULL); \
493 }
494
495#endif /* HAVE_WIRELESS_PAIRING */
496
497lockdownd_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)
498{
499#ifdef HAVE_WIRELESS_PAIRING
500 if (!client || !pairing_callback || (host_info && plist_get_node_type(host_info) != PLIST_DICT) || (acl && plist_get_node_type(acl) != PLIST_DICT))
501 return LOCKDOWN_E_INVALID_ARG;
502
503 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
504
505 if (client->device && client->device->version == 0) {
506 plist_t p_version = NULL;
507 if (lockdownd_get_value(client, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) {
508 int vers[3] = {0, 0, 0};
509 char *s_version = NULL;
510 plist_get_string_val(p_version, &s_version);
511 if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
512 client->device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]);
513 }
514 free(s_version);
515 }
516 plist_free(p_version);
517 }
518
519 char* pairing_uuid = NULL;
520 if (host_info) {
521 plist_t accountid = plist_dict_get_item(host_info, "accountID");
522 if (accountid && plist_get_node_type(accountid) == PLIST_STRING) {
523 plist_get_string_val(accountid, &pairing_uuid);
524 }
525 }
526 if (!pairing_uuid) {
527 userpref_read_system_buid(&pairing_uuid);
528 }
529 if (!pairing_uuid) {
530 pairing_uuid = generate_uuid();
531 }
532 unsigned int pairing_uuid_len = strlen(pairing_uuid);
533
534 SRP_initialize_library();
535
536 SRP* srp = SRP_new(SRP6a_sha512_client_method());
537 if (!srp) {
538 PAIRING_ERROR("Failed to initialize SRP")
539 return LOCKDOWN_E_UNKNOWN_ERROR;
540 }
541
542 char tmp[256];
543 plist_t dict = NULL;
544 uint8_t current_state = 0;
545 uint8_t final_state = 6;
546
547 unsigned char* salt = NULL;
548 unsigned int salt_size = 0;
549 unsigned char* pubkey = NULL;
550 unsigned int pubkey_size = 0;
551
552 unsigned char setup_encryption_key[32];
553
554 cstr *thekey = NULL;
555
556 do {
557 current_state++;
558
559 dict = plist_new_dict();
560 plist_dict_set_item(dict, "Request", plist_new_string("CUPairingCreate"));
561 if (current_state == 1) {
562 plist_dict_set_item(dict, "Flags", plist_new_uint(1));
563 } else {
564 plist_dict_set_item(dict, "Flags", plist_new_uint(0));
565 }
566
567 tlv_buf_t tlv = tlv_buf_new();
568
569 if (current_state == 1) {
570 /* send method */
571 tlv_buf_append(tlv, 0x00, 1, (void*)"\x00"); // 0x00 (Method), 1 bytes, 00
572 } else if (current_state == 3) {
573 /* generate public key */
574 cstr* own_pub = NULL;
575 SRP_gen_pub(srp, &own_pub);
576
577 if (!own_pub) {
578 PAIRING_ERROR("[SRP] Failed to generate public key")
579 ret = LOCKDOWN_E_PAIRING_FAILED;
580 break;
581 }
582
583 /* compute key from remote's public key */
584 if (SRP_compute_key(srp, &thekey, pubkey, pubkey_size) != 0) {
585 cstr_free(own_pub);
586 PAIRING_ERROR("[SRP] Failed to compute key")
587 ret = LOCKDOWN_E_PAIRING_FAILED;
588 break;
589 }
590
591 /* compute response */
592 cstr *response = NULL;
593 SRP_respond(srp, &response);
594
595 /* send our public key + response */
596 tlv_buf_append(tlv, 0x03, own_pub->length, own_pub->data);
597 tlv_buf_append(tlv, 0x04, response->length, response->data);
598 cstr_free(response);
599 cstr_free(own_pub);
600 } else if (current_state == 5) {
601 /* send encrypted info */
602
603 static const char PAIR_SETUP_ENCRYPT_SALT[] = "Pair-Setup-Encrypt-Salt";
604 static const char PAIR_SETUP_ENCRYPT_INFO[] = "Pair-Setup-Encrypt-Info";
605 static const char PAIR_SETUP_CONTROLLER_SIGN_SALT[] = "Pair-Setup-Controller-Sign-Salt";
606 static const char PAIR_SETUP_CONTROLLER_SIGN_INFO[] = "Pair-Setup-Controller-Sign-Info";
607
608 // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Encrypt-Salt + Pair-Setup-Encrypt-Info
609 // result used as key for chacha20-poly1305
610 unsigned int setup_encryption_key_len = sizeof(setup_encryption_key);
611 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);
612
613 unsigned char ed25519_pubkey[32];
614 unsigned char ed25519_privkey[64];
615 unsigned char ed25519seed[32];
616 ed25519_create_seed(ed25519seed);
617
618 ed25519_create_keypair(ed25519_pubkey, ed25519_privkey, ed25519seed);
619
620 unsigned int signbuf_len = pairing_uuid_len + 64;
621 unsigned char* signbuf = malloc(signbuf_len);
622 unsigned int hkdf_len = 32;
623 // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Controller-Sign-Salt + Pair-Setup-Controller-Sign-Info
624 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);
625
626 memcpy(signbuf + 32, pairing_uuid, pairing_uuid_len);
627 memcpy(signbuf + 32 + pairing_uuid_len, ed25519_pubkey, 32);
628
629 unsigned char ed_sig[64];
630 ed25519_sign(ed_sig, signbuf, 0x64, ed25519_pubkey, ed25519_privkey);
631
632 tlv_buf_t tlvbuf = tlv_buf_new();
633 tlv_buf_append(tlvbuf, 0x01, pairing_uuid_len, (void*)pairing_uuid);
634 tlv_buf_append(tlvbuf, 0x03, sizeof(ed25519_pubkey), ed25519_pubkey);
635 tlv_buf_append(tlvbuf, 0x0a, sizeof(ed_sig), ed_sig);
636
637 /* ACL */
638 unsigned char* odata = NULL;
639 unsigned int olen = 0;
640 if (acl) {
641 opack_encode_from_plist(acl, &odata, &olen);
642 } else {
643 /* defaut ACL */
644 plist_t acl_plist = plist_new_dict();
645 plist_dict_set_item(acl_plist, "com.apple.ScreenCapture", plist_new_bool(1));
646 plist_dict_set_item(acl_plist, "com.apple.developer", plist_new_bool(1));
647 opack_encode_from_plist(acl_plist, &odata, &olen);
648 plist_free(acl_plist);
649 }
650 tlv_buf_append(tlvbuf, 0x12, olen, odata);
651 free(odata);
652
653 /* HOST INFORMATION */
654 char hostname[256];
655#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE)
656 CFStringRef cname = SCDynamicStoreCopyComputerName(NULL, NULL);
657 CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8);
658 CFRelease(cname);
659#else
660#ifdef _WIN32
661 DWORD hostname_len = sizeof(hostname);
662 GetComputerName(hostname, &hostname_len);
663#else
664 gethostname(hostname, sizeof(hostname));
665#endif
666#endif
667
668 char modelname[256];
669 modelname[0] = '\0';
670#ifdef __APPLE__
671 size_t len = sizeof(modelname);
672 sysctlbyname("hw.model", &modelname, &len, NULL, 0);
673#endif
674 if (strlen(modelname) == 0) {
675 strcpy(modelname, "HackbookPro13,37");
676 }
677
678 unsigned char primary_mac_addr[6] = { 0, 0, 0, 0, 0, 0 };
679 if (get_primary_mac_address(primary_mac_addr) != 0) {
680 debug_info("Failed to get primary mac address");
681 }
682 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]);
683
684 // "OPACK" encoded device info
685 plist_t info_plist = plist_new_dict();
686 //plist_dict_set_item(info_plist, "altIRK", plist_new_data((char*)altIRK, 16));
687 plist_dict_set_item(info_plist, "accountID", plist_new_string(pairing_uuid));
688 plist_dict_set_item(info_plist, "model", plist_new_string(modelname));
689 plist_dict_set_item(info_plist, "name", plist_new_string(hostname));
690 plist_dict_set_item(info_plist, "mac", plist_new_data((char*)primary_mac_addr, 6));
691 if (host_info) {
692 plist_dict_merge(&info_plist, host_info);
693 }
694 opack_encode_from_plist(info_plist, &odata, &olen);
695 plist_free(info_plist);
696 tlv_buf_append(tlvbuf, 0x11, olen, odata);
697 free(odata);
698
699 size_t encrypted_len = tlvbuf->length + 16;
700 unsigned char* encrypted_buf = (unsigned char*)malloc(encrypted_len);
701
702 chacha20_poly1305_encrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg05", NULL, 0, tlvbuf->data, tlvbuf->length, encrypted_buf, &encrypted_len);
703
704 tlv_buf_free(tlvbuf);
705
706 tlv_buf_append(tlv, 0x05, encrypted_len, encrypted_buf);
707 free(encrypted_buf);
708 } else {
709 tlv_buf_free(tlv);
710 PAIRING_ERROR("[SRP] Invalid state");
711 ret = LOCKDOWN_E_PAIRING_FAILED;
712 break;
713 }
714 tlv_buf_append(tlv, 0x06, 1, &current_state);
715 plist_dict_set_item(dict, "Payload", plist_new_data((char*)tlv->data, tlv->length));
716 tlv_buf_free(tlv);
717
718 plist_dict_set_item(dict, "Label", plist_new_string(client->label));
719 plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2));
720
721 ret = lockdownd_send(client, dict);
722 plist_free(dict);
723 dict = NULL;
724
725 if (ret != LOCKDOWN_E_SUCCESS) {
726 break;
727 }
728
729 current_state++;
730
731 ret = lockdownd_receive(client, &dict);
732 if (ret != LOCKDOWN_E_SUCCESS) {
733 break;
734 }
735 ret = lockdown_check_result(dict, "CUPairingCreate");
736 if (ret != LOCKDOWN_E_SUCCESS) {
737 break;
738 }
739
740 plist_t extresp = plist_dict_get_item(dict, "ExtendedResponse");
741 if (!extresp) {
742 ret = LOCKDOWN_E_PLIST_ERROR;
743 break;
744 }
745 plist_t blob = plist_dict_get_item(extresp, "Payload");
746 if (!blob) {
747 ret = LOCKDOWN_E_PLIST_ERROR;
748 break;
749 }
750 uint64_t data_len = 0;
751 const char* data = plist_get_data_ptr(blob, &data_len);
752
753 uint8_t state = 0;
754 if (!tlv_data_get_uint8(data, data_len, 0x06, &state)) {
755 PAIRING_ERROR("[SRP] ERROR: Could not find state in response");
756 ret = LOCKDOWN_E_PAIRING_FAILED;
757 break;
758 }
759 if (state != current_state) {
760 PAIRING_ERROR_FMT("[SRP] ERROR: Unexpected state %d, expected %d", state, current_state);
761 ret = LOCKDOWN_E_PAIRING_FAILED;
762 break;
763 }
764
765 unsigned int errval = 0;
766 uint64_t u64val = 0;
767 tlv_data_get_uint(data, data_len, 0x07, &u64val);
768debug_buffer(data, data_len);
769 errval = (unsigned int)u64val;
770 if (errval > 0) {
771 if (errval == 3) {
772 u64val = 0;
773 tlv_data_get_uint(data, data_len, 0x08, &u64val);
774 if (u64val > 0) {
775 uint32_t retry_delay = (uint32_t)u64val;
776 PAIRING_ERROR_FMT("[SRP] Pairing is blocked for another %u seconds", retry_delay)
777 ret = LOCKDOWN_E_PAIRING_FAILED;
778 break;
779 }
780 } else if (errval == 2 && state == 4) {
781 PAIRING_ERROR_FMT("[SRP] Invalid PIN")
782 ret = LOCKDOWN_E_PAIRING_FAILED;
783 break;
784 } else {
785 PAIRING_ERROR_FMT("[SRP] Received error %u in state %d.", errval, state);
786 ret = LOCKDOWN_E_PAIRING_FAILED;
787 break;
788 }
789 }
790
791 if (state == 2) {
792 /* receive salt and public key */
793 if (!tlv_data_copy_data(data, data_len, 0x02, (void**)&salt, &salt_size)) {
794 PAIRING_ERROR("[SRP] ERROR: Could not find salt in response");
795 ret = LOCKDOWN_E_PAIRING_FAILED;
796 break;
797 }
798 if (!tlv_data_copy_data(data, data_len, 0x03, (void**)&pubkey, &pubkey_size)) {
799 PAIRING_ERROR("[SRP] ERROR: Could not find public key in response");
800
801 ret = LOCKDOWN_E_PAIRING_FAILED;
802 break;
803 }
804
805 const char PAIR_SETUP[] = "Pair-Setup";
806 if (SRP_set_user_raw(srp, (const unsigned char*)PAIR_SETUP, sizeof(PAIR_SETUP)-1) != 0) {
807 PAIRING_ERROR("[SRP] Failed to set SRP user");
808 ret = LOCKDOWN_E_PAIRING_FAILED;
809 break;
810 }
811
812 /* kSRPParameters_3072_SHA512 */
813 if (SRP_set_params(srp, kSRPModulus3072, sizeof(kSRPModulus3072), &kSRPGenerator5, 1, salt, salt_size) != 0) {
814 PAIRING_ERROR("[SRP] Failed to set SRP parameters");
815 ret = LOCKDOWN_E_PAIRING_FAILED;
816 break;
817
818 }
819
820 if (pairing_callback) {
821 char pin[64];
822 unsigned int pin_len = sizeof(pin);
823 pairing_callback(LOCKDOWN_CU_PAIRING_PIN_REQUESTED, cb_user_data, pin, &pin_len);
824
825 SRP_set_auth_password_raw(srp, (const unsigned char*)pin, pin_len);
826 }
827 } else if (state == 4) {
828 /* receive proof */
829 unsigned char* proof = NULL;
830 unsigned int proof_len = 0;
831
832 if (!tlv_data_copy_data(data, data_len, 0x04, (void**)&proof, &proof_len)) {
833 PAIRING_ERROR("[SRP] ERROR: Could not find proof data in response");
834 ret = LOCKDOWN_E_PAIRING_FAILED;
835 break;
836 }
837
838 /* verify */
839 int vrfy_result = SRP_verify(srp, proof, proof_len);
840 free(proof);
841
842 if (vrfy_result == 0) {
843 debug_info("[SRP] PIN verified successfully");
844 } else {
845 PAIRING_ERROR("[SRP] PIN verification failure");
846 ret = LOCKDOWN_E_PAIRING_FAILED;
847 break;
848 }
849
850 } else if (state == 6) {
851 int srp_pair_success = 0;
852 plist_t node = plist_dict_get_item(extresp, "doSRPPair");
853 if (node) {
854 const char* strv = plist_get_string_ptr(node, NULL);
855 if (strcmp(strv, "succeed") == 0) {
856 srp_pair_success = 1;
857 }
858 }
859 if (!srp_pair_success) {
860 PAIRING_ERROR("SRP Pairing failed");
861 ret = LOCKDOWN_E_PAIRING_FAILED;
862 break;
863 }
864
865 /* receive encrypted info */
866 unsigned char* encrypted_buf = NULL;
867 unsigned int enc_len = 0;
868 if (!tlv_data_copy_data(data, data_len, 0x05, (void**)&encrypted_buf, &enc_len)) {
869 PAIRING_ERROR("[SRP] ERROR: Could not find encrypted data in response");
870 ret = LOCKDOWN_E_PAIRING_FAILED;
871 break;
872 }
873 size_t plain_len = enc_len-16;
874 unsigned char* plain_buf = malloc(plain_len);
875 chacha20_poly1305_decrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg06", NULL, 0, encrypted_buf, enc_len, plain_buf, &plain_len);
876 free(encrypted_buf);
877
878 unsigned char* dev_info = NULL;
879 unsigned int dev_info_len = 0;
880 int res = tlv_data_copy_data(plain_buf, plain_len, 0x11, (void**)&dev_info, &dev_info_len);
881 free(plain_buf);
882 if (!res) {
883 PAIRING_ERROR("[SRP] ERROR: Failed to locate device info in response");
884 ret = LOCKDOWN_E_PAIRING_FAILED;
885 break;
886 }
887 plist_t device_info = NULL;
888 opack_decode_to_plist(dev_info, dev_info_len, &device_info);
889 free(dev_info);
890
891 if (!device_info) {
892 PAIRING_ERROR("[SRP] ERROR: Failed to parse device info");
893 ret = LOCKDOWN_E_PAIRING_FAILED;
894 break;
895 }
896
897 if (pairing_callback) {
898 pairing_callback(LOCKDOWN_CU_PAIRING_DEVICE_INFO, cb_user_data, device_info, NULL);
899 }
900 plist_free(device_info);
901 } else {
902 PAIRING_ERROR("[SRP] ERROR: Invalid state");
903 ret = LOCKDOWN_E_PAIRING_FAILED;
904 break;
905 }
906 plist_free(dict);
907 dict = NULL;
908
909 } while (current_state != final_state);
910
911 plist_free(dict);
912
913 free(salt);
914 free(pubkey);
915
916 SRP_free(srp);
917 srp = NULL;
918
919 if (ret != LOCKDOWN_E_SUCCESS) {
920 if (thekey) {
921 cstr_free(thekey);
922 }
923 return ret;
924 }
925
926 free(client->cu_key);
927 client->cu_key = malloc(thekey->length);
928 memcpy(client->cu_key, thekey->data, thekey->length);
929 client->cu_key_len = thekey->length;
930 cstr_free(thekey);
931
932 return LOCKDOWN_E_SUCCESS;
933#else
934 debug_info("not supported");
935 return LOCKDOWN_E_UNKNOWN_ERROR;
936#endif
937}
938
939lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t client, const char* request, plist_t request_payload, plist_t* reply)
940{
941#ifdef HAVE_WIRELESS_PAIRING
942 if (!client || !request)
943 return LOCKDOWN_E_INVALID_ARG;
944
945 if (!client->cu_key)
946 return LOCKDOWN_E_NO_RUNNING_SESSION;
947
948 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
949
950 /* derive keys */
951 unsigned char cu_write_key[32];
952 unsigned int cu_write_key_len = sizeof(cu_write_key);
953 static const char WRITE_KEY_SALT_MDLD[] = "WriteKeySaltMDLD";
954 static const char WRITE_KEY_INFO_MDLD[] = "WriteKeyInfoMDLD";
955 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);
956
957 unsigned char cu_read_key[32];
958 unsigned int cu_read_key_len = sizeof(cu_write_key);
959 static const char READ_KEY_SALT_MDLD[] = "ReadKeySaltMDLD";
960 static const char READ_KEY_INFO_MDLD[] = "ReadKeyInfoMDLD";
961 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);
962
963 // 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".
964 unsigned char cu_nonce[] = "sendone01234"; // guaranteed to be random by fair dice troll
965 if (client->device->version >= IDEVICE_DEVICE_VERSION(11,2,0)) {
966#if defined(HAVE_OPENSSL)
967 RAND_bytes(cu_nonce, sizeof(cu_nonce)-1);
968#elif defined(HAVE_GCRYPT)
969 gcry_create_nonce(cu_nonce, sizeof(cu_nonce)-1);
970#endif
971 }
972
973 debug_plist(request_payload);
974
975 /* convert request payload to binary */
976 uint32_t bin_len = 0;
977 char* bin = NULL;
978 plist_to_bin(request_payload, &bin, &bin_len);
979
980 /* encrypt request */
981 size_t encrypted_len = bin_len + 16;
982 unsigned char* encrypted_buf = malloc(encrypted_len);
983 chacha20_poly1305_encrypt_96(cu_write_key, cu_nonce, NULL, 0, (unsigned char*)bin, bin_len, encrypted_buf, &encrypted_len);
984 free(bin);
985 bin = NULL;
986
987 plist_t dict = plist_new_dict();
988 plist_dict_set_item(dict,"Request", plist_new_string(request));
989 plist_dict_set_item(dict, "Payload", plist_new_data((char*)encrypted_buf, encrypted_len));
990 free(encrypted_buf);
991 plist_dict_set_item(dict, "Nonce", plist_new_data((char*)cu_nonce, sizeof(cu_nonce)));
992 plist_dict_set_item(dict, "Label", plist_new_string(client->label));
993 plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2));
994
995 /* send to device */
996 ret = lockdownd_send(client, dict);
997 plist_free(dict);
998 dict = NULL;
999
1000 if (ret != LOCKDOWN_E_SUCCESS)
1001 return ret;
1002
1003 /* Now get device's answer */
1004 ret = lockdownd_receive(client, &dict);
1005 if (ret != LOCKDOWN_E_SUCCESS)
1006 return ret;
1007
1008 ret = lockdown_check_result(dict, request);
1009 if (ret != LOCKDOWN_E_SUCCESS) {
1010 plist_free(dict);
1011 return ret;
1012 }
1013
1014 /* get payload */
1015 plist_t blob = plist_dict_get_item(dict, "Payload");
1016 if (!blob) {
1017 plist_free(dict);
1018 return LOCKDOWN_E_DICT_ERROR;
1019 }
1020
1021 uint64_t dl = 0;
1022 const char* dt = plist_get_data_ptr(blob, &dl);
1023
1024 /* see if we have a nonce */
1025 blob = plist_dict_get_item(dict, "Nonce");
1026 const unsigned char* rnonce = (unsigned char*)"receiveone01";
1027 if (blob) {
1028 uint64_t rl = 0;
1029 rnonce = (const unsigned char*)plist_get_data_ptr(blob, &rl);
1030 }
1031
1032 /* decrypt payload */
1033 size_t decrypted_len = dl-16;
1034 unsigned char* decrypted = malloc(decrypted_len);
1035 chacha20_poly1305_decrypt_96(cu_read_key, (unsigned char*)rnonce, NULL, 0, (unsigned char*)dt, dl, decrypted, &decrypted_len);
1036 plist_free(dict);
1037 dict = NULL;
1038
1039 plist_from_memory((const char*)decrypted, decrypted_len, &dict, NULL);
1040 if (!dict) {
1041 ret = LOCKDOWN_E_PLIST_ERROR;
1042 debug_info("Failed to parse PLIST from decrypted payload:");
1043 debug_buffer((const char*)decrypted, decrypted_len);
1044 free(decrypted);
1045 return ret;
1046 }
1047 free(decrypted);
1048
1049 debug_plist(dict);
1050
1051 if (reply) {
1052 *reply = dict;
1053 } else {
1054 plist_free(dict);
1055 }
1056
1057 return LOCKDOWN_E_SUCCESS;
1058#else
1059 debug_info("not supported");
1060 return LOCKDOWN_E_UNKNOWN_ERROR;
1061#endif
1062}
1063
1064lockdownd_error_t lockdownd_get_value_cu(lockdownd_client_t client, const char* domain, const char* key, plist_t* value)
1065{
1066#ifdef HAVE_WIRELESS_PAIRING
1067 if (!client)
1068 return LOCKDOWN_E_INVALID_ARG;
1069
1070 if (!client->cu_key)
1071 return LOCKDOWN_E_NO_RUNNING_SESSION;
1072
1073 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1074
1075 plist_t request = plist_new_dict();
1076 if (domain) {
1077 plist_dict_set_item(request, "Domain", plist_new_string(domain));
1078 }
1079 if (key) {
1080 plist_dict_set_item(request, "Key", plist_new_string(key));
1081 }
1082
1083 plist_t reply = NULL;
1084 ret = lockdownd_cu_send_request_and_get_reply(client, "GetValueCU", request, &reply);
1085 plist_free(request);
1086 if (ret != LOCKDOWN_E_SUCCESS) {
1087 return ret;
1088 }
1089
1090 plist_t value_node = plist_dict_get_item(reply, "Value");
1091 if (value_node) {
1092 debug_info("has a value");
1093 *value = plist_copy(value_node);
1094 }
1095 plist_free(reply);
1096
1097 return ret;
1098#else
1099 debug_info("not supported");
1100 return LOCKDOWN_E_UNKNOWN_ERROR;
1101#endif
1102}
1103
1104lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client)
1105{
1106#ifdef HAVE_WIRELESS_PAIRING
1107 if (!client)
1108 return LOCKDOWN_E_INVALID_ARG;
1109
1110 if (!client->cu_key)
1111 return LOCKDOWN_E_NO_RUNNING_SESSION;
1112
1113 lockdownd_error_t ret;
1114
1115 plist_t wifi_mac = NULL;
1116 ret = lockdownd_get_value_cu(client, NULL, "WiFiAddress", &wifi_mac);
1117 if (ret != LOCKDOWN_E_SUCCESS) {
1118 return ret;
1119 }
1120
1121 plist_t pubkey = NULL;
1122 ret = lockdownd_get_value_cu(client, NULL, "DevicePublicKey", &pubkey);
1123 if (ret != LOCKDOWN_E_SUCCESS) {
1124 plist_free(wifi_mac);
1125 return ret;
1126 }
1127
1128 key_data_t public_key = { NULL, 0 };
1129 uint64_t data_len = 0;
1130 plist_get_data_val(pubkey, (char**)&public_key.data, &data_len);
1131 public_key.size = (unsigned int)data_len;
1132 plist_free(pubkey);
1133
1134 plist_t pair_record_plist = plist_new_dict();
1135 pair_record_generate_keys_and_certs(pair_record_plist, public_key, client->device->version);
1136
1137 char* host_id = NULL;
1138 char* system_buid = NULL;
1139
1140 /* set SystemBUID */
1141 userpref_read_system_buid(&system_buid);
1142 if (system_buid) {
1143 plist_dict_set_item(pair_record_plist, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid));
1144 free(system_buid);
1145 }
1146
1147 /* set HostID */
1148 host_id = generate_uuid();
1149 pair_record_set_host_id(pair_record_plist, host_id);
1150 free(host_id);
1151
1152 plist_t request_pair_record = plist_copy(pair_record_plist);
1153 /* remove stuff that is private */
1154 plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY);
1155 plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY);
1156
1157 plist_t request = plist_new_dict();
1158 plist_dict_set_item(request, "PairRecord", request_pair_record);
1159 plist_t pairing_opts = plist_new_dict();
1160 plist_dict_set_item(pairing_opts, "ExtendedPairingErrors", plist_new_bool(1));
1161 plist_dict_set_item(request, "PairingOptions", pairing_opts);
1162
1163 plist_t reply = NULL;
1164 ret = lockdownd_cu_send_request_and_get_reply(client, "PairCU", request, &reply);
1165 plist_free(request);
1166 if (ret != LOCKDOWN_E_SUCCESS) {
1167 plist_free(wifi_mac);
1168 return ret;
1169 }
1170
1171 char *s_udid = NULL;
1172 plist_t p_udid = plist_dict_get_item(reply, "UDID");
1173 if (p_udid) {
1174 plist_get_string_val(p_udid, &s_udid);
1175 }
1176 plist_t ebag = plist_dict_get_item(reply, "EscrowBag");
1177 if (ebag) {
1178 plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(ebag));
1179 }
1180 plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, wifi_mac);
1181 plist_free(reply);
1182
1183 if (userpref_save_pair_record(s_udid, 0, pair_record_plist) != 0) {
1184 printf("Failed to save pair record for UDID %s\n", s_udid);
1185 }
1186 free(s_udid);
1187 s_udid = NULL;
1188 plist_free(pair_record_plist);
1189
1190 ret = LOCKDOWN_E_SUCCESS;
1191
1192 return ret;
1193#else
1194 debug_info("not supported");
1195 return LOCKDOWN_E_UNKNOWN_ERROR;
1196#endif
1197}
diff --git a/src/lockdown.c b/src/lockdown.c
index 935f24e..32389c9 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,129 @@
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
30#include <libtasn1.h> 36#ifndef _MSC_VER
31#include <gnutls/x509.h> 37#include <unistd.h>
38#endif
39
32#include <plist/plist.h> 40#include <plist/plist.h>
41#include <libimobiledevice-glue/utils.h>
33 42
34#include "property_list_service.h" 43#include "property_list_service.h"
35#include "lockdown.h" 44#include "lockdown.h"
36#include "idevice.h" 45#include "idevice.h"
37#include "debug.h" 46#include "common/debug.h"
38#include "userpref.h" 47#include "common/userpref.h"
39 48#include "asprintf.h"
40#define RESULT_SUCCESS 0 49
41#define RESULT_FAILURE 1 50#ifdef _WIN32
42 51#include <windows.h>
43const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { 52#define sleep(x) Sleep(x*1000)
44 {"PKCS1", 536872976, 0}, 53#endif
45 {0, 1073741836, 0}, 54
46 {"RSAPublicKey", 536870917, 0}, 55struct st_lockdownd_error_str_map {
47 {"modulus", 1073741827, 0}, 56 const char *lockdown_errstr;
48 {"publicExponent", 3, 0}, 57 const char *errstr;
49 {0, 0, 0} 58 lockdownd_error_t errcode;
59};
60
61static struct st_lockdownd_error_str_map lockdownd_error_str_map[] = {
62 { "InvalidResponse", "Invalid response", LOCKDOWN_E_INVALID_RESPONSE },
63 { "MissingKey", "Missing key", LOCKDOWN_E_MISSING_KEY },
64 { "MissingValue", "Missing value", LOCKDOWN_E_MISSING_VALUE },
65 { "GetProhibited", "Get value prohibited", LOCKDOWN_E_GET_PROHIBITED },
66 { "SetProhibited", "Set value prohibited", LOCKDOWN_E_SET_PROHIBITED },
67 { "RemoveProhibited", "Remove value prohibited", LOCKDOWN_E_REMOVE_PROHIBITED },
68 { "ImmutableValue", "Immutable value", LOCKDOWN_E_IMMUTABLE_VALUE },
69 { "PasswordProtected", "Password protected", LOCKDOWN_E_PASSWORD_PROTECTED },
70 { "UserDeniedPairing", "User denied pairing", LOCKDOWN_E_USER_DENIED_PAIRING },
71 { "PairingDialogResponsePending", "Pairing dialog response pending", LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING },
72 { "MissingHostID", "Missing HostID", LOCKDOWN_E_MISSING_HOST_ID },
73 { "InvalidHostID", "Invalid HostID", LOCKDOWN_E_INVALID_HOST_ID },
74 { "SessionActive", "Session active", LOCKDOWN_E_SESSION_ACTIVE },
75 { "SessionInactive", "Session inactive", LOCKDOWN_E_SESSION_INACTIVE },
76 { "MissingSessionID", "Missing session ID", LOCKDOWN_E_MISSING_SESSION_ID },
77 { "InvalidSessionID", "Invalid session ID", LOCKDOWN_E_INVALID_SESSION_ID },
78 { "MissingService", "Missing service", LOCKDOWN_E_MISSING_SERVICE },
79 { "InvalidService", "Invalid service", LOCKDOWN_E_INVALID_SERVICE },
80 { "ServiceLimit", "Service limit reached", LOCKDOWN_E_SERVICE_LIMIT },
81 { "MissingPairRecord", "Missing pair record", LOCKDOWN_E_MISSING_PAIR_RECORD },
82 { "SavePairRecordFailed", "Saving pair record failed", LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED },
83 { "InvalidPairRecord", "Invalid pair record", LOCKDOWN_E_INVALID_PAIR_RECORD },
84 { "InvalidActivationRecord", "Invalid activation record", LOCKDOWN_E_INVALID_ACTIVATION_RECORD },
85 { "MissingActivationRecord", "Missing activation record", LOCKDOWN_E_MISSING_ACTIVATION_RECORD },
86 { "ServiceProhibited", "Service prohibited", LOCKDOWN_E_SERVICE_PROHIBITED },
87 { "EscrowLocked", "Escrow lockded", LOCKDOWN_E_ESCROW_LOCKED },
88 { "PairingProhibitedOverThisConnection", "Pairing prohibited over this connection", LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION },
89 { "FMiPProtected", "Find My iPhone/iPod/iPad protected", LOCKDOWN_E_FMIP_PROTECTED },
90 { "MCProtected", "MC protected" , LOCKDOWN_E_MC_PROTECTED },
91 { "MCChallengeRequired", "MC challenge required", LOCKDOWN_E_MC_CHALLENGE_REQUIRED },
92 { NULL, NULL, 0 }
50}; 93};
51 94
52/** 95/**
96 * Convert an error string identifier to a lockdownd_error_t value.
97 * Used internally to get correct error codes from a response.
98 *
99 * @param name The error name to convert.
100 *
101 * @return A matching lockdownd_error_t error code,
102 * LOCKDOWN_E_UNKNOWN_ERROR otherwise.
103 */
104static lockdownd_error_t lockdownd_strtoerr(const char* name)
105{
106 lockdownd_error_t err = LOCKDOWN_E_UNKNOWN_ERROR;
107 int i = 0;
108 while (lockdownd_error_str_map[i].lockdown_errstr) {
109 if (strcmp(lockdownd_error_str_map[i].lockdown_errstr, name) == 0) {
110 return lockdownd_error_str_map[i].errcode;
111 }
112 i++;
113 }
114 return err;
115}
116
117/**
118 * Convert a property_list_service_error_t value to a lockdownd_error_t
119 * value. Used internally to get correct error codes.
120 *
121 * @param err A property_list_service_error_t error code
122 *
123 * @return A matching lockdownd_error_t error code,
124 * LOCKDOWND_E_UNKNOWN_ERROR otherwise.
125 */
126static lockdownd_error_t lockdownd_error(property_list_service_error_t err)
127{
128 switch (err) {
129 case PROPERTY_LIST_SERVICE_E_SUCCESS:
130 return LOCKDOWN_E_SUCCESS;
131 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
132 return LOCKDOWN_E_INVALID_ARG;
133 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
134 return LOCKDOWN_E_PLIST_ERROR;
135 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
136 return LOCKDOWN_E_MUX_ERROR;
137 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
138 return LOCKDOWN_E_SSL_ERROR;
139 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
140 return LOCKDOWN_E_RECEIVE_TIMEOUT;
141 default:
142 break;
143 }
144 return LOCKDOWN_E_UNKNOWN_ERROR;
145}
146
147/**
53 * Internally used function for checking the result from lockdown's answer 148 * Internally used function for checking the result from lockdown's answer
54 * plist to a previously sent request. 149 * plist to a previously sent request.
55 * 150 *
@@ -57,58 +152,66 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = {
57 * @param query_match Name of the request to match or NULL if no match is 152 * @param query_match Name of the request to match or NULL if no match is
58 * required. 153 * required.
59 * 154 *
60 * @return RESULT_SUCCESS when the result is 'Success', 155 * @return LOCKDOWN_E_SUCCESS when the result is 'Success',
61 * RESULT_FAILURE when the result is 'Failure', 156 * LOCKDOWN_E_UNKNOWN_ERROR when the result is 'Failure',
62 * or a negative value if an error occured during evaluation. 157 * or a specific error code if derieved from the result.
63 */ 158 */
64static int lockdown_check_result(plist_t dict, const char *query_match) 159lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match)
65{ 160{
66 int ret = -1; 161 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
67 162
68 plist_t query_node = plist_dict_get_item(dict, "Request"); 163 plist_t query_node = plist_dict_get_item(dict, "Request");
69 if (!query_node) { 164 if (!query_node) {
70 return ret; 165 return ret;
71 } 166 }
167
72 if (plist_get_node_type(query_node) != PLIST_STRING) { 168 if (plist_get_node_type(query_node) != PLIST_STRING) {
73 return ret; 169 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 } 170 }
86 171
87 plist_t result_node = plist_dict_get_item(dict, "Result"); 172 const char *query_value = plist_get_string_ptr(query_node, NULL);
88 if (!result_node) { 173 if (!query_value) {
89 return ret; 174 return ret;
90 } 175 }
91 176
92 plist_type result_type = plist_get_node_type(result_node); 177 if (query_match && (strcmp(query_value, query_match) != 0)) {
93 178 return ret;
94 if (result_type == PLIST_STRING) { 179 }
95
96 char *result_value = NULL;
97 180
98 plist_get_string_val(result_node, &result_value); 181 /* Check for 'Error' in reply */
182 plist_t err_node = plist_dict_get_item(dict, "Error");
183 if (err_node) {
184 if (plist_get_node_type(err_node) == PLIST_STRING) {
185 const char *err_value = plist_get_string_ptr(err_node, NULL);
186 if (err_value) {
187 debug_info("ERROR: %s", err_value);
188 ret = lockdownd_strtoerr(err_value);
189 } else {
190 debug_info("ERROR: unknown error occurred");
191 }
192 }
193 return ret;
194 }
99 195
196 plist_t result_node = plist_dict_get_item(dict, "Result");
197 if (!result_node) {
198 /* With iOS 5+ 'Result' is not present anymore.
199 If there is no 'Error', we can just assume success. */
200 return LOCKDOWN_E_SUCCESS;
201 }
202 if (plist_get_node_type(result_node) == PLIST_STRING) {
203 const char *result_value = plist_get_string_ptr(result_node, NULL);
100 if (result_value) { 204 if (result_value) {
101 if (!strcmp(result_value, "Success")) { 205 if (!strcmp(result_value, "Success")) {
102 ret = RESULT_SUCCESS; 206 ret = LOCKDOWN_E_SUCCESS;
103 } else if (!strcmp(result_value, "Failure")) { 207 } else if (!strcmp(result_value, "Failure")) {
104 ret = RESULT_FAILURE; 208 ret = LOCKDOWN_E_UNKNOWN_ERROR;
105 } else { 209 } else {
106 debug_info("ERROR: unknown result value '%s'", result_value); 210 debug_info("ERROR: unknown result value '%s'", result_value);
107 } 211 }
108 } 212 }
109 if (result_value)
110 free(result_value);
111 } 213 }
214
112 return ret; 215 return ret;
113} 216}
114 217
@@ -123,20 +226,10 @@ static void plist_dict_add_label(plist_t plist, const char *label)
123{ 226{
124 if (plist && label) { 227 if (plist && label) {
125 if (plist_get_node_type(plist) == PLIST_DICT) 228 if (plist_get_node_type(plist) == PLIST_DICT)
126 plist_dict_insert_item(plist, "Label", plist_new_string(label)); 229 plist_dict_set_item(plist, "Label", plist_new_string(label));
127 } 230 }
128} 231}
129 232
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) 233lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id)
141{ 234{
142 if (!client) 235 if (!client)
@@ -151,8 +244,8 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *
151 244
152 plist_t dict = plist_new_dict(); 245 plist_t dict = plist_new_dict();
153 plist_dict_add_label(dict, client->label); 246 plist_dict_add_label(dict, client->label);
154 plist_dict_insert_item(dict,"Request", plist_new_string("StopSession")); 247 plist_dict_set_item(dict,"Request", plist_new_string("StopSession"));
155 plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id)); 248 plist_dict_set_item(dict,"SessionID", plist_new_string(session_id));
156 249
157 debug_info("stopping session %s", session_id); 250 debug_info("stopping session %s", session_id);
158 251
@@ -168,64 +261,74 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *
168 return LOCKDOWN_E_PLIST_ERROR; 261 return LOCKDOWN_E_PLIST_ERROR;
169 } 262 }
170 263
171 ret = LOCKDOWN_E_UNKNOWN_ERROR; 264 ret = lockdown_check_result(dict, "StopSession");
172 if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) { 265 if (ret == LOCKDOWN_E_SUCCESS) {
173 debug_info("success"); 266 debug_info("success");
174 ret = LOCKDOWN_E_SUCCESS;
175 } 267 }
268
176 plist_free(dict); 269 plist_free(dict);
177 dict = NULL; 270 dict = NULL;
271
272 if (client->session_id) {
273 free(client->session_id);
274 client->session_id = NULL;
275 }
276
178 if (client->ssl_enabled) { 277 if (client->ssl_enabled) {
179 property_list_service_disable_ssl(client->parent); 278 property_list_service_disable_ssl(client->parent);
279 client->ssl_enabled = 0;
180 } 280 }
281
181 return ret; 282 return ret;
182} 283}
183 284
184/** 285static 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{ 286{
194 if (!client) 287 if (!client)
195 return LOCKDOWN_E_INVALID_ARG; 288 return LOCKDOWN_E_INVALID_ARG;
196 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
197 289
198 if (client->session_id) { 290 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
199 lockdownd_stop_session(client, client->session_id);
200 free(client->session_id);
201 }
202 291
203 if (client->parent) { 292 if (client->parent) {
204 lockdownd_goodbye(client);
205
206 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { 293 if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) {
207 ret = LOCKDOWN_E_SUCCESS; 294 ret = LOCKDOWN_E_SUCCESS;
208 } 295 }
209 } 296 }
210 297
211 if (client->uuid) { 298 if (client->session_id) {
212 free(client->uuid); 299 free(client->session_id);
300 client->session_id = NULL;
213 } 301 }
214 if (client->label) { 302 if (client->label) {
215 free(client->label); 303 free(client->label);
216 } 304 }
305 if (client->cu_key) {
306 free(client->cu_key);
307 client->cu_key = NULL;
308 }
217 309
218 free(client); 310 free(client);
311 client = NULL;
312
313 return ret;
314}
315
316lockdownd_error_t lockdownd_client_free(lockdownd_client_t client)
317{
318 if (!client)
319 return LOCKDOWN_E_INVALID_ARG;
320
321 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
322
323 if (client->session_id) {
324 lockdownd_stop_session(client, client->session_id);
325 }
326
327 ret = lockdownd_client_free_simple(client);
328
219 return ret; 329 return ret;
220} 330}
221 331
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) 332void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
230{ 333{
231 if (client) { 334 if (client) {
@@ -236,69 +339,22 @@ void lockdownd_client_set_label(lockdownd_client_t client, const char *label)
236 } 339 }
237} 340}
238 341
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) 342lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist)
249{ 343{
250 if (!client || !plist || (plist && *plist)) 344 if (!client || !plist || (plist && *plist))
251 return LOCKDOWN_E_INVALID_ARG; 345 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 346
260 if (!*plist) 347 return lockdownd_error(property_list_service_receive_plist(client->parent, plist));
261 ret = LOCKDOWN_E_PLIST_ERROR;
262
263 return ret;
264} 348}
265 349
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) 350lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist)
279{ 351{
280 if (!client || !plist) 352 if (!client || !plist)
281 return LOCKDOWN_E_INVALID_ARG; 353 return LOCKDOWN_E_INVALID_ARG;
282 354
283 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 355 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} 356}
292 357
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) 358lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
303{ 359{
304 if (!client) 360 if (!client)
@@ -308,7 +364,7 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
308 364
309 plist_t dict = plist_new_dict(); 365 plist_t dict = plist_new_dict();
310 plist_dict_add_label(dict, client->label); 366 plist_dict_add_label(dict, client->label);
311 plist_dict_insert_item(dict,"Request", plist_new_string("QueryType")); 367 plist_dict_set_item(dict,"Request", plist_new_string("QueryType"));
312 368
313 debug_info("called"); 369 debug_info("called");
314 ret = lockdownd_send(client, dict); 370 ret = lockdownd_send(client, dict);
@@ -322,14 +378,21 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
322 return ret; 378 return ret;
323 379
324 ret = LOCKDOWN_E_UNKNOWN_ERROR; 380 ret = LOCKDOWN_E_UNKNOWN_ERROR;
325 if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) { 381 plist_t type_node = plist_dict_get_item(dict, "Type");
382 if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) {
383 char* typestr = NULL;
384 plist_get_string_val(type_node, &typestr);
385 debug_info("success with type %s", typestr);
326 /* return the type if requested */ 386 /* return the type if requested */
327 if (type != NULL) { 387 if (type != NULL) {
328 plist_t type_node = plist_dict_get_item(dict, "Type"); 388 *type = typestr;
329 plist_get_string_val(type_node, type); 389 } else {
390 free(typestr);
330 } 391 }
331 debug_info("success with type %s", *type);
332 ret = LOCKDOWN_E_SUCCESS; 392 ret = LOCKDOWN_E_SUCCESS;
393 } else {
394 debug_info("hmm. QueryType response does not contain a type?!");
395 debug_plist(dict);
333 } 396 }
334 plist_free(dict); 397 plist_free(dict);
335 dict = NULL; 398 dict = NULL;
@@ -337,16 +400,6 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type)
337 return ret; 400 return ret;
338} 401}
339 402
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) 403lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value)
351{ 404{
352 if (!client) 405 if (!client)
@@ -359,12 +412,12 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
359 dict = plist_new_dict(); 412 dict = plist_new_dict();
360 plist_dict_add_label(dict, client->label); 413 plist_dict_add_label(dict, client->label);
361 if (domain) { 414 if (domain) {
362 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 415 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
363 } 416 }
364 if (key) { 417 if (key) {
365 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 418 plist_dict_set_item(dict,"Key", plist_new_string(key));
366 } 419 }
367 plist_dict_insert_item(dict,"Request", plist_new_string("GetValue")); 420 plist_dict_set_item(dict,"Request", plist_new_string("GetValue"));
368 421
369 /* send to device */ 422 /* send to device */
370 ret = lockdownd_send(client, dict); 423 ret = lockdownd_send(client, dict);
@@ -380,10 +433,11 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
380 if (ret != LOCKDOWN_E_SUCCESS) 433 if (ret != LOCKDOWN_E_SUCCESS)
381 return ret; 434 return ret;
382 435
383 if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) { 436 ret = lockdown_check_result(dict, "GetValue");
437 if (ret == LOCKDOWN_E_SUCCESS) {
384 debug_info("success"); 438 debug_info("success");
385 ret = LOCKDOWN_E_SUCCESS;
386 } 439 }
440
387 if (ret != LOCKDOWN_E_SUCCESS) { 441 if (ret != LOCKDOWN_E_SUCCESS) {
388 plist_free(dict); 442 plist_free(dict);
389 return ret; 443 return ret;
@@ -400,17 +454,6 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom
400 return ret; 454 return ret;
401} 455}
402 456
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) 457lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value)
415{ 458{
416 if (!client || !value) 459 if (!client || !value)
@@ -423,13 +466,13 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
423 dict = plist_new_dict(); 466 dict = plist_new_dict();
424 plist_dict_add_label(dict, client->label); 467 plist_dict_add_label(dict, client->label);
425 if (domain) { 468 if (domain) {
426 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 469 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
427 } 470 }
428 if (key) { 471 if (key) {
429 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 472 plist_dict_set_item(dict,"Key", plist_new_string(key));
430 } 473 }
431 plist_dict_insert_item(dict,"Request", plist_new_string("SetValue")); 474 plist_dict_set_item(dict,"Request", plist_new_string("SetValue"));
432 plist_dict_insert_item(dict,"Value", value); 475 plist_dict_set_item(dict,"Value", value);
433 476
434 /* send to device */ 477 /* send to device */
435 ret = lockdownd_send(client, dict); 478 ret = lockdownd_send(client, dict);
@@ -445,9 +488,9 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
445 if (ret != LOCKDOWN_E_SUCCESS) 488 if (ret != LOCKDOWN_E_SUCCESS)
446 return ret; 489 return ret;
447 490
448 if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) { 491 ret = lockdown_check_result(dict, "SetValue");
492 if (ret == LOCKDOWN_E_SUCCESS) {
449 debug_info("success"); 493 debug_info("success");
450 ret = LOCKDOWN_E_SUCCESS;
451 } 494 }
452 495
453 if (ret != LOCKDOWN_E_SUCCESS) { 496 if (ret != LOCKDOWN_E_SUCCESS) {
@@ -459,17 +502,6 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom
459 return ret; 502 return ret;
460} 503}
461 504
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) 505lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key)
474{ 506{
475 if (!client) 507 if (!client)
@@ -482,12 +514,12 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
482 dict = plist_new_dict(); 514 dict = plist_new_dict();
483 plist_dict_add_label(dict, client->label); 515 plist_dict_add_label(dict, client->label);
484 if (domain) { 516 if (domain) {
485 plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); 517 plist_dict_set_item(dict,"Domain", plist_new_string(domain));
486 } 518 }
487 if (key) { 519 if (key) {
488 plist_dict_insert_item(dict,"Key", plist_new_string(key)); 520 plist_dict_set_item(dict,"Key", plist_new_string(key));
489 } 521 }
490 plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue")); 522 plist_dict_set_item(dict,"Request", plist_new_string("RemoveValue"));
491 523
492 /* send to device */ 524 /* send to device */
493 ret = lockdownd_send(client, dict); 525 ret = lockdownd_send(client, dict);
@@ -503,9 +535,9 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
503 if (ret != LOCKDOWN_E_SUCCESS) 535 if (ret != LOCKDOWN_E_SUCCESS)
504 return ret; 536 return ret;
505 537
506 if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) { 538 ret = lockdown_check_result(dict, "RemoveValue");
539 if (ret == LOCKDOWN_E_SUCCESS) {
507 debug_info("success"); 540 debug_info("success");
508 ret = LOCKDOWN_E_SUCCESS;
509 } 541 }
510 542
511 if (ret != LOCKDOWN_E_SUCCESS) { 543 if (ret != LOCKDOWN_E_SUCCESS) {
@@ -517,16 +549,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *
517 return ret; 549 return ret;
518} 550}
519 551
520/** 552lockdownd_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{ 553{
531 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 554 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
532 plist_t value = NULL; 555 plist_t value = NULL;
@@ -535,7 +558,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu
535 if (ret != LOCKDOWN_E_SUCCESS) { 558 if (ret != LOCKDOWN_E_SUCCESS) {
536 return ret; 559 return ret;
537 } 560 }
538 plist_get_string_val(value, uuid); 561 plist_get_string_val(value, udid);
539 562
540 plist_free(value); 563 plist_free(value);
541 value = NULL; 564 value = NULL;
@@ -551,7 +574,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu
551 * 574 *
552 * @return LOCKDOWN_E_SUCCESS on success 575 * @return LOCKDOWN_E_SUCCESS on success
553 */ 576 */
554lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key) 577static lockdownd_error_t lockdownd_get_device_public_key_as_key_data(lockdownd_client_t client, key_data_t *public_key)
555{ 578{
556 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 579 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
557 plist_t value = NULL; 580 plist_t value = NULL;
@@ -572,15 +595,6 @@ lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnu
572 return ret; 595 return ret;
573} 596}
574 597
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) 598lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name)
585{ 599{
586 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 600 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
@@ -598,29 +612,20 @@ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **de
598 return ret; 612 return ret;
599} 613}
600 614
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) 615lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label)
617{ 616{
618 if (!client) 617 if (!device || !client)
619 return LOCKDOWN_E_INVALID_ARG; 618 return LOCKDOWN_E_INVALID_ARG;
620 619
620 static struct lockdownd_service_descriptor service = {
621 .port = 0xf27e,
622 .ssl_enabled = 0
623 };
624 char *type = NULL;
625
621 property_list_service_client_t plistclient = NULL; 626 property_list_service_client_t plistclient = NULL;
622 if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 627 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); 628 debug_info("could not connect to lockdownd (device %s)", device->udid);
624 return LOCKDOWN_E_MUX_ERROR; 629 return LOCKDOWN_E_MUX_ERROR;
625 } 630 }
626 631
@@ -628,31 +633,69 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli
628 client_loc->parent = plistclient; 633 client_loc->parent = plistclient;
629 client_loc->ssl_enabled = 0; 634 client_loc->ssl_enabled = 0;
630 client_loc->session_id = NULL; 635 client_loc->session_id = NULL;
631 client_loc->uuid = NULL; 636 client_loc->device = device;
637 client_loc->cu_key = NULL;
638 client_loc->cu_key_len = 0;
639
640 if (device->udid) {
641 debug_info("device udid: %s", device->udid);
642 }
643
632 client_loc->label = label ? strdup(label) : NULL; 644 client_loc->label = label ? strdup(label) : NULL;
633 645
646 int is_lockdownd = 0;
647 if (lockdownd_query_type(client_loc, &type) != LOCKDOWN_E_SUCCESS) {
648 debug_info("QueryType failed in the lockdownd client.");
649 } else if (!strcmp("com.apple.mobile.lockdown", type)) {
650 is_lockdownd = 1;
651 } else {
652 debug_info("QueryType request returned \"%s\"", type);
653 }
654 free(type);
655
634 *client = client_loc; 656 *client = client_loc;
635 657
658 if (is_lockdownd && device->version == 0) {
659 plist_t p_version = NULL;
660 if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) {
661 int vers[3] = {0, 0, 0};
662 char *s_version = NULL;
663 plist_get_string_val(p_version, &s_version);
664 if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) {
665 device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]);
666 }
667 free(s_version);
668 }
669 plist_free(p_version);
670 }
671 if (is_lockdownd && device->device_class == 0) {
672 plist_t p_device_class = NULL;
673 if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) {
674 char* s_device_class = NULL;
675 plist_get_string_val(p_device_class, &s_device_class);
676 if (s_device_class != NULL) {
677 if (!strcmp(s_device_class, "iPhone")) {
678 device->device_class = DEVICE_CLASS_IPHONE;
679 } else if (!strcmp(s_device_class, "iPad")) {
680 device->device_class = DEVICE_CLASS_IPAD;
681 } else if (!strcmp(s_device_class, "iPod")) {
682 device->device_class = DEVICE_CLASS_IPOD;
683 } else if (!strcmp(s_device_class, "Watch")) {
684 device->device_class = DEVICE_CLASS_WATCH;
685 } else if (!strcmp(s_device_class, "AppleTV")) {
686 device->device_class = DEVICE_CLASS_APPLETV;
687 } else {
688 device->device_class = DEVICE_CLASS_UNKNOWN;
689 }
690 free(s_device_class);
691 }
692 }
693 plist_free(p_device_class);
694 }
695
636 return LOCKDOWN_E_SUCCESS; 696 return LOCKDOWN_E_SUCCESS;
637} 697}
638 698
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) 699lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label)
657{ 700{
658 if (!client) 701 if (!client)
@@ -660,8 +703,8 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
660 703
661 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 704 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
662 lockdownd_client_t client_loc = NULL; 705 lockdownd_client_t client_loc = NULL;
706 plist_t pair_record = NULL;
663 char *host_id = NULL; 707 char *host_id = NULL;
664 char *type = NULL;
665 708
666 ret = lockdownd_client_new(device, &client_loc, label); 709 ret = lockdownd_client_new(device, &client_loc, label);
667 if (LOCKDOWN_E_SUCCESS != ret) { 710 if (LOCKDOWN_E_SUCCESS != ret) {
@@ -670,60 +713,79 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown
670 } 713 }
671 714
672 /* perform handshake */ 715 /* perform handshake */
673 if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) { 716 userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record);
674 debug_info("QueryType failed in the lockdownd client."); 717 if (uerr == USERPREF_E_READ_ERROR) {
675 ret = LOCKDOWN_E_NOT_ENOUGH_DATA; 718 debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid);
676 } else { 719 lockdownd_client_free(client_loc);
677 if (strcmp("com.apple.mobile.lockdown", type)) { 720 return LOCKDOWN_E_RECEIVE_TIMEOUT;
678 debug_info("Warning QueryType request returned \"%s\".", type);
679 }
680 if (type)
681 free(type);
682 } 721 }
683 722 if (pair_record) {
684 ret = idevice_get_uuid(device, &client_loc->uuid); 723 pair_record_get_host_id(pair_record, &host_id);
685 if (LOCKDOWN_E_SUCCESS != ret) {
686 debug_info("failed to get device uuid.");
687 } 724 }
688 debug_info("device uuid: %s", client_loc->uuid); 725 if (LOCKDOWN_E_SUCCESS == ret && pair_record && !host_id) {
689
690 userpref_get_host_id(&host_id);
691 if (LOCKDOWN_E_SUCCESS == ret && !host_id) {
692 ret = LOCKDOWN_E_INVALID_CONF; 726 ret = LOCKDOWN_E_INVALID_CONF;
693 } 727 }
694 728
695 if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid)) 729 if (LOCKDOWN_E_SUCCESS == ret && !pair_record) {
730 /* attempt pairing */
731 free(host_id);
732 host_id = NULL;
696 ret = lockdownd_pair(client_loc, NULL); 733 ret = lockdownd_pair(client_loc, NULL);
734 }
697 735
698 /* in any case, we need to validate pairing to receive trusted host status */ 736 plist_free(pair_record);
699 ret = lockdownd_validate_pair(client_loc, NULL); 737 pair_record = NULL;
700 738
701 /* if not paired yet, let's do it now */ 739 if (device->version < IDEVICE_DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) {
702 if (LOCKDOWN_E_INVALID_HOST_ID == ret) { 740 /* for older devices, we need to validate pairing to receive trusted host status */
703 ret = lockdownd_pair(client_loc, NULL); 741 ret = lockdownd_validate_pair(client_loc, NULL);
704 if (LOCKDOWN_E_SUCCESS == ret) { 742
705 ret = lockdownd_validate_pair(client_loc, NULL); 743 /* if not paired yet, let's do it now */
744 if (LOCKDOWN_E_INVALID_HOST_ID == ret) {
745 free(host_id);
746 host_id = NULL;
747 ret = lockdownd_pair(client_loc, NULL);
748 if (LOCKDOWN_E_SUCCESS == ret) {
749 ret = lockdownd_validate_pair(client_loc, NULL);
750 }
706 } 751 }
707 } 752 }
708 753
709 if (LOCKDOWN_E_SUCCESS == ret) { 754 if (LOCKDOWN_E_SUCCESS == ret) {
755 if (!host_id) {
756 uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record);
757 if (uerr == USERPREF_E_READ_ERROR) {
758 debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid);
759 lockdownd_client_free(client_loc);
760 return LOCKDOWN_E_RECEIVE_TIMEOUT;
761 } else if (uerr == USERPREF_E_NOENT) {
762 debug_info("ERROR: No pair record for %s", client_loc->device->udid);
763 lockdownd_client_free(client_loc);
764 return LOCKDOWN_E_INVALID_CONF;
765 } else if (uerr != USERPREF_E_SUCCESS) {
766 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client_loc->device->udid);
767 lockdownd_client_free(client_loc);
768 return LOCKDOWN_E_INVALID_CONF;
769 }
770 if (pair_record) {
771 pair_record_get_host_id(pair_record, &host_id);
772 plist_free(pair_record);
773 }
774 }
775
710 ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); 776 ret = lockdownd_start_session(client_loc, host_id, NULL, NULL);
711 if (LOCKDOWN_E_SUCCESS != ret) { 777 if (LOCKDOWN_E_SUCCESS != ret) {
712 debug_info("Session opening failed."); 778 debug_info("Session opening failed.");
713 } 779 }
714 780
715 if (host_id) {
716 free(host_id);
717 host_id = NULL;
718 }
719 } 781 }
720 782
721 if (LOCKDOWN_E_SUCCESS == ret) { 783 if (LOCKDOWN_E_SUCCESS == ret) {
722 *client = client_loc; 784 *client = client_loc;
723 } else { 785 } else {
724 lockdownd_client_free(client_loc); 786 lockdownd_client_free(client_loc);
725 } 787 }
726 788 free(host_id);
727 return ret; 789 return ret;
728} 790}
729 791
@@ -740,68 +802,78 @@ static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_recor
740 if (!pair_record) 802 if (!pair_record)
741 return NULL; 803 return NULL;
742 804
743 char *host_id_loc = pair_record->host_id;
744
745 /* setup request plist */ 805 /* setup request plist */
746 plist_t dict = plist_new_dict(); 806 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))); 807 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))); 808 plist_dict_set_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate)));
749 if (!pair_record->host_id) 809 plist_dict_set_item(dict, "HostID", plist_new_string(pair_record->host_id));
750 userpref_get_host_id(&host_id_loc); 810 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)); 811 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 812
757 return dict; 813 return dict;
758} 814}
759 815
760/** 816/**
761 * Generates a new pairing record plist and required certificates for the 817 * 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 818 * device. If a pairing exists, it is loaded from the computer instead of being
763 * computer. 819 * generated.
764 * 820 *
765 * @param public_key The public key of the device. 821 * @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 * 822 *
769 * @return LOCKDOWN_E_SUCCESS on success 823 * @return LOCKDOWN_E_SUCCESS on success
770 */ 824 */
771static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist) 825static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t *pair_record)
772{ 826{
773 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 827 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
774 828
775 gnutls_datum_t device_cert = { NULL, 0 }; 829 key_data_t public_key = { NULL, 0 };
776 gnutls_datum_t host_cert = { NULL, 0 }; 830 char* host_id = NULL;
777 gnutls_datum_t root_cert = { NULL, 0 }; 831 char* system_buid = NULL;
778 832
779 ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); 833 /* retrieve device public key */
834 ret = lockdownd_get_device_public_key_as_key_data(client, &public_key);
780 if (ret != LOCKDOWN_E_SUCCESS) { 835 if (ret != LOCKDOWN_E_SUCCESS) {
781 return ret; 836 debug_info("device refused to send public key.");
837 goto leave;
838 }
839 debug_info("device public key follows:\n%.*s", public_key.size, public_key.data);
840
841 *pair_record = plist_new_dict();
842
843 /* generate keys and certificates into pair record */
844 userpref_error_t uret = USERPREF_E_SUCCESS;
845 uret = pair_record_generate_keys_and_certs(*pair_record, public_key, client->device->version);
846 switch(uret) {
847 case USERPREF_E_INVALID_ARG:
848 ret = LOCKDOWN_E_INVALID_ARG;
849 break;
850 case USERPREF_E_INVALID_CONF:
851 ret = LOCKDOWN_E_INVALID_CONF;
852 break;
853 case USERPREF_E_SSL_ERROR:
854 ret = LOCKDOWN_E_SSL_ERROR;
855 break;
856 default:
857 break;
782 } 858 }
783 859
784 char *host_id_loc = host_id; 860 /* set SystemBUID */
861 userpref_read_system_buid(&system_buid);
862 if (system_buid) {
863 plist_dict_set_item(*pair_record, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid));
864 }
785 865
786 if (!host_id) 866 /* set HostID */
787 userpref_get_host_id(&host_id_loc); 867 host_id = generate_uuid();
868 pair_record_set_host_id(*pair_record, host_id);
788 869
789 /* setup request plist */ 870leave:
790 *pair_record_plist = plist_new_dict(); 871 if (host_id)
791 plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size)); 872 free(host_id);
792 plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size)); 873 if (system_buid)
793 plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc)); 874 free(system_buid);
794 plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size)); 875 if (public_key.data)
795 876 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 877
806 return ret; 878 return ret;
807} 879}
@@ -809,11 +881,13 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c
809/** 881/**
810 * Function used internally by lockdownd_pair() and lockdownd_validate_pair() 882 * Function used internally by lockdownd_pair() and lockdownd_validate_pair()
811 * 883 *
812 * @param client The lockdown client to pair with. 884 * @param client The lockdown client
813 * @param pair_record The pair record to use for pairing. If NULL is passed, then 885 * @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 886 * the pair records from the current machine are used. New records will be
815 * generated automatically when pairing is done for the first time. 887 * generated automatically when pairing is done for the first time.
816 * @param verb This is either "Pair", "ValidatePair" or "Unpair". 888 * @param verb This is either "Pair", "ValidatePair" or "Unpair".
889 * @param options The pairing options to pass.
890 * @param response If non-NULL a pointer to lockdownd's response dictionary is returned.
817 * 891 *
818 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, 892 * @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, 893 * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong,
@@ -821,73 +895,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, 895 * 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 896 * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id
823 */ 897 */
824static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb) 898static 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{ 899{
826 if (!client) 900 if (!client)
827 return LOCKDOWN_E_INVALID_ARG; 901 return LOCKDOWN_E_INVALID_ARG;
828 902
829 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 903 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
830 plist_t dict = NULL; 904 plist_t dict = NULL;
831 plist_t dict_record = NULL; 905 plist_t pair_record_plist = NULL;
832 gnutls_datum_t public_key = { NULL, 0 }; 906 plist_t wifi_node = NULL;
833 int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ 907 int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */
834 908
835 if (pair_record && pair_record->host_id) { 909 if (pair_record && pair_record->system_buid && pair_record->host_id) {
836 /* valid pair_record passed? */ 910 /* valid pair_record passed? */
837 if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { 911 if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) {
838 return LOCKDOWN_E_PLIST_ERROR; 912 return LOCKDOWN_E_PLIST_ERROR;
839 } 913 }
840 914
841 /* use passed pair_record */ 915 /* use passed pair_record */
842 dict_record = lockdownd_pair_record_to_plist(pair_record); 916 pair_record_plist = lockdownd_pair_record_to_plist(pair_record);
843 917
844 pairing_mode = 1; 918 pairing_mode = 1;
845 } else { 919 } else {
846 ret = lockdownd_get_device_public_key(client, &public_key); 920 /* generate a new pair record if pairing */
847 if (ret != LOCKDOWN_E_SUCCESS) { 921 if (!strcmp("Pair", verb)) {
848 if (public_key.data) 922 ret = pair_record_generate(client, &pair_record_plist);
849 free(public_key.data); 923
850 debug_info("device refused to send public key."); 924 if (ret != LOCKDOWN_E_SUCCESS) {
851 return ret; 925 if (pair_record_plist)
852 } 926 plist_free(pair_record_plist);
853 debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); 927 return ret;
854 /* get libimobiledevice pair_record */ 928 }
855 ret = generate_pair_record_plist(public_key, NULL, &dict_record); 929
856 if (ret != LOCKDOWN_E_SUCCESS) { 930 /* get wifi mac now, if we get it later we fail on iOS 7 which causes a reconnect */
857 if (dict_record) 931 lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node);
858 plist_free(dict_record); 932 } else {
859 return ret; 933 /* use existing pair record */
934 userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record_plist);
935 if (uerr == USERPREF_E_READ_ERROR) {
936 debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid);
937 return LOCKDOWN_E_RECEIVE_TIMEOUT;
938 } else if (uerr == USERPREF_E_NOENT) {
939 debug_info("ERROR: No pair record for %s", client->device->udid);
940 return LOCKDOWN_E_INVALID_CONF;
941 } else if (uerr != USERPREF_E_SUCCESS) {
942 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid);
943 return LOCKDOWN_E_INVALID_CONF;
944 }
860 } 945 }
861 } 946 }
862 947
863 /* Setup Pair request plist */ 948 plist_t request_pair_record = plist_copy(pair_record_plist);
949
950 /* remove stuff that is private */
951 plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY);
952 plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY);
953
954 /* setup pair request plist */
864 dict = plist_new_dict(); 955 dict = plist_new_dict();
865 plist_dict_add_label(dict, client->label); 956 plist_dict_add_label(dict, client->label);
866 plist_dict_insert_item(dict,"PairRecord", dict_record); 957 plist_dict_set_item(dict, "PairRecord", request_pair_record);
867 plist_dict_insert_item(dict, "Request", plist_new_string(verb)); 958 plist_dict_set_item(dict, "Request", plist_new_string(verb));
959 plist_dict_set_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION));
960
961 if (options) {
962 plist_dict_set_item(dict, "PairingOptions", plist_copy(options));
963 }
868 964
869 /* send to device */ 965 /* send to device */
870 ret = lockdownd_send(client, dict); 966 ret = lockdownd_send(client, dict);
871 plist_free(dict); 967 plist_free(dict);
872 dict = NULL; 968 dict = NULL;
873 969
874 if (ret != LOCKDOWN_E_SUCCESS) 970 if (ret != LOCKDOWN_E_SUCCESS) {
971 plist_free(pair_record_plist);
972 if (wifi_node)
973 plist_free(wifi_node);
875 return ret; 974 return ret;
975 }
876 976
877 /* Now get device's answer */ 977 /* Now get device's answer */
878 ret = lockdownd_receive(client, &dict); 978 ret = lockdownd_receive(client, &dict);
879 979
880 if (ret != LOCKDOWN_E_SUCCESS) 980 if (ret != LOCKDOWN_E_SUCCESS) {
981 plist_free(pair_record_plist);
982 if (wifi_node)
983 plist_free(wifi_node);
881 return ret; 984 return ret;
985 }
882 986
883 if (strcmp(verb, "Unpair") == 0) { 987 if (strcmp(verb, "Unpair") == 0) {
884 /* workaround for Unpair giving back ValidatePair, 988 /* workaround for Unpair giving back ValidatePair,
885 * seems to be a bug in the device's fw */ 989 * seems to be a bug in the device's fw */
886 if (lockdown_check_result(dict, NULL) != RESULT_SUCCESS) { 990 if (lockdown_check_result(dict, NULL) != LOCKDOWN_E_SUCCESS) {
887 ret = LOCKDOWN_E_PAIRING_FAILED; 991 ret = LOCKDOWN_E_PAIRING_FAILED;
888 } 992 }
889 } else { 993 } else {
890 if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) { 994 if (lockdown_check_result(dict, verb) != LOCKDOWN_E_SUCCESS) {
891 ret = LOCKDOWN_E_PAIRING_FAILED; 995 ret = LOCKDOWN_E_PAIRING_FAILED;
892 } 996 }
893 } 997 }
@@ -896,13 +1000,32 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_
896 if (ret == LOCKDOWN_E_SUCCESS) { 1000 if (ret == LOCKDOWN_E_SUCCESS) {
897 debug_info("%s success", verb); 1001 debug_info("%s success", verb);
898 if (!pairing_mode) { 1002 if (!pairing_mode) {
1003 debug_info("internal pairing mode");
899 if (!strcmp("Unpair", verb)) { 1004 if (!strcmp("Unpair", verb)) {
900 /* remove public key from config */ 1005 /* remove public key from config */
901 userpref_remove_device_public_key(client->uuid); 1006 userpref_delete_pair_record(client->device->udid);
902 } else { 1007 } else {
903 /* store public key in config */ 1008 if (!strcmp("Pair", verb)) {
904 userpref_set_device_public_key(client->uuid, public_key); 1009 /* add returned escrow bag if available */
1010 plist_t extra_node = plist_dict_get_item(dict, USERPREF_ESCROW_BAG_KEY);
1011 if (extra_node && plist_get_node_type(extra_node) == PLIST_DATA) {
1012 debug_info("Saving EscrowBag from response in pair record");
1013 plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(extra_node));
1014 }
1015
1016 /* save previously retrieved wifi mac address in pair record */
1017 if (wifi_node) {
1018 debug_info("Saving WiFiAddress from device in pair record");
1019 plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_node));
1020 plist_free(wifi_node);
1021 wifi_node = NULL;
1022 }
1023
1024 userpref_save_pair_record(client->device->udid, client->device->mux_id, pair_record_plist);
1025 }
905 } 1026 }
1027 } else {
1028 debug_info("external pairing mode");
906 } 1029 }
907 } else { 1030 } else {
908 debug_info("%s failure", verb); 1031 debug_info("%s failure", verb);
@@ -914,92 +1037,60 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_
914 plist_get_string_val(error_node, &value); 1037 plist_get_string_val(error_node, &value);
915 if (value) { 1038 if (value) {
916 /* the first pairing fails if the device is password protected */ 1039 /* the first pairing fails if the device is password protected */
917 if (!strcmp(value, "PasswordProtected")) { 1040 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); 1041 free(value);
923 } 1042 }
924
925 plist_free(error_node);
926 error_node = NULL;
927 } 1043 }
928 } 1044 }
929 plist_free(dict); 1045
930 dict = NULL; 1046 if (pair_record_plist) {
931 if (public_key.data) 1047 plist_free(pair_record_plist);
932 free(public_key.data); 1048 pair_record_plist = NULL;
1049 }
1050
1051 if (wifi_node) {
1052 plist_free(wifi_node);
1053 wifi_node = NULL;
1054 }
1055
1056 if (result) {
1057 *result = dict;
1058 } else {
1059 plist_free(dict);
1060 dict = NULL;
1061 }
1062
933 return ret; 1063 return ret;
934} 1064}
935 1065
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) 1066lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
951{ 1067{
952 return lockdownd_do_pair(client, pair_record, "Pair"); 1068
1069 plist_t options = plist_new_dict();
1070 plist_dict_set_item(options, "ExtendedPairingErrors", plist_new_bool(1));
1071
1072 lockdownd_error_t ret = lockdownd_do_pair(client, pair_record, "Pair", options, NULL);
1073
1074 plist_free(options);
1075
1076 return ret;
1077}
1078
1079lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response)
1080{
1081 return lockdownd_do_pair(client, pair_record, "Pair", options, response);
953} 1082}
954 1083
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) 1084lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
973{ 1085{
974 return lockdownd_do_pair(client, pair_record, "ValidatePair"); 1086 return lockdownd_do_pair(client, pair_record, "ValidatePair", NULL, NULL);
975} 1087}
976 1088
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) 1089lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record)
992{ 1090{
993 return lockdownd_do_pair(client, pair_record, "Unpair"); 1091 return lockdownd_do_pair(client, pair_record, "Unpair", NULL, NULL);
994} 1092}
995 1093
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) 1094lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1004{ 1095{
1005 if (!client) 1096 if (!client)
@@ -1009,7 +1100,7 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1009 1100
1010 plist_t dict = plist_new_dict(); 1101 plist_t dict = plist_new_dict();
1011 plist_dict_add_label(dict, client->label); 1102 plist_dict_add_label(dict, client->label);
1012 plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery")); 1103 plist_dict_set_item(dict,"Request", plist_new_string("EnterRecovery"));
1013 1104
1014 debug_info("telling device to enter recovery mode"); 1105 debug_info("telling device to enter recovery mode");
1015 1106
@@ -1019,23 +1110,17 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client)
1019 1110
1020 ret = lockdownd_receive(client, &dict); 1111 ret = lockdownd_receive(client, &dict);
1021 1112
1022 if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) { 1113 ret = lockdown_check_result(dict, "EnterRecovery");
1114 if (ret == LOCKDOWN_E_SUCCESS) {
1023 debug_info("success"); 1115 debug_info("success");
1024 ret = LOCKDOWN_E_SUCCESS;
1025 } 1116 }
1117
1026 plist_free(dict); 1118 plist_free(dict);
1027 dict = NULL; 1119 dict = NULL;
1120
1028 return ret; 1121 return ret;
1029} 1122}
1030 1123
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) 1124lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1040{ 1125{
1041 if (!client) 1126 if (!client)
@@ -1045,7 +1130,7 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1045 1130
1046 plist_t dict = plist_new_dict(); 1131 plist_t dict = plist_new_dict();
1047 plist_dict_add_label(dict, client->label); 1132 plist_dict_add_label(dict, client->label);
1048 plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); 1133 plist_dict_set_item(dict,"Request", plist_new_string("Goodbye"));
1049 1134
1050 debug_info("called"); 1135 debug_info("called");
1051 1136
@@ -1059,187 +1144,17 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client)
1059 return LOCKDOWN_E_PLIST_ERROR; 1144 return LOCKDOWN_E_PLIST_ERROR;
1060 } 1145 }
1061 1146
1062 if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) { 1147 ret = lockdown_check_result(dict, "Goodbye");
1148 if (ret == LOCKDOWN_E_SUCCESS) {
1063 debug_info("success"); 1149 debug_info("success");
1064 ret = LOCKDOWN_E_SUCCESS;
1065 } 1150 }
1151
1066 plist_free(dict); 1152 plist_free(dict);
1067 dict = NULL; 1153 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 1154
1227 return ret; 1155 return ret;
1228} 1156}
1229 1157
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) 1158lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled)
1244{ 1159{
1245 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; 1160 lockdownd_error_t ret = LOCKDOWN_E_SUCCESS;
@@ -1251,14 +1166,28 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1251 /* if we have a running session, stop current one first */ 1166 /* if we have a running session, stop current one first */
1252 if (client->session_id) { 1167 if (client->session_id) {
1253 lockdownd_stop_session(client, client->session_id); 1168 lockdownd_stop_session(client, client->session_id);
1254 free(client->session_id);
1255 } 1169 }
1256 1170
1257 /* setup request plist */ 1171 /* setup request plist */
1258 dict = plist_new_dict(); 1172 dict = plist_new_dict();
1259 plist_dict_add_label(dict, client->label); 1173 plist_dict_add_label(dict, client->label);
1260 plist_dict_insert_item(dict,"HostID", plist_new_string(host_id)); 1174 plist_dict_set_item(dict,"Request", plist_new_string("StartSession"));
1261 plist_dict_insert_item(dict,"Request", plist_new_string("StartSession")); 1175
1176 /* add host id */
1177 if (host_id) {
1178 plist_dict_set_item(dict, "HostID", plist_new_string(host_id));
1179 }
1180
1181 /* add system buid */
1182 char *system_buid = NULL;
1183 userpref_read_system_buid(&system_buid);
1184 if (system_buid) {
1185 plist_dict_set_item(dict, "SystemBUID", plist_new_string(system_buid));
1186 if (system_buid) {
1187 free(system_buid);
1188 system_buid = NULL;
1189 }
1190 }
1262 1191
1263 ret = lockdownd_send(client, dict); 1192 ret = lockdownd_send(client, dict);
1264 plist_free(dict); 1193 plist_free(dict);
@@ -1272,17 +1201,8 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1272 if (!dict) 1201 if (!dict)
1273 return LOCKDOWN_E_PLIST_ERROR; 1202 return LOCKDOWN_E_PLIST_ERROR;
1274 1203
1275 if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) { 1204 ret = lockdown_check_result(dict, "StartSession");
1276 plist_t error_node = plist_dict_get_item(dict, "Error"); 1205 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; 1206 uint8_t use_ssl = 0;
1287 1207
1288 plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); 1208 plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL");
@@ -1299,6 +1219,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)) { 1219 if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) {
1300 plist_get_string_val(session_node, &client->session_id); 1220 plist_get_string_val(session_node, &client->session_id);
1301 } 1221 }
1222
1302 if (client->session_id) { 1223 if (client->session_id) {
1303 debug_info("SessionID: %s", client->session_id); 1224 debug_info("SessionID: %s", client->session_id);
1304 if (session_id != NULL) 1225 if (session_id != NULL)
@@ -1306,18 +1227,15 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1306 } else { 1227 } else {
1307 debug_info("Failed to get SessionID!"); 1228 debug_info("Failed to get SessionID!");
1308 } 1229 }
1309 debug_info("Enable SSL Session: %s", (use_ssl?"true":"false")); 1230
1231 debug_info("Enable SSL Session: %s", (use_ssl ? "true" : "false"));
1232
1310 if (use_ssl) { 1233 if (use_ssl) {
1311 ret = property_list_service_enable_ssl(client->parent); 1234 ret = lockdownd_error(property_list_service_enable_ssl(client->parent));
1312 if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) { 1235 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 { 1236 } else {
1319 client->ssl_enabled = 0;
1320 ret = LOCKDOWN_E_SUCCESS; 1237 ret = LOCKDOWN_E_SUCCESS;
1238 client->ssl_enabled = 0;
1321 } 1239 }
1322 } 1240 }
1323 1241
@@ -1328,40 +1246,96 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char
1328} 1246}
1329 1247
1330/** 1248/**
1331 * Requests to start a service and retrieve it's port on success. 1249 * Internal function used by lockdownd_do_start_service to create the
1250 * StartService request's plist.
1332 * 1251 *
1333 * @param client The lockdownd client 1252 * @param client The lockdownd client
1334 * @param service The name of the service to start 1253 * @param identifier The identifier of the service to start
1335 * @param port The port number the service was started on 1254 * @param send_escrow_bag Should we send the device's escrow bag with the request
1336 1255 * @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 1256 *
1257 * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_CONF on failure
1258 * to read the escrow bag from the device's record (when used).
1259 */
1260static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_t client, const char *identifier, int send_escrow_bag, plist_t *request)
1261{
1262 plist_t dict = plist_new_dict();
1263
1264 /* create the basic request params */
1265 plist_dict_add_label(dict, client->label);
1266 plist_dict_set_item(dict, "Request", plist_new_string("StartService"));
1267 plist_dict_set_item(dict, "Service", plist_new_string(identifier));
1268
1269 /* if needed - get the escrow bag for the device and send it with the request */
1270 if (send_escrow_bag) {
1271 /* get the pairing record */
1272 plist_t pair_record = NULL;
1273 userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record);
1274 if (uerr == USERPREF_E_READ_ERROR) {
1275 debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid);
1276 plist_free(dict);
1277 return LOCKDOWN_E_RECEIVE_TIMEOUT;
1278 } else if (uerr == USERPREF_E_NOENT) {
1279 debug_info("ERROR: No pair record for %s", client->device->udid);
1280 plist_free(dict);
1281 return LOCKDOWN_E_INVALID_CONF;
1282 } else if (uerr != USERPREF_E_SUCCESS) {
1283 debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid);
1284 plist_free(dict);
1285 return LOCKDOWN_E_INVALID_CONF;
1286 }
1287
1288 /* try to read the escrow bag from the record */
1289 plist_t escrow_bag = plist_dict_get_item(pair_record, USERPREF_ESCROW_BAG_KEY);
1290 if (!escrow_bag || (PLIST_DATA != plist_get_node_type(escrow_bag))) {
1291 debug_info("ERROR: Failed to retrieve the escrow bag from the device's record");
1292 plist_free(dict);
1293 plist_free(pair_record);
1294 return LOCKDOWN_E_INVALID_CONF;
1295 }
1296
1297 debug_info("Adding escrow bag to StartService for %s", identifier);
1298 plist_dict_set_item(dict, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag));
1299 plist_free(pair_record);
1300 }
1301
1302 *request = dict;
1303 return LOCKDOWN_E_SUCCESS;
1304}
1305
1306/**
1307 * Function used internally by lockdownd_start_service and lockdownd_start_service_with_escrow_bag.
1308 *
1309 * @param client The lockdownd client
1310 * @param identifier The identifier of the service to start
1311 * @param send_escrow_bag Should we send the device's escrow bag with the request
1312 * @param descriptor The service descriptor on success or NULL on failure
1313 *
1314 * @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 1315 * 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 1316 * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because
1340 * started by the device 1317 * started by the device, LOCKDOWN_E_INVALID_CONF if the host id or escrow bag (when
1318 * used) are missing from the device record.
1341 */ 1319 */
1342lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port) 1320static lockdownd_error_t lockdownd_do_start_service(lockdownd_client_t client, const char *identifier, int send_escrow_bag, lockdownd_service_descriptor_t *service)
1343{ 1321{
1344 if (!client || !service || !port) 1322 if (!client || !identifier || !service)
1345 return LOCKDOWN_E_INVALID_ARG; 1323 return LOCKDOWN_E_INVALID_ARG;
1346 1324
1347 char *host_id = NULL; 1325 if (*service) {
1348 userpref_get_host_id(&host_id); 1326 // reset fields if service descriptor is reused
1349 if (!host_id) 1327 (*service)->port = 0;
1350 return LOCKDOWN_E_INVALID_CONF; 1328 (*service)->ssl_enabled = 0;
1351 if (!client->session_id) 1329 }
1352 return LOCKDOWN_E_NO_RUNNING_SESSION;
1353 1330
1354 plist_t dict = NULL; 1331 plist_t dict = NULL;
1355 uint16_t port_loc = 0; 1332 uint16_t port_loc = 0;
1356 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; 1333 lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR;
1357 1334
1358 free(host_id); 1335 /* create StartService request */
1359 host_id = NULL; 1336 ret = lockdownd_build_start_service_request(client, identifier, send_escrow_bag, &dict);
1360 1337 if (LOCKDOWN_E_SUCCESS != ret)
1361 dict = plist_new_dict(); 1338 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 1339
1366 /* send to device */ 1340 /* send to device */
1367 ret = lockdownd_send(client, dict); 1341 ret = lockdownd_send(client, dict);
@@ -1379,57 +1353,63 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char
1379 if (!dict) 1353 if (!dict)
1380 return LOCKDOWN_E_PLIST_ERROR; 1354 return LOCKDOWN_E_PLIST_ERROR;
1381 1355
1382 ret = LOCKDOWN_E_UNKNOWN_ERROR; 1356 ret = lockdown_check_result(dict, "StartService");
1383 if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) { 1357 if (ret == LOCKDOWN_E_SUCCESS) {
1384 plist_t port_value_node = plist_dict_get_item(dict, "Port"); 1358 if (*service == NULL)
1385 1359 *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)) { 1360 (*service)->port = 0;
1361 (*service)->ssl_enabled = 0;
1362 (*service)->identifier = strdup(identifier);
1363
1364 /* read service port number */
1365 plist_t node = plist_dict_get_item(dict, "Port");
1366 if (node && (plist_get_node_type(node) == PLIST_UINT)) {
1387 uint64_t port_value = 0; 1367 uint64_t port_value = 0;
1388 plist_get_uint_val(port_value_node, &port_value); 1368 plist_get_uint_val(node, &port_value);
1389 1369
1390 if (port_value) { 1370 if (port_value) {
1391 port_loc = port_value; 1371 port_loc = port_value;
1392 ret = LOCKDOWN_E_SUCCESS; 1372 ret = LOCKDOWN_E_SUCCESS;
1393 } 1373 }
1394 if (port && ret == LOCKDOWN_E_SUCCESS) 1374 if (port_loc && ret == LOCKDOWN_E_SUCCESS) {
1395 *port = port_loc; 1375 (*service)->port = port_loc;
1376 }
1377 }
1378
1379 /* check if the service requires SSL */
1380 node = plist_dict_get_item(dict, "EnableServiceSSL");
1381 if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) {
1382 uint8_t b = 0;
1383 plist_get_bool_val(node, &b);
1384 (*service)->ssl_enabled = b;
1396 } 1385 }
1397 } else { 1386 } else {
1398 ret = LOCKDOWN_E_START_SERVICE_FAILED;
1399 plist_t error_node = plist_dict_get_item(dict, "Error"); 1387 plist_t error_node = plist_dict_get_item(dict, "Error");
1400 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { 1388 if (error_node && PLIST_STRING == plist_get_node_type(error_node)) {
1401 char *error = NULL; 1389 char *error = NULL;
1402 plist_get_string_val(error_node, &error); 1390 plist_get_string_val(error_node, &error);
1403 if (!strcmp(error, "InvalidService")) { 1391 ret = lockdownd_strtoerr(error);
1404 ret = LOCKDOWN_E_INVALID_SERVICE;
1405 }
1406 free(error); 1392 free(error);
1407 } 1393 }
1408 } 1394 }
1409 1395
1410 plist_free(dict); 1396 plist_free(dict);
1411 dict = NULL; 1397 dict = NULL;
1398
1412 return ret; 1399 return ret;
1413} 1400}
1414 1401
1415/** 1402lockdownd_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. 1403{
1417 * The ActivationRecord plist dictionary must be obtained using the 1404 return lockdownd_do_start_service(client, identifier, 0, service);
1418 * activation protocol requesting from Apple's https webservice. 1405}
1419 * 1406
1420 * @see http://iphone-docs.org/doku.php?id=docs:protocols:activation 1407lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service)
1421 * 1408{
1422 * @param client The lockdown client 1409 return lockdownd_do_start_service(client, identifier, 1, service);
1423 * @param activation_record The activation record plist dictionary 1410}
1424 * 1411
1425 * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or 1412lockdownd_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{ 1413{
1434 if (!client) 1414 if (!client)
1435 return LOCKDOWN_E_INVALID_ARG; 1415 return LOCKDOWN_E_INVALID_ARG;
@@ -1444,8 +1424,8 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati
1444 1424
1445 plist_t dict = plist_new_dict(); 1425 plist_t dict = plist_new_dict();
1446 plist_dict_add_label(dict, client->label); 1426 plist_dict_add_label(dict, client->label);
1447 plist_dict_insert_item(dict,"Request", plist_new_string("Activate")); 1427 plist_dict_set_item(dict,"Request", plist_new_string("Activate"));
1448 plist_dict_insert_item(dict,"ActivationRecord", plist_copy(activation_record)); 1428 plist_dict_set_item(dict,"ActivationRecord", plist_copy(activation_record));
1449 1429
1450 ret = lockdownd_send(client, dict); 1430 ret = lockdownd_send(client, dict);
1451 plist_free(dict); 1431 plist_free(dict);
@@ -1457,39 +1437,17 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati
1457 return LOCKDOWN_E_PLIST_ERROR; 1437 return LOCKDOWN_E_PLIST_ERROR;
1458 } 1438 }
1459 1439
1460 ret = LOCKDOWN_E_ACTIVATION_FAILED; 1440 ret = lockdown_check_result(dict, "Activate");
1461 if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) { 1441 if (ret == LOCKDOWN_E_SUCCESS) {
1462 debug_info("success"); 1442 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 } 1443 }
1476 1444
1477 plist_free(dict); 1445 plist_free(dict);
1478 dict = NULL; 1446 dict = NULL;
1479 1447
1480 return ret; 1448 return ret;
1481} 1449}
1482 1450
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) 1451lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1494{ 1452{
1495 if (!client) 1453 if (!client)
@@ -1502,7 +1460,7 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1502 1460
1503 plist_t dict = plist_new_dict(); 1461 plist_t dict = plist_new_dict();
1504 plist_dict_add_label(dict, client->label); 1462 plist_dict_add_label(dict, client->label);
1505 plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate")); 1463 plist_dict_set_item(dict,"Request", plist_new_string("Deactivate"));
1506 1464
1507 ret = lockdownd_send(client, dict); 1465 ret = lockdownd_send(client, dict);
1508 plist_free(dict); 1466 plist_free(dict);
@@ -1514,11 +1472,11 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client)
1514 return LOCKDOWN_E_PLIST_ERROR; 1472 return LOCKDOWN_E_PLIST_ERROR;
1515 } 1473 }
1516 1474
1517 ret = LOCKDOWN_E_UNKNOWN_ERROR; 1475 ret = lockdown_check_result(dict, "Deactivate");
1518 if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) { 1476 if (ret == LOCKDOWN_E_SUCCESS) {
1519 debug_info("success"); 1477 debug_info("success");
1520 ret = LOCKDOWN_E_SUCCESS;
1521 } 1478 }
1479
1522 plist_free(dict); 1480 plist_free(dict);
1523 dict = NULL; 1481 dict = NULL;
1524 1482
@@ -1537,19 +1495,6 @@ static void str_remove_spaces(char *source)
1537 *dest = 0; 1495 *dest = 0;
1538} 1496}
1539 1497
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) 1498lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count)
1554{ 1499{
1555 if (!client) 1500 if (!client)
@@ -1583,14 +1528,16 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha
1583 } 1528 }
1584 1529
1585 while((value = plist_array_get_item(dict, *count)) != NULL) { 1530 while((value = plist_array_get_item(dict, *count)) != NULL) {
1586 plist_get_string_val(value, &val); 1531 plist_get_string_val(value, &val);
1587 newlist = realloc(*classes, sizeof(char*) * (*count+1)); 1532 newlist = realloc(*classes, sizeof(char*) * (*count+1));
1588 str_remove_spaces(val); 1533 str_remove_spaces(val);
1589 asprintf(&newlist[*count], "com.apple.%s", val); 1534 if (asprintf(&newlist[*count], "com.apple.%s", val) < 0) {
1590 free(val); 1535 debug_info("ERROR: asprintf failed");
1591 val = NULL; 1536 }
1592 *classes = newlist; 1537 free(val);
1593 *count = *count+1; 1538 val = NULL;
1539 *classes = newlist;
1540 *count = *count+1;
1594 } 1541 }
1595 1542
1596 newlist = realloc(*classes, sizeof(char*) * (*count+1)); 1543 newlist = realloc(*classes, sizeof(char*) * (*count+1));
@@ -1603,14 +1550,6 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha
1603 return LOCKDOWN_E_SUCCESS; 1550 return LOCKDOWN_E_SUCCESS;
1604} 1551}
1605 1552
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) 1553lockdownd_error_t lockdownd_data_classes_free(char **classes)
1615{ 1554{
1616 if (classes) { 1555 if (classes) {
@@ -1622,3 +1561,51 @@ lockdownd_error_t lockdownd_data_classes_free(char **classes)
1622 } 1561 }
1623 return LOCKDOWN_E_SUCCESS; 1562 return LOCKDOWN_E_SUCCESS;
1624} 1563}
1564
1565lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service)
1566{
1567 if (service) {
1568 free(service->identifier);
1569 free(service);
1570 }
1571
1572 return LOCKDOWN_E_SUCCESS;
1573}
1574
1575const char* lockdownd_strerror(lockdownd_error_t err)
1576{
1577 switch (err) {
1578 case LOCKDOWN_E_SUCCESS:
1579 return "Success";
1580 case LOCKDOWN_E_INVALID_ARG:
1581 return "Invalid argument";
1582 case LOCKDOWN_E_INVALID_CONF:
1583 return "Invalid configuration";
1584 case LOCKDOWN_E_PLIST_ERROR:
1585 return "PropertyList error";
1586 case LOCKDOWN_E_PAIRING_FAILED:
1587 return "Pairing failed";
1588 case LOCKDOWN_E_SSL_ERROR:
1589 return "SSL error";
1590 case LOCKDOWN_E_DICT_ERROR:
1591 return "Invalid dictionary";
1592 case LOCKDOWN_E_RECEIVE_TIMEOUT:
1593 return "Receive timeout";
1594 case LOCKDOWN_E_MUX_ERROR:
1595 return "Mux error";
1596 case LOCKDOWN_E_NO_RUNNING_SESSION:
1597 return "No running session";
1598 case LOCKDOWN_E_UNKNOWN_ERROR:
1599 return "Unknown Error";
1600 default: {
1601 int i = 0;
1602 while (lockdownd_error_str_map[i].lockdown_errstr) {
1603 if (lockdownd_error_str_map[i].errcode == err) {
1604 return lockdownd_error_str_map[i].errstr;
1605 }
1606 i++;
1607 }
1608 } break;
1609 }
1610 return "Unknown Error";
1611}
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..3fdca4d
--- /dev/null
+++ b/src/misagent.c
@@ -0,0 +1,294 @@
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 <stdio.h>
28
29#ifndef _MSC_VER
30#include <unistd.h>
31#endif
32
33#include <plist/plist.h>
34
35#include "misagent.h"
36#include "property_list_service.h"
37#include "common/debug.h"
38
39/**
40 * Convert a property_list_service_error_t value to a misagent_error_t
41 * value. Used internally to get correct error codes.
42 *
43 * @param err A property_list_service_error_t error code
44 *
45 * @return A matching misagent_error_t error code,
46 * MISAGENT_E_UNKNOWN_ERROR otherwise.
47 */
48static misagent_error_t misagent_error(property_list_service_error_t err)
49{
50 switch (err) {
51 case PROPERTY_LIST_SERVICE_E_SUCCESS:
52 return MISAGENT_E_SUCCESS;
53 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
54 return MISAGENT_E_INVALID_ARG;
55 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
56 return MISAGENT_E_PLIST_ERROR;
57 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
58 return MISAGENT_E_CONN_FAILED;
59 default:
60 break;
61 }
62 return MISAGENT_E_UNKNOWN_ERROR;
63}
64
65/**
66 * Checks the response from misagent to determine if the operation
67 * was successful or an error occurred. Internally used only.
68 *
69 * @param response a PLIST_DICT received from device's misagent
70 * @param status_code pointer to an int that will be set to the status code
71 * contained in the response
72 */
73static misagent_error_t misagent_check_result(plist_t response, int* status_code)
74{
75 if (plist_get_node_type(response) != PLIST_DICT) {
76 return MISAGENT_E_PLIST_ERROR;
77 }
78
79 plist_t node = plist_dict_get_item(response, "Status");
80 if (!node || (plist_get_node_type(node) != PLIST_UINT)) {
81 return MISAGENT_E_PLIST_ERROR;
82 }
83
84 uint64_t val = -1LL;
85 plist_get_uint_val(node, &val);
86 if ((int64_t)val == -1LL) {
87 return MISAGENT_E_PLIST_ERROR;
88 }
89 *status_code = (int)(val & 0xFFFFFFFF);
90 if (*status_code == 0) {
91 return MISAGENT_E_SUCCESS;
92 }
93 return MISAGENT_E_REQUEST_FAILED;
94}
95
96misagent_error_t misagent_client_new(idevice_t device, lockdownd_service_descriptor_t service, misagent_client_t *client)
97{
98 property_list_service_client_t plistclient = NULL;
99 misagent_error_t err = misagent_error(property_list_service_client_new(device, service, &plistclient));
100 if (err != MISAGENT_E_SUCCESS) {
101 return err;
102 }
103
104 misagent_client_t client_loc = (misagent_client_t) malloc(sizeof(struct misagent_client_private));
105 client_loc->parent = plistclient;
106 client_loc->last_error = 0;
107
108 *client = client_loc;
109 return MISAGENT_E_SUCCESS;
110}
111
112misagent_error_t misagent_client_start_service(idevice_t device, misagent_client_t * client, const char* label)
113{
114 misagent_error_t err = MISAGENT_E_UNKNOWN_ERROR;
115 service_client_factory_start_service(device, MISAGENT_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(misagent_client_new), &err);
116 return err;
117}
118
119misagent_error_t misagent_client_free(misagent_client_t client)
120{
121 if (!client)
122 return MISAGENT_E_INVALID_ARG;
123
124 misagent_error_t err = MISAGENT_E_SUCCESS;
125 if (client->parent && client->parent->parent) {
126 misagent_error(property_list_service_client_free(client->parent));
127 }
128 client->parent = NULL;
129 free(client);
130
131 return err;
132}
133
134misagent_error_t misagent_install(misagent_client_t client, plist_t profile)
135{
136 if (!client || !client->parent || !profile || (plist_get_node_type(profile) != PLIST_DATA))
137 return MISAGENT_E_INVALID_ARG;
138
139 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
140
141 plist_t dict = plist_new_dict();
142 plist_dict_set_item(dict, "MessageType", plist_new_string("Install"));
143 plist_dict_set_item(dict, "Profile", plist_copy(profile));
144 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
145
146 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
147 plist_free(dict);
148 dict = NULL;
149
150 if (res != MISAGENT_E_SUCCESS) {
151 debug_info("could not send plist, error %d", res);
152 return res;
153 }
154
155 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
156 if (res != MISAGENT_E_SUCCESS) {
157 debug_info("could not receive response, error %d", res);
158 return res;
159 }
160 if (!dict) {
161 debug_info("could not get response plist");
162 return MISAGENT_E_UNKNOWN_ERROR;
163 }
164
165 res = misagent_check_result(dict, &client->last_error);
166 plist_free(dict);
167
168 return res;
169}
170
171misagent_error_t misagent_copy(misagent_client_t client, plist_t* profiles)
172{
173 if (!client || !client->parent || !profiles)
174 return MISAGENT_E_INVALID_ARG;
175
176 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
177
178 plist_t dict = plist_new_dict();
179 plist_dict_set_item(dict, "MessageType", plist_new_string("Copy"));
180 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
181
182 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
183 plist_free(dict);
184 dict = NULL;
185
186 if (res != MISAGENT_E_SUCCESS) {
187 debug_info("could not send plist, error %d", res);
188 return res;
189 }
190
191 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
192 if (res != MISAGENT_E_SUCCESS) {
193 debug_info("could not receive response, error %d", res);
194 return res;
195 }
196 if (!dict) {
197 debug_info("could not get response plist");
198 return MISAGENT_E_UNKNOWN_ERROR;
199 }
200
201 res = misagent_check_result(dict, &client->last_error);
202 if (res == MISAGENT_E_SUCCESS) {
203 *profiles = plist_copy(plist_dict_get_item(dict, "Payload"));
204 }
205 plist_free(dict);
206
207 return res;
208
209}
210
211misagent_error_t misagent_copy_all(misagent_client_t client, plist_t* profiles)
212{
213 if (!client || !client->parent || !profiles)
214 return MISAGENT_E_INVALID_ARG;
215
216 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
217
218 plist_t dict = plist_new_dict();
219 plist_dict_set_item(dict, "MessageType", plist_new_string("CopyAll"));
220 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
221
222 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
223 plist_free(dict);
224 dict = NULL;
225
226 if (res != MISAGENT_E_SUCCESS) {
227 debug_info("could not send plist, error %d", res);
228 return res;
229 }
230
231 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
232 if (res != MISAGENT_E_SUCCESS) {
233 debug_info("could not receive response, error %d", res);
234 return res;
235 }
236 if (!dict) {
237 debug_info("could not get response plist");
238 return MISAGENT_E_UNKNOWN_ERROR;
239 }
240
241 res = misagent_check_result(dict, &client->last_error);
242 if (res == MISAGENT_E_SUCCESS) {
243 *profiles = plist_copy(plist_dict_get_item(dict, "Payload"));
244 }
245 plist_free(dict);
246
247 return res;
248
249}
250
251misagent_error_t misagent_remove(misagent_client_t client, const char* profileID)
252{
253 if (!client || !client->parent || !profileID)
254 return MISAGENT_E_INVALID_ARG;
255
256 client->last_error = MISAGENT_E_UNKNOWN_ERROR;
257
258 plist_t dict = plist_new_dict();
259 plist_dict_set_item(dict, "MessageType", plist_new_string("Remove"));
260 plist_dict_set_item(dict, "ProfileID", plist_new_string(profileID));
261 plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning"));
262
263 misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict));
264 plist_free(dict);
265 dict = NULL;
266
267 if (res != MISAGENT_E_SUCCESS) {
268 debug_info("could not send plist, error %d", res);
269 return res;
270 }
271
272 res = misagent_error(property_list_service_receive_plist(client->parent, &dict));
273 if (res != MISAGENT_E_SUCCESS) {
274 debug_info("could not receive response, error %d", res);
275 return res;
276 }
277 if (!dict) {
278 debug_info("could not get response plist");
279 return MISAGENT_E_UNKNOWN_ERROR;
280 }
281
282 res = misagent_check_result(dict, &client->last_error);
283 plist_free(dict);
284
285 return res;
286}
287
288int misagent_get_status_code(misagent_client_t client)
289{
290 if (!client) {
291 return -1;
292 }
293 return client->last_error;
294}
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..6677882 100644
--- a/src/mobile_image_mounter.c
+++ b/src/mobile_image_mounter.c
@@ -2,31 +2,38 @@
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>
27
28#ifndef _MSC_VER
24#include <unistd.h> 29#include <unistd.h>
30#endif
31
25#include <plist/plist.h> 32#include <plist/plist.h>
26 33
27#include "mobile_image_mounter.h" 34#include "mobile_image_mounter.h"
28#include "property_list_service.h" 35#include "property_list_service.h"
29#include "debug.h" 36#include "common/debug.h"
30 37
31/** 38/**
32 * Locks a mobile_image_mounter client, used for thread safety. 39 * Locks a mobile_image_mounter client, used for thread safety.
@@ -35,17 +42,17 @@
35 */ 42 */
36static void mobile_image_mounter_lock(mobile_image_mounter_client_t client) 43static void mobile_image_mounter_lock(mobile_image_mounter_client_t client)
37{ 44{
38 g_mutex_lock(client->mutex); 45 mutex_lock(&client->mutex);
39} 46}
40 47
41/** 48/**
42 * Unlocks a mobile_image_mounter client, used for thread safety. 49 * Unlocks a mobile_image_mounter client, used for thread safety.
43 * 50 *
44 * @param client mobile_image_mounter client to unlock 51 * @param client mobile_image_mounter client to unlock
45 */ 52 */
46static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client) 53static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client)
47{ 54{
48 g_mutex_unlock(client->mutex); 55 mutex_unlock(&client->mutex);
49} 56}
50 57
51/** 58/**
@@ -75,51 +82,30 @@ static mobile_image_mounter_error_t mobile_image_mounter_error(property_list_ser
75 return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; 82 return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
76} 83}
77 84
78/** 85mobile_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{ 86{
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; 87 property_list_service_client_t plistclient = NULL;
101 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 88 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; 89 if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
90 return err;
103 } 91 }
104 92
105 mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_private)); 93 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; 94 client_loc->parent = plistclient;
107 95
108 client_loc->mutex = g_mutex_new(); 96 mutex_init(&client_loc->mutex);
109 97
110 *client = client_loc; 98 *client = client_loc;
111 return MOBILE_IMAGE_MOUNTER_E_SUCCESS; 99 return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
112} 100}
113 101
114/** 102mobile_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 103{
116 * mobile_image_mounter client data. 104 mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
117 * 105 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. 106 return err;
119 * 107}
120 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, 108
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) 109mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client)
124{ 110{
125 if (!client) 111 if (!client)
@@ -127,27 +113,12 @@ mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_clie
127 113
128 property_list_service_client_free(client->parent); 114 property_list_service_client_free(client->parent);
129 client->parent = NULL; 115 client->parent = NULL;
130 if (client->mutex) { 116 mutex_destroy(&client->mutex);
131 g_mutex_free(client->mutex);
132 }
133 free(client); 117 free(client);
134 118
135 return MOBILE_IMAGE_MOUNTER_E_SUCCESS; 119 return MOBILE_IMAGE_MOUNTER_E_SUCCESS;
136} 120}
137 121
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) 122mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result)
152{ 123{
153 if (!client || !image_type || !result) { 124 if (!client || !image_type || !result) {
@@ -156,8 +127,8 @@ mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_moun
156 mobile_image_mounter_lock(client); 127 mobile_image_mounter_lock(client);
157 128
158 plist_t dict = plist_new_dict(); 129 plist_t dict = plist_new_dict();
159 plist_dict_insert_item(dict,"Command", plist_new_string("LookupImage")); 130 plist_dict_set_item(dict,"Command", plist_new_string("LookupImage"));
160 plist_dict_insert_item(dict,"ImageType", plist_new_string(image_type)); 131 plist_dict_set_item(dict,"ImageType", plist_new_string(image_type));
161 132
162 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); 133 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
163 plist_free(dict); 134 plist_free(dict);
@@ -177,39 +148,139 @@ leave_unlock:
177 return res; 148 return res;
178} 149}
179 150
180/** 151static mobile_image_mounter_error_t process_result(plist_t result, const char *expected_status)
181 * Mounts an image on the device. 152{
182 * 153 mobile_image_mounter_error_t res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
183 * @param client The connected mobile_image_mounter client. 154 char* strval = NULL;
184 * @param image_path The absolute path of the image to mount. The image must 155 plist_t node;
185 * be present before calling this function. 156
186 * @param image_signature Pointer to a buffer holding the images' signature 157 node = plist_dict_get_item(result, "Error");
187 * @param signature_length Length of the signature image_signature points to 158 if (node && plist_get_node_type(node) == PLIST_STRING) {
188 * @param image_type Type of image to mount 159 plist_get_string_val(node, &strval);
189 * @param result Pointer to a plist that will receive the result of the 160 }
190 * operation. 161 if (strval) {
191 * 162 if (!strcmp(strval, "DeviceLocked")) {
192 * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the 163 debug_info("Device is locked, can't mount");
193 * operation has failed. Check the resulting plist for further information. 164 res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED;
194 * Note that there is no unmounting function. The mount persists until the 165 } else {
195 * device is rebooted. 166 debug_info("Unhandled error '%s' received", strval);
196 * 167 }
197 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, 168 free(strval);
198 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if on ore more parameters are 169 return res;
199 * invalid, or another error code otherwise. 170 }
200 */ 171
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) 172 node = plist_dict_get_item(result, "Status");
173 if (node && plist_get_node_type(node) == PLIST_STRING) {
174 plist_get_string_val(node, &strval);
175 }
176 if (!strval) {
177 debug_info("Error: Unexpected response received!");
178 } else if (strcmp(strval, expected_status) == 0) {
179 res = MOBILE_IMAGE_MOUNTER_E_SUCCESS;
180 } else {
181 debug_info("Error: didn't get %s but %s", expected_status, strval);
182 }
183 free(strval);
184
185 return res;
186}
187
188mobile_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)
202{ 189{
203 if (!client || !image_path || !image_signature || (signature_length == 0) || !image_type || !result) { 190 if (!client || !image_type || (image_size == 0) || !upload_cb) {
204 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; 191 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
205 } 192 }
206 mobile_image_mounter_lock(client); 193 mobile_image_mounter_lock(client);
194 plist_t result = NULL;
207 195
208 plist_t dict = plist_new_dict(); 196 plist_t dict = plist_new_dict();
209 plist_dict_insert_item(dict, "Command", plist_new_string("MountImage")); 197 plist_dict_set_item(dict, "Command", plist_new_string("ReceiveBytes"));
210 plist_dict_insert_item(dict, "ImagePath", plist_new_string(image_path)); 198 if (signature && signature_size != 0)
211 plist_dict_insert_item(dict, "ImageSignature", plist_new_data(image_signature, signature_length)); 199 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
212 plist_dict_insert_item(dict, "ImageType", plist_new_string(image_type)); 200 plist_dict_set_item(dict, "ImageSize", plist_new_uint(image_size));
201 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
202
203 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
204 plist_free(dict);
205
206 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
207 debug_info("Error sending XML plist to device!");
208 goto leave_unlock;
209 }
210
211 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
212 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
213 debug_info("Error receiving response from device!");
214 goto leave_unlock;
215 }
216 res = process_result(result, "ReceiveBytesAck");
217 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
218 goto leave_unlock;
219 }
220
221 size_t tx = 0;
222 size_t bufsize = 65536;
223 unsigned char *buf = (unsigned char*)malloc(bufsize);
224 if (!buf) {
225 debug_info("Out of memory");
226 res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
227 goto leave_unlock;
228 }
229 debug_info("uploading image (%d bytes)", (int)image_size);
230 while (tx < image_size) {
231 size_t remaining = image_size - tx;
232 size_t amount = (remaining < bufsize) ? remaining : bufsize;
233 ssize_t r = upload_cb(buf, amount, userdata);
234 if (r < 0) {
235 debug_info("upload_cb returned %d", (int)r);
236 break;
237 }
238 uint32_t sent = 0;
239 if (service_send(client->parent->parent, (const char*)buf, (uint32_t)r, &sent) != SERVICE_E_SUCCESS) {
240 debug_info("service_send failed");
241 break;
242 }
243 tx += r;
244 }
245 free(buf);
246 if (tx < image_size) {
247 debug_info("Error: failed to upload image");
248 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
249 goto leave_unlock;
250 }
251 debug_info("image uploaded");
252
253 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
254 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
255 debug_info("Error receiving response from device!");
256 goto leave_unlock;
257 }
258 res = process_result(result, "Complete");
259
260leave_unlock:
261 mobile_image_mounter_unlock(client);
262 if (result)
263 plist_free(result);
264 return res;
265
266}
267
268mobile_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)
269{
270 if (!client || !image_path || !image_type || !result) {
271 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
272 }
273 mobile_image_mounter_lock(client);
274
275 plist_t dict = plist_new_dict();
276 plist_dict_set_item(dict, "Command", plist_new_string("MountImage"));
277 plist_dict_set_item(dict, "ImagePath", plist_new_string(image_path));
278 if (signature && signature_size != 0)
279 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
280 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
281 if (PLIST_IS_DICT(options)) {
282 plist_dict_merge(&dict, options);
283 }
213 284
214 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); 285 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
215 plist_free(dict); 286 plist_free(dict);
@@ -229,17 +300,56 @@ leave_unlock:
229 return res; 300 return res;
230} 301}
231 302
232/** 303mobile_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. 304{
234 * This functions has to be called before freeing up a mobile_image_mounter 305 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. 306}
236 * 307
237 * @param client The client to hang up 308mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path)
238 * 309{
239 * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, 310 if (!client || !mount_path) {
240 * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is invalid, 311 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
241 * or another error code otherwise. 312 }
242 */ 313 mobile_image_mounter_lock(client);
314
315 plist_t dict = plist_new_dict();
316 plist_dict_set_item(dict, "Command", plist_new_string("UnmountImage"));
317 plist_dict_set_item(dict, "MountPath", plist_new_string(mount_path));
318 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
319 plist_free(dict);
320
321 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
322 debug_info("%s: Error sending XML plist to device!", __func__);
323 goto leave_unlock;
324 }
325
326 plist_t result = NULL;
327 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
328 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
329 debug_info("%s: Error receiving response from device!", __func__);
330 } else {
331 plist_t p_error = plist_dict_get_item(result, "Error");
332 if (p_error) {
333 plist_t p_detailed = plist_dict_get_item(result, "DetailedError");
334 const char* detailederr = (p_detailed) ? plist_get_string_ptr(p_detailed, NULL) : "";
335 const char* errstr = plist_get_string_ptr(p_error, NULL);
336 if (errstr && !strcmp(errstr, "UnknownCommand")) {
337 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
338 } else if (errstr && !strcmp(errstr, "DeviceLocked")) {
339 res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED;
340 } else if (strstr(detailederr, "no matching entry")) {
341 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
342 } else {
343 res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR;
344 }
345 }
346 }
347
348leave_unlock:
349 mobile_image_mounter_unlock(client);
350 return res;
351}
352
243mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) 353mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client)
244{ 354{
245 if (!client) { 355 if (!client) {
@@ -248,7 +358,7 @@ mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_cl
248 mobile_image_mounter_lock(client); 358 mobile_image_mounter_lock(client);
249 359
250 plist_t dict = plist_new_dict(); 360 plist_t dict = plist_new_dict();
251 plist_dict_insert_item(dict, "Command", plist_new_string("Hangup")); 361 plist_dict_set_item(dict, "Command", plist_new_string("Hangup"));
252 362
253 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); 363 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
254 plist_free(dict); 364 plist_free(dict);
@@ -272,3 +382,215 @@ leave_unlock:
272 mobile_image_mounter_unlock(client); 382 mobile_image_mounter_unlock(client);
273 return res; 383 return res;
274} 384}
385
386mobile_image_mounter_error_t mobile_image_mounter_query_developer_mode_status(mobile_image_mounter_client_t client, plist_t *result)
387{
388 if (!client || !result) {
389 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
390 }
391 mobile_image_mounter_lock(client);
392
393 plist_t dict = plist_new_dict();
394 plist_dict_set_item(dict, "Command", plist_new_string("QueryDeveloperModeStatus"));
395 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
396 plist_free(dict);
397
398 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
399 debug_info("%s: Error sending XML plist to device!", __func__);
400 goto leave_unlock;
401 }
402
403 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result));
404 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
405 debug_info("%s: Error receiving response from device!", __func__);
406 }
407
408leave_unlock:
409 mobile_image_mounter_unlock(client);
410 return res;
411}
412
413mobile_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)
414{
415 if (!client || !nonce || !nonce_size) {
416 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
417 }
418 mobile_image_mounter_lock(client);
419
420 plist_t dict = plist_new_dict();
421 plist_dict_set_item(dict, "Command", plist_new_string("QueryNonce"));
422 if (image_type) {
423 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
424 }
425 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
426 plist_free(dict);
427
428 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
429 debug_info("%s: Error sending XML plist to device!", __func__);
430 goto leave_unlock;
431 }
432
433 plist_t result = NULL;
434 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
435 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
436 debug_info("%s: Error receiving response from device!", __func__);
437 } else {
438 plist_t p_nonce = plist_dict_get_item(result, "PersonalizationNonce");
439 if (!p_nonce) {
440 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
441 } else {
442 uint64_t nonce_size_ = 0;
443 plist_get_data_val(p_nonce, (char**)nonce, &nonce_size_);
444 if (*nonce) {
445 *nonce_size = (unsigned int)nonce_size_;
446 } else {
447 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
448 }
449 }
450 }
451 plist_free(result);
452
453leave_unlock:
454 mobile_image_mounter_unlock(client);
455 return res;
456}
457
458mobile_image_mounter_error_t mobile_image_mounter_query_personalization_identifiers(mobile_image_mounter_client_t client, const char* image_type, plist_t *result)
459{
460 if (!client || !result) {
461 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
462 }
463 mobile_image_mounter_lock(client);
464
465 plist_t dict = plist_new_dict();
466 plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationIdentifiers"));
467 if (image_type) {
468 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
469 }
470 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
471 plist_free(dict);
472
473 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
474 debug_info("%s: Error sending XML plist to device!", __func__);
475 goto leave_unlock;
476 }
477
478 plist_t _result = NULL;
479 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &_result));
480 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
481 debug_info("%s: Error receiving response from device!", __func__);
482 }
483 *result = plist_copy(plist_dict_get_item(_result, "PersonalizationIdentifiers"));
484 if (!*result) {
485 debug_info("%s: Response did not contain PersonalizationIdentifiers!", __func__);
486 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
487 }
488
489leave_unlock:
490 mobile_image_mounter_unlock(client);
491 return res;
492}
493
494mobile_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)
495{
496 if (!client || !image_type || !signature || !signature_size || !manifest || !manifest_size) {
497 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
498 }
499 mobile_image_mounter_lock(client);
500
501 plist_t dict = plist_new_dict();
502 plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationManifest"));
503 plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type));
504 plist_dict_set_item(dict, "ImageType", plist_new_string(image_type));
505 plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size));
506
507 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
508 plist_free(dict);
509
510 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
511 debug_info("%s: Error sending XML plist to device!", __func__);
512 goto leave_unlock;
513 }
514
515 plist_t result = NULL;
516 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
517 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
518 debug_info("%s: Error receiving response from device!", __func__);
519 } else {
520 plist_t p_manifest = plist_dict_get_item(result, "ImageSignature");
521 if (!p_manifest) {
522 res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED;
523 } else {
524 uint64_t manifest_size_ = 0;
525 plist_get_data_val(p_manifest, (char**)manifest, &manifest_size_);
526 if (*manifest) {
527 *manifest_size = (unsigned int)manifest_size_;
528 } else {
529 res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED;
530 }
531 }
532 }
533 plist_free(result);
534
535leave_unlock:
536 mobile_image_mounter_unlock(client);
537 return res;
538}
539
540mobile_image_mounter_error_t mobile_image_mounter_roll_personalization_nonce(mobile_image_mounter_client_t client)
541{
542 if (!client) {
543 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
544 }
545 mobile_image_mounter_lock(client);
546
547 plist_t dict = plist_new_dict();
548 plist_dict_set_item(dict, "Command", plist_new_string("RollPersonalizationNonce"));
549 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
550 plist_free(dict);
551
552 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
553 debug_info("%s: Error sending XML plist to device!", __func__);
554 goto leave_unlock;
555 }
556
557 plist_t result = NULL;
558 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
559 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
560 debug_info("%s: Error receiving response from device!", __func__);
561 }
562 plist_free(result);
563
564leave_unlock:
565 mobile_image_mounter_unlock(client);
566 return res;
567}
568
569mobile_image_mounter_error_t mobile_image_mounter_roll_cryptex_nonce(mobile_image_mounter_client_t client)
570{
571 if (!client) {
572 return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG;
573 }
574 mobile_image_mounter_lock(client);
575
576 plist_t dict = plist_new_dict();
577 plist_dict_set_item(dict, "Command", plist_new_string("RollCryptexNonce"));
578 mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict));
579 plist_free(dict);
580
581 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
582 debug_info("%s: Error sending XML plist to device!", __func__);
583 goto leave_unlock;
584 }
585
586 plist_t result = NULL;
587 res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result));
588 if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
589 debug_info("%s: Error receiving response from device!", __func__);
590 }
591 plist_free(result);
592
593leave_unlock:
594 mobile_image_mounter_unlock(client);
595 return res;
596}
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..c7e4660 100644
--- a/src/notification_proxy.c
+++ b/src/notification_proxy.c
@@ -8,25 +8,37 @@
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>
27
28#ifndef _MSC_VER
24#include <unistd.h> 29#include <unistd.h>
30#endif
31
25#include <plist/plist.h> 32#include <plist/plist.h>
26 33
27#include "notification_proxy.h" 34#include "notification_proxy.h"
28#include "property_list_service.h" 35#include "property_list_service.h"
29#include "debug.h" 36#include "common/debug.h"
37
38#ifdef _WIN32
39#include <windows.h>
40#define sleep(x) Sleep(x*1000)
41#endif
30 42
31struct np_thread { 43struct np_thread {
32 np_client_t client; 44 np_client_t client;
@@ -41,19 +53,19 @@ struct np_thread {
41 */ 53 */
42static void np_lock(np_client_t client) 54static void np_lock(np_client_t client)
43{ 55{
44 debug_info("NP: Locked"); 56 debug_info("Locked");
45 g_mutex_lock(client->mutex); 57 mutex_lock(&client->mutex);
46} 58}
47 59
48/** 60/**
49 * Unlocks a notification_proxy client, used for thread safety. 61 * Unlocks a notification_proxy client, used for thread safety.
50 * 62 *
51 * @param client notification_proxy client to unlock 63 * @param client notification_proxy client to unlock
52 */ 64 */
53static void np_unlock(np_client_t client) 65static void np_unlock(np_client_t client)
54{ 66{
55 debug_info("NP: Unlocked"); 67 debug_info("Unlocked");
56 g_mutex_unlock(client->mutex); 68 mutex_unlock(&client->mutex);
57} 69}
58 70
59/** 71/**
@@ -82,78 +94,85 @@ static np_error_t np_error(property_list_service_error_t err)
82 return NP_E_UNKNOWN_ERROR; 94 return NP_E_UNKNOWN_ERROR;
83} 95}
84 96
85/** 97np_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{ 98{
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; 99 property_list_service_client_t plistclient = NULL;
107 if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { 100 np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient));
108 return NP_E_CONN_FAILED; 101 if (err != NP_E_SUCCESS) {
102 return err;
109 } 103 }
110 104
111 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); 105 np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private));
112 client_loc->parent = plistclient; 106 client_loc->parent = plistclient;
113 107
114 client_loc->mutex = g_mutex_new(); 108 mutex_init(&client_loc->mutex);
115 109 client_loc->notifier = THREAD_T_NULL;
116 client_loc->notifier = NULL;
117 110
118 *client = client_loc; 111 *client = client_loc;
119 return NP_E_SUCCESS; 112 return NP_E_SUCCESS;
120} 113}
121 114
122/** 115np_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 116{
124 * notification_proxy client data. 117 int32_t err = NP_E_UNKNOWN_ERROR;
125 * 118 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. 119 return err;
127 * 120}
128 * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL. 121
129 */
130np_error_t np_client_free(np_client_t client) 122np_error_t np_client_free(np_client_t client)
131{ 123{
124 plist_t dict;
125 property_list_service_client_t parent;
126
132 if (!client) 127 if (!client)
133 return NP_E_INVALID_ARG; 128 return NP_E_INVALID_ARG;
134 129
135 property_list_service_client_free(client->parent); 130 dict = plist_new_dict();
131 plist_dict_set_item(dict,"Command", plist_new_string("Shutdown"));
132 property_list_service_send_xml_plist(client->parent, dict);
133 plist_free(dict);
134
135 parent = client->parent;
136 /* notifies the client->notifier thread that it should terminate */
136 client->parent = NULL; 137 client->parent = NULL;
138
137 if (client->notifier) { 139 if (client->notifier) {
138 debug_info("joining np callback"); 140 debug_info("joining np callback");
139 g_thread_join(client->notifier); 141 thread_join(client->notifier);
140 } 142 thread_free(client->notifier);
141 if (client->mutex) { 143 client->notifier = THREAD_T_NULL;
142 g_mutex_free(client->mutex); 144 } else {
145 dict = NULL;
146 property_list_service_receive_plist(parent, &dict);
147 if (dict) {
148#ifndef STRIP_DEBUG_CODE
149 char *cmd_value = NULL;
150 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
151 if (plist_get_node_type(cmd_value_node) == PLIST_STRING) {
152 plist_get_string_val(cmd_value_node, &cmd_value);
153 }
154 if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
155 // this is the expected answer
156 } else {
157 debug_info("Did not get ProxyDeath but:");
158 debug_plist(dict);
159 }
160 if (cmd_value) {
161 free(cmd_value);
162 }
163#endif
164 plist_free(dict);
165 }
143 } 166 }
167
168 property_list_service_client_free(parent);
169
170 mutex_destroy(&client->mutex);
144 free(client); 171 free(client);
145 172
146 return NP_E_SUCCESS; 173 return NP_E_SUCCESS;
147} 174}
148 175
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) 176np_error_t np_post_notification(np_client_t client, const char *notification)
158{ 177{
159 if (!client || !notification) { 178 if (!client || !notification) {
@@ -162,66 +181,24 @@ np_error_t np_post_notification(np_client_t client, const char *notification)
162 np_lock(client); 181 np_lock(client);
163 182
164 plist_t dict = plist_new_dict(); 183 plist_t dict = plist_new_dict();
165 plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification")); 184 plist_dict_set_item(dict,"Command", plist_new_string("PostNotification"));
166 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 185 plist_dict_set_item(dict,"Name", plist_new_string(notification));
167 186
168 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 187 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
169 plist_free(dict); 188 plist_free(dict);
170 189
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) { 190 if (res != NP_E_SUCCESS) {
178 debug_info("Error sending XML plist to device!"); 191 debug_info("Error sending XML plist to device!");
179 } 192 }
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); 193 np_unlock(client);
203 return res; 194 return res;
204} 195}
205 196
206/** 197static 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{ 198{
217 if (!client || !notification) {
218 return NP_E_INVALID_ARG;
219 }
220 np_lock(client);
221
222 plist_t dict = plist_new_dict(); 199 plist_t dict = plist_new_dict();
223 plist_dict_insert_item(dict,"Command", plist_new_string("ObserveNotification")); 200 plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification"));
224 plist_dict_insert_item(dict,"Name", plist_new_string(notification)); 201 plist_dict_set_item(dict,"Name", plist_new_string(notification));
225 202
226 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); 203 np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict));
227 if (res != NP_E_SUCCESS) { 204 if (res != NP_E_SUCCESS) {
@@ -229,21 +206,20 @@ np_error_t np_observe_notification( np_client_t client, const char *notification
229 } 206 }
230 plist_free(dict); 207 plist_free(dict);
231 208
209 return res;
210}
211
212np_error_t np_observe_notification( np_client_t client, const char *notification )
213{
214 if (!client || !notification) {
215 return NP_E_INVALID_ARG;
216 }
217 np_lock(client);
218 np_error_t res = internal_np_observe_notification(client, notification);
232 np_unlock(client); 219 np_unlock(client);
233 return res; 220 return res;
234} 221}
235 222
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) 223np_error_t np_observe_notifications(np_client_t client, const char **notification_spec)
248{ 224{
249 int i = 0; 225 int i = 0;
@@ -258,13 +234,15 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
258 return NP_E_INVALID_ARG; 234 return NP_E_INVALID_ARG;
259 } 235 }
260 236
237 np_lock(client);
261 while (notifications[i]) { 238 while (notifications[i]) {
262 res = np_observe_notification(client, notifications[i]); 239 res = internal_np_observe_notification(client, notifications[i]);
263 if (res != NP_E_SUCCESS) { 240 if (res != NP_E_SUCCESS) {
264 break; 241 break;
265 } 242 }
266 i++; 243 i++;
267 } 244 }
245 np_unlock(client);
268 246
269 return res; 247 return res;
270} 248}
@@ -277,7 +255,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio
277 * with the notification that has been received. 255 * with the notification that has been received.
278 * 256 *
279 * @return 0 if a notification has been received or nothing has been received, 257 * @return 0 if a notification has been received or nothing has been received,
280 * or a negative value if an error occured. 258 * or a negative value if an error occurred.
281 * 259 *
282 * @note You probably want to check out np_set_notify_callback 260 * @note You probably want to check out np_set_notify_callback
283 * @see np_set_notify_callback 261 * @see np_set_notify_callback
@@ -292,11 +270,15 @@ static int np_get_notification(np_client_t client, char **notification)
292 270
293 np_lock(client); 271 np_lock(client);
294 272
295 property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); 273 property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500);
296 if (!dict) { 274 if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) {
297 debug_info("NotificationProxy: no notification received!"); 275 debug_info("NotificationProxy: no notification received!");
298 res = 0; 276 res = 0;
299 } else { 277 } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) {
278 debug_info("NotificationProxy: error %d occurred!", perr);
279 res = perr;
280 }
281 if (dict) {
300 char *cmd_value = NULL; 282 char *cmd_value = NULL;
301 plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); 283 plist_t cmd_value_node = plist_dict_get_item(dict, "Command");
302 284
@@ -315,11 +297,11 @@ static int np_get_notification(np_client_t client, char **notification)
315 res = -2; 297 res = -2;
316 if (name_value_node && name_value) { 298 if (name_value_node && name_value) {
317 *notification = name_value; 299 *notification = name_value;
318 debug_info("got notification %s\n", __func__, name_value); 300 debug_info("got notification %s", __func__, name_value);
319 res = 0; 301 res = 0;
320 } 302 }
321 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { 303 } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) {
322 debug_info("ERROR: NotificationProxy died!"); 304 debug_info("NotificationProxy died!");
323 res = -1; 305 res = -1;
324 } else if (cmd_value) { 306 } else if (cmd_value) {
325 debug_info("unknown NotificationProxy command '%s' received!", cmd_value); 307 debug_info("unknown NotificationProxy command '%s' received!", cmd_value);
@@ -342,7 +324,7 @@ static int np_get_notification(np_client_t client, char **notification)
342/** 324/**
343 * Internally used thread function. 325 * Internally used thread function.
344 */ 326 */
345gpointer np_notifier( gpointer arg ) 327void* np_notifier( void* arg )
346{ 328{
347 char *notification = NULL; 329 char *notification = NULL;
348 struct np_thread *npt = (struct np_thread*)arg; 330 struct np_thread *npt = (struct np_thread*)arg;
@@ -351,7 +333,10 @@ gpointer np_notifier( gpointer arg )
351 333
352 debug_info("starting callback."); 334 debug_info("starting callback.");
353 while (npt->client->parent) { 335 while (npt->client->parent) {
354 np_get_notification(npt->client, &notification); 336 if (np_get_notification(npt->client, &notification) < 0) {
337 npt->cbfunc("", npt->user_data);
338 break;
339 }
355 if (notification) { 340 if (notification) {
356 npt->cbfunc(notification, npt->user_data); 341 npt->cbfunc(notification, npt->user_data);
357 free(notification); 342 free(notification);
@@ -366,25 +351,6 @@ gpointer np_notifier( gpointer arg )
366 return NULL; 351 return NULL;
367} 352}
368 353
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 ) 354np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data )
389{ 355{
390 if (!client) 356 if (!client)
@@ -394,11 +360,12 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
394 360
395 np_lock(client); 361 np_lock(client);
396 if (client->notifier) { 362 if (client->notifier) {
397 debug_info("callback already set, removing\n"); 363 debug_info("callback already set, removing");
398 property_list_service_client_t parent = client->parent; 364 property_list_service_client_t parent = client->parent;
399 client->parent = NULL; 365 client->parent = NULL;
400 g_thread_join(client->notifier); 366 thread_join(client->notifier);
401 client->notifier = NULL; 367 thread_free(client->notifier);
368 client->notifier = THREAD_T_NULL;
402 client->parent = parent; 369 client->parent = parent;
403 } 370 }
404 371
@@ -409,8 +376,7 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb,
409 npt->cbfunc = notify_cb; 376 npt->cbfunc = notify_cb;
410 npt->user_data = user_data; 377 npt->user_data = user_data;
411 378
412 client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL); 379 if (thread_new(&client->notifier, np_notifier, npt) == 0) {
413 if (client->notifier) {
414 res = NP_E_SUCCESS; 380 res = NP_E_SUCCESS;
415 } 381 }
416 } 382 }
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/ostrace.c b/src/ostrace.c
new file mode 100644
index 0000000..68eb6bf
--- /dev/null
+++ b/src/ostrace.c
@@ -0,0 +1,436 @@
1/*
2 * ostrace.c
3 * com.apple.os_trace_relay service implementation.
4 *
5 * Copyright (c) 2020-2025 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
28#include <plist/plist.h>
29
30#include "ostrace.h"
31#include "lockdown.h"
32#include "common/debug.h"
33#include "endianness.h"
34
35struct ostrace_worker_thread {
36 ostrace_client_t client;
37 ostrace_activity_cb_t cbfunc;
38 void *user_data;
39};
40
41/**
42 * Convert a service_error_t value to a ostrace_error_t value.
43 * Used internally to get correct error codes.
44 *
45 * @param err An service_error_t error code
46 *
47 * @return A matching ostrace_error_t error code,
48 * OSTRACE_E_UNKNOWN_ERROR otherwise.
49 */
50static ostrace_error_t ostrace_error(service_error_t err)
51{
52 switch (err) {
53 case SERVICE_E_SUCCESS:
54 return OSTRACE_E_SUCCESS;
55 case SERVICE_E_INVALID_ARG:
56 return OSTRACE_E_INVALID_ARG;
57 case SERVICE_E_MUX_ERROR:
58 return OSTRACE_E_MUX_ERROR;
59 case SERVICE_E_SSL_ERROR:
60 return OSTRACE_E_SSL_ERROR;
61 case SERVICE_E_NOT_ENOUGH_DATA:
62 return OSTRACE_E_NOT_ENOUGH_DATA;
63 case SERVICE_E_TIMEOUT:
64 return OSTRACE_E_TIMEOUT;
65 default:
66 break;
67 }
68 return OSTRACE_E_UNKNOWN_ERROR;
69}
70
71ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client)
72{
73 *client = NULL;
74
75 if (!device || !service || service->port == 0 || !client || *client) {
76 debug_info("Incorrect parameter passed to ostrace_client_new.");
77 return OSTRACE_E_INVALID_ARG;
78 }
79
80 debug_info("Creating ostrace_client, port = %d.", service->port);
81
82 service_client_t parent = NULL;
83 ostrace_error_t ret = ostrace_error(service_client_new(device, service, &parent));
84 if (ret != OSTRACE_E_SUCCESS) {
85 debug_info("Creating base service client failed. Error: %i", ret);
86 return ret;
87 }
88
89 ostrace_client_t client_loc = (ostrace_client_t) malloc(sizeof(struct ostrace_client_private));
90 client_loc->parent = parent;
91 client_loc->worker = THREAD_T_NULL;
92
93 *client = client_loc;
94
95 debug_info("ostrace_client successfully created.");
96 return 0;
97}
98
99ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label)
100{
101 ostrace_error_t err = OSTRACE_E_UNKNOWN_ERROR;
102 service_client_factory_start_service(device, OSTRACE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(ostrace_client_new), &err);
103 return err;
104}
105
106ostrace_error_t ostrace_client_free(ostrace_client_t client)
107{
108 if (!client)
109 return OSTRACE_E_INVALID_ARG;
110 ostrace_stop_activity(client);
111 ostrace_error_t err = ostrace_error(service_client_free(client->parent));
112 free(client);
113
114 return err;
115}
116
117static ostrace_error_t ostrace_send_plist(ostrace_client_t client, plist_t plist)
118{
119 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
120 uint32_t blen = 0;
121 char* bin = NULL;
122 uint32_t sent = 0;
123 uint32_t swapped_len = 0;
124
125 if (!client || !plist) {
126 return OSTRACE_E_INVALID_ARG;
127 }
128
129 plist_to_bin(plist, &bin, &blen);
130 swapped_len = htobe32(blen);
131
132 res = ostrace_error(service_send(client->parent, (char*)&swapped_len, 4, &sent));
133 if (res == OSTRACE_E_SUCCESS) {
134 res = ostrace_error(service_send(client->parent, bin, blen, &sent));
135 }
136 free(bin);
137 return res;
138}
139
140static ostrace_error_t ostrace_receive_plist(ostrace_client_t client, plist_t *plist)
141{
142 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
143 uint8_t msgtype = 0;
144 uint32_t received = 0;
145 res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
146 if (res != OSTRACE_E_SUCCESS) {
147 debug_info("Failed to read message type from service");
148 return res;
149 }
150 uint32_t rlen = 0;
151 res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
152 if (res != OSTRACE_E_SUCCESS) {
153 debug_info("Failed to read message size from service");
154 return res;
155 }
156
157 if (msgtype == 1) {
158 rlen = be32toh(rlen);
159 } else if (msgtype == 2) {
160 rlen = le32toh(rlen);
161 } else {
162 debug_info("Unexpected message type %d", msgtype);
163 return OSTRACE_E_UNKNOWN_ERROR;
164 }
165 debug_info("got length %d", rlen);
166
167 char* buf = (char*)malloc(rlen);
168 res = ostrace_error(service_receive(client->parent, buf, rlen, &received));
169 if (res != OSTRACE_E_SUCCESS) {
170 return res;
171 }
172
173 plist_t reply = NULL;
174 plist_err_t perr = plist_from_memory(buf, received, &reply, NULL);
175 free(buf);
176 if (perr != PLIST_ERR_SUCCESS) {
177 return OSTRACE_E_UNKNOWN_ERROR;
178 }
179 *plist = reply;
180 return OSTRACE_E_SUCCESS;
181}
182
183static ostrace_error_t _ostrace_check_result(plist_t reply)
184{
185 ostrace_error_t res = OSTRACE_E_REQUEST_FAILED;
186 if (!reply) {
187 return res;
188 }
189 plist_t p_status = plist_dict_get_item(reply, "Status");
190 if (!p_status) {
191 return res;
192 }
193 const char* status = plist_get_string_ptr(p_status, NULL);
194 if (!status) {
195 return res;
196 }
197 if (!strcmp(status, "RequestSuccessful")) {
198 res = OSTRACE_E_SUCCESS;
199 }
200 return res;
201}
202
203void *ostrace_worker(void *arg)
204{
205 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
206 struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)arg;
207
208 if (!oswt)
209 return NULL;
210
211 uint8_t msgtype = 0;
212 uint32_t received = 0;
213
214 debug_info("Running");
215
216 while (oswt->client->parent) {
217 res = ostrace_error(service_receive_with_timeout(oswt->client->parent, (char*)&msgtype, 1, &received, 100));
218 if (res == OSTRACE_E_TIMEOUT) {
219 continue;
220 }
221 if (res != OSTRACE_E_SUCCESS) {
222 debug_info("Failed to read message type from service");
223 break;
224 }
225 uint32_t rlen = 0;
226 res = ostrace_error(service_receive(oswt->client->parent, (char*)&rlen, 4, &received));
227 if (res != OSTRACE_E_SUCCESS) {
228 debug_info("Failed to read message size from service");
229 break;
230 }
231
232 if (msgtype == 1) {
233 rlen = be32toh(rlen);
234 } else if (msgtype == 2) {
235 rlen = le32toh(rlen);
236 } else {
237 debug_info("Unexpected message type %d", msgtype);
238 break;
239 }
240
241 debug_info("got length %d", rlen);
242
243 void* buf = malloc(rlen);
244 res = ostrace_error(service_receive(oswt->client->parent, (char*)buf, rlen, &received));
245 if (res != OSTRACE_E_SUCCESS) {
246 debug_info("Failed to receive %d bytes, error %d", rlen, res);
247 break;
248 }
249 if (received < rlen) {
250 debug_info("Failed to receive all data, got %d/%d", received, rlen);
251 break;
252 }
253 oswt->cbfunc(buf, received, oswt->user_data);
254 }
255
256 if (oswt) {
257 free(oswt);
258 }
259
260 debug_info("Exiting");
261
262 return NULL;
263}
264
265ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data)
266{
267 if (!client || !callback)
268 return OSTRACE_E_INVALID_ARG;
269
270 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
271
272 if (client->worker) {
273 debug_info("Another ostrace activity thread appears to be running already.");
274 return res;
275 }
276
277 plist_t dict = plist_new_dict();
278 plist_dict_set_item(dict, "Pid", plist_new_uint(0x0FFFFFFFF));
279 plist_dict_set_item(dict, "MessageFilter", plist_new_uint(0xFFFF));
280 plist_dict_set_item(dict, "StreamFlags", plist_new_uint(0x3C));
281 if (options) {
282 plist_dict_merge(&dict, options);
283 }
284 plist_dict_set_item(dict, "Request", plist_new_string("StartActivity"));
285
286 res = ostrace_send_plist(client, dict);
287 plist_free(dict);
288 if (res != OSTRACE_E_SUCCESS) {
289 return res;
290 }
291
292 dict = NULL;
293 res = ostrace_receive_plist(client, &dict);
294 if (res != OSTRACE_E_SUCCESS) {
295 return res;
296 }
297 res = _ostrace_check_result(dict);
298 if (res != OSTRACE_E_SUCCESS) {
299 return res;
300 }
301
302 /* start worker thread */
303 struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)malloc(sizeof(struct ostrace_worker_thread));
304 if (oswt) {
305 oswt->client = client;
306 oswt->cbfunc = callback;
307 oswt->user_data = user_data;
308
309 if (thread_new(&client->worker, ostrace_worker, oswt) == 0) {
310 res = OSTRACE_E_SUCCESS;
311 }
312 }
313
314 return res;
315}
316
317ostrace_error_t ostrace_stop_activity(ostrace_client_t client)
318{
319 if (client->worker) {
320 /* notify thread to finish */
321 service_client_t parent = client->parent;
322 client->parent = NULL;
323 /* join thread to make it exit */
324 thread_join(client->worker);
325 thread_free(client->worker);
326 client->worker = THREAD_T_NULL;
327 client->parent = parent;
328 }
329
330 return OSTRACE_E_SUCCESS;
331}
332
333ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list)
334{
335 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
336 plist_t dict = plist_new_dict();
337 plist_dict_set_item(dict, "Request", plist_new_string("PidList"));
338
339 if (!client || !list) {
340 return OSTRACE_E_INVALID_ARG;
341 }
342
343 res = ostrace_send_plist(client, dict);
344 plist_free(dict);
345 if (res != OSTRACE_E_SUCCESS) {
346 return res;
347 }
348
349 plist_t reply = NULL;
350 res = ostrace_receive_plist(client, &reply);
351 if (res != OSTRACE_E_SUCCESS) {
352 return res;
353 }
354 res = _ostrace_check_result(reply);
355 if (res != OSTRACE_E_SUCCESS) {
356 return res;
357 }
358
359 plist_t payload = plist_dict_get_item(reply, "Payload");
360 if (!payload) {
361 return OSTRACE_E_REQUEST_FAILED;
362 }
363 *list = plist_copy(payload);
364 plist_free(reply);
365
366 return OSTRACE_E_SUCCESS;
367}
368
369ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data)
370{
371 ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR;
372 if (!client || !callback) {
373 return OSTRACE_E_INVALID_ARG;
374 }
375 plist_t dict = plist_new_dict();
376 if (options) {
377 plist_dict_merge(&dict, options);
378 }
379 plist_dict_set_item(dict, "Request", plist_new_string("CreateArchive"));
380
381 res = ostrace_send_plist(client, dict);
382 plist_free(dict);
383 if (res != OSTRACE_E_SUCCESS) {
384 return res;
385 }
386
387 plist_t reply = NULL;
388 res = ostrace_receive_plist(client, &reply);
389 if (res != OSTRACE_E_SUCCESS) {
390 return res;
391 }
392
393 res = _ostrace_check_result(reply);
394 if (res != OSTRACE_E_SUCCESS) {
395 return res;
396 }
397
398 debug_info("Receiving archive...\n");
399 while (1) {
400 uint8_t msgtype = 0;
401 uint32_t received = 0;
402 res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received));
403 if (res != OSTRACE_E_SUCCESS) {
404 debug_info("Could not read message type from service: %d", res);
405 break;
406 }
407 if (msgtype != 3) {
408 debug_info("Unexpected packet type %d", msgtype);
409 return OSTRACE_E_REQUEST_FAILED;
410 }
411 uint32_t rlen = 0;
412 res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received));
413 if (res != OSTRACE_E_SUCCESS) {
414 debug_info("Failed to read message size from service");
415 break;
416 }
417
418 rlen = le32toh(rlen);
419 debug_info("got length %d", rlen);
420
421 unsigned char* buf = (unsigned char*)malloc(rlen);
422 res = ostrace_error(service_receive(client->parent, (char*)buf, rlen, &received));
423 if (res != OSTRACE_E_SUCCESS) {
424 debug_info("Could not read data from service: %d", res);
425 break;
426 }
427 if (callback(buf, received, user_data) < 0) {
428 debug_info("Aborted through callback");
429 return OSTRACE_E_REQUEST_FAILED;
430 }
431 }
432 debug_info("Done.\n");
433
434 return OSTRACE_E_SUCCESS;
435}
436
diff --git a/src/ostrace.h b/src/ostrace.h
new file mode 100644
index 0000000..dcc3e8d
--- /dev/null
+++ b/src/ostrace.h
@@ -0,0 +1,37 @@
1/*
2 * ostrace.h
3 * com.apple.os_trace_relay service header file.
4 *
5 * Copyright (c) 2020-2025 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 _OSTRACE_H
23#define _OSTRACE_H
24
25#include "idevice.h"
26#include "libimobiledevice/ostrace.h"
27#include "service.h"
28#include <libimobiledevice-glue/thread.h>
29
30struct ostrace_client_private {
31 service_client_t parent;
32 THREAD_T worker;
33};
34
35void *ostrace_worker(void *arg);
36
37#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..5df5122 100644
--- a/src/sbservices.c
+++ b/src/sbservices.c
@@ -8,46 +8,53 @@
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>
27
28#ifndef _MSC_VER
24#include <unistd.h> 29#include <unistd.h>
30#endif
31
25#include <plist/plist.h> 32#include <plist/plist.h>
26 33
27#include "sbservices.h" 34#include "sbservices.h"
28#include "property_list_service.h" 35#include "property_list_service.h"
29#include "debug.h" 36#include "common/debug.h"
30 37
31/** 38/**
32 * Locks an sbservices client, used for thread safety. 39 * Locks an sbservices client, used for thread safety.
33 * 40 *
34 * @param client sbservices client to lock. 41 * @param client sbservices client to lock.
35 */ 42 */
36static void sbs_lock(sbservices_client_t client) 43static void sbservices_lock(sbservices_client_t client)
37{ 44{
38 debug_info("SBServices: Locked"); 45 debug_info("Locked");
39 g_mutex_lock(client->mutex); 46 mutex_lock(&client->mutex);
40} 47}
41 48
42/** 49/**
43 * Unlocks an sbservices client, used for thread safety. 50 * Unlocks an sbservices client, used for thread safety.
44 * 51 *
45 * @param client sbservices client to unlock 52 * @param client sbservices client to unlock
46 */ 53 */
47static void sbs_unlock(sbservices_client_t client) 54static void sbservices_unlock(sbservices_client_t client)
48{ 55{
49 debug_info("SBServices: Unlocked"); 56 debug_info("Unlocked");
50 g_mutex_unlock(client->mutex); 57 mutex_unlock(&client->mutex);
51} 58}
52 59
53/** 60/**
@@ -61,64 +68,44 @@ static void sbs_unlock(sbservices_client_t client)
61 */ 68 */
62static sbservices_error_t sbservices_error(property_list_service_error_t err) 69static sbservices_error_t sbservices_error(property_list_service_error_t err)
63{ 70{
64 switch (err) { 71 switch (err) {
65 case PROPERTY_LIST_SERVICE_E_SUCCESS: 72 case PROPERTY_LIST_SERVICE_E_SUCCESS:
66 return SBSERVICES_E_SUCCESS; 73 return SBSERVICES_E_SUCCESS;
67 case PROPERTY_LIST_SERVICE_E_INVALID_ARG: 74 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
68 return SBSERVICES_E_INVALID_ARG; 75 return SBSERVICES_E_INVALID_ARG;
69 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: 76 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
70 return SBSERVICES_E_PLIST_ERROR; 77 return SBSERVICES_E_PLIST_ERROR;
71 case PROPERTY_LIST_SERVICE_E_MUX_ERROR: 78 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
72 return SBSERVICES_E_CONN_FAILED; 79 return SBSERVICES_E_CONN_FAILED;
73 default: 80 default:
74 break; 81 break;
75 } 82 }
76 return SBSERVICES_E_UNKNOWN_ERROR; 83 return SBSERVICES_E_UNKNOWN_ERROR;
77} 84}
78 85
79/** 86sbservices_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{ 87{
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; 88 property_list_service_client_t plistclient = NULL;
100 sbservices_error_t err = sbservices_error(property_list_service_client_new(device, port, &plistclient)); 89 sbservices_error_t err = sbservices_error(property_list_service_client_new(device, service, &plistclient));
101 if (err != SBSERVICES_E_SUCCESS) { 90 if (err != SBSERVICES_E_SUCCESS) {
102 return err; 91 return err;
103 } 92 }
104 93
105 sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private)); 94 sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private));
106 client_loc->parent = plistclient; 95 client_loc->parent = plistclient;
107 client_loc->mutex = g_mutex_new(); 96 mutex_init(&client_loc->mutex);
108 97
109 *client = client_loc; 98 *client = client_loc;
110 return SBSERVICES_E_SUCCESS; 99 return SBSERVICES_E_SUCCESS;
111} 100}
112 101
113/** 102sbservices_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 103{
115 * sbservices client data. 104 sbservices_error_t err = SBSERVICES_E_UNKNOWN_ERROR;
116 * 105 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. 106 return err;
118 * 107}
119 * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when 108
120 * client is NULL, or an SBSERVICES_E_* error code otherwise.
121 */
122sbservices_error_t sbservices_client_free(sbservices_client_t client) 109sbservices_error_t sbservices_client_free(sbservices_client_t client)
123{ 110{
124 if (!client) 111 if (!client)
@@ -126,28 +113,12 @@ sbservices_error_t sbservices_client_free(sbservices_client_t client)
126 113
127 sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent)); 114 sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent));
128 client->parent = NULL; 115 client->parent = NULL;
129 if (client->mutex) { 116 mutex_destroy(&client->mutex);
130 g_mutex_free(client->mutex);
131 }
132 free(client); 117 free(client);
133 118
134 return err; 119 return err;
135} 120}
136 121
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) 122sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version)
152{ 123{
153 if (!client || !client->parent || !state) 124 if (!client || !client->parent || !state)
@@ -156,12 +127,12 @@ sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t
156 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 127 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
157 128
158 plist_t dict = plist_new_dict(); 129 plist_t dict = plist_new_dict();
159 plist_dict_insert_item(dict, "command", plist_new_string("getIconState")); 130 plist_dict_set_item(dict, "command", plist_new_string("getIconState"));
160 if (format_version) { 131 if (format_version) {
161 plist_dict_insert_item(dict, "formatVersion", plist_new_string(format_version)); 132 plist_dict_set_item(dict, "formatVersion", plist_new_string(format_version));
162 } 133 }
163 134
164 sbs_lock(client); 135 sbservices_lock(client);
165 136
166 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 137 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
167 if (res != SBSERVICES_E_SUCCESS) { 138 if (res != SBSERVICES_E_SUCCESS) {
@@ -184,19 +155,10 @@ leave_unlock:
184 if (dict) { 155 if (dict) {
185 plist_free(dict); 156 plist_free(dict);
186 } 157 }
187 sbs_unlock(client); 158 sbservices_unlock(client);
188 return res; 159 return res;
189} 160}
190 161
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) 162sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate)
201{ 163{
202 if (!client || !client->parent || !newstate) 164 if (!client || !client->parent || !newstate)
@@ -205,39 +167,27 @@ sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t
205 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 167 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
206 168
207 plist_t dict = plist_new_dict(); 169 plist_t dict = plist_new_dict();
208 plist_dict_insert_item(dict, "command", plist_new_string("setIconState")); 170 plist_dict_set_item(dict, "command", plist_new_string("setIconState"));
209 plist_dict_insert_item(dict, "iconState", plist_copy(newstate)); 171 plist_dict_set_item(dict, "iconState", plist_copy(newstate));
210 172
211 sbs_lock(client); 173 sbservices_lock(client);
212 174
213 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 175 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
214 if (res != SBSERVICES_E_SUCCESS) { 176 if (res != SBSERVICES_E_SUCCESS) {
215 debug_info("could not send plist, error %d", res); 177 debug_info("could not send plist, error %d", res);
216 } 178 }
217 /* NO RESPONSE */ 179
180 uint32_t bytes = 0;
181 service_receive_with_timeout(client->parent->parent, malloc(4), 4, &bytes, 2000);
182 debug_info("setIconState response: %u", bytes);
218 183
219 if (dict) { 184 if (dict) {
220 plist_free(dict); 185 plist_free(dict);
221 } 186 }
222 sbs_unlock(client); 187 sbservices_unlock(client);
223 return res; 188 return res;
224} 189}
225 190
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) 191sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize)
242{ 192{
243 if (!client || !client->parent || !bundleId || !pngdata) 193 if (!client || !client->parent || !bundleId || !pngdata)
@@ -246,10 +196,10 @@ sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const
246 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 196 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
247 197
248 plist_t dict = plist_new_dict(); 198 plist_t dict = plist_new_dict();
249 plist_dict_insert_item(dict, "command", plist_new_string("getIconPNGData")); 199 plist_dict_set_item(dict, "command", plist_new_string("getIconPNGData"));
250 plist_dict_insert_item(dict, "bundleId", plist_new_string(bundleId)); 200 plist_dict_set_item(dict, "bundleId", plist_new_string(bundleId));
251 201
252 sbs_lock(client); 202 sbservices_lock(client);
253 203
254 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 204 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
255 if (res != SBSERVICES_E_SUCCESS) { 205 if (res != SBSERVICES_E_SUCCESS) {
@@ -271,25 +221,48 @@ leave_unlock:
271 if (dict) { 221 if (dict) {
272 plist_free(dict); 222 plist_free(dict);
273 } 223 }
274 sbs_unlock(client); 224 sbservices_unlock(client);
275 return res; 225 return res;
226}
227
228sbservices_error_t sbservices_get_interface_orientation(sbservices_client_t client, sbservices_interface_orientation_t* interface_orientation)
229{
230 if (!client || !client->parent || !interface_orientation)
231 return SBSERVICES_E_INVALID_ARG;
232
233 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
234
235 plist_t dict = plist_new_dict();
236 plist_dict_set_item(dict, "command", plist_new_string("getInterfaceOrientation"));
276 237
238 sbservices_lock(client);
239
240 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
241 if (res != SBSERVICES_E_SUCCESS) {
242 debug_info("could not send plist, error %d", res);
243 goto leave_unlock;
244 }
245 plist_free(dict);
246 dict = NULL;
247
248 res = sbservices_error(property_list_service_receive_plist(client->parent, &dict));
249 if (res == SBSERVICES_E_SUCCESS) {
250 plist_t node = plist_dict_get_item(dict, "interfaceOrientation");
251 if (node) {
252 uint64_t value = SBSERVICES_INTERFACE_ORIENTATION_UNKNOWN;
253 plist_get_uint_val(node, &value);
254 *interface_orientation = (sbservices_interface_orientation_t)value;
255 }
256 }
257
258leave_unlock:
259 if (dict) {
260 plist_free(dict);
261 }
262 sbservices_unlock(client);
263 return res;
277} 264}
278 265
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) 266sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize)
294{ 267{
295 if (!client || !client->parent || !pngdata) 268 if (!client || !client->parent || !pngdata)
@@ -298,9 +271,9 @@ sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_clien
298 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; 271 sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR;
299 272
300 plist_t dict = plist_new_dict(); 273 plist_t dict = plist_new_dict();
301 plist_dict_insert_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData")); 274 plist_dict_set_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData"));
302 275
303 sbs_lock(client); 276 sbservices_lock(client);
304 277
305 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); 278 res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict));
306 if (res != SBSERVICES_E_SUCCESS) { 279 if (res != SBSERVICES_E_SUCCESS) {
@@ -322,6 +295,6 @@ leave_unlock:
322 if (dict) { 295 if (dict) {
323 plist_free(dict); 296 plist_free(dict);
324 } 297 }
325 sbs_unlock(client); 298 sbservices_unlock(client);
326 return res; 299 return res;
327} 300}
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