diff options
Diffstat (limited to 'src')
63 files changed, 10923 insertions, 4680 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 70dc895..58cf07c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
@@ -1,24 +1,69 @@ | |||
1 | AM_CPPFLAGS = -I$(top_srcdir)/include | 1 | AM_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 | ||
3 | AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusbmuxd_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) $(libplist_CFLAGS) $(LFS_CFLAGS) | 7 | AM_CFLAGS = \ |
4 | AM_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 | ||
6 | lib_LTLIBRARIES = libimobiledevice.la | 16 | AM_LDFLAGS = \ |
7 | libimobiledevice_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_SO_VERSION) -no-undefined | 17 | $(ssl_lib_LIBS) \ |
8 | libimobiledevice_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\ | 23 | lib_LTLIBRARIES = libimobiledevice-1.0.la |
14 | afc.c afc.h\ | 24 | libimobiledevice_1_0_la_LIBADD = $(top_builddir)/common/libinternalcommon.la |
15 | file_relay.c file_relay.h\ | 25 | if HAVE_WIRELESS_PAIRING |
16 | notification_proxy.c notification_proxy.h\ | 26 | libimobiledevice_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\ | 27 | endif |
18 | sbservices.c sbservices.h\ | 28 | libimobiledevice_1_0_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBIMOBILEDEVICE_SO_VERSION) -no-undefined |
19 | mobile_image_mounter.c mobile_image_mounter.h\ | 29 | if DARWIN |
20 | screenshotr.c screenshotr.h\ | 30 | libimobiledevice_1_0_la_LDFLAGS += -framework CoreFoundation -framework SystemConfiguration |
21 | mobilesync.c mobilesync.h\ | 31 | endif |
22 | mobilebackup.c mobilebackup.h\ | 32 | libimobiledevice_1_0_la_SOURCES = \ |
23 | house_arrest.c house_arrest.h\ | 33 | idevice.c idevice.h \ |
24 | restore.c restore.h | 34 | service.c service.h \ |
35 | property_list_service.c property_list_service.h \ | ||
36 | device_link_service.c device_link_service.h \ | ||
37 | lockdown.c lockdown.h \ | ||
38 | lockdown-cu.c \ | ||
39 | afc.c afc.h \ | ||
40 | file_relay.c file_relay.h \ | ||
41 | notification_proxy.c notification_proxy.h \ | ||
42 | installation_proxy.c installation_proxy.h \ | ||
43 | sbservices.c sbservices.h \ | ||
44 | mobile_image_mounter.c mobile_image_mounter.h \ | ||
45 | screenshotr.c screenshotr.h \ | ||
46 | mobilesync.c mobilesync.h \ | ||
47 | mobilebackup.c mobilebackup.h \ | ||
48 | house_arrest.c house_arrest.h \ | ||
49 | mobilebackup2.c mobilebackup2.h \ | ||
50 | misagent.c misagent.h \ | ||
51 | restore.c restore.h \ | ||
52 | diagnostics_relay.c diagnostics_relay.h \ | ||
53 | heartbeat.c heartbeat.h \ | ||
54 | debugserver.c debugserver.h \ | ||
55 | webinspector.c webinspector.h \ | ||
56 | mobileactivation.c mobileactivation.h \ | ||
57 | preboard.c preboard.h \ | ||
58 | companion_proxy.c companion_proxy.h \ | ||
59 | reverse_proxy.c reverse_proxy.h \ | ||
60 | syslog_relay.c syslog_relay.h \ | ||
61 | bt_packet_logger.c bt_packet_logger.h | ||
62 | |||
63 | if WIN32 | ||
64 | libimobiledevice_1_0_la_LDFLAGS += -avoid-version -static-libgcc | ||
65 | libimobiledevice_1_0_la_LIBADD += -lole32 -lws2_32 -lgdi32 | ||
66 | endif | ||
67 | |||
68 | pkgconfigdir = $(libdir)/pkgconfig | ||
69 | pkgconfig_DATA = libimobiledevice-1.0.pc | ||
@@ -1,308 +1,243 @@ | |||
1 | /* | 1 | /* |
2 | * afc.c | 2 | * afc.c |
3 | * Contains functions for the built-in AFC client. | 3 | * Contains functions for the built-in AFC client. |
4 | * | 4 | * |
5 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
6 | * Copyright (c) 2009-2014 Nikias Bassen. All Rights Reserved. | ||
5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 7 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
6 | * | 8 | * |
7 | * This library is free software; you can redistribute it and/or | 9 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 10 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 11 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 12 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 13 | * |
12 | * This library is distributed in the hope that it will be useful, | 14 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 17 | * Lesser General Public License for more details. |
16 | * | 18 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 20 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 22 | */ |
21 | 23 | ||
24 | #ifdef HAVE_CONFIG_H | ||
25 | #include <config.h> | ||
26 | #endif | ||
22 | #include <stdio.h> | 27 | #include <stdio.h> |
23 | #include <stdlib.h> | 28 | #include <stdlib.h> |
24 | #include <unistd.h> | 29 | #include <unistd.h> |
25 | #include <string.h> | 30 | #include <string.h> |
26 | 31 | ||
27 | #include "afc.h" | ||
28 | #include "idevice.h" | 32 | #include "idevice.h" |
29 | #include "debug.h" | 33 | #include "afc.h" |
30 | 34 | #include "common/debug.h" | |
31 | /** The maximum size an AFC data packet can be */ | 35 | #include "endianness.h" |
32 | static const int MAXIMUM_PACKET_SIZE = (2 << 15); | ||
33 | 36 | ||
34 | /** | 37 | /** |
35 | * Locks an AFC client, done for thread safety stuff | 38 | * Locks an AFC client, done for thread safety stuff |
36 | * | 39 | * |
37 | * @param client The AFC client connection to lock | 40 | * @param client The AFC client connection to lock |
38 | */ | 41 | */ |
39 | static void afc_lock(afc_client_t client) | 42 | static void afc_lock(afc_client_t client) |
40 | { | 43 | { |
41 | debug_info("Locked"); | 44 | debug_info("Locked"); |
42 | g_mutex_lock(client->mutex); | 45 | mutex_lock(&client->mutex); |
43 | } | 46 | } |
44 | 47 | ||
45 | /** | 48 | /** |
46 | * Unlocks an AFC client, done for thread safety stuff. | 49 | * Unlocks an AFC client, done for thread safety stuff. |
47 | * | 50 | * |
48 | * @param client The AFC | 51 | * @param client The AFC |
49 | */ | 52 | */ |
50 | static void afc_unlock(afc_client_t client) | 53 | static void afc_unlock(afc_client_t client) |
51 | { | 54 | { |
52 | debug_info("Unlocked"); | 55 | debug_info("Unlocked"); |
53 | g_mutex_unlock(client->mutex); | 56 | mutex_unlock(&client->mutex); |
54 | } | 57 | } |
55 | 58 | ||
56 | /** | 59 | /** |
57 | * Makes a connection to the AFC service on the device using the given | 60 | * Makes a connection to the AFC service on the device using the given |
58 | * connection. | 61 | * connection. |
59 | * | 62 | * |
60 | * @param connection An idevice_connection_t that must have been previously | 63 | * @param service_client A connected service client |
61 | * connected using idevice_connect(). Note that this connection will | ||
62 | * not be closed by calling afc_client_free(). | ||
63 | * @param client Pointer that will be set to a newly allocated afc_client_t | 64 | * @param client Pointer that will be set to a newly allocated afc_client_t |
64 | * upon successful return. | 65 | * upon successful return. |
65 | * | 66 | * |
66 | * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if connection is | 67 | * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if connection is |
67 | * invalid, or AFC_E_NO_MEM if there is a memory allocation problem. | 68 | * invalid, or AFC_E_NO_MEM if there is a memory allocation problem. |
68 | */ | 69 | */ |
69 | 70 | ||
70 | afc_error_t afc_client_new_from_connection(idevice_connection_t connection, afc_client_t *client) | 71 | afc_error_t afc_client_new_with_service_client(service_client_t service_client, afc_client_t *client) |
71 | { | 72 | { |
72 | /* makes sure thread environment is available */ | 73 | if (!service_client) |
73 | if (!g_thread_supported()) | ||
74 | g_thread_init(NULL); | ||
75 | |||
76 | if (!connection) | ||
77 | return AFC_E_INVALID_ARG; | 74 | return AFC_E_INVALID_ARG; |
78 | 75 | ||
79 | afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private)); | 76 | afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private)); |
80 | client_loc->connection = connection; | 77 | client_loc->parent = service_client; |
81 | client_loc->own_connection = 0; | 78 | client_loc->free_parent = 0; |
82 | 79 | ||
83 | /* allocate a packet */ | 80 | /* allocate a packet */ |
84 | client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); | 81 | client_loc->packet_extra = 1024; |
82 | client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket) + client_loc->packet_extra); | ||
85 | if (!client_loc->afc_packet) { | 83 | if (!client_loc->afc_packet) { |
86 | free(client_loc); | 84 | free(client_loc); |
87 | return AFC_E_NO_MEM; | 85 | return AFC_E_NO_MEM; |
88 | } | 86 | } |
89 | |||
90 | client_loc->afc_packet->packet_num = 0; | 87 | client_loc->afc_packet->packet_num = 0; |
91 | client_loc->afc_packet->entire_length = 0; | 88 | client_loc->afc_packet->entire_length = 0; |
92 | client_loc->afc_packet->this_length = 0; | 89 | client_loc->afc_packet->this_length = 0; |
93 | memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN); | 90 | memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN); |
94 | client_loc->file_handle = 0; | 91 | mutex_init(&client_loc->mutex); |
95 | client_loc->lock = 0; | ||
96 | client_loc->mutex = g_mutex_new(); | ||
97 | 92 | ||
98 | *client = client_loc; | 93 | *client = client_loc; |
99 | return AFC_E_SUCCESS; | 94 | return AFC_E_SUCCESS; |
100 | } | 95 | } |
101 | 96 | ||
102 | /** | 97 | afc_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 | */ | ||
118 | afc_error_t afc_client_new(idevice_t device, uint16_t port, afc_client_t * client) | ||
119 | { | 98 | { |
120 | /* makes sure thread environment is available */ | 99 | if (!device || !service || service->port == 0) |
121 | if (!g_thread_supported()) | ||
122 | g_thread_init(NULL); | ||
123 | |||
124 | if (!device || port==0) | ||
125 | return AFC_E_INVALID_ARG; | 100 | return AFC_E_INVALID_ARG; |
126 | 101 | ||
127 | /* attempt connection */ | 102 | service_client_t parent = NULL; |
128 | idevice_connection_t connection = NULL; | 103 | if (service_client_new(device, service, &parent) != SERVICE_E_SUCCESS) { |
129 | if (idevice_connect(device, port, &connection) != IDEVICE_E_SUCCESS) { | ||
130 | return AFC_E_MUX_ERROR; | 104 | return AFC_E_MUX_ERROR; |
131 | } | 105 | } |
132 | 106 | ||
133 | afc_error_t err = afc_client_new_from_connection(connection, client); | 107 | afc_error_t err = afc_client_new_with_service_client(parent, client); |
134 | if (err != AFC_E_SUCCESS) { | 108 | if (err != AFC_E_SUCCESS) { |
135 | idevice_disconnect(connection); | 109 | service_client_free(parent); |
136 | } else { | 110 | } else { |
137 | (*client)->own_connection = 1; | 111 | (*client)->free_parent = 1; |
138 | } | 112 | } |
139 | return err; | 113 | return err; |
140 | } | 114 | } |
141 | 115 | ||
142 | /** | 116 | afc_error_t afc_client_start_service(idevice_t device, afc_client_t * client, const char* label) |
143 | * Frees up an AFC client. If the connection was created by the | 117 | { |
144 | * client itself, the connection will be closed. | 118 | afc_error_t err = AFC_E_UNKNOWN_ERROR; |
145 | * | 119 | service_client_factory_start_service(device, AFC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(afc_client_new), &err); |
146 | * @param client The client to free. | 120 | return err; |
147 | */ | 121 | } |
122 | |||
148 | afc_error_t afc_client_free(afc_client_t client) | 123 | afc_error_t afc_client_free(afc_client_t client) |
149 | { | 124 | { |
150 | if (!client || !client->afc_packet) | 125 | if (!client || !client->afc_packet) |
151 | return AFC_E_INVALID_ARG; | 126 | return AFC_E_INVALID_ARG; |
152 | 127 | ||
153 | if (client->own_connection && client->connection) { | 128 | if (client->free_parent && client->parent) { |
154 | idevice_disconnect(client->connection); | 129 | service_client_free(client->parent); |
155 | client->connection = NULL; | 130 | client->parent = NULL; |
156 | } | 131 | } |
157 | free(client->afc_packet); | 132 | free(client->afc_packet); |
158 | if (client->mutex) { | 133 | mutex_destroy(&client->mutex); |
159 | g_mutex_free(client->mutex); | ||
160 | } | ||
161 | free(client); | 134 | free(client); |
162 | return AFC_E_SUCCESS; | 135 | return AFC_E_SUCCESS; |
163 | } | 136 | } |
164 | 137 | ||
165 | /** | 138 | /** |
166 | * Dispatches an AFC packet over a client. | 139 | * Dispatches an AFC packet over a client. |
167 | * | 140 | * |
168 | * @param client The client to send data through. | 141 | * @param client The client to send data through. |
169 | * @param data The data to send. | 142 | * @param operation The operation to perform. |
170 | * @param length The length to send. | 143 | * @param data The data to send together with the header. |
171 | * @param bytes_sent The number of bytes actually sent. | 144 | * @param data_length The length of the data to send with the header. |
145 | * @param payload The data to send after the header has been sent. | ||
146 | * @param payload_length The length of data to send after the header. | ||
147 | * @param bytes_sent The total number of bytes actually sent. | ||
172 | * | 148 | * |
173 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | 149 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. |
174 | * | ||
175 | * @warning set client->afc_packet->this_length and | ||
176 | * client->afc_packet->entire_length to 0 before calling this. The | ||
177 | * reason is that if you set them to different values, it indicates | ||
178 | * you want to send the data as two packets. | ||
179 | */ | 150 | */ |
180 | static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, uint32_t length, uint32_t *bytes_sent) | 151 | static afc_error_t afc_dispatch_packet(afc_client_t client, uint64_t operation, uint32_t data_length, const char* payload, uint32_t payload_length, uint32_t *bytes_sent) |
181 | { | 152 | { |
182 | uint32_t offset = 0; | ||
183 | uint32_t sent = 0; | 153 | uint32_t sent = 0; |
184 | 154 | ||
185 | if (!client || !client->connection || !client->afc_packet) | 155 | if (!client || !client->parent || !client->afc_packet) |
186 | return AFC_E_INVALID_ARG; | 156 | return AFC_E_INVALID_ARG; |
187 | 157 | ||
188 | *bytes_sent = 0; | 158 | *bytes_sent = 0; |
189 | 159 | ||
190 | if (!data || !length) | 160 | if (!payload || !payload_length) |
191 | length = 0; | 161 | payload_length = 0; |
192 | 162 | ||
193 | client->afc_packet->packet_num++; | 163 | client->afc_packet->packet_num++; |
194 | if (!client->afc_packet->entire_length) { | 164 | client->afc_packet->operation = operation; |
195 | client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length : sizeof(AFCPacket); | 165 | client->afc_packet->entire_length = sizeof(AFCPacket) + data_length + payload_length; |
196 | client->afc_packet->this_length = client->afc_packet->entire_length; | 166 | client->afc_packet->this_length = sizeof(AFCPacket) + data_length; |
197 | } | 167 | |
198 | if (!client->afc_packet->this_length) { | 168 | debug_info("packet length = %i", client->afc_packet->this_length); |
199 | client->afc_packet->this_length = sizeof(AFCPacket); | 169 | |
200 | } | 170 | /* send AFC packet header and data */ |
201 | /* We want to send two segments; buffer+sizeof(AFCPacket) to this_length | 171 | AFCPacket_to_LE(client->afc_packet); |
202 | is the parameters and everything beyond that is the next packet. | 172 | debug_buffer((char*)client->afc_packet, sizeof(AFCPacket) + data_length); |
203 | (for writing) */ | 173 | sent = 0; |
204 | if (client->afc_packet->this_length != client->afc_packet->entire_length) { | 174 | service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket) + data_length, &sent); |
205 | offset = client->afc_packet->this_length - sizeof(AFCPacket); | 175 | AFCPacket_from_LE(client->afc_packet); |
206 | 176 | *bytes_sent += sent; | |
207 | debug_info("Offset: %i", offset); | 177 | if (sent < sizeof(AFCPacket) + data_length) { |
208 | if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) { | ||
209 | debug_info("Length did not resemble what it was supposed to based on packet"); | ||
210 | debug_info("length minus offset: %i", length - offset); | ||
211 | debug_info("rest of packet: %i\n", client->afc_packet->entire_length - client->afc_packet->this_length); | ||
212 | return AFC_E_INTERNAL_ERROR; | ||
213 | } | ||
214 | |||
215 | /* send AFC packet header */ | ||
216 | AFCPacket_to_LE(client->afc_packet); | ||
217 | sent = 0; | ||
218 | idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); | ||
219 | AFCPacket_from_LE(client->afc_packet); | ||
220 | if (sent == 0) { | ||
221 | /* FIXME: should this be handled as success?! */ | ||
222 | return AFC_E_SUCCESS; | ||
223 | } | ||
224 | *bytes_sent += sent; | ||
225 | |||
226 | /* send AFC packet data */ | ||
227 | sent = 0; | ||
228 | idevice_connection_send(client->connection, data, offset, &sent); | ||
229 | if (sent == 0) { | ||
230 | return AFC_E_SUCCESS; | ||
231 | } | ||
232 | *bytes_sent += sent; | ||
233 | |||
234 | debug_info("sent the first now go with the second"); | ||
235 | debug_info("Length: %i", length - offset); | ||
236 | debug_info("Buffer: "); | ||
237 | debug_buffer(data + offset, length - offset); | ||
238 | |||
239 | sent = 0; | ||
240 | idevice_connection_send(client->connection, data + offset, length - offset, &sent); | ||
241 | |||
242 | *bytes_sent = sent; | ||
243 | return AFC_E_SUCCESS; | 178 | return AFC_E_SUCCESS; |
244 | } else { | 179 | } |
245 | debug_info("doin things the old way"); | ||
246 | debug_info("packet length = %i", client->afc_packet->this_length); | ||
247 | |||
248 | debug_buffer((char*)client->afc_packet, sizeof(AFCPacket)); | ||
249 | 180 | ||
250 | /* send AFC packet header */ | 181 | sent = 0; |
251 | AFCPacket_to_LE(client->afc_packet); | 182 | if (payload_length > 0) { |
252 | sent = 0; | 183 | if (payload_length > 256) { |
253 | idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); | 184 | debug_info("packet payload follows (256/%u)", payload_length); |
254 | AFCPacket_from_LE(client->afc_packet); | 185 | debug_buffer(payload, 256); |
255 | if (sent == 0) { | 186 | } else { |
256 | return AFC_E_SUCCESS; | 187 | debug_info("packet payload follows"); |
257 | } | 188 | debug_buffer(payload, payload_length); |
258 | *bytes_sent += sent; | ||
259 | /* send AFC packet data (if there's data to send) */ | ||
260 | if (length > 0) { | ||
261 | debug_info("packet data follows"); | ||
262 | |||
263 | debug_buffer(data, length); | ||
264 | idevice_connection_send(client->connection, data, length, &sent); | ||
265 | *bytes_sent += sent; | ||
266 | } | 189 | } |
190 | service_send(client->parent, payload, payload_length, &sent); | ||
191 | } | ||
192 | *bytes_sent += sent; | ||
193 | if (sent < payload_length) { | ||
267 | return AFC_E_SUCCESS; | 194 | return AFC_E_SUCCESS; |
268 | } | 195 | } |
269 | return AFC_E_INTERNAL_ERROR; | 196 | |
197 | return AFC_E_SUCCESS; | ||
270 | } | 198 | } |
271 | 199 | ||
272 | /** | 200 | /** |
273 | * Receives data through an AFC client and sets a variable to the received data. | 201 | * Receives data through an AFC client and sets a variable to the received data. |
274 | * | 202 | * |
275 | * @param client The client to receive data on. | 203 | * @param client The client to receive data on. |
276 | * @param dump_here The char* to point to the newly-received data. | 204 | * @param bytes The char* to point to the newly-received data. |
277 | * @param bytes_recv How much data was received. | 205 | * @param bytes_recv How much data was received. |
278 | * | 206 | * |
279 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | 207 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. |
280 | */ | 208 | */ |
281 | static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint32_t *bytes_recv) | 209 | static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t *bytes_recv) |
282 | { | 210 | { |
283 | AFCPacket header; | 211 | AFCPacket header; |
284 | uint32_t entire_len = 0; | 212 | uint32_t entire_len = 0; |
285 | uint32_t this_len = 0; | 213 | uint32_t this_len = 0; |
286 | uint32_t current_count = 0; | 214 | uint32_t current_count = 0; |
287 | uint64_t param1 = -1; | 215 | uint64_t param1 = -1; |
216 | char *buf = NULL; | ||
217 | uint32_t recv_len = 0; | ||
288 | 218 | ||
289 | *bytes_recv = 0; | 219 | if (bytes_recv) { |
220 | *bytes_recv = 0; | ||
221 | } | ||
222 | if (bytes) { | ||
223 | *bytes = NULL; | ||
224 | } | ||
290 | 225 | ||
291 | /* first, read the AFC header */ | 226 | /* first, read the AFC header */ |
292 | idevice_connection_receive(client->connection, (char*)&header, sizeof(AFCPacket), bytes_recv); | 227 | service_receive(client->parent, (char*)&header, sizeof(AFCPacket), &recv_len); |
293 | AFCPacket_from_LE(&header); | 228 | AFCPacket_from_LE(&header); |
294 | if (*bytes_recv == 0) { | 229 | if (recv_len == 0) { |
295 | debug_info("Just didn't get enough."); | 230 | debug_info("Just didn't get enough."); |
296 | *dump_here = NULL; | ||
297 | return AFC_E_MUX_ERROR; | 231 | return AFC_E_MUX_ERROR; |
298 | } else if (*bytes_recv < sizeof(AFCPacket)) { | 232 | } |
233 | |||
234 | if (recv_len < sizeof(AFCPacket)) { | ||
299 | debug_info("Did not even get the AFCPacket header"); | 235 | debug_info("Did not even get the AFCPacket header"); |
300 | *dump_here = NULL; | ||
301 | return AFC_E_MUX_ERROR; | 236 | return AFC_E_MUX_ERROR; |
302 | } | 237 | } |
303 | 238 | ||
304 | /* check if it's a valid AFC header */ | 239 | /* check if it's a valid AFC header */ |
305 | if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN)) { | 240 | if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN) != 0) { |
306 | debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!"); | 241 | debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!"); |
307 | } | 242 | } |
308 | 243 | ||
@@ -310,25 +245,21 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
310 | if (header.packet_num != client->afc_packet->packet_num) { | 245 | if (header.packet_num != client->afc_packet->packet_num) { |
311 | /* otherwise print a warning but do not abort */ | 246 | /* otherwise print a warning but do not abort */ |
312 | debug_info("ERROR: Unexpected packet number (%lld != %lld) aborting.", header.packet_num, client->afc_packet->packet_num); | 247 | debug_info("ERROR: Unexpected packet number (%lld != %lld) aborting.", header.packet_num, client->afc_packet->packet_num); |
313 | *dump_here = NULL; | ||
314 | return AFC_E_OP_HEADER_INVALID; | 248 | return AFC_E_OP_HEADER_INVALID; |
315 | } | 249 | } |
316 | 250 | ||
317 | /* then, read the attached packet */ | 251 | /* then, read the attached packet */ |
318 | if (header.this_length < sizeof(AFCPacket)) { | 252 | if (header.this_length < sizeof(AFCPacket)) { |
319 | debug_info("Invalid AFCPacket header received!"); | 253 | debug_info("Invalid AFCPacket header received!"); |
320 | *dump_here = NULL; | ||
321 | return AFC_E_OP_HEADER_INVALID; | 254 | return AFC_E_OP_HEADER_INVALID; |
322 | } else if ((header.this_length == header.entire_length) | 255 | } |
323 | && header.entire_length == sizeof(AFCPacket)) { | 256 | if ((header.this_length == header.entire_length) |
257 | && header.entire_length == sizeof(AFCPacket)) { | ||
324 | debug_info("Empty AFCPacket received!"); | 258 | debug_info("Empty AFCPacket received!"); |
325 | *dump_here = NULL; | ||
326 | *bytes_recv = 0; | ||
327 | if (header.operation == AFC_OP_DATA) { | 259 | if (header.operation == AFC_OP_DATA) { |
328 | return AFC_E_SUCCESS; | 260 | return AFC_E_SUCCESS; |
329 | } else { | ||
330 | return AFC_E_IO_ERROR; | ||
331 | } | 261 | } |
262 | return AFC_E_IO_ERROR; | ||
332 | } | 263 | } |
333 | 264 | ||
334 | debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation); | 265 | debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation); |
@@ -336,22 +267,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
336 | entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); | 267 | entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); |
337 | this_len = (uint32_t)header.this_length - sizeof(AFCPacket); | 268 | this_len = (uint32_t)header.this_length - sizeof(AFCPacket); |
338 | 269 | ||
339 | /* this is here as a check (perhaps a different upper limit is good?) */ | 270 | buf = (char*)malloc(entire_len); |
340 | if (entire_len > (uint32_t)MAXIMUM_PACKET_SIZE) { | ||
341 | fprintf(stderr, "%s: entire_len is larger than MAXIMUM_PACKET_SIZE, (%d > %d)!", __func__, entire_len, MAXIMUM_PACKET_SIZE); | ||
342 | } | ||
343 | |||
344 | *dump_here = (char*)malloc(entire_len); | ||
345 | if (this_len > 0) { | 271 | if (this_len > 0) { |
346 | idevice_connection_receive(client->connection, *dump_here, this_len, bytes_recv); | 272 | recv_len = 0; |
347 | if (*bytes_recv <= 0) { | 273 | service_receive(client->parent, buf, this_len, &recv_len); |
348 | free(*dump_here); | 274 | if (recv_len <= 0) { |
349 | *dump_here = NULL; | 275 | free(buf); |
350 | debug_info("Did not get packet contents!"); | 276 | debug_info("Did not get packet contents!"); |
351 | return AFC_E_NOT_ENOUGH_DATA; | 277 | return AFC_E_NOT_ENOUGH_DATA; |
352 | } else if (*bytes_recv < this_len) { | 278 | } |
353 | free(*dump_here); | 279 | if (recv_len < this_len) { |
354 | *dump_here = NULL; | 280 | free(buf); |
355 | debug_info("Could not receive this_len=%d bytes", this_len); | 281 | debug_info("Could not receive this_len=%d bytes", this_len); |
356 | return AFC_E_NOT_ENOUGH_DATA; | 282 | return AFC_E_NOT_ENOUGH_DATA; |
357 | } | 283 | } |
@@ -361,12 +287,13 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
361 | 287 | ||
362 | if (entire_len > this_len) { | 288 | if (entire_len > this_len) { |
363 | while (current_count < entire_len) { | 289 | while (current_count < entire_len) { |
364 | idevice_connection_receive(client->connection, (*dump_here)+current_count, entire_len - current_count, bytes_recv); | 290 | recv_len = 0; |
365 | if (*bytes_recv <= 0) { | 291 | service_receive(client->parent, buf+current_count, entire_len - current_count, &recv_len); |
366 | debug_info("Error receiving data (recv returned %d)", *bytes_recv); | 292 | if (recv_len <= 0) { |
293 | debug_info("Error receiving data (recv returned %d)", recv_len); | ||
367 | break; | 294 | break; |
368 | } | 295 | } |
369 | current_count += *bytes_recv; | 296 | current_count += recv_len; |
370 | } | 297 | } |
371 | if (current_count < entire_len) { | 298 | if (current_count < entire_len) { |
372 | debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len); | 299 | debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len); |
@@ -374,12 +301,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
374 | } | 301 | } |
375 | 302 | ||
376 | if (current_count >= sizeof(uint64_t)) { | 303 | if (current_count >= sizeof(uint64_t)) { |
377 | param1 = GUINT64_FROM_LE(*(uint64_t*)(*dump_here)); | 304 | param1 = le64toh(*(uint64_t*)(buf)); |
378 | } | 305 | } |
379 | 306 | ||
380 | debug_info("packet data size = %i", current_count); | 307 | debug_info("packet data size = %i", current_count); |
381 | debug_info("packet data follows"); | 308 | if (current_count > 256) { |
382 | debug_buffer(*dump_here, current_count); | 309 | debug_info("packet data follows (256/%u)", current_count); |
310 | debug_buffer(buf, 256); | ||
311 | } else { | ||
312 | debug_info("packet data follows"); | ||
313 | debug_buffer(buf, current_count); | ||
314 | } | ||
383 | 315 | ||
384 | /* check operation types */ | 316 | /* check operation types */ |
385 | if (header.operation == AFC_OP_STATUS) { | 317 | if (header.operation == AFC_OP_STATUS) { |
@@ -389,8 +321,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
389 | if (param1 != AFC_E_SUCCESS) { | 321 | if (param1 != AFC_E_SUCCESS) { |
390 | /* error status */ | 322 | /* error status */ |
391 | /* free buffer */ | 323 | /* free buffer */ |
392 | free(*dump_here); | 324 | free(buf); |
393 | *dump_here = NULL; | ||
394 | return (afc_error_t)param1; | 325 | return (afc_error_t)param1; |
395 | } | 326 | } |
396 | } else if (header.operation == AFC_OP_DATA) { | 327 | } else if (header.operation == AFC_OP_DATA) { |
@@ -404,16 +335,22 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
404 | debug_info("got a tell response, position=%lld", param1); | 335 | debug_info("got a tell response, position=%lld", param1); |
405 | } else { | 336 | } else { |
406 | /* unknown operation code received */ | 337 | /* unknown operation code received */ |
407 | free(*dump_here); | 338 | free(buf); |
408 | *dump_here = NULL; | ||
409 | *bytes_recv = 0; | ||
410 | 339 | ||
411 | debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); | 340 | debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); |
341 | #ifndef WIN32 | ||
412 | fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1); | 342 | fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1); |
343 | #endif | ||
413 | 344 | ||
414 | return AFC_E_OP_NOT_SUPPORTED; | 345 | return AFC_E_OP_NOT_SUPPORTED; |
415 | } | 346 | } |
416 | 347 | ||
348 | if (bytes) { | ||
349 | *bytes = buf; | ||
350 | } else { | ||
351 | free(buf); | ||
352 | } | ||
353 | |||
417 | *bytes_recv = current_count; | 354 | *bytes_recv = current_count; |
418 | return AFC_E_SUCCESS; | 355 | return AFC_E_SUCCESS; |
419 | } | 356 | } |
@@ -421,7 +358,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
421 | /** | 358 | /** |
422 | * Returns counts of null characters within a string. | 359 | * Returns counts of null characters within a string. |
423 | */ | 360 | */ |
424 | static uint32_t count_nullspaces(char *string, uint32_t number) | 361 | static uint32_t count_nullspaces(const char *string, uint32_t number) |
425 | { | 362 | { |
426 | uint32_t i = 0, nulls = 0; | 363 | uint32_t i = 0, nulls = 0; |
427 | 364 | ||
@@ -462,32 +399,42 @@ static char **make_strings_list(char *tokens, uint32_t length) | |||
462 | return list; | 399 | return list; |
463 | } | 400 | } |
464 | 401 | ||
465 | /** | 402 | static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len) |
466 | * Gets a directory listing of the directory requested. | 403 | { |
467 | * | 404 | if (data_len > client->packet_extra) { |
468 | * @param client The client to get a directory listing from. | 405 | client->packet_extra = (data_len & ~8) + 8; |
469 | * @param dir The directory to list. (must be a fully-qualified path) | 406 | AFCPacket* newpkt = (AFCPacket*)realloc(client->afc_packet, sizeof(AFCPacket) + client->packet_extra); |
470 | * @param list A char list of files in that directory, terminated by an empty | 407 | if (!newpkt) { |
471 | * string or NULL if there was an error. | 408 | return -1; |
472 | * | 409 | } |
473 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | 410 | client->afc_packet = newpkt; |
474 | */ | 411 | } |
475 | afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***list) | 412 | return 0; |
413 | } | ||
414 | |||
415 | #define AFC_PACKET_DATA_PTR ((char*)client->afc_packet + sizeof(AFCPacket)) | ||
416 | |||
417 | afc_error_t afc_read_directory(afc_client_t client, const char *path, char ***directory_information) | ||
476 | { | 418 | { |
477 | uint32_t bytes = 0; | 419 | uint32_t bytes = 0; |
478 | char *data = NULL, **list_loc = NULL; | 420 | char *data = NULL, **list_loc = NULL; |
479 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 421 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
480 | 422 | ||
481 | if (!client || !dir || !list || (list && *list)) | 423 | if (!client || !path || !directory_information || (directory_information && *directory_information)) |
482 | return AFC_E_INVALID_ARG; | 424 | return AFC_E_INVALID_ARG; |
483 | 425 | ||
484 | afc_lock(client); | 426 | afc_lock(client); |
485 | 427 | ||
428 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
429 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
430 | afc_unlock(client); | ||
431 | debug_info("Failed to realloc packet buffer"); | ||
432 | return AFC_E_NO_MEM; | ||
433 | } | ||
434 | |||
486 | /* Send the command */ | 435 | /* Send the command */ |
487 | client->afc_packet->operation = AFC_OP_READ_DIR; | 436 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
488 | client->afc_packet->entire_length = 0; | 437 | ret = afc_dispatch_packet(client, AFC_OP_READ_DIR, data_len, NULL, 0, &bytes); |
489 | client->afc_packet->this_length = 0; | ||
490 | ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes); | ||
491 | if (ret != AFC_E_SUCCESS) { | 438 | if (ret != AFC_E_SUCCESS) { |
492 | afc_unlock(client); | 439 | afc_unlock(client); |
493 | return AFC_E_NOT_ENOUGH_DATA; | 440 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -495,6 +442,8 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis | |||
495 | /* Receive the data */ | 442 | /* Receive the data */ |
496 | ret = afc_receive_data(client, &data, &bytes); | 443 | ret = afc_receive_data(client, &data, &bytes); |
497 | if (ret != AFC_E_SUCCESS) { | 444 | if (ret != AFC_E_SUCCESS) { |
445 | if (data) | ||
446 | free(data); | ||
498 | afc_unlock(client); | 447 | afc_unlock(client); |
499 | return ret; | 448 | return ret; |
500 | } | 449 | } |
@@ -504,37 +453,24 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis | |||
504 | free(data); | 453 | free(data); |
505 | 454 | ||
506 | afc_unlock(client); | 455 | afc_unlock(client); |
507 | *list = list_loc; | 456 | *directory_information = list_loc; |
508 | 457 | ||
509 | return ret; | 458 | return ret; |
510 | } | 459 | } |
511 | 460 | ||
512 | /** | 461 | afc_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 | */ | ||
523 | afc_error_t afc_get_device_info(afc_client_t client, char ***infos) | ||
524 | { | 462 | { |
525 | uint32_t bytes = 0; | 463 | uint32_t bytes = 0; |
526 | char *data = NULL, **list = NULL; | 464 | char *data = NULL, **list = NULL; |
527 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 465 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
528 | 466 | ||
529 | if (!client || !infos) | 467 | if (!client || !device_information) |
530 | return AFC_E_INVALID_ARG; | 468 | return AFC_E_INVALID_ARG; |
531 | 469 | ||
532 | afc_lock(client); | 470 | afc_lock(client); |
533 | 471 | ||
534 | /* Send the command */ | 472 | /* Send the command */ |
535 | client->afc_packet->operation = AFC_OP_GET_DEVINFO; | 473 | ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes); |
536 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
537 | ret = afc_dispatch_packet(client, NULL, 0, &bytes); | ||
538 | if (ret != AFC_E_SUCCESS) { | 474 | if (ret != AFC_E_SUCCESS) { |
539 | afc_unlock(client); | 475 | afc_unlock(client); |
540 | return AFC_E_NOT_ENOUGH_DATA; | 476 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -542,6 +478,8 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos) | |||
542 | /* Receive the data */ | 478 | /* Receive the data */ |
543 | ret = afc_receive_data(client, &data, &bytes); | 479 | ret = afc_receive_data(client, &data, &bytes); |
544 | if (ret != AFC_E_SUCCESS) { | 480 | if (ret != AFC_E_SUCCESS) { |
481 | if (data) | ||
482 | free(data); | ||
545 | afc_unlock(client); | 483 | afc_unlock(client); |
546 | return ret; | 484 | return ret; |
547 | } | 485 | } |
@@ -552,22 +490,11 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos) | |||
552 | 490 | ||
553 | afc_unlock(client); | 491 | afc_unlock(client); |
554 | 492 | ||
555 | *infos = list; | 493 | *device_information = list; |
556 | 494 | ||
557 | return ret; | 495 | return ret; |
558 | } | 496 | } |
559 | 497 | ||
560 | /** | ||
561 | * Get a specific key of the device info list for a client connection. | ||
562 | * Known key values are: Model, FSTotalBytes, FSFreeBytes and FSBlockSize. | ||
563 | * This is a helper function for afc_get_device_info(). | ||
564 | * | ||
565 | * @param client The client to get device info for. | ||
566 | * @param key The key to get the value of. | ||
567 | * @param value The value for the key if successful or NULL otherwise. | ||
568 | * | ||
569 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
570 | */ | ||
571 | afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) | 498 | afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) |
572 | { | 499 | { |
573 | afc_error_t ret = AFC_E_INTERNAL_ERROR; | 500 | afc_error_t ret = AFC_E_INTERNAL_ERROR; |
@@ -587,43 +514,40 @@ afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char * | |||
587 | break; | 514 | break; |
588 | } | 515 | } |
589 | } | 516 | } |
590 | 517 | for (ptr = kvps; *ptr; ptr++) { | |
591 | g_strfreev(kvps); | 518 | free(*ptr); |
519 | } | ||
520 | free(kvps); | ||
592 | 521 | ||
593 | return ret; | 522 | return ret; |
594 | } | 523 | } |
595 | 524 | ||
596 | /** | ||
597 | * Deletes a file or directory. | ||
598 | * | ||
599 | * @param client The client to use. | ||
600 | * @param path The path to delete. (must be a fully-qualified path) | ||
601 | * | ||
602 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
603 | */ | ||
604 | afc_error_t afc_remove_path(afc_client_t client, const char *path) | 525 | afc_error_t afc_remove_path(afc_client_t client, const char *path) |
605 | { | 526 | { |
606 | char *response = NULL; | ||
607 | uint32_t bytes = 0; | 527 | uint32_t bytes = 0; |
608 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 528 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
609 | 529 | ||
610 | if (!client || !path || !client->afc_packet || !client->connection) | 530 | if (!client || !path || !client->afc_packet || !client->parent) |
611 | return AFC_E_INVALID_ARG; | 531 | return AFC_E_INVALID_ARG; |
612 | 532 | ||
613 | afc_lock(client); | 533 | afc_lock(client); |
614 | 534 | ||
535 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
536 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
537 | afc_unlock(client); | ||
538 | debug_info("Failed to realloc packet buffer"); | ||
539 | return AFC_E_NO_MEM; | ||
540 | } | ||
541 | |||
615 | /* Send command */ | 542 | /* Send command */ |
616 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 543 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
617 | client->afc_packet->operation = AFC_OP_REMOVE_PATH; | 544 | ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH, data_len, NULL, 0, &bytes); |
618 | ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes); | ||
619 | if (ret != AFC_E_SUCCESS) { | 545 | if (ret != AFC_E_SUCCESS) { |
620 | afc_unlock(client); | 546 | afc_unlock(client); |
621 | return AFC_E_NOT_ENOUGH_DATA; | 547 | return AFC_E_NOT_ENOUGH_DATA; |
622 | } | 548 | } |
623 | /* Receive response */ | 549 | /* Receive response */ |
624 | ret = afc_receive_data(client, &response, &bytes); | 550 | ret = afc_receive_data(client, NULL, &bytes); |
625 | if (response) | ||
626 | free(response); | ||
627 | 551 | ||
628 | /* special case; unknown error actually means directory not empty */ | 552 | /* special case; unknown error actually means directory not empty */ |
629 | if (ret == AFC_E_UNKNOWN_ERROR) | 553 | if (ret == AFC_E_UNKNOWN_ERROR) |
@@ -634,61 +558,45 @@ afc_error_t afc_remove_path(afc_client_t client, const char *path) | |||
634 | return ret; | 558 | return ret; |
635 | } | 559 | } |
636 | 560 | ||
637 | /** | ||
638 | * Renames a file or directory on the phone. | ||
639 | * | ||
640 | * @param client The client to have rename. | ||
641 | * @param from The name to rename from. (must be a fully-qualified path) | ||
642 | * @param to The new name. (must also be a fully-qualified path) | ||
643 | * | ||
644 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
645 | */ | ||
646 | afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to) | 561 | afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to) |
647 | { | 562 | { |
648 | char *response = NULL; | 563 | if (!client || !from || !to || !client->afc_packet || !client->parent) |
649 | char *send = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t))); | 564 | return AFC_E_INVALID_ARG; |
565 | |||
650 | uint32_t bytes = 0; | 566 | uint32_t bytes = 0; |
651 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 567 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
652 | 568 | ||
653 | if (!client || !from || !to || !client->afc_packet || !client->connection) | 569 | size_t from_len = strlen(from); |
654 | return AFC_E_INVALID_ARG; | 570 | size_t to_len = strlen(to); |
655 | 571 | ||
656 | afc_lock(client); | 572 | afc_lock(client); |
657 | 573 | ||
574 | uint32_t data_len = (uint32_t)(from_len+1 + to_len+1); | ||
575 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
576 | afc_unlock(client); | ||
577 | debug_info("Failed to realloc packet buffer"); | ||
578 | return AFC_E_NO_MEM; | ||
579 | } | ||
580 | |||
658 | /* Send command */ | 581 | /* Send command */ |
659 | memcpy(send, from, strlen(from) + 1); | 582 | memcpy(AFC_PACKET_DATA_PTR, from, from_len+1); |
660 | memcpy(send + strlen(from) + 1, to, strlen(to) + 1); | 583 | memcpy(AFC_PACKET_DATA_PTR + from_len+1, to, to_len+1); |
661 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 584 | ret = afc_dispatch_packet(client, AFC_OP_RENAME_PATH, data_len, NULL, 0, &bytes); |
662 | client->afc_packet->operation = AFC_OP_RENAME_PATH; | ||
663 | ret = afc_dispatch_packet(client, send, strlen(to)+1 + strlen(from)+1, &bytes); | ||
664 | free(send); | ||
665 | if (ret != AFC_E_SUCCESS) { | 585 | if (ret != AFC_E_SUCCESS) { |
666 | afc_unlock(client); | 586 | afc_unlock(client); |
667 | return AFC_E_NOT_ENOUGH_DATA; | 587 | return AFC_E_NOT_ENOUGH_DATA; |
668 | } | 588 | } |
669 | /* Receive response */ | 589 | /* Receive response */ |
670 | ret = afc_receive_data(client, &response, &bytes); | 590 | ret = afc_receive_data(client, NULL, &bytes); |
671 | if (response) | ||
672 | free(response); | ||
673 | 591 | ||
674 | afc_unlock(client); | 592 | afc_unlock(client); |
675 | 593 | ||
676 | return ret; | 594 | return ret; |
677 | } | 595 | } |
678 | 596 | ||
679 | /** | 597 | afc_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 | */ | ||
688 | afc_error_t afc_make_directory(afc_client_t client, const char *dir) | ||
689 | { | 598 | { |
690 | uint32_t bytes = 0; | 599 | uint32_t bytes = 0; |
691 | char *response = NULL; | ||
692 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 600 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
693 | 601 | ||
694 | if (!client) | 602 | if (!client) |
@@ -696,50 +604,51 @@ afc_error_t afc_make_directory(afc_client_t client, const char *dir) | |||
696 | 604 | ||
697 | afc_lock(client); | 605 | afc_lock(client); |
698 | 606 | ||
607 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
608 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
609 | afc_unlock(client); | ||
610 | debug_info("Failed to realloc packet buffer"); | ||
611 | return AFC_E_NO_MEM; | ||
612 | } | ||
613 | |||
699 | /* Send command */ | 614 | /* Send command */ |
700 | client->afc_packet->operation = AFC_OP_MAKE_DIR; | 615 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
701 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 616 | ret = afc_dispatch_packet(client, AFC_OP_MAKE_DIR, data_len, NULL, 0, &bytes); |
702 | ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes); | ||
703 | if (ret != AFC_E_SUCCESS) { | 617 | if (ret != AFC_E_SUCCESS) { |
704 | afc_unlock(client); | 618 | afc_unlock(client); |
705 | return AFC_E_NOT_ENOUGH_DATA; | 619 | return AFC_E_NOT_ENOUGH_DATA; |
706 | } | 620 | } |
707 | /* Receive response */ | 621 | /* Receive response */ |
708 | ret = afc_receive_data(client, &response, &bytes); | 622 | ret = afc_receive_data(client, NULL, &bytes); |
709 | if (response) | ||
710 | free(response); | ||
711 | 623 | ||
712 | afc_unlock(client); | 624 | afc_unlock(client); |
713 | 625 | ||
714 | return ret; | 626 | return ret; |
715 | } | 627 | } |
716 | 628 | ||
717 | /** | 629 | afc_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 | */ | ||
728 | afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***infolist) | ||
729 | { | 630 | { |
730 | char *received = NULL; | 631 | char *received = NULL; |
731 | uint32_t bytes = 0; | 632 | uint32_t bytes = 0; |
732 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 633 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
733 | 634 | ||
734 | if (!client || !path || !infolist) | 635 | if (!client || !path || !file_information) |
735 | return AFC_E_INVALID_ARG; | 636 | return AFC_E_INVALID_ARG; |
736 | 637 | ||
737 | afc_lock(client); | 638 | afc_lock(client); |
738 | 639 | ||
640 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
641 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
642 | afc_unlock(client); | ||
643 | debug_info("Failed to realloc packet buffer"); | ||
644 | return AFC_E_NO_MEM; | ||
645 | } | ||
646 | |||
647 | debug_info("We got %p and %p", client->afc_packet, AFC_PACKET_DATA_PTR); | ||
648 | |||
739 | /* Send command */ | 649 | /* Send command */ |
740 | client->afc_packet->operation = AFC_OP_GET_FILE_INFO; | 650 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
741 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 651 | ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); |
742 | ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes); | ||
743 | if (ret != AFC_E_SUCCESS) { | 652 | if (ret != AFC_E_SUCCESS) { |
744 | afc_unlock(client); | 653 | afc_unlock(client); |
745 | return AFC_E_NOT_ENOUGH_DATA; | 654 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -748,7 +657,7 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf | |||
748 | /* Receive data */ | 657 | /* Receive data */ |
749 | ret = afc_receive_data(client, &received, &bytes); | 658 | ret = afc_receive_data(client, &received, &bytes); |
750 | if (received) { | 659 | if (received) { |
751 | *infolist = make_strings_list(received, bytes); | 660 | *file_information = make_strings_list(received, bytes); |
752 | free(received); | 661 | free(received); |
753 | } | 662 | } |
754 | 663 | ||
@@ -757,51 +666,39 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf | |||
757 | return ret; | 666 | return ret; |
758 | } | 667 | } |
759 | 668 | ||
760 | /** | 669 | afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) |
761 | * Opens a file on the phone. | ||
762 | * | ||
763 | * @param client The client to use to open the file. | ||
764 | * @param filename The file to open. (must be a fully-qualified path) | ||
765 | * @param file_mode The mode to use to open the file. Can be AFC_FILE_READ or | ||
766 | * AFC_FILE_WRITE; the former lets you read and write, | ||
767 | * however, and the second one will *create* the file, | ||
768 | * destroying anything previously there. | ||
769 | * @param handle Pointer to a uint64_t that will hold the handle of the file | ||
770 | * | ||
771 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
772 | */ | ||
773 | idevice_error_t | ||
774 | afc_file_open(afc_client_t client, const char *filename, | ||
775 | afc_file_mode_t file_mode, uint64_t *handle) | ||
776 | { | 670 | { |
777 | uint64_t file_mode_loc = GUINT64_TO_LE(file_mode); | 671 | if (!client || !client->parent || !client->afc_packet) |
672 | return AFC_E_INVALID_ARG; | ||
673 | |||
674 | //uint64_t file_mode_loc = htole64(file_mode); | ||
778 | uint32_t bytes = 0; | 675 | uint32_t bytes = 0; |
779 | char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1)); | ||
780 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 676 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
781 | 677 | ||
782 | /* set handle to 0 so in case an error occurs, the handle is invalid */ | 678 | /* set handle to 0 so in case an error occurs, the handle is invalid */ |
783 | *handle = 0; | 679 | *handle = 0; |
784 | 680 | ||
785 | if (!client || !client->connection || !client->afc_packet) | ||
786 | return AFC_E_INVALID_ARG; | ||
787 | |||
788 | afc_lock(client); | 681 | afc_lock(client); |
789 | 682 | ||
790 | /* Send command */ | 683 | uint32_t data_len = (uint32_t)(strlen(filename)+1 + 8); |
791 | memcpy(data, &file_mode_loc, 8); | 684 | if (_afc_check_packet_buffer(client, data_len) < 0) { |
792 | memcpy(data + 8, filename, strlen(filename)); | 685 | afc_unlock(client); |
793 | data[8 + strlen(filename)] = '\0'; | 686 | debug_info("Failed to realloc packet buffer"); |
794 | client->afc_packet->operation = AFC_OP_FILE_OPEN; | 687 | return AFC_E_NO_MEM; |
795 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 688 | } |
796 | ret = afc_dispatch_packet(client, data, 8 + strlen(filename) + 1, &bytes); | ||
797 | free(data); | ||
798 | 689 | ||
690 | /* Send command */ | ||
691 | //memcpy(AFC_PACKET_DATA_PTR, &file_mode_loc, 8); | ||
692 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(file_mode); | ||
693 | memcpy(AFC_PACKET_DATA_PTR + 8, filename, data_len-8); | ||
694 | ret = afc_dispatch_packet(client, AFC_OP_FILE_OPEN, data_len, NULL, 0, &bytes); | ||
799 | if (ret != AFC_E_SUCCESS) { | 695 | if (ret != AFC_E_SUCCESS) { |
800 | debug_info("Didn't receive a response to the command"); | 696 | debug_info("Didn't receive a response to the command"); |
801 | afc_unlock(client); | 697 | afc_unlock(client); |
802 | return AFC_E_NOT_ENOUGH_DATA; | 698 | return AFC_E_NOT_ENOUGH_DATA; |
803 | } | 699 | } |
804 | /* Receive the data */ | 700 | /* Receive the data */ |
701 | char* data = NULL; | ||
805 | ret = afc_receive_data(client, &data, &bytes); | 702 | ret = afc_receive_data(client, &data, &bytes); |
806 | if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { | 703 | if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { |
807 | afc_unlock(client); | 704 | afc_unlock(client); |
@@ -811,6 +708,8 @@ afc_file_open(afc_client_t client, const char *filename, | |||
811 | free(data); | 708 | free(data); |
812 | return ret; | 709 | return ret; |
813 | } | 710 | } |
711 | /* in case memory was allocated but no data received or an error occurred */ | ||
712 | free(data); | ||
814 | 713 | ||
815 | debug_info("Didn't get any further data"); | 714 | debug_info("Didn't get any further data"); |
816 | 715 | ||
@@ -819,156 +718,81 @@ afc_file_open(afc_client_t client, const char *filename, | |||
819 | return ret; | 718 | return ret; |
820 | } | 719 | } |
821 | 720 | ||
822 | /** | 721 | afc_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 | */ | ||
833 | idevice_error_t | ||
834 | afc_file_read(afc_client_t client, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read) | ||
835 | { | 722 | { |
836 | char *input = NULL; | 723 | char *input = NULL; |
837 | uint32_t current_count = 0, bytes_loc = 0; | 724 | uint32_t current_count = 0, bytes_loc = 0; |
838 | const uint32_t MAXIMUM_READ_SIZE = 1 << 16; | 725 | struct readinfo { |
726 | uint64_t handle; | ||
727 | uint64_t size; | ||
728 | }; | ||
839 | afc_error_t ret = AFC_E_SUCCESS; | 729 | afc_error_t ret = AFC_E_SUCCESS; |
840 | 730 | ||
841 | if (!client || !client->afc_packet || !client->connection || handle == 0) | 731 | if (!client || !client->afc_packet || !client->parent || handle == 0) |
842 | return AFC_E_INVALID_ARG; | 732 | return AFC_E_INVALID_ARG; |
843 | debug_info("called for length %i", length); | 733 | debug_info("called for length %i", length); |
844 | 734 | ||
735 | //uint32_t data_len = 8 + 8; | ||
736 | |||
845 | afc_lock(client); | 737 | afc_lock(client); |
846 | 738 | ||
847 | /* Looping here to get around the maximum amount of data that | 739 | /* Send the read command */ |
848 | afc_receive_data can handle */ | 740 | struct readinfo* readinfo = (struct readinfo*)(AFC_PACKET_DATA_PTR); |
849 | while (current_count < length) { | 741 | readinfo->handle = handle; |
850 | debug_info("current count is %i but length is %i", current_count, length); | 742 | readinfo->size = htole64(length); |
851 | 743 | ret = afc_dispatch_packet(client, AFC_OP_FILE_READ, sizeof(struct readinfo), NULL, 0, &bytes_loc); | |
852 | /* Send the read command */ | 744 | if (ret != AFC_E_SUCCESS) { |
853 | AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); | 745 | afc_unlock(client); |
854 | packet->filehandle = handle; | 746 | return AFC_E_NOT_ENOUGH_DATA; |
855 | packet->size = GUINT64_TO_LE(((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE); | 747 | } |
856 | client->afc_packet->operation = AFC_OP_READ; | 748 | /* Receive the data */ |
857 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 749 | ret = afc_receive_data(client, &input, &bytes_loc); |
858 | ret = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket), &bytes_loc); | 750 | debug_info("afc_receive_data returned error: %d", ret); |
859 | free(packet); | 751 | debug_info("bytes returned: %i", bytes_loc); |
860 | 752 | if (ret != AFC_E_SUCCESS) { | |
861 | if (ret != AFC_E_SUCCESS) { | 753 | afc_unlock(client); |
862 | afc_unlock(client); | 754 | return ret; |
863 | return AFC_E_NOT_ENOUGH_DATA; | 755 | } |
864 | } | 756 | if (bytes_loc == 0) { |
865 | /* Receive the data */ | 757 | if (input) |
866 | ret = afc_receive_data(client, &input, &bytes_loc); | 758 | free(input); |
867 | debug_info("afc_receive_data returned error: %d", ret); | 759 | afc_unlock(client); |
868 | debug_info("bytes returned: %i", bytes_loc); | 760 | *bytes_read = current_count; |
869 | if (ret != AFC_E_SUCCESS) { | 761 | /* FIXME: check that's actually a success */ |
870 | afc_unlock(client); | 762 | return ret; |
871 | return ret; | 763 | } |
872 | } else if (bytes_loc == 0) { | 764 | if (input) { |
873 | if (input) | 765 | debug_info("%d", bytes_loc); |
874 | free(input); | 766 | memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); |
875 | afc_unlock(client); | 767 | free(input); |
876 | *bytes_read = current_count; | 768 | input = NULL; |
877 | /* FIXME: check that's actually a success */ | 769 | current_count += (bytes_loc > length) ? length : bytes_loc; |
878 | return ret; | ||
879 | } else { | ||
880 | if (input) { | ||
881 | debug_info("%d", bytes_loc); | ||
882 | memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); | ||
883 | free(input); | ||
884 | input = NULL; | ||
885 | current_count += (bytes_loc > length) ? length : bytes_loc; | ||
886 | } | ||
887 | } | ||
888 | } | 770 | } |
889 | debug_info("returning current_count as %i", current_count); | ||
890 | 771 | ||
891 | afc_unlock(client); | 772 | afc_unlock(client); |
892 | *bytes_read = current_count; | 773 | *bytes_read = current_count; |
893 | return ret; | 774 | return ret; |
894 | } | 775 | } |
895 | 776 | ||
896 | /** | 777 | afc_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 | */ | ||
907 | idevice_error_t | ||
908 | afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written) | ||
909 | { | 778 | { |
910 | char *acknowledgement = NULL; | 779 | uint32_t current_count = 0; |
911 | const uint32_t MAXIMUM_WRITE_SIZE = 1 << 15; | ||
912 | uint32_t current_count = 0, i = 0; | ||
913 | uint32_t segments = (length / MAXIMUM_WRITE_SIZE); | ||
914 | uint32_t bytes_loc = 0; | 780 | uint32_t bytes_loc = 0; |
915 | char *out_buffer = NULL; | ||
916 | afc_error_t ret = AFC_E_SUCCESS; | 781 | afc_error_t ret = AFC_E_SUCCESS; |
917 | 782 | ||
918 | if (!client || !client->afc_packet || !client->connection || !bytes_written || (handle == 0)) | 783 | if (!client || !client->afc_packet || !client->parent || !bytes_written || (handle == 0)) |
919 | return AFC_E_INVALID_ARG; | 784 | return AFC_E_INVALID_ARG; |
920 | 785 | ||
786 | uint32_t data_len = 8; | ||
787 | |||
921 | afc_lock(client); | 788 | afc_lock(client); |
922 | 789 | ||
923 | debug_info("Write length: %i", length); | 790 | debug_info("Write length: %i", length); |
924 | 791 | ||
925 | /* Divide the file into segments. */ | 792 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; |
926 | for (i = 0; i < segments; i++) { | 793 | ret = afc_dispatch_packet(client, AFC_OP_FILE_WRITE, data_len, data, length, &bytes_loc); |
927 | /* Send the segment */ | ||
928 | client->afc_packet->this_length = sizeof(AFCPacket) + 8; | ||
929 | client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE; | ||
930 | client->afc_packet->operation = AFC_OP_WRITE; | ||
931 | out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); | ||
932 | memcpy(out_buffer, (char *)&handle, sizeof(uint64_t)); | ||
933 | memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE); | ||
934 | ret = afc_dispatch_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8, &bytes_loc); | ||
935 | if (ret != AFC_E_SUCCESS) { | ||
936 | afc_unlock(client); | ||
937 | return AFC_E_NOT_ENOUGH_DATA; | ||
938 | } | ||
939 | free(out_buffer); | ||
940 | out_buffer = NULL; | ||
941 | |||
942 | current_count += bytes_loc; | ||
943 | ret = afc_receive_data(client, &acknowledgement, &bytes_loc); | ||
944 | if (ret != AFC_E_SUCCESS) { | ||
945 | afc_unlock(client); | ||
946 | return ret; | ||
947 | } else { | ||
948 | free(acknowledgement); | ||
949 | } | ||
950 | } | ||
951 | |||
952 | /* By this point, we should be at the end. i.e. the last segment that didn't | ||
953 | get sent in the for loop. This length is fine because it's always | ||
954 | sizeof(AFCPacket) + 8, but to be sure we do it again */ | ||
955 | if (current_count == length) { | ||
956 | afc_unlock(client); | ||
957 | *bytes_written = current_count; | ||
958 | return ret; | ||
959 | } | ||
960 | |||
961 | client->afc_packet->this_length = sizeof(AFCPacket) + 8; | ||
962 | client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count); | ||
963 | client->afc_packet->operation = AFC_OP_WRITE; | ||
964 | out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); | ||
965 | memcpy(out_buffer, (char *) &handle, sizeof(uint64_t)); | ||
966 | memcpy(out_buffer + 8, data + current_count, (length - current_count)); | ||
967 | ret = afc_dispatch_packet(client, out_buffer, (length - current_count) + 8, &bytes_loc); | ||
968 | free(out_buffer); | ||
969 | out_buffer = NULL; | ||
970 | 794 | ||
971 | current_count += bytes_loc; | 795 | current_count += bytes_loc - (sizeof(AFCPacket) + 8); |
972 | 796 | ||
973 | if (ret != AFC_E_SUCCESS) { | 797 | if (ret != AFC_E_SUCCESS) { |
974 | afc_unlock(client); | 798 | afc_unlock(client); |
@@ -976,43 +800,32 @@ afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t | |||
976 | return AFC_E_SUCCESS; | 800 | return AFC_E_SUCCESS; |
977 | } | 801 | } |
978 | 802 | ||
979 | ret = afc_receive_data(client, &acknowledgement, &bytes_loc); | 803 | ret = afc_receive_data(client, NULL, &bytes_loc); |
980 | afc_unlock(client); | 804 | afc_unlock(client); |
981 | if (ret != AFC_E_SUCCESS) { | 805 | if (ret != AFC_E_SUCCESS) { |
982 | debug_info("uh oh?"); | 806 | debug_info("Failed to receive reply (%d)", ret); |
983 | } else { | ||
984 | free(acknowledgement); | ||
985 | } | 807 | } |
986 | *bytes_written = current_count; | 808 | *bytes_written = current_count; |
987 | return ret; | 809 | return ret; |
988 | } | 810 | } |
989 | 811 | ||
990 | /** | ||
991 | * Closes a file on the phone. | ||
992 | * | ||
993 | * @param client The client to close the file with. | ||
994 | * @param handle File handle of a previously opened file. | ||
995 | */ | ||
996 | afc_error_t afc_file_close(afc_client_t client, uint64_t handle) | 812 | afc_error_t afc_file_close(afc_client_t client, uint64_t handle) |
997 | { | 813 | { |
998 | char *buffer = malloc(sizeof(char) * 8); | ||
999 | uint32_t bytes = 0; | 814 | uint32_t bytes = 0; |
1000 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 815 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1001 | 816 | ||
1002 | if (!client || (handle == 0)) | 817 | if (!client || (handle == 0)) |
1003 | return AFC_E_INVALID_ARG; | 818 | return AFC_E_INVALID_ARG; |
1004 | 819 | ||
820 | uint32_t data_len = 8; | ||
821 | |||
1005 | afc_lock(client); | 822 | afc_lock(client); |
1006 | 823 | ||
1007 | debug_info("File handle %i", handle); | 824 | debug_info("File handle %i", handle); |
1008 | 825 | ||
1009 | /* Send command */ | 826 | /* Send command */ |
1010 | memcpy(buffer, &handle, sizeof(uint64_t)); | 827 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; |
1011 | client->afc_packet->operation = AFC_OP_FILE_CLOSE; | 828 | ret = afc_dispatch_packet(client, AFC_OP_FILE_CLOSE, data_len, NULL, 0, &bytes); |
1012 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
1013 | ret = afc_dispatch_packet(client, buffer, 8, &bytes); | ||
1014 | free(buffer); | ||
1015 | buffer = NULL; | ||
1016 | 829 | ||
1017 | if (ret != AFC_E_SUCCESS) { | 830 | if (ret != AFC_E_SUCCESS) { |
1018 | afc_unlock(client); | 831 | afc_unlock(client); |
@@ -1020,32 +833,20 @@ afc_error_t afc_file_close(afc_client_t client, uint64_t handle) | |||
1020 | } | 833 | } |
1021 | 834 | ||
1022 | /* Receive the response */ | 835 | /* Receive the response */ |
1023 | ret = afc_receive_data(client, &buffer, &bytes); | 836 | ret = afc_receive_data(client, NULL, &bytes); |
1024 | if (buffer) | ||
1025 | free(buffer); | ||
1026 | 837 | ||
1027 | afc_unlock(client); | 838 | afc_unlock(client); |
1028 | 839 | ||
1029 | return ret; | 840 | return ret; |
1030 | } | 841 | } |
1031 | 842 | ||
1032 | /** | ||
1033 | * Locks or unlocks a file on the phone. | ||
1034 | * | ||
1035 | * makes use of flock on the device, see | ||
1036 | * http://developer.apple.com/documentation/Darwin/Reference/ManPages/man2/flock.2.html | ||
1037 | * | ||
1038 | * @param client The client to lock the file with. | ||
1039 | * @param handle File handle of a previously opened file. | ||
1040 | * @param operation the lock or unlock operation to perform, this is one of | ||
1041 | * AFC_LOCK_SH (shared lock), AFC_LOCK_EX (exclusive lock), | ||
1042 | * or AFC_LOCK_UN (unlock). | ||
1043 | */ | ||
1044 | afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) | 843 | afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) |
1045 | { | 844 | { |
1046 | char *buffer = malloc(16); | ||
1047 | uint32_t bytes = 0; | 845 | uint32_t bytes = 0; |
1048 | uint64_t op = GUINT64_TO_LE(operation); | 846 | struct lockinfo { |
847 | uint64_t handle; | ||
848 | uint64_t op; | ||
849 | }; | ||
1049 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 850 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1050 | 851 | ||
1051 | if (!client || (handle == 0)) | 852 | if (!client || (handle == 0)) |
@@ -1056,47 +857,31 @@ afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t op | |||
1056 | debug_info("file handle %i", handle); | 857 | debug_info("file handle %i", handle); |
1057 | 858 | ||
1058 | /* Send command */ | 859 | /* Send command */ |
1059 | memcpy(buffer, &handle, sizeof(uint64_t)); | 860 | struct lockinfo* lockinfo = (struct lockinfo*)(AFC_PACKET_DATA_PTR); |
1060 | memcpy(buffer + 8, &op, 8); | 861 | lockinfo->handle = handle; |
1061 | 862 | lockinfo->op = htole64(operation); | |
1062 | client->afc_packet->operation = AFC_OP_FILE_LOCK; | 863 | ret = afc_dispatch_packet(client, AFC_OP_FILE_LOCK, sizeof(struct lockinfo), NULL, 0, &bytes); |
1063 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
1064 | ret = afc_dispatch_packet(client, buffer, 16, &bytes); | ||
1065 | free(buffer); | ||
1066 | buffer = NULL; | ||
1067 | |||
1068 | if (ret != AFC_E_SUCCESS) { | 864 | if (ret != AFC_E_SUCCESS) { |
1069 | afc_unlock(client); | 865 | afc_unlock(client); |
1070 | debug_info("could not send lock command"); | 866 | debug_info("could not send lock command"); |
1071 | return AFC_E_UNKNOWN_ERROR; | 867 | return AFC_E_UNKNOWN_ERROR; |
1072 | } | 868 | } |
1073 | /* Receive the response */ | 869 | /* Receive the response */ |
1074 | ret = afc_receive_data(client, &buffer, &bytes); | 870 | ret = afc_receive_data(client, NULL, &bytes); |
1075 | if (buffer) { | 871 | |
1076 | debug_buffer(buffer, bytes); | ||
1077 | free(buffer); | ||
1078 | } | ||
1079 | afc_unlock(client); | 872 | afc_unlock(client); |
1080 | 873 | ||
1081 | return ret; | 874 | return ret; |
1082 | } | 875 | } |
1083 | 876 | ||
1084 | /** | ||
1085 | * Seeks to a given position of a pre-opened file on the phone. | ||
1086 | * | ||
1087 | * @param client The client to use to seek to the position. | ||
1088 | * @param handle File handle of a previously opened. | ||
1089 | * @param offset Seek offset. | ||
1090 | * @param whence Seeking direction, one of SEEK_SET, SEEK_CUR, or SEEK_END. | ||
1091 | * | ||
1092 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1093 | */ | ||
1094 | afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) | 877 | afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) |
1095 | { | 878 | { |
1096 | char *buffer = (char *) malloc(sizeof(char) * 24); | ||
1097 | int64_t offset_loc = (int64_t)GUINT64_TO_LE(offset); | ||
1098 | uint64_t whence_loc = GUINT64_TO_LE(whence); | ||
1099 | uint32_t bytes = 0; | 879 | uint32_t bytes = 0; |
880 | struct seekinfo { | ||
881 | uint64_t handle; | ||
882 | uint64_t whence; | ||
883 | int64_t offset; | ||
884 | }; | ||
1100 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 885 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1101 | 886 | ||
1102 | if (!client || (handle == 0)) | 887 | if (!client || (handle == 0)) |
@@ -1105,57 +890,40 @@ afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, | |||
1105 | afc_lock(client); | 890 | afc_lock(client); |
1106 | 891 | ||
1107 | /* Send the command */ | 892 | /* Send the command */ |
1108 | memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ | 893 | struct seekinfo* seekinfo = (struct seekinfo*)(AFC_PACKET_DATA_PTR); |
1109 | memcpy(buffer + 8, &whence_loc, sizeof(uint64_t)); /* fromwhere */ | 894 | seekinfo->handle = handle; |
1110 | memcpy(buffer + 16, &offset_loc, sizeof(uint64_t)); /* offset */ | 895 | seekinfo->whence = htole64(whence); |
1111 | client->afc_packet->operation = AFC_OP_FILE_SEEK; | 896 | seekinfo->offset = (int64_t)htole64(offset); |
1112 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 897 | ret = afc_dispatch_packet(client, AFC_OP_FILE_SEEK, sizeof(struct seekinfo), NULL, 0, &bytes); |
1113 | ret = afc_dispatch_packet(client, buffer, 24, &bytes); | ||
1114 | free(buffer); | ||
1115 | buffer = NULL; | ||
1116 | 898 | ||
1117 | if (ret != AFC_E_SUCCESS) { | 899 | if (ret != AFC_E_SUCCESS) { |
1118 | afc_unlock(client); | 900 | afc_unlock(client); |
1119 | return AFC_E_NOT_ENOUGH_DATA; | 901 | return AFC_E_NOT_ENOUGH_DATA; |
1120 | } | 902 | } |
1121 | /* Receive response */ | 903 | /* Receive response */ |
1122 | ret = afc_receive_data(client, &buffer, &bytes); | 904 | ret = afc_receive_data(client, NULL, &bytes); |
1123 | if (buffer) | ||
1124 | free(buffer); | ||
1125 | 905 | ||
1126 | afc_unlock(client); | 906 | afc_unlock(client); |
1127 | 907 | ||
1128 | return ret; | 908 | return ret; |
1129 | } | 909 | } |
1130 | 910 | ||
1131 | /** | ||
1132 | * Returns current position in a pre-opened file on the phone. | ||
1133 | * | ||
1134 | * @param client The client to use. | ||
1135 | * @param handle File handle of a previously opened file. | ||
1136 | * @param position Position in bytes of indicator | ||
1137 | * | ||
1138 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1139 | */ | ||
1140 | afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position) | 911 | afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position) |
1141 | { | 912 | { |
1142 | char *buffer = (char *) malloc(sizeof(char) * 8); | 913 | char *buffer = NULL; |
1143 | uint32_t bytes = 0; | 914 | uint32_t bytes = 0; |
1144 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 915 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1145 | 916 | ||
1146 | if (!client || (handle == 0)) | 917 | if (!client || (handle == 0)) |
1147 | return AFC_E_INVALID_ARG; | 918 | return AFC_E_INVALID_ARG; |
1148 | 919 | ||
920 | uint32_t data_len = 8; | ||
921 | |||
1149 | afc_lock(client); | 922 | afc_lock(client); |
1150 | 923 | ||
1151 | /* Send the command */ | 924 | /* Send the command */ |
1152 | memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ | 925 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; |
1153 | client->afc_packet->operation = AFC_OP_FILE_TELL; | 926 | ret = afc_dispatch_packet(client, AFC_OP_FILE_TELL, data_len, NULL, 0, &bytes); |
1154 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | ||
1155 | ret = afc_dispatch_packet(client, buffer, 8, &bytes); | ||
1156 | free(buffer); | ||
1157 | buffer = NULL; | ||
1158 | |||
1159 | if (ret != AFC_E_SUCCESS) { | 927 | if (ret != AFC_E_SUCCESS) { |
1160 | afc_unlock(client); | 928 | afc_unlock(client); |
1161 | return AFC_E_NOT_ENOUGH_DATA; | 929 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -1166,33 +934,22 @@ afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *positi | |||
1166 | if (bytes > 0 && buffer) { | 934 | if (bytes > 0 && buffer) { |
1167 | /* Get the position */ | 935 | /* Get the position */ |
1168 | memcpy(position, buffer, sizeof(uint64_t)); | 936 | memcpy(position, buffer, sizeof(uint64_t)); |
1169 | *position = GUINT64_FROM_LE(*position); | 937 | *position = le64toh(*position); |
1170 | } | 938 | } |
1171 | if (buffer) | 939 | free(buffer); |
1172 | free(buffer); | ||
1173 | 940 | ||
1174 | afc_unlock(client); | 941 | afc_unlock(client); |
1175 | 942 | ||
1176 | return ret; | 943 | return ret; |
1177 | } | 944 | } |
1178 | 945 | ||
1179 | /** | ||
1180 | * Sets the size of a file on the phone. | ||
1181 | * | ||
1182 | * @param client The client to use to set the file size. | ||
1183 | * @param handle File handle of a previously opened file. | ||
1184 | * @param newsize The size to set the file to. | ||
1185 | * | ||
1186 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1187 | * | ||
1188 | * @note This function is more akin to ftruncate than truncate, and truncate | ||
1189 | * calls would have to open the file before calling this, sadly. | ||
1190 | */ | ||
1191 | afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize) | 946 | afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize) |
1192 | { | 947 | { |
1193 | char *buffer = (char *) malloc(sizeof(char) * 16); | ||
1194 | uint32_t bytes = 0; | 948 | uint32_t bytes = 0; |
1195 | uint64_t newsize_loc = GUINT64_TO_LE(newsize); | 949 | struct truncinfo { |
950 | uint64_t handle; | ||
951 | uint64_t newsize; | ||
952 | }; | ||
1196 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 953 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1197 | 954 | ||
1198 | if (!client || (handle == 0)) | 955 | if (!client || (handle == 0)) |
@@ -1201,160 +958,240 @@ afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t new | |||
1201 | afc_lock(client); | 958 | afc_lock(client); |
1202 | 959 | ||
1203 | /* Send command */ | 960 | /* Send command */ |
1204 | memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ | 961 | struct truncinfo* truncinfo = (struct truncinfo*)(AFC_PACKET_DATA_PTR); |
1205 | memcpy(buffer + 8, &newsize_loc, sizeof(uint64_t)); /* newsize */ | 962 | truncinfo->handle = handle; |
1206 | client->afc_packet->operation = AFC_OP_FILE_SET_SIZE; | 963 | truncinfo->newsize = htole64(newsize); |
1207 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 964 | ret = afc_dispatch_packet(client, AFC_OP_FILE_SET_SIZE, sizeof(struct truncinfo), NULL, 0, &bytes); |
1208 | ret = afc_dispatch_packet(client, buffer, 16, &bytes); | ||
1209 | free(buffer); | ||
1210 | buffer = NULL; | ||
1211 | 965 | ||
1212 | if (ret != AFC_E_SUCCESS) { | 966 | if (ret != AFC_E_SUCCESS) { |
1213 | afc_unlock(client); | 967 | afc_unlock(client); |
1214 | return AFC_E_NOT_ENOUGH_DATA; | 968 | return AFC_E_NOT_ENOUGH_DATA; |
1215 | } | 969 | } |
1216 | /* Receive response */ | 970 | /* Receive response */ |
1217 | ret = afc_receive_data(client, &buffer, &bytes); | 971 | ret = afc_receive_data(client, NULL, &bytes); |
1218 | if (buffer) | ||
1219 | free(buffer); | ||
1220 | 972 | ||
1221 | afc_unlock(client); | 973 | afc_unlock(client); |
1222 | 974 | ||
1223 | return ret; | 975 | return ret; |
1224 | } | 976 | } |
1225 | 977 | ||
1226 | /** | ||
1227 | * Sets the size of a file on the phone without prior opening it. | ||
1228 | * | ||
1229 | * @param client The client to use to set the file size. | ||
1230 | * @param path The path of the file to be truncated. | ||
1231 | * @param newsize The size to set the file to. | ||
1232 | * | ||
1233 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1234 | */ | ||
1235 | afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize) | 978 | afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize) |
1236 | { | 979 | { |
1237 | char *response = NULL; | 980 | if (!client || !path || !client->afc_packet || !client->parent) |
1238 | char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); | 981 | return AFC_E_INVALID_ARG; |
982 | |||
1239 | uint32_t bytes = 0; | 983 | uint32_t bytes = 0; |
1240 | uint64_t size_requested = GUINT64_TO_LE(newsize); | ||
1241 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 984 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1242 | 985 | ||
1243 | if (!client || !path || !client->afc_packet || !client->connection) | ||
1244 | return AFC_E_INVALID_ARG; | ||
1245 | |||
1246 | afc_lock(client); | 986 | afc_lock(client); |
1247 | 987 | ||
988 | uint32_t data_len = 8 + (uint32_t)(strlen(path)+1); | ||
989 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
990 | afc_unlock(client); | ||
991 | debug_info("Failed to realloc packet buffer"); | ||
992 | return AFC_E_NO_MEM; | ||
993 | } | ||
994 | |||
1248 | /* Send command */ | 995 | /* Send command */ |
1249 | memcpy(send, &size_requested, 8); | 996 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(newsize); |
1250 | memcpy(send + 8, path, strlen(path) + 1); | 997 | memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8); |
1251 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 998 | ret = afc_dispatch_packet(client, AFC_OP_TRUNCATE, data_len, NULL, 0, &bytes); |
1252 | client->afc_packet->operation = AFC_OP_TRUNCATE; | ||
1253 | ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes); | ||
1254 | free(send); | ||
1255 | if (ret != AFC_E_SUCCESS) { | 999 | if (ret != AFC_E_SUCCESS) { |
1256 | afc_unlock(client); | 1000 | afc_unlock(client); |
1257 | return AFC_E_NOT_ENOUGH_DATA; | 1001 | return AFC_E_NOT_ENOUGH_DATA; |
1258 | } | 1002 | } |
1259 | /* Receive response */ | 1003 | /* Receive response */ |
1260 | ret = afc_receive_data(client, &response, &bytes); | 1004 | ret = afc_receive_data(client, NULL, &bytes); |
1261 | if (response) | ||
1262 | free(response); | ||
1263 | 1005 | ||
1264 | afc_unlock(client); | 1006 | afc_unlock(client); |
1265 | 1007 | ||
1266 | return ret; | 1008 | return ret; |
1267 | } | 1009 | } |
1268 | 1010 | ||
1269 | /** | ||
1270 | * Creates a hard link or symbolic link on the device. | ||
1271 | * | ||
1272 | * @param client The client to use for making a link | ||
1273 | * @param linktype 1 = hard link, 2 = symlink | ||
1274 | * @param target The file to be linked. | ||
1275 | * @param linkname The name of link. | ||
1276 | * | ||
1277 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1278 | */ | ||
1279 | afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) | 1011 | afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) |
1280 | { | 1012 | { |
1281 | char *response = NULL; | 1013 | if (!client || !target || !linkname || !client->afc_packet || !client->parent) |
1282 | char *send = (char *) malloc(sizeof(char) * (strlen(target)+1 + strlen(linkname)+1 + 8)); | 1014 | return AFC_E_INVALID_ARG; |
1015 | |||
1283 | uint32_t bytes = 0; | 1016 | uint32_t bytes = 0; |
1284 | uint64_t type = GUINT64_TO_LE(linktype); | ||
1285 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1017 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1286 | 1018 | ||
1287 | if (!client || !target || !linkname || !client->afc_packet || !client->connection) | 1019 | size_t target_len = strlen(target); |
1288 | return AFC_E_INVALID_ARG; | 1020 | size_t link_len = strlen(linkname); |
1289 | 1021 | ||
1290 | afc_lock(client); | 1022 | afc_lock(client); |
1291 | 1023 | ||
1292 | debug_info("link type: %lld", type); | 1024 | uint32_t data_len = 8 + target_len + 1 + link_len + 1; |
1293 | debug_info("target: %s, length:%d", target, strlen(target)); | 1025 | if (_afc_check_packet_buffer(client, data_len) < 0) { |
1294 | debug_info("linkname: %s, length:%d", linkname, strlen(linkname)); | 1026 | afc_unlock(client); |
1027 | debug_info("Failed to realloc packet buffer"); | ||
1028 | return AFC_E_NO_MEM; | ||
1029 | } | ||
1030 | |||
1031 | debug_info("link type: %lld", htole64(linktype)); | ||
1032 | debug_info("target: %s, length:%d", target, target_len); | ||
1033 | debug_info("linkname: %s, length:%d", linkname, link_len); | ||
1295 | 1034 | ||
1296 | /* Send command */ | 1035 | /* Send command */ |
1297 | memcpy(send, &type, 8); | 1036 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(linktype); |
1298 | memcpy(send + 8, target, strlen(target) + 1); | 1037 | memcpy(AFC_PACKET_DATA_PTR + 8, target, target_len + 1); |
1299 | memcpy(send + 8 + strlen(target) + 1, linkname, strlen(linkname) + 1); | 1038 | memcpy(AFC_PACKET_DATA_PTR + 8 + target_len + 1, linkname, link_len + 1); |
1300 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 1039 | ret = afc_dispatch_packet(client, AFC_OP_MAKE_LINK, data_len, NULL, 0, &bytes); |
1301 | client->afc_packet->operation = AFC_OP_MAKE_LINK; | ||
1302 | ret = afc_dispatch_packet(client, send, 8 + strlen(linkname) + 1 + strlen(target) + 1, &bytes); | ||
1303 | free(send); | ||
1304 | if (ret != AFC_E_SUCCESS) { | 1040 | if (ret != AFC_E_SUCCESS) { |
1305 | afc_unlock(client); | 1041 | afc_unlock(client); |
1306 | return AFC_E_NOT_ENOUGH_DATA; | 1042 | return AFC_E_NOT_ENOUGH_DATA; |
1307 | } | 1043 | } |
1308 | /* Receive response */ | 1044 | /* Receive response */ |
1309 | ret = afc_receive_data(client, &response, &bytes); | 1045 | ret = afc_receive_data(client, NULL, &bytes); |
1310 | if (response) | ||
1311 | free(response); | ||
1312 | 1046 | ||
1313 | afc_unlock(client); | 1047 | afc_unlock(client); |
1314 | 1048 | ||
1315 | return ret; | 1049 | return ret; |
1316 | } | 1050 | } |
1317 | 1051 | ||
1318 | /** | ||
1319 | * Sets the modification time of a file on the phone. | ||
1320 | * | ||
1321 | * @param client The client to use to set the file size. | ||
1322 | * @param path Path of the file for which the modification time should be set. | ||
1323 | * @param mtime The modification time to set in nanoseconds since epoch. | ||
1324 | * | ||
1325 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1326 | */ | ||
1327 | afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime) | 1052 | afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime) |
1328 | { | 1053 | { |
1329 | char *response = NULL; | 1054 | if (!client || !path || !client->afc_packet || !client->parent) |
1330 | char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); | 1055 | return AFC_E_INVALID_ARG; |
1056 | |||
1331 | uint32_t bytes = 0; | 1057 | uint32_t bytes = 0; |
1332 | uint64_t mtime_loc = GUINT64_TO_LE(mtime); | ||
1333 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1058 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1334 | 1059 | ||
1335 | if (!client || !path || !client->afc_packet || !client->connection) | 1060 | afc_lock(client); |
1061 | |||
1062 | uint32_t data_len = 8 + strlen(path) + 1; | ||
1063 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
1064 | afc_unlock(client); | ||
1065 | debug_info("Failed to realloc packet buffer"); | ||
1066 | return AFC_E_NO_MEM; | ||
1067 | } | ||
1068 | |||
1069 | /* Send command */ | ||
1070 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(mtime); | ||
1071 | memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8); | ||
1072 | ret = afc_dispatch_packet(client, AFC_OP_SET_FILE_MOD_TIME, data_len, NULL, 0, &bytes); | ||
1073 | if (ret != AFC_E_SUCCESS) { | ||
1074 | afc_unlock(client); | ||
1075 | return AFC_E_NOT_ENOUGH_DATA; | ||
1076 | } | ||
1077 | /* Receive response */ | ||
1078 | ret = afc_receive_data(client, NULL, &bytes); | ||
1079 | |||
1080 | afc_unlock(client); | ||
1081 | |||
1082 | return ret; | ||
1083 | } | ||
1084 | |||
1085 | afc_error_t afc_remove_path_and_contents(afc_client_t client, const char *path) | ||
1086 | { | ||
1087 | uint32_t bytes = 0; | ||
1088 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | ||
1089 | |||
1090 | if (!client || !path || !client->afc_packet || !client->parent) | ||
1336 | return AFC_E_INVALID_ARG; | 1091 | return AFC_E_INVALID_ARG; |
1337 | 1092 | ||
1338 | afc_lock(client); | 1093 | afc_lock(client); |
1339 | 1094 | ||
1095 | uint32_t data_len = strlen(path) + 1; | ||
1096 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
1097 | afc_unlock(client); | ||
1098 | debug_info("Failed to realloc packet buffer"); | ||
1099 | return AFC_E_NO_MEM; | ||
1100 | } | ||
1101 | |||
1340 | /* Send command */ | 1102 | /* Send command */ |
1341 | memcpy(send, &mtime_loc, 8); | 1103 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
1342 | memcpy(send + 8, path, strlen(path) + 1); | 1104 | ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH_AND_CONTENTS, data_len, NULL, 0, &bytes); |
1343 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
1344 | client->afc_packet->operation = AFC_OP_SET_FILE_TIME; | ||
1345 | ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes); | ||
1346 | free(send); | ||
1347 | if (ret != AFC_E_SUCCESS) { | 1105 | if (ret != AFC_E_SUCCESS) { |
1348 | afc_unlock(client); | 1106 | afc_unlock(client); |
1349 | return AFC_E_NOT_ENOUGH_DATA; | 1107 | return AFC_E_NOT_ENOUGH_DATA; |
1350 | } | 1108 | } |
1351 | /* Receive response */ | 1109 | /* Receive response */ |
1352 | ret = afc_receive_data(client, &response, &bytes); | 1110 | ret = afc_receive_data(client, NULL, &bytes); |
1353 | if (response) | ||
1354 | free(response); | ||
1355 | 1111 | ||
1356 | afc_unlock(client); | 1112 | afc_unlock(client); |
1357 | 1113 | ||
1358 | return ret; | 1114 | return ret; |
1359 | } | 1115 | } |
1360 | 1116 | ||
1117 | afc_error_t afc_dictionary_free(char **dictionary) | ||
1118 | { | ||
1119 | int i = 0; | ||
1120 | |||
1121 | if (!dictionary) | ||
1122 | return AFC_E_INVALID_ARG; | ||
1123 | |||
1124 | for (i = 0; dictionary[i]; i++) { | ||
1125 | free(dictionary[i]); | ||
1126 | } | ||
1127 | free(dictionary); | ||
1128 | |||
1129 | return AFC_E_SUCCESS; | ||
1130 | } | ||
1131 | |||
1132 | const char* afc_strerror(afc_error_t err) | ||
1133 | { | ||
1134 | switch (err) { | ||
1135 | case AFC_E_SUCCESS: | ||
1136 | return "Success"; | ||
1137 | case AFC_E_UNKNOWN_ERROR: | ||
1138 | return "Unknown Error"; | ||
1139 | case AFC_E_OP_HEADER_INVALID: | ||
1140 | return "Operation header invalid"; | ||
1141 | case AFC_E_NO_RESOURCES: | ||
1142 | return "No resources"; | ||
1143 | case AFC_E_READ_ERROR: | ||
1144 | return "Read error"; | ||
1145 | case AFC_E_WRITE_ERROR: | ||
1146 | return "Write error"; | ||
1147 | case AFC_E_UNKNOWN_PACKET_TYPE: | ||
1148 | return "Unknown packet type"; | ||
1149 | case AFC_E_INVALID_ARG: | ||
1150 | return "Invalid argument"; | ||
1151 | case AFC_E_OBJECT_NOT_FOUND: | ||
1152 | return "Not found"; | ||
1153 | case AFC_E_OBJECT_IS_DIR: | ||
1154 | return "Object is a directory"; | ||
1155 | case AFC_E_PERM_DENIED: | ||
1156 | return "Permission denied"; | ||
1157 | case AFC_E_SERVICE_NOT_CONNECTED: | ||
1158 | return "Service not connected"; | ||
1159 | case AFC_E_OP_TIMEOUT: | ||
1160 | return "Timeout"; | ||
1161 | case AFC_E_TOO_MUCH_DATA: | ||
1162 | return "Too much data"; | ||
1163 | case AFC_E_END_OF_DATA: | ||
1164 | return "End of data"; | ||
1165 | case AFC_E_OP_NOT_SUPPORTED: | ||
1166 | return "Operation not supported"; | ||
1167 | case AFC_E_OBJECT_EXISTS: | ||
1168 | return "Object exists"; | ||
1169 | case AFC_E_OBJECT_BUSY: | ||
1170 | return "Object busy"; | ||
1171 | case AFC_E_NO_SPACE_LEFT: | ||
1172 | return "No space left on device"; | ||
1173 | case AFC_E_OP_WOULD_BLOCK: | ||
1174 | return "Operation would block"; | ||
1175 | case AFC_E_IO_ERROR: | ||
1176 | return "I/O error"; | ||
1177 | case AFC_E_OP_INTERRUPTED: | ||
1178 | return "Operation interrupted"; | ||
1179 | case AFC_E_OP_IN_PROGRESS: | ||
1180 | return "Operation on progress"; | ||
1181 | case AFC_E_INTERNAL_ERROR: | ||
1182 | return "Internal error"; | ||
1183 | case AFC_E_MUX_ERROR: | ||
1184 | return "MUX error"; | ||
1185 | case AFC_E_NO_MEM: | ||
1186 | return "Out of memory"; | ||
1187 | case AFC_E_NOT_ENOUGH_DATA: | ||
1188 | return "Not enough data"; | ||
1189 | case AFC_E_DIR_NOT_EMPTY: | ||
1190 | return "Directory not empty"; | ||
1191 | case AFC_E_FORCE_SIGNED_TYPE: | ||
1192 | return "Force signed type"; | ||
1193 | default: | ||
1194 | break; | ||
1195 | } | ||
1196 | return "Unknown Error"; | ||
1197 | } | ||
@@ -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 | |||
47 | typedef struct { | ||
48 | uint64_t filehandle, size; | ||
49 | } AFCFilePacket; | ||
50 | 52 | ||
51 | struct afc_client_private { | 53 | struct 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 */ |
61 | enum { | 62 | enum { |
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 | ||
108 | afc_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 | |||
32 | struct 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 | */ | ||
51 | static 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 | |||
72 | bt_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 | |||
98 | bt_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 | |||
105 | bt_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 | |||
116 | bt_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 | |||
136 | void *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 | |||
189 | bt_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 | |||
217 | bt_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 | |||
30 | struct bt_packet_logger_client_private { | ||
31 | service_client_t parent; | ||
32 | THREAD_T worker; | ||
33 | }; | ||
34 | |||
35 | void *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 | */ | ||
42 | static 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 | |||
65 | companion_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 | |||
93 | companion_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 | |||
100 | companion_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 | |||
119 | companion_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 | |||
132 | companion_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 | |||
146 | companion_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 | |||
186 | struct companion_proxy_cb_data { | ||
187 | companion_proxy_client_t client; | ||
188 | companion_proxy_device_event_cb_t cbfunc; | ||
189 | void* user_data; | ||
190 | }; | ||
191 | |||
192 | static 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 | |||
229 | companion_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 | |||
255 | companion_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 | |||
269 | companion_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 | |||
313 | companion_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 | |||
356 | companion_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 | |||
30 | struct 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 | |||
36 | int 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 | */ | ||
44 | void idevice_set_debug_level(int level) | ||
45 | { | ||
46 | debug_level = level; | ||
47 | } | ||
48 | |||
49 | #ifndef STRIP_DEBUG_CODE | ||
50 | static 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 | |||
79 | inline 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 | |||
99 | inline 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 | |||
134 | inline 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 | |||
146 | inline 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 | |||
40 | G_GNUC_INTERNAL inline void debug_info_real(const char *func, | ||
41 | const char *file, | ||
42 | int line, | ||
43 | const char *format, ...); | ||
44 | |||
45 | G_GNUC_INTERNAL inline void debug_buffer(const char *data, const int length); | ||
46 | G_GNUC_INTERNAL inline void debug_buffer_to_file(const char *file, const char *data, const int length); | ||
47 | G_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 | */ | ||
48 | static 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 | |||
67 | debugserver_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 | |||
101 | debugserver_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 | |||
112 | debugserver_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 | |||
124 | debugserver_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 | |||
145 | debugserver_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 | |||
165 | debugserver_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 | |||
175 | debugserver_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 | |||
200 | debugserver_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 | |||
224 | static 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 | |||
236 | static 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 | |||
247 | static 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 | |||
259 | static 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 | |||
278 | void 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 | |||
294 | void 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 | |||
307 | static 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 | |||
341 | static 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 | |||
347 | static 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 | |||
353 | debugserver_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 | |||
365 | debugserver_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 | |||
378 | static 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 | |||
395 | debugserver_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 | |||
495 | cleanup: | ||
496 | if (response) { | ||
497 | debug_info("response: %s", *response); | ||
498 | } | ||
499 | |||
500 | if (buffer) | ||
501 | free(buffer); | ||
502 | |||
503 | return res; | ||
504 | } | ||
505 | |||
506 | debugserver_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 | |||
552 | cleanup: | ||
553 | if (command_arguments) | ||
554 | free(command_arguments); | ||
555 | |||
556 | if (send_buffer) | ||
557 | free(send_buffer); | ||
558 | |||
559 | return res; | ||
560 | } | ||
561 | |||
562 | debugserver_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 | |||
581 | debugserver_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 | |||
31 | struct debugserver_client_private { | ||
32 | service_client_t parent; | ||
33 | int noack_mode; | ||
34 | int (*cancel_receive)(); | ||
35 | int receive_loop_timeout; | ||
36 | }; | ||
37 | |||
38 | struct 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 | |||
31 | static 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 | */ |
85 | device_link_service_error_t device_link_service_client_new(idevice_t device, uint16_t port, device_link_service_client_t *client) | 110 | device_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 | */ |
244 | device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client) | 270 | device_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 | 29 | typedef 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 |
36 | typedef int16_t device_link_service_error_t; | 38 | } device_link_service_error_t; |
37 | 39 | ||
38 | struct device_link_service_client_private { | 40 | struct 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 | ||
42 | typedef struct device_link_service_client_private *device_link_service_client_t; | 44 | typedef struct device_link_service_client_private *device_link_service_client_t; |
43 | 45 | ||
44 | device_link_service_error_t device_link_service_client_new(idevice_t device, uint16_t port, device_link_service_client_t *client); | 46 | device_link_service_error_t device_link_service_client_new(idevice_t device, lockdownd_service_descriptor_t service, device_link_service_client_t *client); |
45 | device_link_service_error_t device_link_service_client_free(device_link_service_client_t client); | 47 | device_link_service_error_t device_link_service_client_free(device_link_service_client_t client); |
46 | device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor); | 48 | device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor); |
47 | device_link_service_error_t device_link_service_send_ping(device_link_service_client_t client, const char *message); | 49 | device_link_service_error_t device_link_service_send_ping(device_link_service_client_t client, const char *message); |
48 | device_link_service_error_t device_link_service_receive_message(device_link_service_client_t client, plist_t *msg_plist, char **dlmessage); | 50 | device_link_service_error_t device_link_service_receive_message(device_link_service_client_t client, plist_t *msg_plist, char **dlmessage); |
49 | device_link_service_error_t device_link_service_send_process_message(device_link_service_client_t client, plist_t message); | 51 | device_link_service_error_t device_link_service_send_process_message(device_link_service_client_t client, plist_t message); |
50 | device_link_service_error_t device_link_service_receive_process_message(device_link_service_client_t client, plist_t *message); | 52 | device_link_service_error_t device_link_service_receive_process_message(device_link_service_client_t client, plist_t *message); |
51 | device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client); | 53 | device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client, const char *message); |
52 | device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist); | 54 | device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist); |
53 | device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist); | 55 | device_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 | */ | ||
45 | static 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 | |||
76 | diagnostics_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 | |||
96 | diagnostics_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 | |||
103 | diagnostics_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 | */ | ||
124 | static 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 | */ | ||
155 | static 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 | |||
170 | diagnostics_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 | |||
204 | diagnostics_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 | |||
236 | static 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 | |||
280 | diagnostics_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 | |||
285 | diagnostics_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 | |||
290 | diagnostics_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 | |||
334 | diagnostics_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 | |||
379 | diagnostics_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 | |||
427 | diagnostics_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 | |||
29 | struct 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 | /** | 31 | file_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 | */ | ||
39 | file_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 | /** | 51 | file_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 | */ | ||
70 | file_relay_error_t file_relay_client_free(file_relay_client_t client) | 58 | file_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 | /** | 70 | file_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 | */ | ||
111 | file_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 | ||
193 | leave: | 155 | leave: |
194 | if (dict) { | 156 | if (dict) { |
@@ -196,3 +158,8 @@ leave: | |||
196 | } | 158 | } |
197 | return err; | 159 | return err; |
198 | } | 160 | } |
161 | |||
162 | file_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 | |||
36 | struct file_relay_client_private { | 29 | struct 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 | */ | ||
42 | static 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 | |||
65 | heartbeat_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 | |||
92 | heartbeat_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 | |||
99 | heartbeat_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 | |||
110 | heartbeat_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 | |||
125 | heartbeat_error_t heartbeat_receive(heartbeat_client_t client, plist_t * plist) | ||
126 | { | ||
127 | return heartbeat_receive_with_timeout(client, plist, 1000); | ||
128 | } | ||
129 | |||
130 | heartbeat_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 | |||
29 | struct heartbeat_client_private { | ||
30 | property_list_service_client_t parent; | ||
31 | }; | ||
32 | |||
33 | #endif | ||
diff --git a/src/house_arrest.c b/src/house_arrest.c index 5baa76e..caad731 100644 --- a/src/house_arrest.c +++ b/src/house_arrest.c | |||
@@ -8,17 +8,20 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
24 | #include <unistd.h> | 27 | #include <unistd.h> |
@@ -27,7 +30,7 @@ | |||
27 | #include "house_arrest.h" | 30 | #include "house_arrest.h" |
28 | #include "property_list_service.h" | 31 | #include "property_list_service.h" |
29 | #include "afc.h" | 32 | #include "afc.h" |
30 | #include "debug.h" | 33 | #include "common/debug.h" |
31 | 34 | ||
32 | /** | 35 | /** |
33 | * Convert a property_list_service_error_t value to a house_arrest_error_t | 36 | * Convert a property_list_service_error_t value to a house_arrest_error_t |
@@ -40,39 +43,25 @@ | |||
40 | */ | 43 | */ |
41 | static house_arrest_error_t house_arrest_error(property_list_service_error_t err) | 44 | static house_arrest_error_t house_arrest_error(property_list_service_error_t err) |
42 | { | 45 | { |
43 | switch (err) { | 46 | switch (err) { |
44 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | 47 | case PROPERTY_LIST_SERVICE_E_SUCCESS: |
45 | return HOUSE_ARREST_E_SUCCESS; | 48 | return HOUSE_ARREST_E_SUCCESS; |
46 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | 49 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: |
47 | return HOUSE_ARREST_E_INVALID_ARG; | 50 | return HOUSE_ARREST_E_INVALID_ARG; |
48 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | 51 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: |
49 | return HOUSE_ARREST_E_PLIST_ERROR; | 52 | return HOUSE_ARREST_E_PLIST_ERROR; |
50 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | 53 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: |
51 | return HOUSE_ARREST_E_CONN_FAILED; | 54 | return HOUSE_ARREST_E_CONN_FAILED; |
52 | default: | 55 | default: |
53 | break; | 56 | break; |
54 | } | 57 | } |
55 | return HOUSE_ARREST_E_UNKNOWN_ERROR; | 58 | return HOUSE_ARREST_E_UNKNOWN_ERROR; |
56 | } | 59 | } |
57 | 60 | ||
58 | /** | 61 | house_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 | */ | ||
69 | house_arrest_error_t house_arrest_client_new(idevice_t device, uint16_t port, house_arrest_client_t *client) | ||
70 | { | 62 | { |
71 | if (!device) | ||
72 | return HOUSE_ARREST_E_INVALID_ARG; | ||
73 | |||
74 | property_list_service_client_t plistclient = NULL; | 63 | property_list_service_client_t plistclient = NULL; |
75 | house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, port, &plistclient)); | 64 | house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, service, &plistclient)); |
76 | if (err != HOUSE_ARREST_E_SUCCESS) { | 65 | if (err != HOUSE_ARREST_E_SUCCESS) { |
77 | return err; | 66 | return err; |
78 | } | 67 | } |
@@ -85,27 +74,20 @@ house_arrest_error_t house_arrest_client_new(idevice_t device, uint16_t port, ho | |||
85 | return HOUSE_ARREST_E_SUCCESS; | 74 | return HOUSE_ARREST_E_SUCCESS; |
86 | } | 75 | } |
87 | 76 | ||
88 | /** | 77 | house_arrest_error_t house_arrest_client_start_service(idevice_t device, house_arrest_client_t * client, const char* label) |
89 | * Disconnects an house_arrest client from the device and frees up the | 78 | { |
90 | * house_arrest client data. | 79 | house_arrest_error_t err = HOUSE_ARREST_E_UNKNOWN_ERROR; |
91 | * | 80 | service_client_factory_start_service(device, HOUSE_ARREST_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(house_arrest_client_new), &err); |
92 | * @note After using afc_client_new_from_house_arrest_client(), make sure | 81 | return err; |
93 | * you call afc_client_free() before calling this function to ensure | 82 | } |
94 | * a proper cleanup. Do not call this function if you still need to | 83 | |
95 | * perform AFC operations since it will close the connection. | ||
96 | * | ||
97 | * @param client The house_arrest client to disconnect and free. | ||
98 | * | ||
99 | * @return HOUSE_ARREST_E_SUCCESS on success, HOUSE_ARREST_E_INVALID_ARG when | ||
100 | * client is NULL, or an HOUSE_ARREST_E_* error code otherwise. | ||
101 | */ | ||
102 | house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) | 84 | house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) |
103 | { | 85 | { |
104 | if (!client) | 86 | if (!client) |
105 | return HOUSE_ARREST_E_INVALID_ARG; | 87 | return HOUSE_ARREST_E_INVALID_ARG; |
106 | 88 | ||
107 | house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS; | 89 | house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS; |
108 | if (client->parent && client->parent->connection) { | 90 | if (client->parent && client->parent->parent->connection) { |
109 | house_arrest_error(property_list_service_client_free(client->parent)); | 91 | house_arrest_error(property_list_service_client_free(client->parent)); |
110 | } | 92 | } |
111 | client->parent = NULL; | 93 | client->parent = NULL; |
@@ -114,70 +96,34 @@ house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) | |||
114 | return err; | 96 | return err; |
115 | } | 97 | } |
116 | 98 | ||
117 | /** | ||
118 | * Sends a generic request to the connected house_arrest service. | ||
119 | * | ||
120 | * @param client The house_arrest client to use. | ||
121 | * @param dict The request to send as a plist of type PLIST_DICT. | ||
122 | * | ||
123 | * @note If this function returns HOUSE_ARREST_E_SUCCESS it does not mean | ||
124 | * that the request was successful. To check for success or failure you | ||
125 | * need to call house_arrest_get_result(). | ||
126 | * @see house_arrest_get_result | ||
127 | * | ||
128 | * @return HOUSE_ARREST_E_SUCCESS if the request was successfully sent, | ||
129 | * HOUSE_ARREST_E_INVALID_ARG if client or dict is invalid, | ||
130 | * HOUSE_ARREST_E_PLIST_ERROR if dict is not a plist of type PLIST_DICT, | ||
131 | * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode, | ||
132 | * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured. | ||
133 | */ | ||
134 | house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict) | 99 | house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict) |
135 | { | 100 | { |
136 | if (!client || !client->parent || !dict) | 101 | if (!client || !client->parent || !dict) |
137 | return HOUSE_ARREST_E_INVALID_ARG; | 102 | return HOUSE_ARREST_E_INVALID_ARG; |
138 | if (plist_get_node_type(dict) != PLIST_DICT) | 103 | if (plist_get_node_type(dict) != PLIST_DICT) |
139 | return HOUSE_ARREST_E_PLIST_ERROR; | 104 | return HOUSE_ARREST_E_PLIST_ERROR; |
140 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) | 105 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) |
141 | return HOUSE_ARREST_E_INVALID_MODE; | 106 | return HOUSE_ARREST_E_INVALID_MODE; |
142 | 107 | ||
143 | house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict)); | 108 | house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict)); |
144 | if (res != HOUSE_ARREST_E_SUCCESS) { | 109 | if (res != HOUSE_ARREST_E_SUCCESS) { |
145 | debug_info("could not send plist, error %d", res); | 110 | debug_info("could not send plist, error %d", res); |
146 | } | 111 | } |
147 | return res; | 112 | return res; |
148 | } | 113 | } |
149 | 114 | ||
150 | /** | ||
151 | * Send a command to the connected house_arrest service. | ||
152 | * Calls house_arrest_send_request() internally. | ||
153 | * | ||
154 | * @param client The house_arrest client to use. | ||
155 | * @param command The command to send. Currently, only VendContainer and | ||
156 | * VendDocuments are known. | ||
157 | * @param appid The application identifier to pass along with the . | ||
158 | * | ||
159 | * @note If this function returns HOUSE_ARREST_E_SUCCESS it does not mean | ||
160 | * that the command was successful. To check for success or failure you | ||
161 | * need to call house_arrest_get_result(). | ||
162 | * @see house_arrest_get_result | ||
163 | * | ||
164 | * @return HOUSE_ARREST_E_SUCCESS if the command was successfully sent, | ||
165 | * HOUSE_ARREST_E_INVALID_ARG if client, command, or appid is invalid, | ||
166 | * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode, | ||
167 | * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured. | ||
168 | */ | ||
169 | house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid) | 115 | house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid) |
170 | { | 116 | { |
171 | if (!client || !client->parent || !command || !appid) | 117 | if (!client || !client->parent || !command || !appid) |
172 | return HOUSE_ARREST_E_INVALID_ARG; | 118 | return HOUSE_ARREST_E_INVALID_ARG; |
173 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) | 119 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) |
174 | return HOUSE_ARREST_E_INVALID_MODE; | 120 | return HOUSE_ARREST_E_INVALID_MODE; |
175 | 121 | ||
176 | house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR; | 122 | house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR; |
177 | 123 | ||
178 | plist_t dict = plist_new_dict(); | 124 | plist_t dict = plist_new_dict(); |
179 | plist_dict_insert_item(dict, "Command", plist_new_string(command)); | 125 | plist_dict_set_item(dict, "Command", plist_new_string(command)); |
180 | plist_dict_insert_item(dict, "Identifier", plist_new_string(appid)); | 126 | plist_dict_set_item(dict, "Identifier", plist_new_string(appid)); |
181 | 127 | ||
182 | res = house_arrest_send_request(client, dict); | 128 | res = house_arrest_send_request(client, dict); |
183 | 129 | ||
@@ -186,63 +132,30 @@ house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, con | |||
186 | return res; | 132 | return res; |
187 | } | 133 | } |
188 | 134 | ||
189 | /** | ||
190 | * Retrieves the result of a previously sent house_arrest_request_* request. | ||
191 | * | ||
192 | * @param client The house_arrest client to use | ||
193 | * @param dict Pointer that will be set to a plist containing the result to | ||
194 | * the last performed operation. It holds a key 'Status' with the value | ||
195 | * 'Complete' on success or a key 'Error' with an error description as | ||
196 | * value. The caller is responsible for freeing the returned plist. | ||
197 | * | ||
198 | * @return HOUSE_ARREST_E_SUCCESS if a result plist was retrieved, | ||
199 | * HOUSE_ARREST_E_INVALID_ARG if client is invalid, | ||
200 | * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode, | ||
201 | * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured. | ||
202 | */ | ||
203 | house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict) | 135 | house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict) |
204 | { | 136 | { |
205 | if (!client || !client->parent) | 137 | if (!client || !client->parent) |
206 | return HOUSE_ARREST_E_INVALID_ARG; | 138 | return HOUSE_ARREST_E_INVALID_ARG; |
207 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) | 139 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) |
208 | return HOUSE_ARREST_E_INVALID_MODE; | 140 | return HOUSE_ARREST_E_INVALID_MODE; |
209 | 141 | ||
210 | house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict)); | 142 | house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict)); |
211 | if (res != HOUSE_ARREST_E_SUCCESS) { | 143 | if (res != HOUSE_ARREST_E_SUCCESS) { |
212 | debug_info("could not get result, error %d", res); | 144 | debug_info("could not get result, error %d", res); |
213 | if (*dict) { | 145 | if (*dict) { |
214 | plist_free(*dict); | 146 | plist_free(*dict); |
215 | *dict = NULL; | 147 | *dict = NULL; |
216 | } | 148 | } |
217 | } | 149 | } |
218 | return res; | 150 | return res; |
219 | } | 151 | } |
220 | 152 | ||
221 | /** | ||
222 | * Creates an AFC client using the given house_arrest client's connection | ||
223 | * allowing file access to a specific application directory requested by | ||
224 | * functions like house_arrest_request_vendor_documents(). | ||
225 | * | ||
226 | * @param client The house_arrest client to use. | ||
227 | * @param afc_client Pointer that will be set to a newly allocated afc_client_t | ||
228 | * upon successful return. | ||
229 | * | ||
230 | * @note After calling this function the house_arrest client will go in an | ||
231 | * AFC mode that will only allow calling house_arrest_client_free(). | ||
232 | * Only call house_arrest_client_free() if all AFC operations have | ||
233 | * completed since it will close the connection. | ||
234 | * | ||
235 | * @return AFC_E_SUCCESS if the afc client was successfully created, | ||
236 | * AFC_E_INVALID_ARG if client is invalid or was already used to create | ||
237 | * an afc client, or an AFC_E_* error code returned by | ||
238 | * afc_client_new_from_connection(). | ||
239 | */ | ||
240 | afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client) | 153 | afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client) |
241 | { | 154 | { |
242 | if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { | 155 | if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { |
243 | return AFC_E_INVALID_ARG; | 156 | return AFC_E_INVALID_ARG; |
244 | } | 157 | } |
245 | afc_error_t err = afc_client_new_from_connection(client->parent->connection, afc_client); | 158 | afc_error_t err = afc_client_new_with_service_client(client->parent->parent, afc_client); |
246 | if (err == AFC_E_SUCCESS) { | 159 | if (err == AFC_E_SUCCESS) { |
247 | client->mode = HOUSE_ARREST_CLIENT_MODE_AFC; | 160 | client->mode = HOUSE_ARREST_CLIENT_MODE_AFC; |
248 | } | 161 | } |
diff --git a/src/house_arrest.h b/src/house_arrest.h index 6d13a88..5612a29 100644 --- a/src/house_arrest.h +++ b/src/house_arrest.h | |||
@@ -8,21 +8,21 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef IHOUSE_ARREST_H | ||
22 | #define IHOUSE_ARREST_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __HOUSE_ARREST_H |
23 | #define __HOUSE_ARREST_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/house_arrest.h" | 26 | #include "libimobiledevice/house_arrest.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | 28 | ||
diff --git a/src/idevice.c b/src/idevice.c index 5a9d49b..b9bbb1f 100644 --- a/src/idevice.c +++ b/src/idevice.c | |||
@@ -1,98 +1,387 @@ | |||
1 | /* | 1 | /* |
2 | * idevice.c | 2 | * idevice.c |
3 | * Device discovery and communication interface. | 3 | * Device discovery and communication interface. |
4 | * | 4 | * |
5 | * Copyright (c) 2009-2021 Nikias Bassen. All Rights Reserved. | ||
6 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 7 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
6 | * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. | ||
7 | * | 8 | * |
8 | * This library is free software; you can redistribute it and/or | 9 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public | 10 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either | 11 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. | 12 | * version 2.1 of the License, or (at your option) any later version. |
12 | * | 13 | * |
13 | * This library is distributed in the hope that it will be useful, | 14 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. | 17 | * Lesser General Public License for more details. |
17 | * | 18 | * |
18 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, write to the Free Software | 20 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ | 22 | */ |
22 | 23 | ||
24 | #ifdef HAVE_CONFIG_H | ||
25 | #include <config.h> | ||
26 | #endif | ||
27 | |||
23 | #include <stdlib.h> | 28 | #include <stdlib.h> |
24 | #include <string.h> | 29 | #include <string.h> |
25 | #include <errno.h> | 30 | #include <errno.h> |
31 | #include <time.h> | ||
32 | |||
33 | #ifdef WIN32 | ||
34 | #include <winsock2.h> | ||
35 | #include <ws2tcpip.h> | ||
36 | #include <windows.h> | ||
37 | #else | ||
38 | #include <sys/socket.h> | ||
39 | #include <netinet/in.h> | ||
40 | #endif | ||
26 | 41 | ||
27 | #include <usbmuxd.h> | 42 | #include <usbmuxd.h> |
43 | |||
44 | #if defined(HAVE_OPENSSL) | ||
45 | #include <openssl/err.h> | ||
46 | #include <openssl/rsa.h> | ||
47 | #include <openssl/ssl.h> | ||
48 | #elif defined(HAVE_GNUTLS) | ||
28 | #include <gnutls/gnutls.h> | 49 | #include <gnutls/gnutls.h> |
50 | #elif defined(HAVE_MBEDTLS) | ||
51 | #include <mbedtls/rsa.h> | ||
52 | #include <mbedtls/ssl.h> | ||
53 | #include <mbedtls/entropy.h> | ||
54 | #include <mbedtls/ctr_drbg.h> | ||
55 | #include <mbedtls/debug.h> | ||
56 | #else | ||
57 | #error No supported TLS/SSL library enabled | ||
58 | #endif | ||
59 | |||
60 | #include <libimobiledevice-glue/socket.h> | ||
61 | #include <libimobiledevice-glue/thread.h> | ||
62 | |||
29 | #include "idevice.h" | 63 | #include "idevice.h" |
30 | #include "userpref.h" | 64 | #include "lockdown.h" |
31 | #include "debug.h" | 65 | #include "common/userpref.h" |
66 | #include "common/debug.h" | ||
67 | |||
68 | #ifndef ECONNREFUSED | ||
69 | #define ECONNREFUSED 107 | ||
70 | #endif | ||
71 | #ifndef ETIMEDOUT | ||
72 | #define ETIMEDOUT 138 | ||
73 | #endif | ||
74 | |||
75 | |||
76 | #ifdef HAVE_OPENSSL | ||
77 | |||
78 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || \ | ||
79 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20020000L)) | ||
80 | #define TLS_method TLSv1_method | ||
81 | #endif | ||
82 | |||
83 | #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) | ||
84 | static void SSL_COMP_free_compression_methods(void) | ||
85 | { | ||
86 | sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); | ||
87 | } | ||
88 | #endif | ||
89 | |||
90 | static 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) | ||
104 | static mutex_t *mutex_buf = NULL; | ||
105 | static 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 | ||
114 | static unsigned long id_function(void) | ||
115 | { | ||
116 | return ((unsigned long)THREAD_ID); | ||
117 | } | ||
118 | #else | ||
119 | static 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 | static void internal_idevice_init(void) | ||
128 | { | ||
129 | #if defined(HAVE_OPENSSL) | ||
130 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
131 | int i; | ||
132 | SSL_library_init(); | ||
133 | |||
134 | mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); | ||
135 | if (!mutex_buf) | ||
136 | return; | ||
137 | for (i = 0; i < CRYPTO_num_locks(); i++) | ||
138 | mutex_init(&mutex_buf[i]); | ||
139 | |||
140 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
141 | CRYPTO_set_id_callback(id_function); | ||
142 | #else | ||
143 | CRYPTO_THREADID_set_callback(id_function); | ||
144 | #endif | ||
145 | CRYPTO_set_locking_callback(locking_function); | ||
146 | #endif | ||
147 | #elif defined(HAVE_GNUTLS) | ||
148 | gnutls_global_init(); | ||
149 | #elif defined(HAVE_MBEDTLS) | ||
150 | // NO-OP | ||
151 | #endif | ||
152 | } | ||
153 | |||
154 | static 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 | |||
184 | static thread_once_t init_once = THREAD_ONCE_INIT; | ||
185 | static thread_once_t deinit_once = THREAD_ONCE_INIT; | ||
186 | |||
187 | #ifndef HAVE_ATTRIBUTE_CONSTRUCTOR | ||
188 | #if defined(__llvm__) || defined(__GNUC__) | ||
189 | #define HAVE_ATTRIBUTE_CONSTRUCTOR | ||
190 | #endif | ||
191 | #endif | ||
192 | |||
193 | #ifdef HAVE_ATTRIBUTE_CONSTRUCTOR | ||
194 | static void __attribute__((constructor)) libimobiledevice_initialize(void) | ||
195 | { | ||
196 | thread_once(&init_once, internal_idevice_init); | ||
197 | } | ||
198 | |||
199 | static void __attribute__((destructor)) libimobiledevice_deinitialize(void) | ||
200 | { | ||
201 | thread_once(&deinit_once, internal_idevice_deinit); | ||
202 | } | ||
203 | #elif defined(WIN32) | ||
204 | BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved) | ||
205 | { | ||
206 | switch (dwReason) { | ||
207 | case DLL_PROCESS_ATTACH: | ||
208 | thread_once(&init_once, internal_idevice_init); | ||
209 | break; | ||
210 | case DLL_PROCESS_DETACH: | ||
211 | thread_once(&deinit_once, internal_idevice_deinit); | ||
212 | break; | ||
213 | default: | ||
214 | break; | ||
215 | } | ||
216 | return 1; | ||
217 | } | ||
218 | #else | ||
219 | #warning No compiler support for constructor/destructor attributes, some features might not be available. | ||
220 | #endif | ||
221 | |||
222 | const char* libimobiledevice_version() | ||
223 | { | ||
224 | #ifndef PACKAGE_VERSION | ||
225 | #error PACKAGE_VERSION is not defined! | ||
226 | #endif | ||
227 | return PACKAGE_VERSION; | ||
228 | } | ||
229 | |||
230 | struct idevice_subscription_context { | ||
231 | idevice_event_cb_t callback; | ||
232 | void *user_data; | ||
233 | usbmuxd_subscription_context_t ctx; | ||
234 | }; | ||
32 | 235 | ||
33 | static idevice_event_cb_t event_cb = NULL; | 236 | static idevice_subscription_context_t event_ctx = NULL; |
34 | 237 | ||
35 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) | 238 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) |
36 | { | 239 | { |
240 | idevice_subscription_context_t context = (idevice_subscription_context_t)user_data; | ||
37 | idevice_event_t ev; | 241 | idevice_event_t ev; |
38 | 242 | ||
39 | ev.event = event->event; | 243 | ev.event = event->event; |
40 | ev.uuid = event->device.uuid; | 244 | ev.udid = event->device.udid; |
41 | ev.conn_type = CONNECTION_USBMUXD; | 245 | ev.conn_type = 0; |
246 | if (event->device.conn_type == CONNECTION_TYPE_USB) { | ||
247 | ev.conn_type = CONNECTION_USBMUXD; | ||
248 | } else if (event->device.conn_type == CONNECTION_TYPE_NETWORK) { | ||
249 | ev.conn_type = CONNECTION_NETWORK; | ||
250 | } else { | ||
251 | debug_info("Unknown connection type %d", event->device.conn_type); | ||
252 | } | ||
42 | 253 | ||
43 | if (event_cb) { | 254 | if (context->callback) { |
44 | event_cb(&ev, user_data); | 255 | context->callback(&ev, context->user_data); |
45 | } | 256 | } |
46 | } | 257 | } |
47 | 258 | ||
48 | /** | 259 | idevice_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 | */ | ||
58 | idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) | ||
59 | { | 260 | { |
60 | event_cb = callback; | 261 | if (!context || !callback) { |
61 | int res = usbmuxd_subscribe(usbmux_event_cb, user_data); | 262 | return IDEVICE_E_INVALID_ARG; |
62 | if (res != 0) { | 263 | } |
63 | event_cb = NULL; | 264 | *context = malloc(sizeof(struct idevice_subscription_context)); |
64 | debug_info("Error %d when subscribing usbmux event callback!", res); | 265 | if (!*context) { |
266 | debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__); | ||
267 | return IDEVICE_E_UNKNOWN_ERROR; | ||
268 | } | ||
269 | (*context)->callback = callback; | ||
270 | (*context)->user_data = user_data; | ||
271 | int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context); | ||
272 | if (res != 0) { | ||
273 | free(*context); | ||
274 | *context = NULL; | ||
275 | debug_info("ERROR: usbmuxd_subscribe() returned %d!", res); | ||
65 | return IDEVICE_E_UNKNOWN_ERROR; | 276 | return IDEVICE_E_UNKNOWN_ERROR; |
66 | } | 277 | } |
67 | return IDEVICE_E_SUCCESS; | 278 | return IDEVICE_E_SUCCESS; |
68 | } | 279 | } |
69 | 280 | ||
70 | /** | 281 | idevice_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 | */ | ||
76 | idevice_error_t idevice_event_unsubscribe() | ||
77 | { | 282 | { |
78 | event_cb = NULL; | 283 | if (!context) { |
79 | int res = usbmuxd_unsubscribe(); | 284 | return IDEVICE_E_INVALID_ARG; |
285 | } | ||
286 | int res = usbmuxd_events_unsubscribe(context->ctx); | ||
80 | if (res != 0) { | 287 | if (res != 0) { |
81 | debug_info("Error %d when unsubscribing usbmux event callback!", res); | 288 | debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res); |
82 | return IDEVICE_E_UNKNOWN_ERROR; | 289 | return IDEVICE_E_UNKNOWN_ERROR; |
83 | } | 290 | } |
291 | if (context == event_ctx) { | ||
292 | event_ctx = NULL; | ||
293 | } | ||
294 | free(context); | ||
295 | return IDEVICE_E_SUCCESS; | ||
296 | } | ||
297 | |||
298 | idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) | ||
299 | { | ||
300 | if (event_ctx) { | ||
301 | idevice_events_unsubscribe(event_ctx); | ||
302 | } | ||
303 | return idevice_events_subscribe(&event_ctx, callback, user_data); | ||
304 | } | ||
305 | |||
306 | idevice_error_t idevice_event_unsubscribe(void) | ||
307 | { | ||
308 | if (!event_ctx) { | ||
309 | return IDEVICE_E_SUCCESS; | ||
310 | } | ||
311 | event_ctx->callback = NULL; | ||
312 | return idevice_events_unsubscribe(event_ctx); | ||
313 | } | ||
314 | |||
315 | idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count) | ||
316 | { | ||
317 | usbmuxd_device_info_t *dev_list; | ||
318 | |||
319 | *devices = NULL; | ||
320 | *count = 0; | ||
321 | |||
322 | if (usbmuxd_get_device_list(&dev_list) < 0) { | ||
323 | debug_info("ERROR: usbmuxd is not running!", __func__); | ||
324 | return IDEVICE_E_NO_DEVICE; | ||
325 | } | ||
326 | |||
327 | idevice_info_t *newlist = NULL; | ||
328 | int i, newcount = 0; | ||
329 | |||
330 | for (i = 0; dev_list[i].handle > 0; i++) { | ||
331 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
332 | newlist[newcount] = malloc(sizeof(struct idevice_info)); | ||
333 | newlist[newcount]->udid = strdup(dev_list[i].udid); | ||
334 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { | ||
335 | newlist[newcount]->conn_type = CONNECTION_USBMUXD; | ||
336 | newlist[newcount]->conn_data = NULL; | ||
337 | } else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) { | ||
338 | newlist[newcount]->conn_type = CONNECTION_NETWORK; | ||
339 | struct sockaddr* saddr = (struct sockaddr*)(dev_list[i].conn_data); | ||
340 | size_t addrlen = 0; | ||
341 | switch (saddr->sa_family) { | ||
342 | case AF_INET: | ||
343 | addrlen = sizeof(struct sockaddr_in); | ||
344 | break; | ||
345 | #ifdef AF_INET6 | ||
346 | case AF_INET6: | ||
347 | addrlen = sizeof(struct sockaddr_in6); | ||
348 | break; | ||
349 | #endif | ||
350 | default: | ||
351 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
352 | continue; | ||
353 | } | ||
354 | newlist[newcount]->conn_data = malloc(addrlen); | ||
355 | memcpy(newlist[newcount]->conn_data, dev_list[i].conn_data, addrlen); | ||
356 | } | ||
357 | newcount++; | ||
358 | *devices = newlist; | ||
359 | } | ||
360 | usbmuxd_device_list_free(&dev_list); | ||
361 | |||
362 | *count = newcount; | ||
363 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
364 | newlist[newcount] = NULL; | ||
365 | *devices = newlist; | ||
366 | |||
367 | return IDEVICE_E_SUCCESS; | ||
368 | } | ||
369 | |||
370 | idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices) | ||
371 | { | ||
372 | if (devices) { | ||
373 | int i = 0; | ||
374 | while (devices[i]) { | ||
375 | free(devices[i]->udid); | ||
376 | free(devices[i]->conn_data); | ||
377 | free(devices[i]); | ||
378 | i++; | ||
379 | } | ||
380 | free(devices); | ||
381 | } | ||
84 | return IDEVICE_E_SUCCESS; | 382 | return IDEVICE_E_SUCCESS; |
85 | } | 383 | } |
86 | 384 | ||
87 | /** | ||
88 | * Get a list of currently available devices. | ||
89 | * | ||
90 | * @param devices List of uuids of devices that are currently available. | ||
91 | * This list is terminated by a NULL pointer. | ||
92 | * @param count Number of devices found. | ||
93 | * | ||
94 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
95 | */ | ||
96 | idevice_error_t idevice_get_device_list(char ***devices, int *count) | 385 | idevice_error_t idevice_get_device_list(char ***devices, int *count) |
97 | { | 386 | { |
98 | usbmuxd_device_info_t *dev_list; | 387 | usbmuxd_device_info_t *dev_list; |
@@ -101,7 +390,7 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
101 | *count = 0; | 390 | *count = 0; |
102 | 391 | ||
103 | if (usbmuxd_get_device_list(&dev_list) < 0) { | 392 | if (usbmuxd_get_device_list(&dev_list) < 0) { |
104 | debug_info("ERROR: usbmuxd is not running!\n", __func__); | 393 | debug_info("ERROR: usbmuxd is not running!", __func__); |
105 | return IDEVICE_E_NO_DEVICE; | 394 | return IDEVICE_E_NO_DEVICE; |
106 | } | 395 | } |
107 | 396 | ||
@@ -109,9 +398,11 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
109 | int i, newcount = 0; | 398 | int i, newcount = 0; |
110 | 399 | ||
111 | for (i = 0; dev_list[i].handle > 0; i++) { | 400 | for (i = 0; dev_list[i].handle > 0; i++) { |
112 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | 401 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { |
113 | newlist[newcount++] = strdup(dev_list[i].uuid); | 402 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); |
114 | *devices = newlist; | 403 | newlist[newcount++] = strdup(dev_list[i].udid); |
404 | *devices = newlist; | ||
405 | } | ||
115 | } | 406 | } |
116 | usbmuxd_device_list_free(&dev_list); | 407 | usbmuxd_device_list_free(&dev_list); |
117 | 408 | ||
@@ -123,62 +414,101 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
123 | return IDEVICE_E_SUCCESS; | 414 | return IDEVICE_E_SUCCESS; |
124 | } | 415 | } |
125 | 416 | ||
126 | /** | ||
127 | * Free a list of device uuids. | ||
128 | * | ||
129 | * @param devices List of uuids to free. | ||
130 | * | ||
131 | * @return Always returnes IDEVICE_E_SUCCESS. | ||
132 | */ | ||
133 | idevice_error_t idevice_device_list_free(char **devices) | 417 | idevice_error_t idevice_device_list_free(char **devices) |
134 | { | 418 | { |
135 | if (devices) { | 419 | if (devices) { |
136 | int i = 0; | 420 | int i = 0; |
137 | while (devices[i++]) { | 421 | while (devices[i]) { |
138 | free(devices[i]); | 422 | free(devices[i]); |
423 | i++; | ||
139 | } | 424 | } |
140 | free(devices); | 425 | free(devices); |
141 | } | 426 | } |
142 | return IDEVICE_E_SUCCESS; | 427 | return IDEVICE_E_SUCCESS; |
143 | } | 428 | } |
144 | 429 | ||
145 | /** | 430 | void idevice_set_debug_level(int level) |
146 | * Creates an idevice_t structure for the device specified by uuid, | 431 | { |
147 | * if the device is available. | 432 | internal_set_debug_level(level); |
148 | * | 433 | } |
149 | * @note The resulting idevice_t structure has to be freed with | 434 | |
150 | * idevice_free() if it is no longer used. | 435 | static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev) |
151 | * | 436 | { |
152 | * @param device Upon calling this function, a pointer to a location of type | 437 | if (!muxdev) |
153 | * idevice_t. On successful return, this location will be populated. | 438 | return NULL; |
154 | * @param uuid The UUID to match. | 439 | |
155 | * | 440 | idevice_t device = (idevice_t)malloc(sizeof(struct idevice_private)); |
156 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | 441 | if (!device) |
157 | */ | 442 | return NULL; |
158 | idevice_error_t idevice_new(idevice_t * device, const char *uuid) | 443 | |
444 | device->udid = strdup(muxdev->udid); | ||
445 | device->mux_id = muxdev->handle; | ||
446 | device->version = 0; | ||
447 | device->device_class = 0; | ||
448 | switch (muxdev->conn_type) { | ||
449 | case CONNECTION_TYPE_USB: | ||
450 | device->conn_type = CONNECTION_USBMUXD; | ||
451 | device->conn_data = NULL; | ||
452 | break; | ||
453 | case CONNECTION_TYPE_NETWORK: | ||
454 | device->conn_type = CONNECTION_NETWORK; | ||
455 | struct sockaddr* saddr = (struct sockaddr*)(muxdev->conn_data); | ||
456 | size_t addrlen = 0; | ||
457 | switch (saddr->sa_family) { | ||
458 | case AF_INET: | ||
459 | addrlen = sizeof(struct sockaddr_in); | ||
460 | break; | ||
461 | #ifdef AF_INET6 | ||
462 | case AF_INET6: | ||
463 | addrlen = sizeof(struct sockaddr_in6); | ||
464 | break; | ||
465 | #endif | ||
466 | default: | ||
467 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
468 | free(device->udid); | ||
469 | free(device); | ||
470 | return NULL; | ||
471 | } | ||
472 | device->conn_data = malloc(addrlen); | ||
473 | memcpy(device->conn_data, muxdev->conn_data, addrlen); | ||
474 | break; | ||
475 | default: | ||
476 | device->conn_type = 0; | ||
477 | device->conn_data = NULL; | ||
478 | break; | ||
479 | } | ||
480 | return device; | ||
481 | } | ||
482 | |||
483 | idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options) | ||
159 | { | 484 | { |
160 | usbmuxd_device_info_t muxdev; | 485 | usbmuxd_device_info_t muxdev; |
161 | int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); | 486 | int usbmux_options = 0; |
487 | if (options & IDEVICE_LOOKUP_USBMUX) { | ||
488 | usbmux_options |= DEVICE_LOOKUP_USBMUX; | ||
489 | } | ||
490 | if (options & IDEVICE_LOOKUP_NETWORK) { | ||
491 | usbmux_options |= DEVICE_LOOKUP_NETWORK; | ||
492 | } | ||
493 | if (options & IDEVICE_LOOKUP_PREFER_NETWORK) { | ||
494 | usbmux_options |= DEVICE_LOOKUP_PREFER_NETWORK; | ||
495 | } | ||
496 | int res = usbmuxd_get_device(udid, &muxdev, usbmux_options); | ||
162 | if (res > 0) { | 497 | if (res > 0) { |
163 | idevice_t phone = (idevice_t) malloc(sizeof(struct idevice_private)); | 498 | *device = idevice_from_mux_device(&muxdev); |
164 | phone->uuid = strdup(muxdev.uuid); | 499 | if (!*device) { |
165 | phone->conn_type = CONNECTION_USBMUXD; | 500 | return IDEVICE_E_UNKNOWN_ERROR; |
166 | phone->conn_data = (void*)(long)muxdev.handle; | 501 | } |
167 | *device = phone; | ||
168 | return IDEVICE_E_SUCCESS; | 502 | return IDEVICE_E_SUCCESS; |
169 | } | 503 | } |
170 | /* other connection types could follow here */ | ||
171 | |||
172 | return IDEVICE_E_NO_DEVICE; | 504 | return IDEVICE_E_NO_DEVICE; |
173 | } | 505 | } |
174 | 506 | ||
175 | /** | 507 | idevice_error_t idevice_new(idevice_t * device, const char *udid) |
176 | * Cleans up an idevice structure, then frees the structure itself. | 508 | { |
177 | * This is a library-level function; deals directly with the device to tear | 509 | return idevice_new_with_options(device, udid, 0); |
178 | * down relations, but otherwise is mostly internal. | 510 | } |
179 | * | 511 | |
180 | * @param device idevice_t to free. | ||
181 | */ | ||
182 | idevice_error_t idevice_free(idevice_t device) | 512 | idevice_error_t idevice_free(idevice_t device) |
183 | { | 513 | { |
184 | if (!device) | 514 | if (!device) |
@@ -187,11 +517,8 @@ idevice_error_t idevice_free(idevice_t device) | |||
187 | 517 | ||
188 | ret = IDEVICE_E_SUCCESS; | 518 | ret = IDEVICE_E_SUCCESS; |
189 | 519 | ||
190 | free(device->uuid); | 520 | free(device->udid); |
191 | 521 | ||
192 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
193 | device->conn_data = 0; | ||
194 | } | ||
195 | if (device->conn_data) { | 522 | if (device->conn_data) { |
196 | free(device->conn_data); | 523 | free(device->conn_data); |
197 | } | 524 | } |
@@ -199,16 +526,6 @@ idevice_error_t idevice_free(idevice_t device) | |||
199 | return ret; | 526 | return ret; |
200 | } | 527 | } |
201 | 528 | ||
202 | /** | ||
203 | * Set up a connection to the given device. | ||
204 | * | ||
205 | * @param device The device to connect to. | ||
206 | * @param port The destination port to connect to. | ||
207 | * @param connection Pointer to an idevice_connection_t that will be filled | ||
208 | * with the necessary data of the connection. | ||
209 | * | ||
210 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
211 | */ | ||
212 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) | 529 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) |
213 | { | 530 | { |
214 | if (!device) { | 531 | if (!device) { |
@@ -216,31 +533,80 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect | |||
216 | } | 533 | } |
217 | 534 | ||
218 | if (device->conn_type == CONNECTION_USBMUXD) { | 535 | if (device->conn_type == CONNECTION_USBMUXD) { |
219 | int sfd = usbmuxd_connect((uint32_t)(long)device->conn_data, port); | 536 | int sfd = usbmuxd_connect(device->mux_id, port); |
220 | if (sfd < 0) { | 537 | if (sfd < 0) { |
221 | debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); | 538 | debug_info("ERROR: Connecting to usbmux device failed: %d (%s)", sfd, strerror(-sfd)); |
539 | switch (-sfd) { | ||
540 | case ECONNREFUSED: | ||
541 | return IDEVICE_E_CONNREFUSED; | ||
542 | case ENODEV: | ||
543 | return IDEVICE_E_NO_DEVICE; | ||
544 | default: | ||
545 | break; | ||
546 | } | ||
222 | return IDEVICE_E_UNKNOWN_ERROR; | 547 | return IDEVICE_E_UNKNOWN_ERROR; |
223 | } | 548 | } |
224 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | 549 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); |
225 | new_connection->type = CONNECTION_USBMUXD; | 550 | new_connection->type = CONNECTION_USBMUXD; |
226 | new_connection->data = (void*)(long)sfd; | 551 | new_connection->data = (void*)(long)sfd; |
227 | new_connection->ssl_data = NULL; | 552 | new_connection->ssl_data = NULL; |
553 | new_connection->device = device; | ||
554 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
555 | new_connection->status = IDEVICE_E_SUCCESS; | ||
228 | *connection = new_connection; | 556 | *connection = new_connection; |
229 | return IDEVICE_E_SUCCESS; | 557 | return IDEVICE_E_SUCCESS; |
230 | } else { | 558 | } |
231 | debug_info("Unknown connection type %d", device->conn_type); | 559 | if (device->conn_type == CONNECTION_NETWORK) { |
560 | struct sockaddr* saddr = (struct sockaddr*)(device->conn_data); | ||
561 | switch (saddr->sa_family) { | ||
562 | case AF_INET: | ||
563 | #ifdef AF_INET6 | ||
564 | case AF_INET6: | ||
565 | #endif | ||
566 | break; | ||
567 | default: | ||
568 | debug_info("Unsupported address family 0x%02x", saddr->sa_family); | ||
569 | return IDEVICE_E_UNKNOWN_ERROR; | ||
570 | } | ||
571 | |||
572 | char addrtxt[48]; | ||
573 | addrtxt[0] = '\0'; | ||
574 | |||
575 | if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) { | ||
576 | debug_info("Failed to convert network address: %d (%s)", errno, strerror(errno)); | ||
577 | } | ||
578 | |||
579 | debug_info("Connecting to %s port %d...", addrtxt, port); | ||
580 | |||
581 | int sfd = socket_connect_addr(saddr, port); | ||
582 | if (sfd < 0) { | ||
583 | int result = errno; | ||
584 | debug_info("ERROR: Connecting to network device failed: %d (%s)", result, strerror(result)); | ||
585 | switch (result) { | ||
586 | case ECONNREFUSED: | ||
587 | return IDEVICE_E_CONNREFUSED; | ||
588 | default: | ||
589 | break; | ||
590 | } | ||
591 | return IDEVICE_E_NO_DEVICE; | ||
592 | } | ||
593 | |||
594 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | ||
595 | new_connection->type = CONNECTION_NETWORK; | ||
596 | new_connection->data = (void*)(long)sfd; | ||
597 | new_connection->ssl_data = NULL; | ||
598 | new_connection->device = device; | ||
599 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
600 | |||
601 | *connection = new_connection; | ||
602 | |||
603 | return IDEVICE_E_SUCCESS; | ||
232 | } | 604 | } |
233 | 605 | ||
606 | debug_info("Unknown connection type %d", device->conn_type); | ||
234 | return IDEVICE_E_UNKNOWN_ERROR; | 607 | return IDEVICE_E_UNKNOWN_ERROR; |
235 | } | 608 | } |
236 | 609 | ||
237 | /** | ||
238 | * Disconnect from the device and clean up the connection structure. | ||
239 | * | ||
240 | * @param connection The connection to close. | ||
241 | * | ||
242 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
243 | */ | ||
244 | idevice_error_t idevice_disconnect(idevice_connection_t connection) | 610 | idevice_error_t idevice_disconnect(idevice_connection_t connection) |
245 | { | 611 | { |
246 | if (!connection) { | 612 | if (!connection) { |
@@ -253,11 +619,19 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection) | |||
253 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; | 619 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; |
254 | if (connection->type == CONNECTION_USBMUXD) { | 620 | if (connection->type == CONNECTION_USBMUXD) { |
255 | usbmuxd_disconnect((int)(long)connection->data); | 621 | usbmuxd_disconnect((int)(long)connection->data); |
622 | connection->data = NULL; | ||
623 | result = IDEVICE_E_SUCCESS; | ||
624 | } else if (connection->type == CONNECTION_NETWORK) { | ||
625 | socket_close((int)(long)connection->data); | ||
626 | connection->data = NULL; | ||
256 | result = IDEVICE_E_SUCCESS; | 627 | result = IDEVICE_E_SUCCESS; |
257 | } else { | 628 | } else { |
258 | debug_info("Unknown connection type %d", connection->type); | 629 | debug_info("Unknown connection type %d", connection->type); |
259 | } | 630 | } |
631 | |||
260 | free(connection); | 632 | free(connection); |
633 | connection = NULL; | ||
634 | |||
261 | return result; | 635 | return result; |
262 | } | 636 | } |
263 | 637 | ||
@@ -271,46 +645,111 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection, | |||
271 | } | 645 | } |
272 | 646 | ||
273 | if (connection->type == CONNECTION_USBMUXD) { | 647 | if (connection->type == CONNECTION_USBMUXD) { |
274 | int res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); | 648 | int res; |
649 | do { | ||
650 | res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); | ||
651 | } while (res == -EAGAIN); | ||
275 | if (res < 0) { | 652 | if (res < 0) { |
276 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); | 653 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); |
277 | return IDEVICE_E_UNKNOWN_ERROR; | 654 | return IDEVICE_E_UNKNOWN_ERROR; |
278 | } | 655 | } |
279 | return IDEVICE_E_SUCCESS; | 656 | return IDEVICE_E_SUCCESS; |
280 | } else { | ||
281 | debug_info("Unknown connection type %d", connection->type); | ||
282 | } | 657 | } |
658 | if (connection->type == CONNECTION_NETWORK) { | ||
659 | int s = socket_send((int)(long)connection->data, (void*)data, len); | ||
660 | if (s < 0) { | ||
661 | *sent_bytes = 0; | ||
662 | return IDEVICE_E_UNKNOWN_ERROR; | ||
663 | } | ||
664 | *sent_bytes = s; | ||
665 | return IDEVICE_E_SUCCESS; | ||
666 | } | ||
667 | |||
668 | debug_info("Unknown connection type %d", connection->type); | ||
283 | return IDEVICE_E_UNKNOWN_ERROR; | 669 | return IDEVICE_E_UNKNOWN_ERROR; |
284 | 670 | ||
285 | } | 671 | } |
286 | 672 | ||
287 | /** | ||
288 | * Send data to a device via the given connection. | ||
289 | * | ||
290 | * @param connection The connection to send data over. | ||
291 | * @param data Buffer with data to send. | ||
292 | * @param len Size of the buffer to send. | ||
293 | * @param sent_bytes Pointer to an uint32_t that will be filled | ||
294 | * with the number of bytes actually sent. | ||
295 | * | ||
296 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
297 | */ | ||
298 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) | 673 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) |
299 | { | 674 | { |
300 | if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { | 675 | if (!connection || !data |
676 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
677 | || (connection->ssl_data && !connection->ssl_data->session) | ||
678 | #endif | ||
679 | ) { | ||
301 | return IDEVICE_E_INVALID_ARG; | 680 | return IDEVICE_E_INVALID_ARG; |
302 | } | 681 | } |
303 | 682 | ||
304 | if (connection->ssl_data) { | 683 | if (connection->ssl_data) { |
305 | ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); | 684 | connection->status = IDEVICE_E_SUCCESS; |
306 | if ((uint32_t)sent == (uint32_t)len) { | 685 | uint32_t sent = 0; |
307 | *sent_bytes = sent; | 686 | while (sent < len) { |
308 | return IDEVICE_E_SUCCESS; | 687 | #if defined(HAVE_OPENSSL) |
688 | int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent)); | ||
689 | if (s <= 0) { | ||
690 | int sslerr = SSL_get_error(connection->ssl_data->session, s); | ||
691 | if (sslerr == SSL_ERROR_WANT_WRITE) { | ||
692 | continue; | ||
693 | } | ||
694 | break; | ||
695 | } | ||
696 | #elif defined(HAVE_GNUTLS) | ||
697 | ssize_t s = gnutls_record_send(connection->ssl_data->session, (void*)(data+sent), (size_t)(len-sent)); | ||
698 | #elif defined(HAVE_MBEDTLS) | ||
699 | int s = mbedtls_ssl_write(&connection->ssl_data->ctx, (const unsigned char*)(data+sent), (size_t)(len-sent)); | ||
700 | #endif | ||
701 | if (s < 0) { | ||
702 | break; | ||
703 | } | ||
704 | sent += s; | ||
705 | } | ||
706 | debug_info("SSL_write %d, sent %d", len, sent); | ||
707 | if (sent < len) { | ||
708 | *sent_bytes = 0; | ||
709 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; | ||
710 | } | ||
711 | *sent_bytes = sent; | ||
712 | return IDEVICE_E_SUCCESS; | ||
713 | } | ||
714 | uint32_t sent = 0; | ||
715 | while (sent < len) { | ||
716 | uint32_t bytes = 0; | ||
717 | int s = internal_connection_send(connection, data+sent, len-sent, &bytes); | ||
718 | if (s < 0) { | ||
719 | break; | ||
720 | } | ||
721 | sent += bytes; | ||
722 | } | ||
723 | debug_info("internal_connection_send %d, sent %d", len, sent); | ||
724 | if (sent < len) { | ||
725 | *sent_bytes = sent; | ||
726 | if (sent == 0) { | ||
727 | return IDEVICE_E_UNKNOWN_ERROR; | ||
728 | } | ||
729 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
730 | } | ||
731 | *sent_bytes = sent; | ||
732 | return IDEVICE_E_SUCCESS; | ||
733 | } | ||
734 | |||
735 | static inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received) | ||
736 | { | ||
737 | if (conn_error < 0) { | ||
738 | switch (conn_error) { | ||
739 | case -EAGAIN: | ||
740 | if (len) { | ||
741 | debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error)); | ||
742 | } else { | ||
743 | debug_info("ERROR: received partial data (%s)", strerror(-conn_error)); | ||
744 | } | ||
745 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
746 | case -ETIMEDOUT: | ||
747 | return IDEVICE_E_TIMEOUT; | ||
748 | default: | ||
749 | return IDEVICE_E_UNKNOWN_ERROR; | ||
309 | } | 750 | } |
310 | *sent_bytes = 0; | ||
311 | return IDEVICE_E_SSL_ERROR; | ||
312 | } | 751 | } |
313 | return internal_connection_send(connection, data, len, sent_bytes); | 752 | return IDEVICE_E_SUCCESS; |
314 | } | 753 | } |
315 | 754 | ||
316 | /** | 755 | /** |
@@ -324,47 +763,92 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t | |||
324 | } | 763 | } |
325 | 764 | ||
326 | if (connection->type == CONNECTION_USBMUXD) { | 765 | if (connection->type == CONNECTION_USBMUXD) { |
327 | int res = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); | 766 | int conn_error = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); |
328 | if (res < 0) { | 767 | idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes); |
329 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res)); | 768 | if (error == IDEVICE_E_UNKNOWN_ERROR) { |
330 | return IDEVICE_E_UNKNOWN_ERROR; | 769 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error)); |
331 | } | 770 | } |
332 | return IDEVICE_E_SUCCESS; | 771 | return error; |
333 | } else { | ||
334 | debug_info("Unknown connection type %d", connection->type); | ||
335 | } | 772 | } |
773 | if (connection->type == CONNECTION_NETWORK) { | ||
774 | int res = socket_receive_timeout((int)(long)connection->data, data, len, 0, timeout); | ||
775 | idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0); | ||
776 | if (error == IDEVICE_E_SUCCESS) { | ||
777 | *recv_bytes = (uint32_t)res; | ||
778 | } else if (error == IDEVICE_E_UNKNOWN_ERROR) { | ||
779 | debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res)); | ||
780 | } | ||
781 | return error; | ||
782 | } | ||
783 | |||
784 | debug_info("Unknown connection type %d", connection->type); | ||
336 | return IDEVICE_E_UNKNOWN_ERROR; | 785 | return IDEVICE_E_UNKNOWN_ERROR; |
337 | } | 786 | } |
338 | 787 | ||
339 | /** | ||
340 | * Receive data from a device via the given connection. | ||
341 | * This function will return after the given timeout even if no data has been | ||
342 | * received. | ||
343 | * | ||
344 | * @param connection The connection to receive data from. | ||
345 | * @param data Buffer that will be filled with the received data. | ||
346 | * This buffer has to be large enough to hold len bytes. | ||
347 | * @param len Buffer size or number of bytes to receive. | ||
348 | * @param recv_bytes Number of bytes actually received. | ||
349 | * @param timeout Timeout in milliseconds after which this function should | ||
350 | * return even if no data has been received. | ||
351 | * | ||
352 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
353 | */ | ||
354 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) | 788 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) |
355 | { | 789 | { |
356 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 790 | if (!connection |
791 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
792 | || (connection->ssl_data && !connection->ssl_data->session) | ||
793 | #endif | ||
794 | || len == 0 | ||
795 | ) { | ||
357 | return IDEVICE_E_INVALID_ARG; | 796 | return IDEVICE_E_INVALID_ARG; |
358 | } | 797 | } |
359 | 798 | ||
360 | if (connection->ssl_data) { | 799 | if (connection->ssl_data) { |
361 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 800 | uint32_t received = 0; |
362 | if (received > 0) { | 801 | |
802 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
803 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
804 | } | ||
805 | |||
806 | // this should be reset after the SSL_read call on all codepaths, as | ||
807 | // the supplied timeout should only apply to the current read. | ||
808 | connection->ssl_recv_timeout = timeout; | ||
809 | connection->status = IDEVICE_E_SUCCESS; | ||
810 | while (received < len) { | ||
811 | #if defined(HAVE_OPENSSL) | ||
812 | int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received); | ||
813 | if (r > 0) { | ||
814 | received += r; | ||
815 | } else { | ||
816 | int sslerr = SSL_get_error(connection->ssl_data->session, r); | ||
817 | if (sslerr == SSL_ERROR_WANT_READ) { | ||
818 | continue; | ||
819 | } else if (sslerr == SSL_ERROR_ZERO_RETURN) { | ||
820 | if (connection->status == IDEVICE_E_TIMEOUT) { | ||
821 | SSL_set_shutdown(connection->ssl_data->session, 0); | ||
822 | } | ||
823 | } | ||
824 | break; | ||
825 | } | ||
826 | #elif defined(HAVE_GNUTLS) | ||
827 | ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received); | ||
828 | if (r > 0) { | ||
829 | received += r; | ||
830 | } else { | ||
831 | break; | ||
832 | } | ||
833 | #elif defined(HAVE_MBEDTLS) | ||
834 | int r = mbedtls_ssl_read(&connection->ssl_data->ctx, (void*)(data+received), (size_t)len-received); | ||
835 | if (r > 0) { | ||
836 | received += r; | ||
837 | } else { | ||
838 | break; | ||
839 | } | ||
840 | #endif | ||
841 | } | ||
842 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
843 | |||
844 | debug_info("SSL_read %d, received %d", len, received); | ||
845 | if (received < len) { | ||
363 | *recv_bytes = received; | 846 | *recv_bytes = received; |
364 | return IDEVICE_E_SUCCESS; | 847 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; |
365 | } | 848 | } |
366 | *recv_bytes = 0; | 849 | |
367 | return IDEVICE_E_SSL_ERROR; | 850 | *recv_bytes = received; |
851 | return IDEVICE_E_SUCCESS; | ||
368 | } | 852 | } |
369 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); | 853 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); |
370 | } | 854 | } |
@@ -384,35 +868,45 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti | |||
384 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); | 868 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); |
385 | return IDEVICE_E_UNKNOWN_ERROR; | 869 | return IDEVICE_E_UNKNOWN_ERROR; |
386 | } | 870 | } |
387 | |||
388 | return IDEVICE_E_SUCCESS; | 871 | return IDEVICE_E_SUCCESS; |
389 | } else { | ||
390 | debug_info("Unknown connection type %d", connection->type); | ||
391 | } | 872 | } |
873 | if (connection->type == CONNECTION_NETWORK) { | ||
874 | int res = socket_receive((int)(long)connection->data, data, len); | ||
875 | if (res < 0) { | ||
876 | debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res)); | ||
877 | return IDEVICE_E_UNKNOWN_ERROR; | ||
878 | } | ||
879 | *recv_bytes = (uint32_t)res; | ||
880 | return IDEVICE_E_SUCCESS; | ||
881 | } | ||
882 | |||
883 | debug_info("Unknown connection type %d", connection->type); | ||
392 | return IDEVICE_E_UNKNOWN_ERROR; | 884 | return IDEVICE_E_UNKNOWN_ERROR; |
393 | } | 885 | } |
394 | 886 | ||
395 | /** | ||
396 | * Receive data from a device via the given connection. | ||
397 | * This function is like idevice_connection_receive_timeout, but with a | ||
398 | * predefined reasonable timeout. | ||
399 | * | ||
400 | * @param connection The connection to receive data from. | ||
401 | * @param data Buffer that will be filled with the received data. | ||
402 | * This buffer has to be large enough to hold len bytes. | ||
403 | * @param len Buffer size or number of bytes to receive. | ||
404 | * @param recv_bytes Number of bytes actually received. | ||
405 | * | ||
406 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
407 | */ | ||
408 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) | 887 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) |
409 | { | 888 | { |
410 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 889 | if (!connection |
890 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
891 | || (connection->ssl_data && !connection->ssl_data->session) | ||
892 | #endif | ||
893 | ) { | ||
411 | return IDEVICE_E_INVALID_ARG; | 894 | return IDEVICE_E_INVALID_ARG; |
412 | } | 895 | } |
413 | 896 | ||
414 | if (connection->ssl_data) { | 897 | if (connection->ssl_data) { |
898 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
899 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
900 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
901 | } | ||
902 | #if defined(HAVE_OPENSSL) | ||
903 | int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len); | ||
904 | debug_info("SSL_read %d, received %d", len, received); | ||
905 | #elif defined(HAVE_GNUTLS) | ||
415 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 906 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); |
907 | #elif defined(HAVE_MBEDTLS) | ||
908 | int received = mbedtls_ssl_read(&connection->ssl_data->ctx, (unsigned char*)data, (size_t)len); | ||
909 | #endif | ||
416 | if (received > 0) { | 910 | if (received > 0) { |
417 | *recv_bytes = received; | 911 | *recv_bytes = received; |
418 | return IDEVICE_E_SUCCESS; | 912 | return IDEVICE_E_SUCCESS; |
@@ -423,89 +917,105 @@ idevice_error_t idevice_connection_receive(idevice_connection_t connection, char | |||
423 | return internal_connection_receive(connection, data, len, recv_bytes); | 917 | return internal_connection_receive(connection, data, len, recv_bytes); |
424 | } | 918 | } |
425 | 919 | ||
426 | /** | 920 | idevice_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 | */ | ||
429 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) | ||
430 | { | 921 | { |
431 | if (!device) | 922 | if (!connection || !fd) { |
432 | return IDEVICE_E_INVALID_ARG; | 923 | return IDEVICE_E_INVALID_ARG; |
924 | } | ||
433 | 925 | ||
434 | if (device->conn_type == CONNECTION_USBMUXD) { | 926 | if (connection->type == CONNECTION_USBMUXD) { |
435 | *handle = (uint32_t)(long)device->conn_data; | 927 | *fd = (int)(long)connection->data; |
928 | return IDEVICE_E_SUCCESS; | ||
929 | } | ||
930 | if (connection->type == CONNECTION_NETWORK) { | ||
931 | *fd = (int)(long)connection->data; | ||
436 | return IDEVICE_E_SUCCESS; | 932 | return IDEVICE_E_SUCCESS; |
437 | } else { | ||
438 | debug_info("Unknown connection type %d", device->conn_type); | ||
439 | } | 933 | } |
934 | |||
935 | debug_info("Unknown connection type %d", connection->type); | ||
440 | return IDEVICE_E_UNKNOWN_ERROR; | 936 | return IDEVICE_E_UNKNOWN_ERROR; |
441 | } | 937 | } |
442 | 938 | ||
443 | /** | 939 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) |
444 | * Gets the unique id for the device. | 940 | { |
445 | */ | 941 | if (!device || !handle) |
446 | idevice_error_t idevice_get_uuid(idevice_t device, char **uuid) | 942 | return IDEVICE_E_INVALID_ARG; |
943 | |||
944 | *handle = device->mux_id; | ||
945 | return IDEVICE_E_SUCCESS; | ||
946 | } | ||
947 | |||
948 | idevice_error_t idevice_get_udid(idevice_t device, char **udid) | ||
447 | { | 949 | { |
448 | if (!device || !uuid) | 950 | if (!device || !udid) |
449 | return IDEVICE_E_INVALID_ARG; | 951 | return IDEVICE_E_INVALID_ARG; |
450 | 952 | ||
451 | *uuid = strdup(device->uuid); | 953 | if (device->udid) { |
954 | *udid = strdup(device->udid); | ||
955 | } | ||
452 | return IDEVICE_E_SUCCESS; | 956 | return IDEVICE_E_SUCCESS; |
453 | } | 957 | } |
454 | 958 | ||
959 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
960 | typedef ssize_t ssl_cb_ret_type_t; | ||
961 | #elif defined(HAVE_MBEDTLS) | ||
962 | typedef int ssl_cb_ret_type_t; | ||
963 | #endif | ||
964 | |||
455 | /** | 965 | /** |
456 | * Internally used gnutls callback function for receiving encrypted data. | 966 | * Internally used SSL callback function for receiving encrypted data. |
457 | */ | 967 | */ |
458 | static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 968 | static ssl_cb_ret_type_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length) |
459 | { | 969 | { |
460 | int bytes = 0, pos_start_fill = 0; | 970 | uint32_t bytes = 0; |
461 | size_t tbytes = 0; | 971 | uint32_t pos = 0; |
462 | int this_len = length; | ||
463 | idevice_error_t res; | 972 | idevice_error_t res; |
464 | idevice_connection_t connection = (idevice_connection_t)transport; | 973 | unsigned int timeout = connection->ssl_recv_timeout; |
465 | char *recv_buffer; | ||
466 | |||
467 | debug_info("pre-read client wants %zi bytes", length); | ||
468 | 974 | ||
469 | recv_buffer = (char *) malloc(sizeof(char) * this_len); | 975 | debug_info("pre-read length = %zi bytes", length); |
470 | 976 | ||
471 | /* repeat until we have the full data or an error occurs */ | 977 | /* repeat until we have the full data or an error occurs */ |
472 | do { | 978 | do { |
473 | if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { | 979 | bytes = 0; |
474 | debug_info("ERROR: idevice_connection_receive returned %d", res); | 980 | if (timeout == (unsigned int)-1) { |
475 | return res; | 981 | res = internal_connection_receive(connection, buffer + pos, (uint32_t)length - pos, &bytes); |
982 | } else { | ||
983 | res = internal_connection_receive_timeout(connection, buffer + pos, (uint32_t)length - pos, &bytes, (unsigned int)timeout); | ||
476 | } | 984 | } |
477 | debug_info("post-read we got %i bytes", bytes); | 985 | if (res != IDEVICE_E_SUCCESS) { |
986 | if (res != IDEVICE_E_TIMEOUT) { | ||
987 | debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res); | ||
988 | } | ||
989 | connection->status = res; | ||
990 | return -1; | ||
991 | } | ||
992 | debug_info("read %i bytes", bytes); | ||
478 | 993 | ||
479 | /* increase read count */ | 994 | /* increase read count */ |
480 | tbytes += bytes; | 995 | pos += bytes; |
481 | 996 | if (pos < (uint32_t)length) { | |
482 | /* fill the buffer with what we got right now */ | 997 | debug_info("re-read trying to read missing %i bytes", (uint32_t)length - pos); |
483 | memcpy(buffer + pos_start_fill, recv_buffer, bytes); | ||
484 | pos_start_fill += bytes; | ||
485 | |||
486 | if (tbytes >= length) { | ||
487 | break; | ||
488 | } | 998 | } |
999 | } while (pos < (uint32_t)length); | ||
489 | 1000 | ||
490 | this_len = length - tbytes; | 1001 | debug_info("post-read received %i bytes", pos); |
491 | debug_info("re-read trying to read missing %i bytes", this_len); | ||
492 | } while (tbytes < length); | ||
493 | 1002 | ||
494 | if (recv_buffer) { | 1003 | return pos; |
495 | free(recv_buffer); | ||
496 | } | ||
497 | return tbytes; | ||
498 | } | 1004 | } |
499 | 1005 | ||
500 | /** | 1006 | /** |
501 | * Internally used gnutls callback function for sending encrypted data. | 1007 | * Internally used SSL callback function for sending encrypted data. |
502 | */ | 1008 | */ |
503 | static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 1009 | static ssl_cb_ret_type_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length) |
504 | { | 1010 | { |
505 | uint32_t bytes = 0; | 1011 | uint32_t bytes = 0; |
506 | idevice_connection_t connection = (idevice_connection_t)transport; | 1012 | idevice_error_t res; |
507 | debug_info("pre-send length = %zi", length); | 1013 | debug_info("pre-send length = %zi bytes", length); |
508 | internal_connection_send(connection, buffer, length, &bytes); | 1014 | if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) { |
1015 | debug_info("ERROR: internal_connection_send returned %d", res); | ||
1016 | connection->status = res; | ||
1017 | return -1; | ||
1018 | } | ||
509 | debug_info("post-send sent %i bytes", bytes); | 1019 | debug_info("post-send sent %i bytes", bytes); |
510 | return bytes; | 1020 | return bytes; |
511 | } | 1021 | } |
@@ -518,6 +1028,14 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
518 | if (!ssl_data) | 1028 | if (!ssl_data) |
519 | return; | 1029 | return; |
520 | 1030 | ||
1031 | #if defined(HAVE_OPENSSL) | ||
1032 | if (ssl_data->session) { | ||
1033 | SSL_free(ssl_data->session); | ||
1034 | } | ||
1035 | if (ssl_data->ctx) { | ||
1036 | SSL_CTX_free(ssl_data->ctx); | ||
1037 | } | ||
1038 | #elif defined(HAVE_GNUTLS) | ||
521 | if (ssl_data->session) { | 1039 | if (ssl_data->session) { |
522 | gnutls_deinit(ssl_data->session); | 1040 | gnutls_deinit(ssl_data->session); |
523 | } | 1041 | } |
@@ -536,20 +1054,118 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
536 | if (ssl_data->host_privkey) { | 1054 | if (ssl_data->host_privkey) { |
537 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); | 1055 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); |
538 | } | 1056 | } |
1057 | #elif defined(HAVE_MBEDTLS) | ||
1058 | mbedtls_pk_free(&ssl_data->root_privkey); | ||
1059 | mbedtls_x509_crt_free(&ssl_data->certificate); | ||
1060 | mbedtls_entropy_free(&ssl_data->entropy); | ||
1061 | mbedtls_ctr_drbg_free(&ssl_data->ctr_drbg); | ||
1062 | mbedtls_ssl_config_free(&ssl_data->config); | ||
1063 | mbedtls_ssl_free(&ssl_data->ctx); | ||
1064 | #endif | ||
1065 | } | ||
1066 | |||
1067 | #ifdef HAVE_OPENSSL | ||
1068 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1069 | static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, size_t len, int argi, long argl, int retvalue, size_t *processed) | ||
1070 | #else | ||
1071 | static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue) | ||
1072 | #endif | ||
1073 | { | ||
1074 | ssize_t bytes = 0; | ||
1075 | idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b); | ||
1076 | #if OPENSSL_VERSION_NUMBER < 0x30000000L | ||
1077 | size_t len = (size_t)argi; | ||
1078 | size_t *processed = (size_t*)&bytes; | ||
1079 | #endif | ||
1080 | switch (oper) { | ||
1081 | case (BIO_CB_READ|BIO_CB_RETURN): | ||
1082 | if (argp) { | ||
1083 | bytes = internal_ssl_read(conn, (char *)argp, len); | ||
1084 | *processed = bytes; | ||
1085 | return (long)bytes; | ||
1086 | } | ||
1087 | return 0; | ||
1088 | case (BIO_CB_PUTS|BIO_CB_RETURN): | ||
1089 | len = strlen(argp); | ||
1090 | // fallthrough | ||
1091 | case (BIO_CB_WRITE|BIO_CB_RETURN): | ||
1092 | bytes = internal_ssl_write(conn, argp, len); | ||
1093 | *processed = bytes; | ||
1094 | return (long)bytes; | ||
1095 | default: | ||
1096 | return retvalue; | ||
1097 | } | ||
1098 | } | ||
1099 | |||
1100 | static BIO *ssl_idevice_bio_new(idevice_connection_t conn) | ||
1101 | { | ||
1102 | BIO *b = BIO_new(BIO_s_null()); | ||
1103 | if (!b) return NULL; | ||
1104 | BIO_set_callback_arg(b, (char *)conn); | ||
1105 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1106 | BIO_set_callback_ex(b, ssl_idevice_bio_callback); | ||
1107 | #else | ||
1108 | BIO_set_callback(b, ssl_idevice_bio_callback); | ||
1109 | #endif | ||
1110 | return b; | ||
1111 | } | ||
1112 | |||
1113 | static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx) | ||
1114 | { | ||
1115 | return 1; | ||
1116 | } | ||
1117 | |||
1118 | #ifndef STRIP_DEBUG_CODE | ||
1119 | static const char *ssl_error_to_string(int e) | ||
1120 | { | ||
1121 | switch(e) { | ||
1122 | case SSL_ERROR_NONE: | ||
1123 | return "SSL_ERROR_NONE"; | ||
1124 | case SSL_ERROR_SSL: | ||
1125 | return ERR_error_string(ERR_get_error(), NULL); | ||
1126 | case SSL_ERROR_WANT_READ: | ||
1127 | return "SSL_ERROR_WANT_READ"; | ||
1128 | case SSL_ERROR_WANT_WRITE: | ||
1129 | return "SSL_ERROR_WANT_WRITE"; | ||
1130 | case SSL_ERROR_WANT_X509_LOOKUP: | ||
1131 | return "SSL_ERROR_WANT_X509_LOOKUP"; | ||
1132 | case SSL_ERROR_SYSCALL: | ||
1133 | return "SSL_ERROR_SYSCALL"; | ||
1134 | case SSL_ERROR_ZERO_RETURN: | ||
1135 | return "SSL_ERROR_ZERO_RETURN"; | ||
1136 | case SSL_ERROR_WANT_CONNECT: | ||
1137 | return "SSL_ERROR_WANT_CONNECT"; | ||
1138 | case SSL_ERROR_WANT_ACCEPT: | ||
1139 | return "SSL_ERROR_WANT_ACCEPT"; | ||
1140 | default: | ||
1141 | return "UNKOWN_ERROR_VALUE"; | ||
1142 | } | ||
539 | } | 1143 | } |
1144 | #endif | ||
1145 | #endif | ||
540 | 1146 | ||
1147 | #if defined(HAVE_GNUTLS) | ||
541 | /** | 1148 | /** |
542 | * Internally used gnutls callback function that gets called during handshake. | 1149 | * Internally used gnutls callback function that gets called during handshake. |
543 | */ | 1150 | */ |
544 | static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) | 1151 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
1152 | static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st) | ||
1153 | #else | ||
1154 | static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) | ||
1155 | #endif | ||
545 | { | 1156 | { |
546 | int res = -1; | 1157 | int res = -1; |
547 | gnutls_certificate_type_t type = gnutls_certificate_type_get (session); | 1158 | gnutls_certificate_type_t type = gnutls_certificate_type_get(session); |
548 | if (type == GNUTLS_CRT_X509) { | 1159 | if (type == GNUTLS_CRT_X509) { |
549 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr (session); | 1160 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr(session); |
550 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { | 1161 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { |
551 | debug_info("Passing certificate"); | 1162 | debug_info("Passing certificate"); |
1163 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 | ||
1164 | st->cert_type = type; | ||
1165 | st->key_type = GNUTLS_PRIVKEY_X509; | ||
1166 | #else | ||
552 | st->type = type; | 1167 | st->type = type; |
1168 | #endif | ||
553 | st->ncerts = 1; | 1169 | st->ncerts = 1; |
554 | st->cert.x509 = &ssl_data->host_cert; | 1170 | st->cert.x509 = &ssl_data->host_cert; |
555 | st->key.x509 = ssl_data->host_privkey; | 1171 | st->key.x509 = ssl_data->host_privkey; |
@@ -559,46 +1175,204 @@ static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_ | |||
559 | } | 1175 | } |
560 | return res; | 1176 | return res; |
561 | } | 1177 | } |
1178 | #elif defined(HAVE_MBEDTLS) | ||
1179 | static void _mbedtls_log_cb(void* ctx, int level, const char* filename, int line, const char* message) | ||
1180 | { | ||
1181 | fprintf(stderr, "[mbedtls][%d] %s:%d => %s", level, filename, line, message); | ||
1182 | } | ||
1183 | |||
1184 | static int cert_verify_cb(void* ctx, mbedtls_x509_crt* cert, int depth, uint32_t *flags) | ||
1185 | { | ||
1186 | *flags = 0; | ||
1187 | return 0; | ||
1188 | } | ||
1189 | |||
1190 | static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len) | ||
1191 | { | ||
1192 | memset(buf, 4, len); | ||
1193 | return 0; | ||
1194 | } | ||
1195 | #endif | ||
562 | 1196 | ||
563 | /** | ||
564 | * Enables SSL for the given connection. | ||
565 | * | ||
566 | * @param connection The connection to enable SSL for. | ||
567 | * | ||
568 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
569 | * is NULL or connection->ssl_data is non-NULL, or IDEVICE_E_SSL_ERROR when | ||
570 | * SSL initialization, setup, or handshake fails. | ||
571 | */ | ||
572 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | 1197 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) |
573 | { | 1198 | { |
574 | if (!connection || connection->ssl_data) | 1199 | if (!connection || connection->ssl_data) |
575 | return IDEVICE_E_INVALID_ARG; | 1200 | return IDEVICE_E_INVALID_ARG; |
576 | 1201 | ||
577 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; | 1202 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; |
578 | uint32_t return_me = 0; | 1203 | plist_t pair_record = NULL; |
1204 | |||
1205 | userpref_error_t uerr = userpref_read_pair_record(connection->device->udid, &pair_record); | ||
1206 | if (uerr != USERPREF_E_SUCCESS) { | ||
1207 | debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s (%d)", connection->device->udid, uerr); | ||
1208 | return ret; | ||
1209 | } | ||
1210 | |||
1211 | #if defined(HAVE_OPENSSL) | ||
1212 | key_data_t root_cert = { NULL, 0 }; | ||
1213 | key_data_t root_privkey = { NULL, 0 }; | ||
1214 | |||
1215 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
1216 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
579 | 1217 | ||
1218 | if (pair_record) | ||
1219 | plist_free(pair_record); | ||
1220 | |||
1221 | BIO *ssl_bio = ssl_idevice_bio_new(connection); | ||
1222 | if (!ssl_bio) { | ||
1223 | debug_info("ERROR: Could not create SSL bio."); | ||
1224 | return ret; | ||
1225 | } | ||
1226 | |||
1227 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); | ||
1228 | if (ssl_ctx == NULL) { | ||
1229 | debug_info("ERROR: Could not create SSL context."); | ||
1230 | BIO_free(ssl_bio); | ||
1231 | return ret; | ||
1232 | } | ||
1233 | |||
1234 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || \ | ||
1235 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3060000fL)) | ||
1236 | SSL_CTX_set_security_level(ssl_ctx, 0); | ||
1237 | #endif | ||
1238 | |||
1239 | #if OPENSSL_VERSION_NUMBER < 0x10100002L || \ | ||
1240 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL)) | ||
1241 | /* force use of TLSv1 for older devices */ | ||
1242 | if (connection->device->version < DEVICE_VERSION(10,0,0)) { | ||
1243 | #ifdef SSL_OP_NO_TLSv1_1 | ||
1244 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); | ||
1245 | #endif | ||
1246 | #ifdef SSL_OP_NO_TLSv1_2 | ||
1247 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); | ||
1248 | #endif | ||
1249 | #ifdef SSL_OP_NO_TLSv1_3 | ||
1250 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); | ||
1251 | #endif | ||
1252 | } | ||
1253 | #else | ||
1254 | SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); | ||
1255 | if (connection->device->version < DEVICE_VERSION(10,0,0)) { | ||
1256 | SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION); | ||
1257 | if (connection->device->version == 0) { | ||
1258 | /* | ||
1259 | iOS 1 doesn't understand TLS1_VERSION, it can only speak SSL3_VERSION. | ||
1260 | However, modern OpenSSL is usually compiled without SSLv3 support. | ||
1261 | So if we set min_proto_version to SSL3_VERSION on an OpenSSL instance which doesn't support it, | ||
1262 | it will just ignore min_proto_version altogether and fall back to an even higher version. | ||
1263 | To avoid accidentally breaking iOS 2.0+, we set min version to 0 instead. | ||
1264 | Here is what documentation says: | ||
1265 | Setting the minimum or maximum version to 0, | ||
1266 | will enable protocol versions down to the lowest version, | ||
1267 | or up to the highest version supported by the library, respectively. | ||
1268 | */ | ||
1269 | SSL_CTX_set_min_proto_version(ssl_ctx, 0); | ||
1270 | } | ||
1271 | } | ||
1272 | #endif | ||
1273 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1274 | #if defined(SSL_OP_IGNORE_UNEXPECTED_EOF) | ||
1275 | /* | ||
1276 | * For OpenSSL 3 and later, mark close_notify alerts as optional. | ||
1277 | * For prior versions of OpenSSL we check for SSL_ERROR_SYSCALL when | ||
1278 | * reading instead (this error changes to SSL_ERROR_SSL in OpenSSL 3). | ||
1279 | */ | ||
1280 | SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); | ||
1281 | #endif | ||
1282 | #if defined(SSL_OP_LEGACY_SERVER_CONNECT) | ||
1283 | /* | ||
1284 | * Without setting SSL_OP_LEGACY_SERVER_CONNECT, OpenSSL 3 fails with | ||
1285 | * error "unsafe legacy renegotiation disabled" when talking to iOS 5 | ||
1286 | */ | ||
1287 | SSL_CTX_set_options(ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT); | ||
1288 | #endif | ||
1289 | #endif | ||
1290 | |||
1291 | BIO* membp; | ||
1292 | X509* rootCert = NULL; | ||
1293 | membp = BIO_new_mem_buf(root_cert.data, root_cert.size); | ||
1294 | PEM_read_bio_X509(membp, &rootCert, NULL, NULL); | ||
1295 | BIO_free(membp); | ||
1296 | if (SSL_CTX_use_certificate(ssl_ctx, rootCert) != 1) { | ||
1297 | debug_info("WARNING: Could not load RootCertificate"); | ||
1298 | } | ||
1299 | X509_free(rootCert); | ||
1300 | free(root_cert.data); | ||
1301 | |||
1302 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1303 | EVP_PKEY* rootPrivKey = NULL; | ||
1304 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
1305 | PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
1306 | BIO_free(membp); | ||
1307 | if (SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
1308 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
1309 | } | ||
1310 | EVP_PKEY_free(rootPrivKey); | ||
1311 | #else | ||
1312 | RSA* rootPrivKey = NULL; | ||
1313 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
1314 | PEM_read_bio_RSAPrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
1315 | BIO_free(membp); | ||
1316 | if (SSL_CTX_use_RSAPrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
1317 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
1318 | } | ||
1319 | RSA_free(rootPrivKey); | ||
1320 | #endif | ||
1321 | free(root_privkey.data); | ||
1322 | |||
1323 | SSL *ssl = SSL_new(ssl_ctx); | ||
1324 | if (!ssl) { | ||
1325 | debug_info("ERROR: Could not create SSL object"); | ||
1326 | BIO_free(ssl_bio); | ||
1327 | SSL_CTX_free(ssl_ctx); | ||
1328 | return ret; | ||
1329 | } | ||
1330 | SSL_set_connect_state(ssl); | ||
1331 | SSL_set_verify(ssl, 0, ssl_verify_callback); | ||
1332 | SSL_set_bio(ssl, ssl_bio, ssl_bio); | ||
1333 | |||
1334 | debug_info("Performing SSL handshake"); | ||
1335 | int ssl_error = 0; | ||
1336 | do { | ||
1337 | ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl)); | ||
1338 | if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) { | ||
1339 | break; | ||
1340 | } | ||
1341 | #ifdef WIN32 | ||
1342 | Sleep(100); | ||
1343 | #else | ||
1344 | struct timespec ts = { 0, 100000000 }; | ||
1345 | nanosleep(&ts, NULL); | ||
1346 | #endif | ||
1347 | } while (1); | ||
1348 | if (ssl_error != 0) { | ||
1349 | debug_info("ERROR during SSL handshake: %s", ssl_error_to_string(ssl_error)); | ||
1350 | SSL_free(ssl); | ||
1351 | SSL_CTX_free(ssl_ctx); | ||
1352 | } else { | ||
1353 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
1354 | ssl_data_loc->session = ssl; | ||
1355 | ssl_data_loc->ctx = ssl_ctx; | ||
1356 | connection->ssl_data = ssl_data_loc; | ||
1357 | ret = IDEVICE_E_SUCCESS; | ||
1358 | debug_info("SSL mode enabled, %s, cipher: %s", SSL_get_version(ssl), SSL_get_cipher(ssl)); | ||
1359 | } | ||
1360 | /* required for proper multi-thread clean up to prevent leaks */ | ||
1361 | openssl_remove_thread_state(); | ||
1362 | #elif defined(HAVE_GNUTLS) | ||
580 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | 1363 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); |
581 | 1364 | ||
582 | /* Set up GnuTLS... */ | 1365 | /* Set up GnuTLS... */ |
583 | debug_info("enabling SSL mode"); | 1366 | debug_info("enabling SSL mode"); |
584 | errno = 0; | 1367 | errno = 0; |
585 | gnutls_global_init(); | ||
586 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); | 1368 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); |
587 | gnutls_certificate_client_set_retrieve_function (ssl_data_loc->certificate, internal_cert_callback); | 1369 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
1370 | gnutls_certificate_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
1371 | #else | ||
1372 | gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
1373 | #endif | ||
588 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); | 1374 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); |
589 | { | 1375 | gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-TLS1.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL); |
590 | int protocol_priority[16] = { GNUTLS_SSL3, 0 }; | ||
591 | int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 }; | ||
592 | int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; | ||
593 | int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; | ||
594 | int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; | ||
595 | |||
596 | gnutls_cipher_set_priority(ssl_data_loc->session, cipher_priority); | ||
597 | gnutls_compression_set_priority(ssl_data_loc->session, comp_priority); | ||
598 | gnutls_kx_set_priority(ssl_data_loc->session, kx_priority); | ||
599 | gnutls_protocol_set_priority(ssl_data_loc->session, protocol_priority); | ||
600 | gnutls_mac_set_priority(ssl_data_loc->session, mac_priority); | ||
601 | } | ||
602 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); | 1376 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); |
603 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); | 1377 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); |
604 | 1378 | ||
@@ -607,10 +1381,13 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
607 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); | 1381 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); |
608 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); | 1382 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); |
609 | 1383 | ||
610 | userpref_error_t uerr = userpref_get_keys_and_certs(ssl_data_loc->root_privkey, ssl_data_loc->root_cert, ssl_data_loc->host_privkey, ssl_data_loc->host_cert); | 1384 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, ssl_data_loc->root_cert); |
611 | if (uerr != USERPREF_E_SUCCESS) { | 1385 | pair_record_import_crt_with_name(pair_record, USERPREF_HOST_CERTIFICATE_KEY, ssl_data_loc->host_cert); |
612 | debug_info("Error %d when loading keys and certificates! %d", uerr); | 1386 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, ssl_data_loc->root_privkey); |
613 | } | 1387 | pair_record_import_key_with_name(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, ssl_data_loc->host_privkey); |
1388 | |||
1389 | if (pair_record) | ||
1390 | plist_free(pair_record); | ||
614 | 1391 | ||
615 | debug_info("GnuTLS step 1..."); | 1392 | debug_info("GnuTLS step 1..."); |
616 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); | 1393 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); |
@@ -619,46 +1396,146 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
619 | debug_info("GnuTLS step 3..."); | 1396 | debug_info("GnuTLS step 3..."); |
620 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); | 1397 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); |
621 | debug_info("GnuTLS step 4 -- now handshaking..."); | 1398 | debug_info("GnuTLS step 4 -- now handshaking..."); |
622 | if (errno) | 1399 | if (errno) { |
623 | debug_info("WARN: errno says %s before handshake!", strerror(errno)); | 1400 | debug_info("WARNING: errno says %s before handshake!", strerror(errno)); |
624 | return_me = gnutls_handshake(ssl_data_loc->session); | 1401 | } |
1402 | |||
1403 | int return_me = 0; | ||
1404 | do { | ||
1405 | return_me = gnutls_handshake(ssl_data_loc->session); | ||
1406 | } while(return_me == GNUTLS_E_AGAIN || return_me == GNUTLS_E_INTERRUPTED); | ||
1407 | |||
625 | debug_info("GnuTLS handshake done..."); | 1408 | debug_info("GnuTLS handshake done..."); |
626 | 1409 | ||
627 | if (return_me != GNUTLS_E_SUCCESS) { | 1410 | if (return_me != GNUTLS_E_SUCCESS) { |
628 | internal_ssl_cleanup(ssl_data_loc); | 1411 | internal_ssl_cleanup(ssl_data_loc); |
629 | free(ssl_data_loc); | 1412 | free(ssl_data_loc); |
630 | debug_info("GnuTLS reported something wrong."); | 1413 | debug_info("GnuTLS reported something wrong: %s", gnutls_strerror(return_me)); |
631 | gnutls_perror(return_me); | ||
632 | debug_info("oh.. errno says %s", strerror(errno)); | 1414 | debug_info("oh.. errno says %s", strerror(errno)); |
633 | } else { | 1415 | } else { |
634 | connection->ssl_data = ssl_data_loc; | 1416 | connection->ssl_data = ssl_data_loc; |
635 | ret = IDEVICE_E_SUCCESS; | 1417 | ret = IDEVICE_E_SUCCESS; |
636 | debug_info("SSL mode enabled"); | 1418 | debug_info("SSL mode enabled"); |
637 | } | 1419 | } |
1420 | #elif defined(HAVE_MBEDTLS) | ||
1421 | key_data_t root_cert = { NULL, 0 }; | ||
1422 | key_data_t root_privkey = { NULL, 0 }; | ||
1423 | |||
1424 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
1425 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
1426 | |||
1427 | plist_free(pair_record); | ||
1428 | |||
1429 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
1430 | |||
1431 | mbedtls_ssl_init(&ssl_data_loc->ctx); | ||
1432 | mbedtls_ssl_config_init(&ssl_data_loc->config); | ||
1433 | mbedtls_entropy_init(&ssl_data_loc->entropy); | ||
1434 | mbedtls_ctr_drbg_init(&ssl_data_loc->ctr_drbg); | ||
1435 | |||
1436 | int r = mbedtls_ctr_drbg_seed(&ssl_data_loc->ctr_drbg, mbedtls_entropy_func, &ssl_data_loc->entropy, NULL, 0); | ||
1437 | if (r != 0) { | ||
1438 | debug_info("ERROR: [mbedtls] mbedtls_ctr_drbg_seed failed: %d", r); | ||
1439 | return ret; | ||
1440 | } | ||
1441 | |||
1442 | if (mbedtls_ssl_config_defaults(&ssl_data_loc->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { | ||
1443 | debug_info("ERROR: [mbedtls] Failed to set config defaults"); | ||
1444 | return ret; | ||
1445 | } | ||
1446 | |||
1447 | mbedtls_ssl_conf_rng(&ssl_data_loc->config, mbedtls_ctr_drbg_random, &ssl_data_loc->ctr_drbg); | ||
1448 | |||
1449 | mbedtls_ssl_conf_dbg(&ssl_data_loc->config, _mbedtls_log_cb, NULL); | ||
1450 | |||
1451 | mbedtls_ssl_conf_verify(&ssl_data_loc->config, cert_verify_cb, NULL); | ||
1452 | |||
1453 | mbedtls_ssl_setup(&ssl_data_loc->ctx, &ssl_data_loc->config); | ||
1454 | |||
1455 | mbedtls_ssl_set_bio(&ssl_data_loc->ctx, connection, (mbedtls_ssl_send_t*)&internal_ssl_write, (mbedtls_ssl_recv_t*)&internal_ssl_read, NULL); | ||
1456 | |||
1457 | mbedtls_x509_crt_init(&ssl_data_loc->certificate); | ||
1458 | |||
1459 | int crterr = mbedtls_x509_crt_parse(&ssl_data_loc->certificate, root_cert.data, root_cert.size); | ||
1460 | if (crterr < 0) { | ||
1461 | debug_info("ERROR: [mbedtls] parsing root cert failed: %d", crterr); | ||
1462 | return ret; | ||
1463 | } | ||
1464 | |||
1465 | mbedtls_ssl_conf_ca_chain(&ssl_data_loc->config, &ssl_data_loc->certificate, NULL); | ||
1466 | |||
1467 | mbedtls_pk_init(&ssl_data_loc->root_privkey); | ||
1468 | |||
1469 | #if MBEDTLS_VERSION_NUMBER >= 0x03000000 | ||
1470 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0, &_mbedtls_f_rng, NULL); | ||
1471 | #else | ||
1472 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0); | ||
1473 | #endif | ||
1474 | if (pkerr < 0) { | ||
1475 | debug_info("ERROR: [mbedtls] parsing private key failed: %d (size=%d)", pkerr, root_privkey.size); | ||
1476 | return ret; | ||
1477 | } | ||
1478 | |||
1479 | mbedtls_ssl_conf_own_cert(&ssl_data_loc->config, &ssl_data_loc->certificate, &ssl_data_loc->root_privkey); | ||
1480 | |||
1481 | int return_me = 0; | ||
1482 | do { | ||
1483 | return_me = mbedtls_ssl_handshake(&ssl_data_loc->ctx); | ||
1484 | } while (return_me == MBEDTLS_ERR_SSL_WANT_READ || return_me == MBEDTLS_ERR_SSL_WANT_WRITE); | ||
1485 | |||
1486 | if (return_me != 0) { | ||
1487 | debug_info("ERROR during SSL handshake: %d", return_me); | ||
1488 | internal_ssl_cleanup(ssl_data_loc); | ||
1489 | free(ssl_data_loc); | ||
1490 | } else { | ||
1491 | connection->ssl_data = ssl_data_loc; | ||
1492 | ret = IDEVICE_E_SUCCESS; | ||
1493 | debug_info("SSL mode enabled, %s, cipher: %s", mbedtls_ssl_get_version(&ssl_data_loc->ctx), mbedtls_ssl_get_ciphersuite(&ssl_data_loc->ctx)); | ||
1494 | debug_info("SSL mode enabled"); | ||
1495 | } | ||
1496 | #endif | ||
638 | return ret; | 1497 | return ret; |
639 | } | 1498 | } |
640 | 1499 | ||
641 | /** | ||
642 | * Disable SSL for the given connection. | ||
643 | * | ||
644 | * @param connection The connection to disable SSL for. | ||
645 | * | ||
646 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
647 | * is NULL. This function also returns IDEVICE_E_SUCCESS when SSL is not | ||
648 | * enabled and does no further error checking on cleanup. | ||
649 | */ | ||
650 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | 1500 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) |
651 | { | 1501 | { |
1502 | return idevice_connection_disable_bypass_ssl(connection, 0); | ||
1503 | } | ||
1504 | |||
1505 | idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass) | ||
1506 | { | ||
652 | if (!connection) | 1507 | if (!connection) |
653 | return IDEVICE_E_INVALID_ARG; | 1508 | return IDEVICE_E_INVALID_ARG; |
654 | if (!connection->ssl_data) { | 1509 | if (!connection->ssl_data) { |
655 | /* ignore if ssl is not enabled */ | 1510 | /* ignore if ssl is not enabled */ |
656 | return IDEVICE_E_SUCCESS; | 1511 | return IDEVICE_E_SUCCESS; |
657 | } | 1512 | } |
658 | 1513 | ||
659 | if (connection->ssl_data->session) { | 1514 | // some services require plain text communication after SSL handshake |
660 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | 1515 | // sending out SSL_shutdown will cause bytes |
1516 | if (!sslBypass) { | ||
1517 | #if defined(HAVE_OPENSSL) | ||
1518 | if (connection->ssl_data->session) { | ||
1519 | /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */ | ||
1520 | if (SSL_shutdown(connection->ssl_data->session) == 0) { | ||
1521 | /* Only try bidirectional shutdown if we know it can complete */ | ||
1522 | int ssl_error; | ||
1523 | if ((ssl_error = SSL_get_error(connection->ssl_data->session, 0)) == SSL_ERROR_NONE) { | ||
1524 | SSL_shutdown(connection->ssl_data->session); | ||
1525 | } else { | ||
1526 | debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error); | ||
1527 | } | ||
1528 | } | ||
1529 | } | ||
1530 | #elif defined(HAVE_GNUTLS) | ||
1531 | if (connection->ssl_data->session) { | ||
1532 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | ||
1533 | } | ||
1534 | #elif defined(HAVE_MBEDTLS) | ||
1535 | mbedtls_ssl_close_notify(&connection->ssl_data->ctx); | ||
1536 | #endif | ||
661 | } | 1537 | } |
1538 | |||
662 | internal_ssl_cleanup(connection->ssl_data); | 1539 | internal_ssl_cleanup(connection->ssl_data); |
663 | free(connection->ssl_data); | 1540 | free(connection->ssl_data); |
664 | connection->ssl_data = NULL; | 1541 | connection->ssl_data = NULL; |
@@ -667,4 +1544,3 @@ idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | |||
667 | 1544 | ||
668 | return IDEVICE_E_SUCCESS; | 1545 | return IDEVICE_E_SUCCESS; |
669 | } | 1546 | } |
670 | |||
diff --git a/src/idevice.h b/src/idevice.h index 231b3ab..dd72f9d 100644 --- a/src/idevice.h +++ b/src/idevice.h | |||
@@ -8,51 +8,97 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef IDEVICE_H | ||
22 | #define IDEVICE_H | ||
23 | 21 | ||
22 | #ifndef __DEVICE_H | ||
23 | #define __DEVICE_H | ||
24 | |||
25 | #ifdef HAVE_CONFIG_H | ||
26 | #include <config.h> | ||
27 | #endif | ||
28 | |||
29 | #if defined(HAVE_OPENSSL) | ||
30 | #include <openssl/ssl.h> | ||
31 | #elif defined(HAVE_GNUTLS) | ||
24 | #include <gnutls/gnutls.h> | 32 | #include <gnutls/gnutls.h> |
25 | #include <gnutls/x509.h> | 33 | #include <gnutls/x509.h> |
34 | #elif defined(HAVE_MBEDTLS) | ||
35 | #include <mbedtls/ssl.h> | ||
36 | #include <mbedtls/entropy.h> | ||
37 | #include <mbedtls/ctr_drbg.h> | ||
38 | #endif | ||
26 | 39 | ||
40 | #ifdef LIBIMOBILEDEVICE_STATIC | ||
41 | #define LIBIMOBILEDEVICE_API | ||
42 | #elif defined(_WIN32) | ||
43 | #define LIBIMOBILEDEVICE_API __declspec( dllexport ) | ||
44 | #else | ||
45 | #if __GNUC__ >= 4 | ||
46 | #define LIBIMOBILEDEVICE_API __attribute__((visibility("default"))) | ||
47 | #else | ||
48 | #define LIBIMOBILEDEVICE_API | ||
49 | #endif | ||
50 | #endif | ||
51 | |||
52 | #include "common/userpref.h" | ||
27 | #include "libimobiledevice/libimobiledevice.h" | 53 | #include "libimobiledevice/libimobiledevice.h" |
28 | 54 | ||
29 | enum connection_type { | 55 | #define DEVICE_VERSION(maj, min, patch) (((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF)) |
30 | CONNECTION_USBMUXD = 1 | 56 | |
31 | }; | 57 | #define DEVICE_CLASS_IPHONE 1 |
58 | #define DEVICE_CLASS_IPAD 2 | ||
59 | #define DEVICE_CLASS_IPOD 3 | ||
60 | #define DEVICE_CLASS_APPLETV 4 | ||
61 | #define DEVICE_CLASS_WATCH 5 | ||
62 | #define DEVICE_CLASS_UNKNOWN 255 | ||
32 | 63 | ||
33 | struct ssl_data_private { | 64 | struct ssl_data_private { |
65 | #if defined(HAVE_OPENSSL) | ||
66 | SSL *session; | ||
67 | SSL_CTX *ctx; | ||
68 | #elif defined(HAVE_GNUTLS) | ||
34 | gnutls_certificate_credentials_t certificate; | 69 | gnutls_certificate_credentials_t certificate; |
35 | gnutls_session_t session; | 70 | gnutls_session_t session; |
36 | gnutls_x509_privkey_t root_privkey; | 71 | gnutls_x509_privkey_t root_privkey; |
37 | gnutls_x509_crt_t root_cert; | 72 | gnutls_x509_crt_t root_cert; |
38 | gnutls_x509_privkey_t host_privkey; | 73 | gnutls_x509_privkey_t host_privkey; |
39 | gnutls_x509_crt_t host_cert; | 74 | gnutls_x509_crt_t host_cert; |
75 | #elif defined(HAVE_MBEDTLS) | ||
76 | mbedtls_ssl_context ctx; | ||
77 | mbedtls_ssl_config config; | ||
78 | mbedtls_entropy_context entropy; | ||
79 | mbedtls_ctr_drbg_context ctr_drbg; | ||
80 | mbedtls_x509_crt certificate; | ||
81 | mbedtls_pk_context root_privkey; | ||
82 | #endif | ||
40 | }; | 83 | }; |
41 | typedef struct ssl_data_private *ssl_data_t; | 84 | typedef struct ssl_data_private *ssl_data_t; |
42 | 85 | ||
43 | struct idevice_connection_private { | 86 | struct idevice_connection_private { |
44 | enum connection_type type; | 87 | idevice_t device; |
88 | enum idevice_connection_type type; | ||
45 | void *data; | 89 | void *data; |
46 | ssl_data_t ssl_data; | 90 | ssl_data_t ssl_data; |
91 | unsigned int ssl_recv_timeout; | ||
92 | idevice_error_t status; | ||
47 | }; | 93 | }; |
48 | 94 | ||
49 | struct idevice_private { | 95 | struct idevice_private { |
50 | char *uuid; | 96 | char *udid; |
51 | enum connection_type conn_type; | 97 | uint32_t mux_id; |
98 | enum idevice_connection_type conn_type; | ||
52 | void *conn_data; | 99 | void *conn_data; |
100 | int version; | ||
101 | int device_class; | ||
53 | }; | 102 | }; |
54 | 103 | ||
55 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection); | ||
56 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection); | ||
57 | |||
58 | #endif | 104 | #endif |
diff --git a/src/installation_proxy.c b/src/installation_proxy.c index 4a76dd2..ec19da0 100644 --- a/src/installation_proxy.c +++ b/src/installation_proxy.c | |||
@@ -2,63 +2,210 @@ | |||
2 | * installation_proxy.c | 2 | * installation_proxy.c |
3 | * com.apple.mobile.installation_proxy service implementation. | 3 | * com.apple.mobile.installation_proxy service implementation. |
4 | * | 4 | * |
5 | * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved. |
6 | * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved. | ||
6 | * | 7 | * |
7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 9 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 10 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 11 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 12 | * |
12 | * This library is distributed in the hope that it will be useful, | 13 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 16 | * Lesser General Public License for more details. |
16 | * | 17 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 18 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 19 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 21 | */ |
21 | 22 | ||
23 | #ifdef HAVE_CONFIG_H | ||
24 | #include <config.h> | ||
25 | #endif | ||
22 | #include <string.h> | 26 | #include <string.h> |
23 | #include <stdlib.h> | 27 | #include <stdlib.h> |
28 | #include <inttypes.h> | ||
24 | #include <unistd.h> | 29 | #include <unistd.h> |
25 | #include <plist/plist.h> | 30 | #include <plist/plist.h> |
26 | 31 | ||
27 | #include "installation_proxy.h" | 32 | #include "installation_proxy.h" |
28 | #include "property_list_service.h" | 33 | #include "property_list_service.h" |
29 | #include "debug.h" | 34 | #include "common/debug.h" |
35 | |||
36 | typedef enum { | ||
37 | INSTPROXY_COMMAND_TYPE_ASYNC, | ||
38 | INSTPROXY_COMMAND_TYPE_SYNC | ||
39 | } instproxy_command_type_t; | ||
30 | 40 | ||
31 | struct instproxy_status_data { | 41 | struct instproxy_status_data { |
32 | instproxy_client_t client; | 42 | instproxy_client_t client; |
43 | plist_t command; | ||
33 | instproxy_status_cb_t cbfunc; | 44 | instproxy_status_cb_t cbfunc; |
34 | char *operation; | ||
35 | void *user_data; | 45 | void *user_data; |
36 | }; | 46 | }; |
37 | 47 | ||
38 | /** | 48 | /** |
49 | * Converts an error string identifier to a instproxy_error_t value. | ||
50 | * Used internally to get correct error codes from a response. | ||
51 | * | ||
52 | * @param name The error name to convert. | ||
53 | * @param error_detail Pointer to store error detail text if available. The | ||
54 | * caller is reponsible for freeing the allocated buffer after use. If NULL | ||
55 | * is passed no error detail will be returned. | ||
56 | * | ||
57 | * @return A matching instproxy_error_t error code or | ||
58 | * INSTPROXY_E_UNKNOWN_ERROR otherwise. | ||
59 | */ | ||
60 | static instproxy_error_t instproxy_strtoerr(const char* name) | ||
61 | { | ||
62 | instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR; | ||
63 | |||
64 | if (strcmp(name, "AlreadyArchived") == 0) { | ||
65 | err = INSTPROXY_E_ALREADY_ARCHIVED; | ||
66 | } else if (strcmp(name, "APIInternalError") == 0) { | ||
67 | err = INSTPROXY_E_API_INTERNAL_ERROR; | ||
68 | } else if (strcmp(name, "ApplicationAlreadyInstalled") == 0) { | ||
69 | err = INSTPROXY_E_APPLICATION_ALREADY_INSTALLED; | ||
70 | } else if (strcmp(name, "ApplicationMoveFailed") == 0) { | ||
71 | err = INSTPROXY_E_APPLICATION_MOVE_FAILED; | ||
72 | } else if (strcmp(name, "ApplicationSINFCaptureFailed") == 0) { | ||
73 | err = INSTPROXY_E_APPLICATION_SINF_CAPTURE_FAILED; | ||
74 | } else if (strcmp(name, "ApplicationSandboxFailed") == 0) { | ||
75 | err = INSTPROXY_E_APPLICATION_SANDBOX_FAILED; | ||
76 | } else if (strcmp(name, "ApplicationVerificationFailed") == 0) { | ||
77 | err = INSTPROXY_E_APPLICATION_VERIFICATION_FAILED; | ||
78 | } else if (strcmp(name, "ArchiveDestructionFailed") == 0) { | ||
79 | err = INSTPROXY_E_ARCHIVE_DESTRUCTION_FAILED; | ||
80 | } else if (strcmp(name, "BundleVerificationFailed") == 0) { | ||
81 | err = INSTPROXY_E_BUNDLE_VERIFICATION_FAILED; | ||
82 | } else if (strcmp(name, "CarrierBundleCopyFailed") == 0) { | ||
83 | err = INSTPROXY_E_CARRIER_BUNDLE_COPY_FAILED; | ||
84 | } else if (strcmp(name, "CarrierBundleDirectoryCreationFailed") == 0) { | ||
85 | err = INSTPROXY_E_CARRIER_BUNDLE_DIRECTORY_CREATION_FAILED; | ||
86 | } else if (strcmp(name, "CarrierBundleMissingSupportedSIMs") == 0) { | ||
87 | err = INSTPROXY_E_CARRIER_BUNDLE_MISSING_SUPPORTED_SIMS; | ||
88 | } else if (strcmp(name, "CommCenterNotificationFailed") == 0) { | ||
89 | err = INSTPROXY_E_COMM_CENTER_NOTIFICATION_FAILED; | ||
90 | } else if (strcmp(name, "ContainerCreationFailed") == 0) { | ||
91 | err = INSTPROXY_E_CONTAINER_CREATION_FAILED; | ||
92 | } else if (strcmp(name, "ContainerP0wnFailed") == 0) { | ||
93 | err = INSTPROXY_E_CONTAINER_P0WN_FAILED; | ||
94 | } else if (strcmp(name, "ContainerRemovalFailed") == 0) { | ||
95 | err = INSTPROXY_E_CONTAINER_REMOVAL_FAILED; | ||
96 | } else if (strcmp(name, "EmbeddedProfileInstallFailed") == 0) { | ||
97 | err = INSTPROXY_E_EMBEDDED_PROFILE_INSTALL_FAILED; | ||
98 | } else if (strcmp(name, "ExecutableTwiddleFailed") == 0) { | ||
99 | err = INSTPROXY_E_EXECUTABLE_TWIDDLE_FAILED; | ||
100 | } else if (strcmp(name, "ExistenceCheckFailed") == 0) { | ||
101 | err = INSTPROXY_E_EXISTENCE_CHECK_FAILED; | ||
102 | } else if (strcmp(name, "InstallMapUpdateFailed") == 0) { | ||
103 | err = INSTPROXY_E_INSTALL_MAP_UPDATE_FAILED; | ||
104 | } else if (strcmp(name, "ManifestCaptureFailed") == 0) { | ||
105 | err = INSTPROXY_E_MANIFEST_CAPTURE_FAILED; | ||
106 | } else if (strcmp(name, "MapGenerationFailed") == 0) { | ||
107 | err = INSTPROXY_E_MAP_GENERATION_FAILED; | ||
108 | } else if (strcmp(name, "MissingBundleExecutable") == 0) { | ||
109 | err = INSTPROXY_E_MISSING_BUNDLE_EXECUTABLE; | ||
110 | } else if (strcmp(name, "MissingBundleIdentifier") == 0) { | ||
111 | err = INSTPROXY_E_MISSING_BUNDLE_IDENTIFIER; | ||
112 | } else if (strcmp(name, "MissingBundlePath") == 0) { | ||
113 | err = INSTPROXY_E_MISSING_BUNDLE_PATH; | ||
114 | } else if (strcmp(name, "MissingContainer") == 0) { | ||
115 | err = INSTPROXY_E_MISSING_CONTAINER; | ||
116 | } else if (strcmp(name, "NotificationFailed") == 0) { | ||
117 | err = INSTPROXY_E_NOTIFICATION_FAILED; | ||
118 | } else if (strcmp(name, "PackageExtractionFailed") == 0) { | ||
119 | err = INSTPROXY_E_PACKAGE_EXTRACTION_FAILED; | ||
120 | } else if (strcmp(name, "PackageInspectionFailed") == 0) { | ||
121 | err = INSTPROXY_E_PACKAGE_INSPECTION_FAILED; | ||
122 | } else if (strcmp(name, "PackageMoveFailed") == 0) { | ||
123 | err = INSTPROXY_E_PACKAGE_MOVE_FAILED; | ||
124 | } else if (strcmp(name, "PathConversionFailed") == 0) { | ||
125 | err = INSTPROXY_E_PATH_CONVERSION_FAILED; | ||
126 | } else if (strcmp(name, "RestoreContainerFailed") == 0) { | ||
127 | err = INSTPROXY_E_RESTORE_CONTAINER_FAILED; | ||
128 | } else if (strcmp(name, "SeatbeltProfileRemovalFailed") == 0) { | ||
129 | err = INSTPROXY_E_SEATBELT_PROFILE_REMOVAL_FAILED; | ||
130 | } else if (strcmp(name, "StageCreationFailed") == 0) { | ||
131 | err = INSTPROXY_E_STAGE_CREATION_FAILED; | ||
132 | } else if (strcmp(name, "SymlinkFailed") == 0) { | ||
133 | err = INSTPROXY_E_SYMLINK_FAILED; | ||
134 | } else if (strcmp(name, "UnknownCommand") == 0) { | ||
135 | err = INSTPROXY_E_UNKNOWN_COMMAND; | ||
136 | } else if (strcmp(name, "iTunesArtworkCaptureFailed") == 0) { | ||
137 | err = INSTPROXY_E_ITUNES_ARTWORK_CAPTURE_FAILED; | ||
138 | } else if (strcmp(name, "iTunesMetadataCaptureFailed") == 0) { | ||
139 | err = INSTPROXY_E_ITUNES_METADATA_CAPTURE_FAILED; | ||
140 | } else if (strcmp(name, "DeviceOSVersionTooLow") == 0) { | ||
141 | err = INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW; | ||
142 | } else if (strcmp(name, "DeviceFamilyNotSupported") == 0) { | ||
143 | err = INSTPROXY_E_DEVICE_FAMILY_NOT_SUPPORTED; | ||
144 | } else if (strcmp(name, "PackagePatchFailed") == 0) { | ||
145 | err = INSTPROXY_E_PACKAGE_PATCH_FAILED; | ||
146 | } else if (strcmp(name, "IncorrectArchitecture") == 0) { | ||
147 | err = INSTPROXY_E_INCORRECT_ARCHITECTURE; | ||
148 | } else if (strcmp(name, "PluginCopyFailed") == 0) { | ||
149 | err = INSTPROXY_E_PLUGIN_COPY_FAILED; | ||
150 | } else if (strcmp(name, "BreadcrumbFailed") == 0) { | ||
151 | err = INSTPROXY_E_BREADCRUMB_FAILED; | ||
152 | } else if (strcmp(name, "BreadcrumbUnlockFailed") == 0) { | ||
153 | err = INSTPROXY_E_BREADCRUMB_UNLOCK_FAILED; | ||
154 | } else if (strcmp(name, "GeoJSONCaptureFailed") == 0) { | ||
155 | err = INSTPROXY_E_GEOJSON_CAPTURE_FAILED; | ||
156 | } else if (strcmp(name, "NewsstandArtworkCaptureFailed") == 0) { | ||
157 | err = INSTPROXY_E_NEWSSTAND_ARTWORK_CAPTURE_FAILED; | ||
158 | } else if (strcmp(name, "MissingCommand") == 0) { | ||
159 | err = INSTPROXY_E_MISSING_COMMAND; | ||
160 | } else if (strcmp(name, "NotEntitled") == 0) { | ||
161 | err = INSTPROXY_E_NOT_ENTITLED; | ||
162 | } else if (strcmp(name, "MissingPackagePath") == 0) { | ||
163 | err = INSTPROXY_E_MISSING_PACKAGE_PATH; | ||
164 | } else if (strcmp(name, "MissingContainerPath") == 0) { | ||
165 | err = INSTPROXY_E_MISSING_CONTAINER_PATH; | ||
166 | } else if (strcmp(name, "MissingApplicationIdentifier") == 0) { | ||
167 | err = INSTPROXY_E_MISSING_APPLICATION_IDENTIFIER; | ||
168 | } else if (strcmp(name, "MissingAttributeValue") == 0) { | ||
169 | err = INSTPROXY_E_MISSING_ATTRIBUTE_VALUE; | ||
170 | } else if (strcmp(name, "LookupFailed") == 0) { | ||
171 | err = INSTPROXY_E_LOOKUP_FAILED; | ||
172 | } else if (strcmp(name, "DictCreationFailed") == 0) { | ||
173 | err = INSTPROXY_E_DICT_CREATION_FAILED; | ||
174 | } else if (strcmp(name, "InstallProhibited") == 0) { | ||
175 | err = INSTPROXY_E_INSTALL_PROHIBITED; | ||
176 | } else if (strcmp(name, "UninstallProhibited") == 0) { | ||
177 | err = INSTPROXY_E_UNINSTALL_PROHIBITED; | ||
178 | } else if (strcmp(name, "MissingBundleVersion") == 0) { | ||
179 | err = INSTPROXY_E_MISSING_BUNDLE_VERSION; | ||
180 | } | ||
181 | |||
182 | return err; | ||
183 | } | ||
184 | |||
185 | /** | ||
39 | * Locks an installation_proxy client, used for thread safety. | 186 | * Locks an installation_proxy client, used for thread safety. |
40 | * | 187 | * |
41 | * @param client The installation_proxy client to lock | 188 | * @param client The installation_proxy client to lock |
42 | */ | 189 | */ |
43 | static void instproxy_lock(instproxy_client_t client) | 190 | static void instproxy_lock(instproxy_client_t client) |
44 | { | 191 | { |
45 | debug_info("InstallationProxy: Locked"); | 192 | debug_info("Locked"); |
46 | g_mutex_lock(client->mutex); | 193 | mutex_lock(&client->mutex); |
47 | } | 194 | } |
48 | 195 | ||
49 | /** | 196 | /** |
50 | * Unlocks an installation_proxy client, used for thread safety. | 197 | * Unlocks an installation_proxy client, used for thread safety. |
51 | * | 198 | * |
52 | * @param client The installation_proxy client to lock | 199 | * @param client The installation_proxy client to lock |
53 | */ | 200 | */ |
54 | static void instproxy_unlock(instproxy_client_t client) | 201 | static void instproxy_unlock(instproxy_client_t client) |
55 | { | 202 | { |
56 | debug_info("InstallationProxy: Unlocked"); | 203 | debug_info("Unlocked"); |
57 | g_mutex_unlock(client->mutex); | 204 | mutex_unlock(&client->mutex); |
58 | } | 205 | } |
59 | 206 | ||
60 | /** | 207 | /** |
61 | * Convert a property_list_service_error_t value to an instproxy_error_t value. | 208 | * Converts a property_list_service_error_t value to an instproxy_error_t value. |
62 | * Used internally to get correct error codes. | 209 | * Used internally to get correct error codes. |
63 | * | 210 | * |
64 | * @param err A property_list_service_error_t error code | 211 | * @param err A property_list_service_error_t error code |
@@ -77,186 +224,80 @@ static instproxy_error_t instproxy_error(property_list_service_error_t err) | |||
77 | return INSTPROXY_E_PLIST_ERROR; | 224 | return INSTPROXY_E_PLIST_ERROR; |
78 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | 225 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: |
79 | return INSTPROXY_E_CONN_FAILED; | 226 | return INSTPROXY_E_CONN_FAILED; |
227 | case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: | ||
228 | return INSTPROXY_E_RECEIVE_TIMEOUT; | ||
80 | default: | 229 | default: |
81 | break; | 230 | break; |
82 | } | 231 | } |
83 | return INSTPROXY_E_UNKNOWN_ERROR; | 232 | return INSTPROXY_E_UNKNOWN_ERROR; |
84 | } | 233 | } |
85 | 234 | ||
86 | /** | 235 | instproxy_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 | */ | ||
97 | instproxy_error_t instproxy_client_new(idevice_t device, uint16_t port, instproxy_client_t *client) | ||
98 | { | 236 | { |
99 | /* makes sure thread environment is available */ | ||
100 | if (!g_thread_supported()) | ||
101 | g_thread_init(NULL); | ||
102 | |||
103 | if (!device) | ||
104 | return INSTPROXY_E_INVALID_ARG; | ||
105 | |||
106 | property_list_service_client_t plistclient = NULL; | 237 | property_list_service_client_t plistclient = NULL; |
107 | if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 238 | instproxy_error_t err = instproxy_error(property_list_service_client_new(device, service, &plistclient)); |
108 | return INSTPROXY_E_CONN_FAILED; | 239 | if (err != INSTPROXY_E_SUCCESS) { |
240 | return err; | ||
109 | } | 241 | } |
110 | 242 | ||
111 | instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); | 243 | instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); |
112 | client_loc->parent = plistclient; | 244 | client_loc->parent = plistclient; |
113 | client_loc->mutex = g_mutex_new(); | 245 | mutex_init(&client_loc->mutex); |
114 | client_loc->status_updater = NULL; | 246 | client_loc->receive_status_thread = THREAD_T_NULL; |
115 | 247 | ||
116 | *client = client_loc; | 248 | *client = client_loc; |
117 | return INSTPROXY_E_SUCCESS; | 249 | return INSTPROXY_E_SUCCESS; |
118 | } | 250 | } |
119 | 251 | ||
120 | /** | 252 | instproxy_error_t instproxy_client_start_service(idevice_t device, instproxy_client_t * client, const char* label) |
121 | * Disconnects an installation_proxy client from the device and frees up the | 253 | { |
122 | * installation_proxy client data. | 254 | instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR; |
123 | * | 255 | service_client_factory_start_service(device, INSTPROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(instproxy_client_new), &err); |
124 | * @param client The installation_proxy client to disconnect and free. | 256 | return err; |
125 | * | 257 | } |
126 | * @return INSTPROXY_E_SUCCESS on success | 258 | |
127 | * or INSTPROXY_E_INVALID_ARG if client is NULL. | ||
128 | */ | ||
129 | instproxy_error_t instproxy_client_free(instproxy_client_t client) | 259 | instproxy_error_t instproxy_client_free(instproxy_client_t client) |
130 | { | 260 | { |
131 | if (!client) | 261 | if (!client) |
132 | return INSTPROXY_E_INVALID_ARG; | 262 | return INSTPROXY_E_INVALID_ARG; |
133 | 263 | ||
134 | property_list_service_client_free(client->parent); | 264 | property_list_service_client_t parent = client->parent; |
135 | client->parent = NULL; | 265 | client->parent = NULL; |
136 | if (client->status_updater) { | 266 | if (client->receive_status_thread) { |
137 | debug_info("joining status_updater"); | 267 | debug_info("joining receive_status_thread"); |
138 | g_thread_join(client->status_updater); | 268 | thread_join(client->receive_status_thread); |
139 | } | 269 | thread_free(client->receive_status_thread); |
140 | if (client->mutex) { | 270 | client->receive_status_thread = THREAD_T_NULL; |
141 | g_mutex_free(client->mutex); | ||
142 | } | 271 | } |
272 | property_list_service_client_free(parent); | ||
273 | mutex_destroy(&client->mutex); | ||
143 | free(client); | 274 | free(client); |
144 | 275 | ||
145 | return INSTPROXY_E_SUCCESS; | 276 | return INSTPROXY_E_SUCCESS; |
146 | } | 277 | } |
147 | 278 | ||
148 | /** | 279 | /** |
149 | * Send a command with specified options to the device. | 280 | * Sends a command to the device. |
150 | * Only used internally. | 281 | * Only used internally. |
151 | * | 282 | * |
152 | * @param client The connected installation_proxy client. | 283 | * @param client The connected installation_proxy client. |
153 | * @param command The command to execute. Required. | 284 | * @param command The command to execute. Required. |
154 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | ||
155 | * @param appid The ApplicationIdentifier to add or NULL if not required. | ||
156 | * @param package_path The installation package path or NULL if not required. | ||
157 | * | 285 | * |
158 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | 286 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if |
159 | * an error occured. | 287 | * an error occurred. |
160 | */ | 288 | */ |
161 | static instproxy_error_t instproxy_send_command(instproxy_client_t client, const char *command, plist_t client_options, const char *appid, const char *package_path) | 289 | static instproxy_error_t instproxy_send_command(instproxy_client_t client, plist_t command) |
162 | { | 290 | { |
163 | if (!client || !command || (client_options && (plist_get_node_type(client_options) != PLIST_DICT))) | 291 | if (!client || !command) |
164 | return INSTPROXY_E_INVALID_ARG; | 292 | return INSTPROXY_E_INVALID_ARG; |
165 | 293 | ||
166 | plist_t dict = plist_new_dict(); | 294 | instproxy_error_t res = instproxy_error(property_list_service_send_xml_plist(client->parent, command)); |
167 | if (appid) { | ||
168 | plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid)); | ||
169 | } | ||
170 | if (client_options && (plist_dict_get_size(client_options) > 0)) { | ||
171 | plist_dict_insert_item(dict, "ClientOptions", plist_copy(client_options)); | ||
172 | } | ||
173 | plist_dict_insert_item(dict, "Command", plist_new_string(command)); | ||
174 | if (package_path) { | ||
175 | plist_dict_insert_item(dict, "PackagePath", plist_new_string(package_path)); | ||
176 | } | ||
177 | |||
178 | instproxy_error_t err = instproxy_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
179 | plist_free(dict); | ||
180 | return err; | ||
181 | } | ||
182 | 295 | ||
183 | /** | ||
184 | * List installed applications. This function runs synchronously. | ||
185 | * | ||
186 | * @param client The connected installation_proxy client | ||
187 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | ||
188 | * Valid client options include: | ||
189 | * "ApplicationType" -> "User" | ||
190 | * "ApplicationType" -> "System" | ||
191 | * @param result Pointer that will be set to a plist that will hold an array | ||
192 | * of PLIST_DICT holding information about the applications found. | ||
193 | * | ||
194 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | ||
195 | * an error occured. | ||
196 | */ | ||
197 | instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result) | ||
198 | { | ||
199 | if (!client || !client->parent || !result) | ||
200 | return INSTPROXY_E_INVALID_ARG; | ||
201 | |||
202 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
203 | |||
204 | instproxy_lock(client); | ||
205 | res = instproxy_send_command(client, "Browse", client_options, NULL, NULL); | ||
206 | if (res != INSTPROXY_E_SUCCESS) { | 296 | if (res != INSTPROXY_E_SUCCESS) { |
207 | debug_info("could not send plist"); | 297 | debug_info("could not send command plist, error %d", res); |
208 | goto leave_unlock; | 298 | return res; |
209 | } | ||
210 | |||
211 | int browsing = 0; | ||
212 | plist_t apps_array = plist_new_array(); | ||
213 | plist_t dict = NULL; | ||
214 | |||
215 | do { | ||
216 | browsing = 0; | ||
217 | dict = NULL; | ||
218 | res = instproxy_error(property_list_service_receive_plist(client->parent, &dict)); | ||
219 | if (res != INSTPROXY_E_SUCCESS) { | ||
220 | break; | ||
221 | } | ||
222 | if (dict) { | ||
223 | uint64_t i; | ||
224 | uint64_t current_amount = 0; | ||
225 | char *status = NULL; | ||
226 | plist_t camount = plist_dict_get_item(dict, "CurrentAmount"); | ||
227 | plist_t pstatus = plist_dict_get_item(dict, "Status"); | ||
228 | if (camount) { | ||
229 | plist_get_uint_val(camount, ¤t_amount); | ||
230 | } | ||
231 | if (current_amount > 0) { | ||
232 | plist_t current_list = plist_dict_get_item(dict, "CurrentList"); | ||
233 | for (i = 0; current_list && (i < current_amount); i++) { | ||
234 | plist_t item = plist_array_get_item(current_list, i); | ||
235 | plist_array_append_item(apps_array, plist_copy(item)); | ||
236 | } | ||
237 | } | ||
238 | if (pstatus) { | ||
239 | plist_get_string_val(pstatus, &status); | ||
240 | } | ||
241 | if (status) { | ||
242 | if (!strcmp(status, "BrowsingApplications")) { | ||
243 | browsing = 1; | ||
244 | } else if (!strcmp(status, "Complete")) { | ||
245 | debug_info("Browsing applications completed"); | ||
246 | res = INSTPROXY_E_SUCCESS; | ||
247 | } | ||
248 | free(status); | ||
249 | } | ||
250 | plist_free(dict); | ||
251 | } | ||
252 | } while (browsing); | ||
253 | |||
254 | if (res == INSTPROXY_E_SUCCESS) { | ||
255 | *result = apps_array; | ||
256 | } | 299 | } |
257 | 300 | ||
258 | leave_unlock: | ||
259 | instproxy_unlock(client); | ||
260 | return res; | 301 | return res; |
261 | } | 302 | } |
262 | 303 | ||
@@ -269,78 +310,99 @@ leave_unlock: | |||
269 | * | 310 | * |
270 | * @param client The connected installation proxy client | 311 | * @param client The connected installation proxy client |
271 | * @param status_cb Pointer to a callback function or NULL | 312 | * @param status_cb Pointer to a callback function or NULL |
272 | * @param operation Operation name. Will be passed to the callback function | 313 | * @param command Operation specificiation in plist. Will be passed to the |
273 | * in async mode or shown in debug messages in sync mode. | 314 | * status_cb callback. |
274 | * @param user_data Callback data passed to status_cb. | 315 | * @param user_data Callback data passed to status_cb. |
275 | */ | 316 | */ |
276 | static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) | 317 | static instproxy_error_t instproxy_receive_status_loop(instproxy_client_t client, plist_t command, instproxy_status_cb_t status_cb, void *user_data) |
277 | { | 318 | { |
278 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | 319 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
279 | int ok = 1; | 320 | int complete = 0; |
280 | plist_t dict = NULL; | 321 | plist_t node = NULL; |
322 | char* command_name = NULL; | ||
323 | char* status_name = NULL; | ||
324 | char* error_name = NULL; | ||
325 | char* error_description = NULL; | ||
326 | uint64_t error_code = 0; | ||
327 | #ifndef STRIP_DEBUG_CODE | ||
328 | int percent_complete = 0; | ||
329 | #endif | ||
330 | |||
331 | instproxy_command_get_name(command, &command_name); | ||
281 | 332 | ||
282 | do { | 333 | do { |
334 | /* receive status response */ | ||
283 | instproxy_lock(client); | 335 | instproxy_lock(client); |
284 | res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 30000)); | 336 | res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000)); |
285 | instproxy_unlock(client); | 337 | instproxy_unlock(client); |
286 | if (res != INSTPROXY_E_SUCCESS) { | 338 | |
339 | /* break out if we have a communication problem */ | ||
340 | if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) { | ||
287 | debug_info("could not receive plist, error %d", res); | 341 | debug_info("could not receive plist, error %d", res); |
288 | break; | 342 | break; |
289 | } | 343 | } |
290 | if (dict) { | 344 | |
291 | /* invoke callback function */ | 345 | /* parse status response */ |
292 | if (status_cb) { | 346 | if (node) { |
293 | status_cb(operation, dict, user_data); | 347 | /* check status for possible error to allow reporting it and aborting it gracefully */ |
348 | res = instproxy_status_get_error(node, &error_name, &error_description, &error_code); | ||
349 | if (res != INSTPROXY_E_SUCCESS) { | ||
350 | debug_info("command: %s, error %d, code 0x%08"PRIx64", name: %s, description: \"%s\"", command_name, res, error_code, error_name, error_description ? error_description: "N/A"); | ||
351 | complete = 1; | ||
352 | } | ||
353 | |||
354 | if (error_name) { | ||
355 | free(error_name); | ||
356 | error_name = NULL; | ||
294 | } | 357 | } |
295 | /* check for 'Error', so we can abort cleanly */ | 358 | |
296 | plist_t err = plist_dict_get_item(dict, "Error"); | 359 | if (error_description) { |
297 | if (err) { | 360 | free(error_description); |
361 | error_description = NULL; | ||
362 | } | ||
363 | |||
364 | /* check status from response */ | ||
365 | instproxy_status_get_name(node, &status_name); | ||
366 | if (!status_name) { | ||
367 | debug_info("ignoring message without Status key:"); | ||
368 | debug_plist(node); | ||
369 | } else { | ||
370 | if (!strcmp(status_name, "Complete")) { | ||
371 | complete = 1; | ||
372 | } else { | ||
373 | res = INSTPROXY_E_OP_IN_PROGRESS; | ||
374 | } | ||
298 | #ifndef STRIP_DEBUG_CODE | 375 | #ifndef STRIP_DEBUG_CODE |
299 | char *err_msg = NULL; | 376 | percent_complete = -1; |
300 | plist_get_string_val(err, &err_msg); | 377 | instproxy_status_get_percent_complete(node, &percent_complete); |
301 | if (err_msg) { | 378 | if (percent_complete >= 0) { |
302 | debug_info("(%s): ERROR: %s", operation, err_msg); | 379 | debug_info("command: %s, status: %s, percent (%d%%)", command_name, status_name, percent_complete); |
303 | free(err_msg); | 380 | } else { |
381 | debug_info("command: %s, status: %s", command_name, status_name); | ||
304 | } | 382 | } |
305 | #endif | 383 | #endif |
306 | ok = 0; | 384 | free(status_name); |
307 | res = INSTPROXY_E_OP_FAILED; | 385 | status_name = NULL; |
308 | } | 386 | } |
309 | /* get 'Status' */ | 387 | |
310 | plist_t status = plist_dict_get_item(dict, "Status"); | 388 | /* invoke status callback function */ |
311 | if (status) { | 389 | if (status_cb) { |
312 | char *status_msg = NULL; | 390 | status_cb(command, node, user_data); |
313 | plist_get_string_val(status, &status_msg); | ||
314 | if (status_msg) { | ||
315 | if (!strcmp(status_msg, "Complete")) { | ||
316 | ok = 0; | ||
317 | res = INSTPROXY_E_SUCCESS; | ||
318 | } | ||
319 | #ifndef STRIP_DEBUG_CODE | ||
320 | plist_t npercent = plist_dict_get_item(dict, "PercentComplete"); | ||
321 | if (npercent) { | ||
322 | uint64_t val = 0; | ||
323 | int percent; | ||
324 | plist_get_uint_val(npercent, &val); | ||
325 | percent = val; | ||
326 | debug_info("(%s): %s (%d%%)", operation, status_msg, percent); | ||
327 | } else { | ||
328 | debug_info("(%s): %s", operation, status_msg); | ||
329 | } | ||
330 | #endif | ||
331 | free(status_msg); | ||
332 | } | ||
333 | } | 391 | } |
334 | plist_free(dict); | 392 | |
335 | dict = NULL; | 393 | plist_free(node); |
394 | node = NULL; | ||
336 | } | 395 | } |
337 | } while (ok && client->parent); | 396 | } while (!complete && client->parent); |
397 | |||
398 | if (command_name) | ||
399 | free(command_name); | ||
338 | 400 | ||
339 | return res; | 401 | return res; |
340 | } | 402 | } |
341 | 403 | ||
342 | /** | 404 | /** |
343 | * Internally used status updater thread function that will call the specified | 405 | * Internally used "receive status" thread function that will call the specified |
344 | * callback function when status update messages (or error messages) are | 406 | * callback function when status update messages (or error messages) are |
345 | * received. | 407 | * received. |
346 | * | 408 | * |
@@ -349,20 +411,27 @@ static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, | |||
349 | * | 411 | * |
350 | * @return Always NULL. | 412 | * @return Always NULL. |
351 | */ | 413 | */ |
352 | static gpointer instproxy_status_updater(gpointer arg) | 414 | static void* instproxy_receive_status_loop_thread(void* arg) |
353 | { | 415 | { |
354 | struct instproxy_status_data *data = (struct instproxy_status_data*)arg; | 416 | struct instproxy_status_data *data = (struct instproxy_status_data*)arg; |
355 | 417 | ||
356 | /* run until the operation is complete or an error occurs */ | 418 | /* run until the command is complete or an error occurs */ |
357 | (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation, data->user_data); | 419 | (void)instproxy_receive_status_loop(data->client, data->command, data->cbfunc, data->user_data); |
358 | 420 | ||
359 | /* cleanup */ | 421 | /* cleanup */ |
360 | instproxy_lock(data->client); | 422 | instproxy_lock(data->client); |
423 | |||
361 | debug_info("done, cleaning up."); | 424 | debug_info("done, cleaning up."); |
362 | if (data->operation) { | 425 | |
363 | free(data->operation); | 426 | if (data->command) { |
427 | plist_free(data->command); | ||
428 | } | ||
429 | |||
430 | if (data->client->receive_status_thread) { | ||
431 | thread_free(data->client->receive_status_thread); | ||
432 | data->client->receive_status_thread = THREAD_T_NULL; | ||
364 | } | 433 | } |
365 | data->client->status_updater = NULL; | 434 | |
366 | instproxy_unlock(data->client); | 435 | instproxy_unlock(data->client); |
367 | free(data); | 436 | free(data); |
368 | 437 | ||
@@ -370,379 +439,493 @@ static gpointer instproxy_status_updater(gpointer arg) | |||
370 | } | 439 | } |
371 | 440 | ||
372 | /** | 441 | /** |
373 | * Internally used helper function that creates a status updater thread which | 442 | * Internally used helper function that creates a "receive status" thread which |
374 | * will call the passed callback function when status updates occur. | 443 | * will call the passed callback function when a status is received. |
375 | * If status_cb is NULL no thread will be created, but the operation will | 444 | * |
376 | * run synchronously until it completes or an error occurs. | 445 | * If async is 0 no thread will be created and the command will run |
446 | * synchronously until it completes or an error occurs. | ||
377 | * | 447 | * |
378 | * @param client The connected installation proxy client | 448 | * @param client The connected installation proxy client |
379 | * @param status_cb Pointer to a callback function or NULL | 449 | * @param command Operation name. Will be passed to the callback function |
380 | * @param operation Operation name. Will be passed to the callback function | ||
381 | * in async mode or shown in debug messages in sync mode. | 450 | * in async mode or shown in debug messages in sync mode. |
451 | * @param async A boolean indicating if receive loop should be run | ||
452 | * asynchronously or block. | ||
453 | * @param status_cb Pointer to a callback function or NULL. | ||
382 | * @param user_data Callback data passed to status_cb. | 454 | * @param user_data Callback data passed to status_cb. |
383 | * | 455 | * |
384 | * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or | 456 | * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or |
385 | * when the operation completed successfully (sync). | 457 | * when the command completed successfully (sync). |
386 | * An INSTPROXY_E_* error value is returned if an error occured. | 458 | * An INSTPROXY_E_* error value is returned if an error occurred. |
387 | */ | 459 | */ |
388 | static instproxy_error_t instproxy_create_status_updater(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) | 460 | static instproxy_error_t instproxy_receive_status_loop_with_callback(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data) |
389 | { | 461 | { |
462 | if (!client || !client->parent || !command) { | ||
463 | return INSTPROXY_E_INVALID_ARG; | ||
464 | } | ||
465 | |||
466 | if (client->receive_status_thread) { | ||
467 | return INSTPROXY_E_OP_IN_PROGRESS; | ||
468 | } | ||
469 | |||
390 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | 470 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
391 | if (status_cb) { | 471 | if (async == INSTPROXY_COMMAND_TYPE_ASYNC) { |
392 | /* async mode */ | 472 | /* async mode */ |
393 | struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); | 473 | struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); |
394 | if (data) { | 474 | if (data) { |
395 | data->client = client; | 475 | data->client = client; |
476 | data->command = plist_copy(command); | ||
396 | data->cbfunc = status_cb; | 477 | data->cbfunc = status_cb; |
397 | data->operation = strdup(operation); | ||
398 | data->user_data = user_data; | 478 | data->user_data = user_data; |
399 | 479 | ||
400 | client->status_updater = g_thread_create(instproxy_status_updater, data, TRUE, NULL); | 480 | if (thread_new(&client->receive_status_thread, instproxy_receive_status_loop_thread, data) == 0) { |
401 | if (client->status_updater) { | ||
402 | res = INSTPROXY_E_SUCCESS; | 481 | res = INSTPROXY_E_SUCCESS; |
403 | } | 482 | } |
404 | } | 483 | } |
405 | } else { | 484 | } else { |
406 | /* sync mode */ | 485 | /* sync mode as a fallback */ |
407 | res = instproxy_perform_operation(client, NULL, operation, NULL); | 486 | res = instproxy_receive_status_loop(client, command, status_cb, user_data); |
408 | } | 487 | } |
488 | |||
409 | return res; | 489 | return res; |
410 | } | 490 | } |
411 | 491 | ||
412 | |||
413 | /** | 492 | /** |
414 | * Internal function used by instproxy_install and instproxy_upgrade. | 493 | * Internal core function to send a command and process the response. |
415 | * | 494 | * |
416 | * @param client The connected installation_proxy client | 495 | * @param client The connected installation_proxy client |
417 | * @param pkg_path Path of the installation package (inside the AFC jail) | 496 | * @param command The command specification dictionary. |
418 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | 497 | * @param async A boolean indicating whether the receive loop should be run |
419 | * @param status_cb Callback function for progress and status information. If | 498 | * asynchronously or block until completing the command. |
420 | * NULL is passed, this function will run synchronously. | 499 | * @param status_cb Callback function to call if a command status is received. |
421 | * @param command The command to execute. | ||
422 | * @param user_data Callback data passed to status_cb. | 500 | * @param user_data Callback data passed to status_cb. |
423 | * | 501 | * |
424 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | 502 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if |
425 | * an error occured. | 503 | * an error occurred. |
426 | */ | 504 | */ |
427 | static 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) | 505 | static instproxy_error_t instproxy_perform_command(instproxy_client_t client, plist_t command, instproxy_command_type_t async, instproxy_status_cb_t status_cb, void *user_data) |
428 | { | 506 | { |
429 | if (!client || !client->parent || !pkg_path) { | 507 | if (!client || !client->parent || !command) { |
430 | return INSTPROXY_E_INVALID_ARG; | 508 | return INSTPROXY_E_INVALID_ARG; |
431 | } | 509 | } |
432 | if (client->status_updater) { | 510 | |
511 | if (client->receive_status_thread) { | ||
433 | return INSTPROXY_E_OP_IN_PROGRESS; | 512 | return INSTPROXY_E_OP_IN_PROGRESS; |
434 | } | 513 | } |
435 | 514 | ||
515 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
516 | |||
517 | /* send command */ | ||
436 | instproxy_lock(client); | 518 | instproxy_lock(client); |
437 | instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path); | 519 | res = instproxy_send_command(client, command); |
438 | instproxy_unlock(client); | 520 | instproxy_unlock(client); |
439 | 521 | ||
440 | if (res != INSTPROXY_E_SUCCESS) { | 522 | /* loop until status or error is received */ |
441 | debug_info("could not send plist, error %d", res); | 523 | res = instproxy_receive_status_loop_with_callback(client, command, async, status_cb, user_data); |
442 | return res; | ||
443 | } | ||
444 | 524 | ||
445 | return instproxy_create_status_updater(client, status_cb, command, user_data); | 525 | return res; |
446 | } | 526 | } |
447 | 527 | ||
448 | /** | 528 | instproxy_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 | */ | ||
472 | instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
473 | { | 529 | { |
474 | return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Install", user_data); | 530 | if (!client || !client->parent || !status_cb) |
531 | return INSTPROXY_E_INVALID_ARG; | ||
532 | |||
533 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
534 | |||
535 | plist_t command = plist_new_dict(); | ||
536 | plist_dict_set_item(command, "Command", plist_new_string("Browse")); | ||
537 | if (client_options) | ||
538 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
539 | |||
540 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, (void*)user_data); | ||
541 | |||
542 | plist_free(command); | ||
543 | |||
544 | return res; | ||
475 | } | 545 | } |
476 | 546 | ||
477 | /** | 547 | static 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 | */ | ||
503 | instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
504 | { | 548 | { |
505 | return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Upgrade", user_data); | 549 | plist_t *result_array = (plist_t*)user_data; |
550 | uint64_t current_amount = 0; | ||
551 | plist_t current_list = NULL; | ||
552 | uint64_t i; | ||
553 | |||
554 | instproxy_status_get_current_list(status, NULL, NULL, ¤t_amount, ¤t_list); | ||
555 | |||
556 | debug_info("current_amount: %d", current_amount); | ||
557 | |||
558 | if (current_amount > 0) { | ||
559 | for (i = 0; current_list && (i < current_amount); i++) { | ||
560 | plist_t item = plist_array_get_item(current_list, i); | ||
561 | plist_array_append_item(*result_array, plist_copy(item)); | ||
562 | } | ||
563 | } | ||
564 | |||
565 | if (current_list) | ||
566 | plist_free(current_list); | ||
506 | } | 567 | } |
507 | 568 | ||
508 | /** | 569 | instproxy_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 | */ | ||
527 | instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
528 | { | 570 | { |
529 | if (!client || !client->parent || !appid) { | 571 | if (!client || !client->parent || !result) |
530 | return INSTPROXY_E_INVALID_ARG; | 572 | return INSTPROXY_E_INVALID_ARG; |
531 | } | ||
532 | |||
533 | if (client->status_updater) { | ||
534 | return INSTPROXY_E_OP_IN_PROGRESS; | ||
535 | } | ||
536 | 573 | ||
537 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | 574 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
538 | plist_t dict = plist_new_dict(); | ||
539 | plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid)); | ||
540 | plist_dict_insert_item(dict, "Command", plist_new_string("Uninstall")); | ||
541 | 575 | ||
542 | instproxy_lock(client); | 576 | plist_t result_array = plist_new_array(); |
543 | res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL); | ||
544 | instproxy_unlock(client); | ||
545 | 577 | ||
546 | plist_free(dict); | 578 | plist_t command = plist_new_dict(); |
579 | plist_dict_set_item(command, "Command", plist_new_string("Browse")); | ||
580 | if (client_options) | ||
581 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
547 | 582 | ||
548 | if (res != INSTPROXY_E_SUCCESS) { | 583 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_append_current_list_to_result_cb, (void*)&result_array); |
549 | debug_info("could not send plist, error %d", res); | 584 | |
550 | return res; | 585 | if (res == INSTPROXY_E_SUCCESS) { |
586 | *result = result_array; | ||
587 | } else { | ||
588 | plist_free(result_array); | ||
551 | } | 589 | } |
552 | 590 | ||
553 | return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data); | 591 | plist_free(command); |
592 | |||
593 | return res; | ||
554 | } | 594 | } |
555 | 595 | ||
556 | /** | 596 | static void instproxy_copy_lookup_result_cb(plist_t command, plist_t status, void *user_data) |
557 | * List archived applications. This function runs synchronously. | 597 | { |
558 | * | 598 | plist_t* result = (plist_t*)user_data; |
559 | * @see instproxy_archive | 599 | |
560 | * | 600 | plist_t node = plist_dict_get_item(status, "LookupResult"); |
561 | * @param client The connected installation_proxy client | 601 | if (node) { |
562 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | 602 | *result = plist_copy(node); |
563 | * Currently there are no known client options, so pass NULL here. | 603 | } |
564 | * @param result Pointer that will be set to a plist containing a PLIST_DICT | 604 | } |
565 | * holding information about the archived applications found. | 605 | |
566 | * | 606 | instproxy_error_t instproxy_lookup(instproxy_client_t client, const char** appids, plist_t client_options, plist_t *result) |
567 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | ||
568 | * an error occured. | ||
569 | */ | ||
570 | instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) | ||
571 | { | 607 | { |
608 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
609 | int i = 0; | ||
610 | plist_t lookup_result = NULL; | ||
611 | plist_t command = NULL; | ||
612 | plist_t appid_array = NULL; | ||
613 | plist_t node = NULL; | ||
614 | |||
572 | if (!client || !client->parent || !result) | 615 | if (!client || !client->parent || !result) |
573 | return INSTPROXY_E_INVALID_ARG; | 616 | return INSTPROXY_E_INVALID_ARG; |
574 | 617 | ||
575 | instproxy_lock(client); | 618 | command = plist_new_dict(); |
576 | instproxy_error_t res = instproxy_send_command(client, "LookupArchives", client_options, NULL, NULL); | 619 | plist_dict_set_item(command, "Command", plist_new_string("Lookup")); |
620 | if (client_options) { | ||
621 | node = plist_copy(client_options); | ||
622 | } else if (appids) { | ||
623 | node = plist_new_dict(); | ||
624 | } | ||
577 | 625 | ||
578 | if (res != INSTPROXY_E_SUCCESS) { | 626 | /* add bundle identifiers to client options */ |
579 | debug_info("could not send plist, error %d", res); | 627 | if (appids) { |
580 | goto leave_unlock; | 628 | appid_array = plist_new_array(); |
629 | while (appids[i]) { | ||
630 | plist_array_append_item(appid_array, plist_new_string(appids[i])); | ||
631 | i++; | ||
632 | } | ||
633 | plist_dict_set_item(node, "BundleIDs", appid_array); | ||
581 | } | 634 | } |
582 | 635 | ||
583 | res = instproxy_error(property_list_service_receive_plist(client->parent, result)); | 636 | if (node) { |
584 | if (res != INSTPROXY_E_SUCCESS) { | 637 | plist_dict_set_item(command, "ClientOptions", node); |
585 | debug_info("could not receive plist, error %d", res); | ||
586 | goto leave_unlock; | ||
587 | } | 638 | } |
588 | 639 | ||
589 | res = INSTPROXY_E_SUCCESS; | 640 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result); |
641 | |||
642 | if (res == INSTPROXY_E_SUCCESS) { | ||
643 | *result = lookup_result; | ||
644 | } else { | ||
645 | plist_free(lookup_result); | ||
646 | } | ||
647 | |||
648 | plist_free(command); | ||
649 | |||
650 | return res; | ||
651 | } | ||
652 | |||
653 | instproxy_error_t instproxy_install(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
654 | { | ||
655 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
656 | |||
657 | plist_t command = plist_new_dict(); | ||
658 | plist_dict_set_item(command, "Command", plist_new_string("Install")); | ||
659 | if (client_options) | ||
660 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
661 | plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path)); | ||
662 | |||
663 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
664 | |||
665 | plist_free(command); | ||
666 | |||
667 | return res; | ||
668 | } | ||
669 | |||
670 | instproxy_error_t instproxy_upgrade(instproxy_client_t client, const char *pkg_path, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
671 | { | ||
672 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
673 | |||
674 | plist_t command = plist_new_dict(); | ||
675 | plist_dict_set_item(command, "Command", plist_new_string("Upgrade")); | ||
676 | if (client_options) | ||
677 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
678 | plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path)); | ||
679 | |||
680 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
681 | |||
682 | plist_free(command); | ||
683 | |||
684 | return res; | ||
685 | } | ||
686 | |||
687 | instproxy_error_t instproxy_uninstall(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
688 | { | ||
689 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
690 | |||
691 | plist_t command = plist_new_dict(); | ||
692 | plist_dict_set_item(command, "Command", plist_new_string("Uninstall")); | ||
693 | if (client_options) | ||
694 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
695 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
696 | |||
697 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
698 | |||
699 | plist_free(command); | ||
700 | |||
701 | return res; | ||
702 | } | ||
703 | |||
704 | instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) | ||
705 | { | ||
706 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
707 | |||
708 | plist_t command = plist_new_dict(); | ||
709 | plist_dict_set_item(command, "Command", plist_new_string("LookupArchives")); | ||
710 | if (client_options) | ||
711 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
712 | |||
713 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)result); | ||
714 | |||
715 | plist_free(command); | ||
590 | 716 | ||
591 | leave_unlock: | ||
592 | instproxy_unlock(client); | ||
593 | return res; | 717 | return res; |
594 | } | 718 | } |
595 | 719 | ||
596 | /** | ||
597 | * Archive an application on the device. | ||
598 | * This function tells the device to make an archive of the specified | ||
599 | * application. This results in the device creating a ZIP archive in the | ||
600 | * 'ApplicationArchives' directory and uninstalling the application. | ||
601 | * | ||
602 | * @param client The connected installation proxy client | ||
603 | * @param appid ApplicationIdentifier of the app to archive. | ||
604 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | ||
605 | * Valid options include: | ||
606 | * "SkipUninstall" -> Boolean | ||
607 | * "ArchiveType" -> "ApplicationOnly" | ||
608 | * @param status_cb Callback function for progress and status information. If | ||
609 | * NULL is passed, this function will run synchronously. | ||
610 | * @param user_data Callback data passed to status_cb. | ||
611 | * | ||
612 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | ||
613 | * an error occured. | ||
614 | * | ||
615 | * @note If a callback function is given (async mode), this function returns | ||
616 | * INSTPROXY_E_SUCCESS immediately if the status updater thread has been | ||
617 | * created successfully; any error occuring during the operation has to be | ||
618 | * handled inside the specified callback function. | ||
619 | */ | ||
620 | instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | 720 | instproxy_error_t instproxy_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) |
621 | { | 721 | { |
622 | if (!client || !client->parent || !appid) | 722 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
723 | |||
724 | plist_t command = plist_new_dict(); | ||
725 | plist_dict_set_item(command, "Command", plist_new_string("Archive")); | ||
726 | if (client_options) | ||
727 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
728 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
729 | |||
730 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
731 | |||
732 | plist_free(command); | ||
733 | |||
734 | return res; | ||
735 | } | ||
736 | |||
737 | instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
738 | { | ||
739 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
740 | |||
741 | plist_t command = plist_new_dict(); | ||
742 | plist_dict_set_item(command, "Command", plist_new_string("Restore")); | ||
743 | if (client_options) | ||
744 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
745 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
746 | |||
747 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
748 | |||
749 | plist_free(command); | ||
750 | |||
751 | return res; | ||
752 | } | ||
753 | |||
754 | instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
755 | { | ||
756 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
757 | |||
758 | plist_t command = plist_new_dict(); | ||
759 | plist_dict_set_item(command, "Command", plist_new_string("RemoveArchive")); | ||
760 | if (client_options) | ||
761 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
762 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
763 | |||
764 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
765 | |||
766 | plist_free(command); | ||
767 | |||
768 | return res; | ||
769 | } | ||
770 | |||
771 | instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, const char** capabilities, plist_t client_options, plist_t *result) | ||
772 | { | ||
773 | if (!client || !capabilities || !result) | ||
623 | return INSTPROXY_E_INVALID_ARG; | 774 | return INSTPROXY_E_INVALID_ARG; |
624 | 775 | ||
625 | if (client->status_updater) { | 776 | plist_t lookup_result = NULL; |
626 | return INSTPROXY_E_OP_IN_PROGRESS; | 777 | |
778 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
779 | |||
780 | plist_t command = plist_new_dict(); | ||
781 | plist_dict_set_item(command, "Command", plist_new_string("CheckCapabilitiesMatch")); | ||
782 | if (client_options) | ||
783 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
784 | |||
785 | if (capabilities) { | ||
786 | int i = 0; | ||
787 | plist_t capabilities_array = plist_new_array(); | ||
788 | while (capabilities[i]) { | ||
789 | plist_array_append_item(capabilities_array, plist_new_string(capabilities[i])); | ||
790 | i++; | ||
791 | } | ||
792 | plist_dict_set_item(command, "Capabilities", capabilities_array); | ||
627 | } | 793 | } |
628 | 794 | ||
629 | instproxy_lock(client); | 795 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result); |
630 | instproxy_error_t res = instproxy_send_command(client, "Archive", client_options, appid, NULL); | ||
631 | instproxy_unlock(client); | ||
632 | 796 | ||
633 | if (res != INSTPROXY_E_SUCCESS) { | 797 | if (res == INSTPROXY_E_SUCCESS) { |
634 | debug_info("could not send plist, error %d", res); | 798 | *result = lookup_result; |
635 | return res; | 799 | } else { |
800 | plist_free(lookup_result); | ||
636 | } | 801 | } |
637 | return instproxy_create_status_updater(client, status_cb, "Archive", user_data); | 802 | |
803 | plist_free(command); | ||
804 | |||
805 | return res; | ||
638 | } | 806 | } |
639 | 807 | ||
640 | /** | 808 | instproxy_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 | */ | ||
661 | instproxy_error_t instproxy_restore(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
662 | { | 809 | { |
663 | if (!client || !client->parent || !appid) | 810 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
811 | |||
812 | if (!status || !name) | ||
664 | return INSTPROXY_E_INVALID_ARG; | 813 | return INSTPROXY_E_INVALID_ARG; |
665 | 814 | ||
666 | if (client->status_updater) { | 815 | plist_t node = plist_dict_get_item(status, "Error"); |
667 | return INSTPROXY_E_OP_IN_PROGRESS; | 816 | if (node) { |
817 | plist_get_string_val(node, name); | ||
818 | } else { | ||
819 | /* no error here */ | ||
820 | res = INSTPROXY_E_SUCCESS; | ||
668 | } | 821 | } |
669 | 822 | ||
670 | instproxy_lock(client); | 823 | if (code != NULL) { |
671 | instproxy_error_t res = instproxy_send_command(client, "Restore", client_options, appid, NULL); | 824 | *code = 0; |
672 | instproxy_unlock(client); | 825 | node = plist_dict_get_item(status, "ErrorDetail"); |
826 | if (node) { | ||
827 | plist_get_uint_val(node, code); | ||
828 | *code &= 0xffffffff; | ||
829 | } | ||
830 | } | ||
673 | 831 | ||
674 | if (res != INSTPROXY_E_SUCCESS) { | 832 | if (description != NULL) { |
675 | debug_info("could not send plist, error %d", res); | 833 | node = plist_dict_get_item(status, "ErrorDescription"); |
676 | return res; | 834 | if (node) { |
835 | plist_get_string_val(node, description); | ||
836 | } | ||
837 | } | ||
838 | |||
839 | if (*name) { | ||
840 | res = instproxy_strtoerr(*name); | ||
677 | } | 841 | } |
678 | return instproxy_create_status_updater(client, status_cb, "Restore", user_data); | 842 | |
843 | return res; | ||
679 | } | 844 | } |
680 | 845 | ||
681 | /** | 846 | void 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 | */ | ||
702 | instproxy_error_t instproxy_remove_archive(instproxy_client_t client, const char *appid, plist_t client_options, instproxy_status_cb_t status_cb, void *user_data) | ||
703 | { | 847 | { |
704 | if (!client || !client->parent || !appid) | 848 | if (name) { |
705 | return INSTPROXY_E_INVALID_ARG; | 849 | plist_t node = plist_dict_get_item(status, "Status"); |
850 | if (node) { | ||
851 | plist_get_string_val(node, name); | ||
852 | } else { | ||
853 | *name = NULL; | ||
854 | } | ||
855 | } | ||
856 | } | ||
706 | 857 | ||
707 | if (client->status_updater) { | 858 | void instproxy_status_get_percent_complete(plist_t status, int *percent) |
708 | return INSTPROXY_E_OP_IN_PROGRESS; | 859 | { |
860 | uint64_t val = 0; | ||
861 | if (percent) { | ||
862 | plist_t node = plist_dict_get_item(status, "PercentComplete"); | ||
863 | if (node) { | ||
864 | plist_get_uint_val(node, &val); | ||
865 | *percent = val; | ||
866 | } | ||
709 | } | 867 | } |
868 | } | ||
710 | 869 | ||
711 | instproxy_lock(client); | 870 | void instproxy_status_get_current_list(plist_t status, uint64_t* total, uint64_t* current_index, uint64_t* current_amount, plist_t* list) |
712 | instproxy_error_t res = instproxy_send_command(client, "RemoveArchive", client_options, appid, NULL); | 871 | { |
713 | instproxy_unlock(client); | 872 | plist_t node = NULL; |
873 | |||
874 | if (status && plist_get_node_type(status) == PLIST_DICT) { | ||
875 | /* command specific logic: parse browsed list */ | ||
876 | if (list != NULL) { | ||
877 | node = plist_dict_get_item(status, "CurrentList"); | ||
878 | if (node) { | ||
879 | *current_amount = plist_array_get_size(node); | ||
880 | *list = plist_copy(node); | ||
881 | } | ||
882 | } | ||
714 | 883 | ||
715 | if (res != INSTPROXY_E_SUCCESS) { | 884 | if (total != NULL) { |
716 | debug_info("could not send plist, error %d", res); | 885 | node = plist_dict_get_item(status, "Total"); |
717 | return res; | 886 | if (node) { |
887 | plist_get_uint_val(node, total); | ||
888 | } | ||
889 | } | ||
890 | |||
891 | if (current_amount != NULL) { | ||
892 | node = plist_dict_get_item(status, "CurrentAmount"); | ||
893 | if (node) { | ||
894 | plist_get_uint_val(node, current_amount); | ||
895 | } | ||
896 | } | ||
897 | |||
898 | if (current_index != NULL) { | ||
899 | node = plist_dict_get_item(status, "CurrentIndex"); | ||
900 | if (node) { | ||
901 | plist_get_uint_val(node, current_index); | ||
902 | } | ||
903 | } | ||
718 | } | 904 | } |
719 | return instproxy_create_status_updater(client, status_cb, "RemoveArchive", user_data); | ||
720 | } | 905 | } |
721 | 906 | ||
722 | /** | 907 | void instproxy_command_get_name(plist_t command, char** name) |
723 | * Create a new client_options plist. | 908 | { |
724 | * | 909 | if (name) { |
725 | * @return A new plist_t of type PLIST_DICT. | 910 | plist_t node = plist_dict_get_item(command, "Command"); |
726 | */ | 911 | if (node) { |
727 | plist_t instproxy_client_options_new() | 912 | plist_get_string_val(node, name); |
913 | } else { | ||
914 | *name = NULL; | ||
915 | } | ||
916 | } | ||
917 | } | ||
918 | |||
919 | plist_t instproxy_client_options_new(void) | ||
728 | { | 920 | { |
729 | return plist_new_dict(); | 921 | return plist_new_dict(); |
730 | } | 922 | } |
731 | 923 | ||
732 | /** | ||
733 | * Add one or more new key:value pairs to the given client_options. | ||
734 | * | ||
735 | * @param client_options The client options to modify. | ||
736 | * @param ... KEY, VALUE, [KEY, VALUE], NULL | ||
737 | * | ||
738 | * @note The keys and values passed are expected to be strings, except for | ||
739 | * "ApplicationSINF" and "iTunesMetadata" expecting a plist node of type | ||
740 | * PLIST_DATA as value, or "SkipUninstall" needing int as value. | ||
741 | */ | ||
742 | void instproxy_client_options_add(plist_t client_options, ...) | 924 | void instproxy_client_options_add(plist_t client_options, ...) |
743 | { | 925 | { |
744 | if (!client_options) | 926 | if (!client_options) |
745 | return; | 927 | return; |
928 | |||
746 | va_list args; | 929 | va_list args; |
747 | va_start(args, client_options); | 930 | va_start(args, client_options); |
748 | char *arg = va_arg(args, char*); | 931 | char *arg = va_arg(args, char*); |
@@ -750,21 +933,21 @@ void instproxy_client_options_add(plist_t client_options, ...) | |||
750 | char *key = strdup(arg); | 933 | char *key = strdup(arg); |
751 | if (!strcmp(key, "SkipUninstall")) { | 934 | if (!strcmp(key, "SkipUninstall")) { |
752 | int intval = va_arg(args, int); | 935 | int intval = va_arg(args, int); |
753 | plist_dict_insert_item(client_options, key, plist_new_bool(intval)); | 936 | plist_dict_set_item(client_options, key, plist_new_bool(intval)); |
754 | } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata")) { | 937 | } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata") || !strcmp(key, "ReturnAttributes") || !strcmp(key, "BundleIDs")) { |
755 | plist_t plistval = va_arg(args, plist_t); | 938 | plist_t plistval = va_arg(args, plist_t); |
756 | if (!plistval) { | 939 | if (!plistval) { |
757 | free(key); | 940 | free(key); |
758 | break; | 941 | break; |
759 | } | 942 | } |
760 | plist_dict_insert_item(client_options, key, plist_copy(plistval)); | 943 | plist_dict_set_item(client_options, key, plist_copy(plistval)); |
761 | } else { | 944 | } else { |
762 | char *strval = va_arg(args, char*); | 945 | char *strval = va_arg(args, char*); |
763 | if (!strval) { | 946 | if (!strval) { |
764 | free(key); | 947 | free(key); |
765 | break; | 948 | break; |
766 | } | 949 | } |
767 | plist_dict_insert_item(client_options, key, plist_new_string(strval)); | 950 | plist_dict_set_item(client_options, key, plist_new_string(strval)); |
768 | } | 951 | } |
769 | free(key); | 952 | free(key); |
770 | arg = va_arg(args, char*); | 953 | arg = va_arg(args, char*); |
@@ -772,15 +955,106 @@ void instproxy_client_options_add(plist_t client_options, ...) | |||
772 | va_end(args); | 955 | va_end(args); |
773 | } | 956 | } |
774 | 957 | ||
775 | /** | 958 | void instproxy_client_options_set_return_attributes(plist_t client_options, ...) |
776 | * Free client_options plist. | 959 | { |
777 | * | 960 | if (!client_options) |
778 | * @param client_options The client options plist to free. Does nothing if NULL | 961 | return; |
779 | * is passed. | 962 | |
780 | */ | 963 | plist_t return_attributes = plist_new_array(); |
964 | |||
965 | va_list args; | ||
966 | va_start(args, client_options); | ||
967 | char *arg = va_arg(args, char*); | ||
968 | while (arg) { | ||
969 | char *attribute = strdup(arg); | ||
970 | plist_array_append_item(return_attributes, plist_new_string(attribute)); | ||
971 | free(attribute); | ||
972 | arg = va_arg(args, char*); | ||
973 | } | ||
974 | va_end(args); | ||
975 | |||
976 | plist_dict_set_item(client_options, "ReturnAttributes", return_attributes); | ||
977 | } | ||
978 | |||
781 | void instproxy_client_options_free(plist_t client_options) | 979 | void instproxy_client_options_free(plist_t client_options) |
782 | { | 980 | { |
783 | if (client_options) { | 981 | if (client_options) { |
784 | plist_free(client_options); | 982 | plist_free(client_options); |
785 | } | 983 | } |
786 | } | 984 | } |
985 | |||
986 | instproxy_error_t instproxy_client_get_path_for_bundle_identifier(instproxy_client_t client, const char* bundle_id, char** path) | ||
987 | { | ||
988 | if (!client || !client->parent || !bundle_id) | ||
989 | return INSTPROXY_E_INVALID_ARG; | ||
990 | |||
991 | plist_t apps = NULL; | ||
992 | |||
993 | // create client options for any application types | ||
994 | plist_t client_opts = instproxy_client_options_new(); | ||
995 | instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL); | ||
996 | |||
997 | // only return attributes we need | ||
998 | instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "CFBundleExecutable", "Path", NULL); | ||
999 | |||
1000 | // only query for specific appid | ||
1001 | const char* appids[] = {bundle_id, NULL}; | ||
1002 | |||
1003 | // query device for list of apps | ||
1004 | instproxy_error_t ierr = instproxy_lookup(client, appids, client_opts, &apps); | ||
1005 | |||
1006 | instproxy_client_options_free(client_opts); | ||
1007 | |||
1008 | if (ierr != INSTPROXY_E_SUCCESS) { | ||
1009 | return ierr; | ||
1010 | } | ||
1011 | |||
1012 | plist_t app_found = plist_access_path(apps, 1, bundle_id); | ||
1013 | if (!app_found) { | ||
1014 | if (apps) | ||
1015 | plist_free(apps); | ||
1016 | *path = NULL; | ||
1017 | return INSTPROXY_E_OP_FAILED; | ||
1018 | } | ||
1019 | |||
1020 | char* path_str = NULL; | ||
1021 | plist_t path_p = plist_dict_get_item(app_found, "Path"); | ||
1022 | if (path_p) { | ||
1023 | plist_get_string_val(path_p, &path_str); | ||
1024 | } | ||
1025 | |||
1026 | char* exec_str = NULL; | ||
1027 | plist_t exec_p = plist_dict_get_item(app_found, "CFBundleExecutable"); | ||
1028 | if (exec_p) { | ||
1029 | plist_get_string_val(exec_p, &exec_str); | ||
1030 | } | ||
1031 | |||
1032 | if (!path_str) { | ||
1033 | debug_info("app path not found"); | ||
1034 | return INSTPROXY_E_OP_FAILED; | ||
1035 | } | ||
1036 | |||
1037 | if (!exec_str) { | ||
1038 | debug_info("bundle executable not found"); | ||
1039 | return INSTPROXY_E_OP_FAILED; | ||
1040 | } | ||
1041 | |||
1042 | plist_free(apps); | ||
1043 | |||
1044 | char* ret = (char*)malloc(strlen(path_str) + 1 + strlen(exec_str) + 1); | ||
1045 | strcpy(ret, path_str); | ||
1046 | strcat(ret, "/"); | ||
1047 | strcat(ret, exec_str); | ||
1048 | |||
1049 | *path = ret; | ||
1050 | |||
1051 | if (path_str) { | ||
1052 | free(path_str); | ||
1053 | } | ||
1054 | |||
1055 | if (exec_str) { | ||
1056 | free(exec_str); | ||
1057 | } | ||
1058 | |||
1059 | return INSTPROXY_E_SUCCESS; | ||
1060 | } | ||
diff --git a/src/installation_proxy.h b/src/installation_proxy.h index b497d62..5bdbb71 100644 --- a/src/installation_proxy.h +++ b/src/installation_proxy.h | |||
@@ -2,34 +2,36 @@ | |||
2 | * installation_proxy.h | 2 | * installation_proxy.h |
3 | * com.apple.mobile.installation_proxy service header file. | 3 | * com.apple.mobile.installation_proxy service header file. |
4 | * | 4 | * |
5 | * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved. |
6 | * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved. | ||
6 | * | 7 | * |
7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 9 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 10 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 11 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 12 | * |
12 | * This library is distributed in the hope that it will be useful, | 13 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 16 | * Lesser General Public License for more details. |
16 | * | 17 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 18 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 19 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 21 | */ |
21 | #ifndef IINSTALLATION_PROXY_H | ||
22 | #define IINSTALLATION_PROXY_H | ||
23 | 22 | ||
24 | #include <glib.h> | 23 | #ifndef __INSTALLATION_PROXY_H |
24 | #define __INSTALLATION_PROXY_H | ||
25 | 25 | ||
26 | #include "idevice.h" | ||
26 | #include "libimobiledevice/installation_proxy.h" | 27 | #include "libimobiledevice/installation_proxy.h" |
27 | #include "property_list_service.h" | 28 | #include "property_list_service.h" |
29 | #include <libimobiledevice-glue/thread.h> | ||
28 | 30 | ||
29 | struct instproxy_client_private { | 31 | struct 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 @@ | |||
1 | prefix=@prefix@ | ||
2 | exec_prefix=@exec_prefix@ | ||
3 | libdir=@libdir@ | ||
4 | includedir=@includedir@ | ||
5 | |||
6 | Name: @PACKAGE_NAME@ | ||
7 | Description: A library to communicate with services running on Apple iOS devices. | ||
8 | Version: @PACKAGE_VERSION@ | ||
9 | Libs: -L${libdir} -limobiledevice-1.0 | ||
10 | Cflags: -I${includedir} | ||
11 | Requires: libplist-2.0 >= @LIBPLIST_VERSION@ | ||
12 | Requires.private: libusbmuxd-2.0 >= @LIBUSBMUXD_VERSION@ libimobiledevice-glue-1.0 >= @LIMD_GLUE_VERSION@ @ssl_requires@ | ||
diff --git a/src/lockdown-cu.c b/src/lockdown-cu.c new file mode 100644 index 0000000..1afc2c5 --- /dev/null +++ b/src/lockdown-cu.c | |||
@@ -0,0 +1,1193 @@ | |||
1 | /* | ||
2 | * lockdown-cu.c | ||
3 | * com.apple.mobile.lockdownd service CU additions | ||
4 | * | ||
5 | * Copyright (c) 2021 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | |||
26 | #include <string.h> | ||
27 | #include <stdlib.h> | ||
28 | #define _GNU_SOURCE 1 | ||
29 | #define __USE_GNU 1 | ||
30 | #include <stdio.h> | ||
31 | #include <ctype.h> | ||
32 | #include <unistd.h> | ||
33 | #include <plist/plist.h> | ||
34 | |||
35 | #include "idevice.h" | ||
36 | #include "lockdown.h" | ||
37 | #include "common/debug.h" | ||
38 | |||
39 | #ifdef HAVE_WIRELESS_PAIRING | ||
40 | |||
41 | #include <libimobiledevice-glue/utils.h> | ||
42 | #include <libimobiledevice-glue/socket.h> | ||
43 | #include <libimobiledevice-glue/opack.h> | ||
44 | #include <libimobiledevice-glue/tlv.h> | ||
45 | |||
46 | #if defined(HAVE_OPENSSL) | ||
47 | #include <openssl/hmac.h> | ||
48 | #include <openssl/evp.h> | ||
49 | #include <openssl/rand.h> | ||
50 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) | ||
51 | #include <openssl/chacha.h> | ||
52 | #include <openssl/poly1305.h> | ||
53 | #endif | ||
54 | #elif defined(HAVE_GCRYPT) | ||
55 | #include <gcrypt.h> | ||
56 | #elif defined(HAVE_MBEDTLS) | ||
57 | #include <mbedtls/md.h> | ||
58 | #include <mbedtls/chachapoly.h> | ||
59 | #endif | ||
60 | |||
61 | #ifdef __APPLE__ | ||
62 | #include <sys/sysctl.h> | ||
63 | #include <SystemConfiguration/SystemConfiguration.h> | ||
64 | #include <CoreFoundation/CoreFoundation.h> | ||
65 | #include <TargetConditionals.h> | ||
66 | #endif | ||
67 | |||
68 | #include "property_list_service.h" | ||
69 | #include "common/userpref.h" | ||
70 | |||
71 | #include "endianness.h" | ||
72 | |||
73 | #include "srp.h" | ||
74 | #include "ed25519.h" | ||
75 | |||
76 | /* {{{ SRP6a parameters */ | ||
77 | static const unsigned char kSRPModulus3072[384] = { | ||
78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, | ||
79 | 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, | ||
80 | 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, | ||
81 | 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, | ||
82 | 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, | ||
83 | 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, | ||
84 | 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, | ||
85 | 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, | ||
86 | 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, | ||
87 | 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, | ||
88 | 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, | ||
89 | 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, | ||
90 | 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, | ||
91 | 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, | ||
92 | 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, | ||
93 | 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, | ||
94 | 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, | ||
95 | 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, | ||
96 | 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, | ||
97 | 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, | ||
98 | 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, | ||
99 | 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, | ||
100 | 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, | ||
101 | 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
102 | }; | ||
103 | |||
104 | static const unsigned char kSRPGenerator5 = 5; | ||
105 | /* }}} */ | ||
106 | |||
107 | /* {{{ HKDF */ | ||
108 | #if defined(HAVE_OPENSSL) | ||
109 | #define MD_ALGO_SHA512 EVP_sha512() | ||
110 | typedef const EVP_MD* MD_ALGO_TYPE_T; | ||
111 | #define MD_ALGO_DIGEST_SIZE EVP_MD_size | ||
112 | #define MD_MAX_DIGEST_SIZE EVP_MAX_MD_SIZE | ||
113 | |||
114 | #elif defined(HAVE_GCRYPT) | ||
115 | #define MD_ALGO_SHA512 GCRY_MD_SHA512 | ||
116 | typedef int MD_ALGO_TYPE_T; | ||
117 | #define MD_ALGO_DIGEST_SIZE gcry_md_get_algo_dlen | ||
118 | #define MD_MAX_DIGEST_SIZE 64 | ||
119 | |||
120 | static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len) | ||
121 | { | ||
122 | gcry_md_hd_t hd; | ||
123 | if (gcry_md_open(&hd, md, GCRY_MD_FLAG_HMAC)) { | ||
124 | debug_info("gcry_md_open() failed"); | ||
125 | return; | ||
126 | } | ||
127 | if (gcry_md_setkey(hd, key, key_len)) { | ||
128 | gcry_md_close (hd); | ||
129 | debug_info("gcry_md_setkey() failed"); | ||
130 | return; | ||
131 | } | ||
132 | gcry_md_write(hd, data, data_len); | ||
133 | |||
134 | unsigned char* digest = gcry_md_read(hd, md); | ||
135 | if (!digest) { | ||
136 | gcry_md_close(hd); | ||
137 | debug_info("gcry_md_read() failed"); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | *out_len = gcry_md_get_algo_dlen(md); | ||
142 | memcpy(out, digest, *out_len); | ||
143 | gcry_md_close(hd); | ||
144 | } | ||
145 | #elif defined(HAVE_MBEDTLS) | ||
146 | #define MD_ALGO_SHA512 MBEDTLS_MD_SHA512 | ||
147 | typedef mbedtls_md_type_t MD_ALGO_TYPE_T; | ||
148 | #define MD_ALGO_DIGEST_SIZE(x) mbedtls_md_get_size(mbedtls_md_info_from_type(x)) | ||
149 | #define MD_MAX_DIGEST_SIZE MBEDTLS_MD_MAX_SIZE | ||
150 | |||
151 | static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len) | ||
152 | { | ||
153 | mbedtls_md_context_t mdctx; | ||
154 | mbedtls_md_init(&mdctx); | ||
155 | int mr = mbedtls_md_setup(&mdctx, mbedtls_md_info_from_type(md), 1); | ||
156 | if (mr != 0) { | ||
157 | debug_info("mbedtls_md_setup() failed: %d", mr); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | mr = mbedtls_md_hmac_starts(&mdctx, key, key_len); | ||
162 | if (mr != 0) { | ||
163 | mbedtls_md_free(&mdctx); | ||
164 | debug_info("mbedtls_md_hmac_starts() failed: %d", mr); | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | mbedtls_md_hmac_update(&mdctx, data, data_len); | ||
169 | |||
170 | mr = mbedtls_md_hmac_finish(&mdctx, out); | ||
171 | if (mr == 0) { | ||
172 | *out_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md)); | ||
173 | } else { | ||
174 | debug_info("mbedtls_md_hmac_finish() failed: %d", mr); | ||
175 | } | ||
176 | mbedtls_md_free(&mdctx); | ||
177 | } | ||
178 | #endif | ||
179 | |||
180 | static void hkdf_md_extract(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* input_key_material, unsigned int input_key_material_len, unsigned char* out, unsigned int* out_len) | ||
181 | { | ||
182 | unsigned char empty_salt[MD_MAX_DIGEST_SIZE]; | ||
183 | if (!md || !out || !out_len || !*out_len) return; | ||
184 | if (salt_len == 0) { | ||
185 | salt_len = MD_ALGO_DIGEST_SIZE(md); | ||
186 | salt = (unsigned char*)empty_salt; | ||
187 | } | ||
188 | HMAC(md, salt, salt_len, input_key_material, input_key_material_len, out, out_len); | ||
189 | } | ||
190 | |||
191 | static void hkdf_md_expand(MD_ALGO_TYPE_T md, unsigned char* prk, unsigned int prk_len, unsigned char* info, unsigned int info_len, unsigned char* out, unsigned int* out_len) | ||
192 | { | ||
193 | if (!md || !out || !out_len || !*out_len) return; | ||
194 | unsigned int md_size = MD_ALGO_DIGEST_SIZE(md); | ||
195 | if (*out_len > 255 * md_size) { | ||
196 | *out_len = 0; | ||
197 | return; | ||
198 | } | ||
199 | int blocks_needed = (*out_len) / md_size; | ||
200 | if (((*out_len) % md_size) != 0) blocks_needed++; | ||
201 | unsigned int okm_len = 0; | ||
202 | unsigned char okm_block[MD_MAX_DIGEST_SIZE]; | ||
203 | unsigned int okm_block_len = 0; | ||
204 | int i; | ||
205 | for (i = 0; i < blocks_needed; i++) { | ||
206 | unsigned int output_block_len = okm_block_len + info_len + 1; | ||
207 | unsigned char* output_block = malloc(output_block_len); | ||
208 | if (okm_block_len > 0) { | ||
209 | memcpy(output_block, okm_block, okm_block_len); | ||
210 | } | ||
211 | memcpy(output_block + okm_block_len, info, info_len); | ||
212 | output_block[okm_block_len + info_len] = (uint8_t)(i+1); | ||
213 | |||
214 | HMAC(md, prk, prk_len, output_block, output_block_len, okm_block, &okm_block_len); | ||
215 | if (okm_len < *out_len) { | ||
216 | memcpy(out + okm_len, okm_block, (okm_len + okm_block_len > *out_len) ? *out_len - okm_len : okm_block_len); | ||
217 | } | ||
218 | okm_len += okm_block_len; | ||
219 | free(output_block); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static void hkdf_md(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* info, unsigned int info_len, unsigned char* initial_key_material, unsigned int initial_key_material_size, unsigned char* out, unsigned int *out_len) | ||
224 | { | ||
225 | if (!md || !initial_key_material || !out || !out_len || !*out_len) return; | ||
226 | |||
227 | unsigned char prk[MD_MAX_DIGEST_SIZE]; | ||
228 | unsigned int prk_len = MD_ALGO_DIGEST_SIZE(md); | ||
229 | |||
230 | hkdf_md_extract(md, salt, salt_len, initial_key_material, initial_key_material_size, prk, &prk_len); | ||
231 | if (prk_len > 0) { | ||
232 | hkdf_md_expand(md, prk, prk_len, info, info_len, out, out_len); | ||
233 | } else { | ||
234 | *out_len = 0; | ||
235 | } | ||
236 | } | ||
237 | /* }}} */ | ||
238 | |||
239 | /* {{{ chacha20 poly1305 encryption/decryption */ | ||
240 | #if defined(HAVE_OPENSSL) && defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) | ||
241 | /* {{{ From: OpenBSD's e_chacha20poly1305.c */ | ||
242 | /* | ||
243 | * Copyright (c) 2015 Reyk Floter <reyk@openbsd.org> | ||
244 | * Copyright (c) 2014, Google Inc. | ||
245 | * | ||
246 | * Permission to use, copy, modify, and/or distribute this software for any | ||
247 | * purpose with or without fee is hereby granted, provided that the above | ||
248 | * copyright notice and this permission notice appear in all copies. | ||
249 | * | ||
250 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
251 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
252 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||
253 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
254 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
255 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
256 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
257 | */ | ||
258 | static void | ||
259 | poly1305_update_with_length(poly1305_state *poly1305, | ||
260 | const unsigned char *data, size_t data_len) | ||
261 | { | ||
262 | size_t j = data_len; | ||
263 | unsigned char length_bytes[8]; | ||
264 | unsigned i; | ||
265 | |||
266 | for (i = 0; i < sizeof(length_bytes); i++) { | ||
267 | length_bytes[i] = j; | ||
268 | j >>= 8; | ||
269 | } | ||
270 | |||
271 | if (data != NULL) | ||
272 | CRYPTO_poly1305_update(poly1305, data, data_len); | ||
273 | CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes)); | ||
274 | } | ||
275 | |||
276 | static void | ||
277 | poly1305_update_with_pad16(poly1305_state *poly1305, | ||
278 | const unsigned char *data, size_t data_len) | ||
279 | { | ||
280 | static const unsigned char zero_pad16[16]; | ||
281 | size_t pad_len; | ||
282 | |||
283 | CRYPTO_poly1305_update(poly1305, data, data_len); | ||
284 | |||
285 | /* pad16() is defined in RFC 7539 2.8.1. */ | ||
286 | if ((pad_len = data_len % 16) == 0) | ||
287 | return; | ||
288 | |||
289 | CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len); | ||
290 | } | ||
291 | /* }}} */ | ||
292 | #endif | ||
293 | |||
294 | static void chacha20_poly1305_encrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
295 | { | ||
296 | #if defined(HAVE_OPENSSL) | ||
297 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) | ||
298 | #if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) | ||
299 | const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); | ||
300 | EVP_AEAD_CTX ctx; | ||
301 | EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); | ||
302 | EVP_AEAD_CTX_seal(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); | ||
303 | #else | ||
304 | unsigned char poly1305_key[32]; | ||
305 | poly1305_state poly1305; | ||
306 | uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; | ||
307 | const unsigned char* iv = nonce + 4; | ||
308 | |||
309 | memset(poly1305_key, 0, sizeof(poly1305_key)); | ||
310 | CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); | ||
311 | |||
312 | CRYPTO_poly1305_init(&poly1305, poly1305_key); | ||
313 | poly1305_update_with_pad16(&poly1305, ad, ad_len); | ||
314 | CRYPTO_chacha_20(out, in, in_len, key, iv, ctr + 1); | ||
315 | poly1305_update_with_pad16(&poly1305, out, in_len); | ||
316 | poly1305_update_with_length(&poly1305, NULL, ad_len); | ||
317 | poly1305_update_with_length(&poly1305, NULL, in_len); | ||
318 | |||
319 | CRYPTO_poly1305_finish(&poly1305, out + in_len); | ||
320 | |||
321 | *out_len = in_len + 16; | ||
322 | #endif | ||
323 | #elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) | ||
324 | int outl = 0; | ||
325 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
326 | EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); | ||
327 | EVP_EncryptUpdate(ctx, out, &outl, in, in_len); | ||
328 | *out_len = outl; | ||
329 | outl = 0; | ||
330 | EVP_EncryptFinal_ex(ctx, out + *out_len, &outl); | ||
331 | *out_len += outl; | ||
332 | EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, out + *out_len); | ||
333 | EVP_CIPHER_CTX_free(ctx); | ||
334 | *out_len += 16; | ||
335 | #else | ||
336 | #error Please use a newer version of OpenSSL (>= 1.1.0) | ||
337 | #endif | ||
338 | #elif defined(HAVE_GCRYPT) | ||
339 | #if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) | ||
340 | gcry_cipher_hd_t hd; | ||
341 | if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { | ||
342 | debug_info("gcry_cipher_open() failed"); | ||
343 | return; | ||
344 | } | ||
345 | gcry_cipher_setkey(hd, key, 32); | ||
346 | gcry_cipher_setiv(hd, nonce, 12); | ||
347 | gcry_cipher_authenticate(hd, ad, ad_len); | ||
348 | *out_len = in_len + 16; | ||
349 | if (gcry_cipher_encrypt(hd, out, *out_len, in, in_len)) { | ||
350 | *out_len = 0; | ||
351 | } | ||
352 | gcry_cipher_gettag(hd, out+in_len, 16); | ||
353 | gcry_cipher_close(hd); | ||
354 | #else | ||
355 | #error Please use a newer version of libgcrypt (>= 1.7.0) | ||
356 | #endif | ||
357 | #elif defined (HAVE_MBEDTLS) | ||
358 | mbedtls_chachapoly_context ctx; | ||
359 | mbedtls_chachapoly_init(&ctx); | ||
360 | mbedtls_chachapoly_setkey(&ctx, key); | ||
361 | if (mbedtls_chachapoly_encrypt_and_tag(&ctx, in_len, nonce, ad, ad_len, in, out, out+in_len) != 0) { | ||
362 | *out_len = 0; | ||
363 | } | ||
364 | mbedtls_chachapoly_free(&ctx); | ||
365 | #else | ||
366 | #error chacha20_poly1305_encrypt_96 is not implemented | ||
367 | #endif | ||
368 | } | ||
369 | |||
370 | static void chacha20_poly1305_encrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
371 | { | ||
372 | unsigned char _nonce[12]; | ||
373 | *(uint32_t*)(&_nonce[0]) = 0; | ||
374 | memcpy(&_nonce[4], nonce, 8); | ||
375 | chacha20_poly1305_encrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); | ||
376 | } | ||
377 | |||
378 | static void chacha20_poly1305_decrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
379 | { | ||
380 | #if defined(HAVE_OPENSSL) | ||
381 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) | ||
382 | #if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) | ||
383 | const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); | ||
384 | EVP_AEAD_CTX ctx; | ||
385 | EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); | ||
386 | EVP_AEAD_CTX_open(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); | ||
387 | #else | ||
388 | unsigned char mac[16]; | ||
389 | unsigned char poly1305_key[32]; | ||
390 | poly1305_state poly1305; | ||
391 | size_t plaintext_len = in_len - 16; | ||
392 | uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; | ||
393 | const unsigned char *iv = nonce + 4; | ||
394 | |||
395 | memset(poly1305_key, 0, sizeof(poly1305_key)); | ||
396 | CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); | ||
397 | |||
398 | CRYPTO_poly1305_init(&poly1305, poly1305_key); | ||
399 | poly1305_update_with_pad16(&poly1305, ad, ad_len); | ||
400 | poly1305_update_with_pad16(&poly1305, in, plaintext_len); | ||
401 | poly1305_update_with_length(&poly1305, NULL, ad_len); | ||
402 | poly1305_update_with_length(&poly1305, NULL, plaintext_len); | ||
403 | |||
404 | CRYPTO_poly1305_finish(&poly1305, mac); | ||
405 | |||
406 | if (memcmp(mac, in + plaintext_len, 16) != 0) { | ||
407 | *out_len = 0; | ||
408 | return; | ||
409 | } | ||
410 | |||
411 | CRYPTO_chacha_20(out, in, plaintext_len, key, iv, ctr + 1); | ||
412 | *out_len = plaintext_len; | ||
413 | #endif | ||
414 | #elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) | ||
415 | int outl = 0; | ||
416 | size_t plaintext_len = in_len - 16; | ||
417 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
418 | EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); | ||
419 | EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, in + plaintext_len); | ||
420 | EVP_DecryptUpdate(ctx, out, &outl, in, plaintext_len); | ||
421 | *out_len = outl; | ||
422 | outl = 0; | ||
423 | if (EVP_DecryptFinal_ex(ctx, out + *out_len, &outl) == 1) { | ||
424 | *out_len += outl; | ||
425 | } else { | ||
426 | *out_len = 0; | ||
427 | } | ||
428 | EVP_CIPHER_CTX_free(ctx); | ||
429 | #else | ||
430 | #error Please use a newer version of OpenSSL (>= 1.1.0) | ||
431 | #endif | ||
432 | #elif defined(HAVE_GCRYPT) | ||
433 | #if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) | ||
434 | gcry_cipher_hd_t hd; | ||
435 | if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { | ||
436 | debug_info("gcry_cipher_open() failed"); | ||
437 | return; | ||
438 | } | ||
439 | gcry_cipher_setkey(hd, key, 32); | ||
440 | gcry_cipher_setiv(hd, nonce, 12); | ||
441 | gcry_cipher_authenticate(hd, ad, ad_len); | ||
442 | unsigned int plaintext_len = in_len - 16; | ||
443 | gcry_cipher_decrypt(hd, out, *out_len, in, plaintext_len); | ||
444 | if (gcry_cipher_checktag(hd, in + plaintext_len, 16) == 0) { | ||
445 | *out_len = plaintext_len; | ||
446 | } else { | ||
447 | *out_len = 0; | ||
448 | } | ||
449 | gcry_cipher_close(hd); | ||
450 | #else | ||
451 | #error Please use a newer version of libgcrypt (>= 1.7.0) | ||
452 | #endif | ||
453 | #elif defined(HAVE_MBEDTLS) | ||
454 | mbedtls_chachapoly_context ctx; | ||
455 | mbedtls_chachapoly_init(&ctx); | ||
456 | mbedtls_chachapoly_setkey(&ctx, key); | ||
457 | unsigned int plaintext_len = in_len - 16; | ||
458 | if (mbedtls_chachapoly_auth_decrypt(&ctx, plaintext_len, nonce, ad, ad_len, in + plaintext_len, in, out) == 0) { | ||
459 | *out_len = plaintext_len; | ||
460 | } else { | ||
461 | *out_len = 0; | ||
462 | } | ||
463 | mbedtls_chachapoly_free(&ctx); | ||
464 | #else | ||
465 | #error chacha20_poly1305_decrypt_96 is not implemented | ||
466 | #endif | ||
467 | } | ||
468 | |||
469 | static void chacha20_poly1305_decrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
470 | { | ||
471 | unsigned char _nonce[12]; | ||
472 | *(uint32_t*)(&_nonce[0]) = 0; | ||
473 | memcpy(&_nonce[4], nonce, 8); | ||
474 | chacha20_poly1305_decrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); | ||
475 | } | ||
476 | /* }}} */ | ||
477 | |||
478 | #define PAIRING_ERROR(x) \ | ||
479 | debug_info(x); \ | ||
480 | if (pairing_callback) { \ | ||
481 | pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, (char*)x, NULL); \ | ||
482 | } | ||
483 | |||
484 | #define PAIRING_ERROR_FMT(...) \ | ||
485 | sprintf(tmp, __VA_ARGS__); \ | ||
486 | debug_info(tmp); \ | ||
487 | if (pairing_callback) { \ | ||
488 | pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, tmp, NULL); \ | ||
489 | } | ||
490 | |||
491 | #endif /* HAVE_WIRELESS_PAIRING */ | ||
492 | |||
493 | lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdownd_cu_pairing_cb_t pairing_callback, void* cb_user_data, plist_t host_info, plist_t acl) | ||
494 | { | ||
495 | #ifdef HAVE_WIRELESS_PAIRING | ||
496 | if (!client || !pairing_callback || (host_info && plist_get_node_type(host_info) != PLIST_DICT) || (acl && plist_get_node_type(acl) != PLIST_DICT)) | ||
497 | return LOCKDOWN_E_INVALID_ARG; | ||
498 | |||
499 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
500 | |||
501 | if (client->device && client->device->version == 0) { | ||
502 | plist_t p_version = NULL; | ||
503 | if (lockdownd_get_value(client, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { | ||
504 | int vers[3] = {0, 0, 0}; | ||
505 | char *s_version = NULL; | ||
506 | plist_get_string_val(p_version, &s_version); | ||
507 | if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { | ||
508 | client->device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); | ||
509 | } | ||
510 | free(s_version); | ||
511 | } | ||
512 | plist_free(p_version); | ||
513 | } | ||
514 | |||
515 | char* pairing_uuid = NULL; | ||
516 | if (host_info) { | ||
517 | plist_t accountid = plist_dict_get_item(host_info, "accountID"); | ||
518 | if (accountid && plist_get_node_type(accountid) == PLIST_STRING) { | ||
519 | plist_get_string_val(accountid, &pairing_uuid); | ||
520 | } | ||
521 | } | ||
522 | if (!pairing_uuid) { | ||
523 | userpref_read_system_buid(&pairing_uuid); | ||
524 | } | ||
525 | if (!pairing_uuid) { | ||
526 | pairing_uuid = generate_uuid(); | ||
527 | } | ||
528 | unsigned int pairing_uuid_len = strlen(pairing_uuid); | ||
529 | |||
530 | SRP_initialize_library(); | ||
531 | |||
532 | SRP* srp = SRP_new(SRP6a_sha512_client_method()); | ||
533 | if (!srp) { | ||
534 | PAIRING_ERROR("Failed to initialize SRP") | ||
535 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
536 | } | ||
537 | |||
538 | char tmp[256]; | ||
539 | plist_t dict = NULL; | ||
540 | uint8_t current_state = 0; | ||
541 | uint8_t final_state = 6; | ||
542 | |||
543 | unsigned char* salt = NULL; | ||
544 | unsigned int salt_size = 0; | ||
545 | unsigned char* pubkey = NULL; | ||
546 | unsigned int pubkey_size = 0; | ||
547 | |||
548 | unsigned char setup_encryption_key[32]; | ||
549 | |||
550 | cstr *thekey = NULL; | ||
551 | |||
552 | do { | ||
553 | current_state++; | ||
554 | |||
555 | dict = plist_new_dict(); | ||
556 | plist_dict_set_item(dict, "Request", plist_new_string("CUPairingCreate")); | ||
557 | if (current_state == 1) { | ||
558 | plist_dict_set_item(dict, "Flags", plist_new_uint(1)); | ||
559 | } else { | ||
560 | plist_dict_set_item(dict, "Flags", plist_new_uint(0)); | ||
561 | } | ||
562 | |||
563 | tlv_buf_t tlv = tlv_buf_new(); | ||
564 | |||
565 | if (current_state == 1) { | ||
566 | /* send method */ | ||
567 | tlv_buf_append(tlv, 0x00, 1, (void*)"\x00"); // 0x00 (Method), 1 bytes, 00 | ||
568 | } else if (current_state == 3) { | ||
569 | /* generate public key */ | ||
570 | cstr* own_pub = NULL; | ||
571 | SRP_gen_pub(srp, &own_pub); | ||
572 | |||
573 | if (!own_pub) { | ||
574 | PAIRING_ERROR("[SRP] Failed to generate public key") | ||
575 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
576 | break; | ||
577 | } | ||
578 | |||
579 | /* compute key from remote's public key */ | ||
580 | if (SRP_compute_key(srp, &thekey, pubkey, pubkey_size) != 0) { | ||
581 | cstr_free(own_pub); | ||
582 | PAIRING_ERROR("[SRP] Failed to compute key") | ||
583 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
584 | break; | ||
585 | } | ||
586 | |||
587 | /* compute response */ | ||
588 | cstr *response = NULL; | ||
589 | SRP_respond(srp, &response); | ||
590 | |||
591 | /* send our public key + response */ | ||
592 | tlv_buf_append(tlv, 0x03, own_pub->length, own_pub->data); | ||
593 | tlv_buf_append(tlv, 0x04, response->length, response->data); | ||
594 | cstr_free(response); | ||
595 | cstr_free(own_pub); | ||
596 | } else if (current_state == 5) { | ||
597 | /* send encrypted info */ | ||
598 | |||
599 | static const char PAIR_SETUP_ENCRYPT_SALT[] = "Pair-Setup-Encrypt-Salt"; | ||
600 | static const char PAIR_SETUP_ENCRYPT_INFO[] = "Pair-Setup-Encrypt-Info"; | ||
601 | static const char PAIR_SETUP_CONTROLLER_SIGN_SALT[] = "Pair-Setup-Controller-Sign-Salt"; | ||
602 | static const char PAIR_SETUP_CONTROLLER_SIGN_INFO[] = "Pair-Setup-Controller-Sign-Info"; | ||
603 | |||
604 | // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Encrypt-Salt + Pair-Setup-Encrypt-Info | ||
605 | // result used as key for chacha20-poly1305 | ||
606 | unsigned int setup_encryption_key_len = sizeof(setup_encryption_key); | ||
607 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_ENCRYPT_SALT, sizeof(PAIR_SETUP_ENCRYPT_SALT)-1, (unsigned char*)PAIR_SETUP_ENCRYPT_INFO, sizeof(PAIR_SETUP_ENCRYPT_INFO)-1, (unsigned char*)thekey->data, thekey->length, setup_encryption_key, &setup_encryption_key_len); | ||
608 | |||
609 | unsigned char ed25519_pubkey[32]; | ||
610 | unsigned char ed25519_privkey[64]; | ||
611 | unsigned char ed25519seed[32]; | ||
612 | ed25519_create_seed(ed25519seed); | ||
613 | |||
614 | ed25519_create_keypair(ed25519_pubkey, ed25519_privkey, ed25519seed); | ||
615 | |||
616 | unsigned int signbuf_len = pairing_uuid_len + 64; | ||
617 | unsigned char* signbuf = malloc(signbuf_len); | ||
618 | unsigned int hkdf_len = 32; | ||
619 | // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Controller-Sign-Salt + Pair-Setup-Controller-Sign-Info | ||
620 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_SALT, sizeof(PAIR_SETUP_CONTROLLER_SIGN_SALT)-1, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_INFO, sizeof(PAIR_SETUP_CONTROLLER_SIGN_INFO)-1, (unsigned char*)thekey->data, thekey->length, signbuf, &hkdf_len); | ||
621 | |||
622 | memcpy(signbuf + 32, pairing_uuid, pairing_uuid_len); | ||
623 | memcpy(signbuf + 32 + pairing_uuid_len, ed25519_pubkey, 32); | ||
624 | |||
625 | unsigned char ed_sig[64]; | ||
626 | ed25519_sign(ed_sig, signbuf, 0x64, ed25519_pubkey, ed25519_privkey); | ||
627 | |||
628 | tlv_buf_t tlvbuf = tlv_buf_new(); | ||
629 | tlv_buf_append(tlvbuf, 0x01, pairing_uuid_len, (void*)pairing_uuid); | ||
630 | tlv_buf_append(tlvbuf, 0x03, sizeof(ed25519_pubkey), ed25519_pubkey); | ||
631 | tlv_buf_append(tlvbuf, 0x0a, sizeof(ed_sig), ed_sig); | ||
632 | |||
633 | /* ACL */ | ||
634 | unsigned char* odata = NULL; | ||
635 | unsigned int olen = 0; | ||
636 | if (acl) { | ||
637 | opack_encode_from_plist(acl, &odata, &olen); | ||
638 | } else { | ||
639 | /* defaut ACL */ | ||
640 | plist_t acl_plist = plist_new_dict(); | ||
641 | plist_dict_set_item(acl_plist, "com.apple.ScreenCapture", plist_new_bool(1)); | ||
642 | plist_dict_set_item(acl_plist, "com.apple.developer", plist_new_bool(1)); | ||
643 | opack_encode_from_plist(acl_plist, &odata, &olen); | ||
644 | plist_free(acl_plist); | ||
645 | } | ||
646 | tlv_buf_append(tlvbuf, 0x12, olen, odata); | ||
647 | free(odata); | ||
648 | |||
649 | /* HOST INFORMATION */ | ||
650 | char hostname[256]; | ||
651 | #if defined(__APPLE__) && !defined(TARGET_OS_IPHONE) | ||
652 | CFStringRef cname = SCDynamicStoreCopyComputerName(NULL, NULL); | ||
653 | CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8); | ||
654 | CFRelease(cname); | ||
655 | #else | ||
656 | #ifdef WIN32 | ||
657 | DWORD hostname_len = sizeof(hostname); | ||
658 | GetComputerName(hostname, &hostname_len); | ||
659 | #else | ||
660 | gethostname(hostname, sizeof(hostname)); | ||
661 | #endif | ||
662 | #endif | ||
663 | |||
664 | char modelname[256]; | ||
665 | modelname[0] = '\0'; | ||
666 | #ifdef __APPLE__ | ||
667 | size_t len = sizeof(modelname); | ||
668 | sysctlbyname("hw.model", &modelname, &len, NULL, 0); | ||
669 | #endif | ||
670 | if (strlen(modelname) == 0) { | ||
671 | strcpy(modelname, "HackbookPro13,37"); | ||
672 | } | ||
673 | |||
674 | unsigned char primary_mac_addr[6] = { 0, 0, 0, 0, 0, 0 }; | ||
675 | if (get_primary_mac_address(primary_mac_addr) != 0) { | ||
676 | debug_info("Failed to get primary mac address"); | ||
677 | } | ||
678 | debug_info("Primary mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", primary_mac_addr[0], primary_mac_addr[1], primary_mac_addr[2], primary_mac_addr[3], primary_mac_addr[4], primary_mac_addr[5]); | ||
679 | |||
680 | // "OPACK" encoded device info | ||
681 | plist_t info_plist = plist_new_dict(); | ||
682 | //plist_dict_set_item(info_plist, "altIRK", plist_new_data((char*)altIRK, 16)); | ||
683 | plist_dict_set_item(info_plist, "accountID", plist_new_string(pairing_uuid)); | ||
684 | plist_dict_set_item(info_plist, "model", plist_new_string(modelname)); | ||
685 | plist_dict_set_item(info_plist, "name", plist_new_string(hostname)); | ||
686 | plist_dict_set_item(info_plist, "mac", plist_new_data((char*)primary_mac_addr, 6)); | ||
687 | if (host_info) { | ||
688 | plist_dict_merge(&info_plist, host_info); | ||
689 | } | ||
690 | opack_encode_from_plist(info_plist, &odata, &olen); | ||
691 | plist_free(info_plist); | ||
692 | tlv_buf_append(tlvbuf, 0x11, olen, odata); | ||
693 | free(odata); | ||
694 | |||
695 | size_t encrypted_len = tlvbuf->length + 16; | ||
696 | unsigned char* encrypted_buf = (unsigned char*)malloc(encrypted_len); | ||
697 | |||
698 | chacha20_poly1305_encrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg05", NULL, 0, tlvbuf->data, tlvbuf->length, encrypted_buf, &encrypted_len); | ||
699 | |||
700 | tlv_buf_free(tlvbuf); | ||
701 | |||
702 | tlv_buf_append(tlv, 0x05, encrypted_len, encrypted_buf); | ||
703 | free(encrypted_buf); | ||
704 | } else { | ||
705 | tlv_buf_free(tlv); | ||
706 | PAIRING_ERROR("[SRP] Invalid state"); | ||
707 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
708 | break; | ||
709 | } | ||
710 | tlv_buf_append(tlv, 0x06, 1, ¤t_state); | ||
711 | plist_dict_set_item(dict, "Payload", plist_new_data((char*)tlv->data, tlv->length)); | ||
712 | tlv_buf_free(tlv); | ||
713 | |||
714 | plist_dict_set_item(dict, "Label", plist_new_string(client->label)); | ||
715 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); | ||
716 | |||
717 | ret = lockdownd_send(client, dict); | ||
718 | plist_free(dict); | ||
719 | dict = NULL; | ||
720 | |||
721 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
722 | break; | ||
723 | } | ||
724 | |||
725 | current_state++; | ||
726 | |||
727 | ret = lockdownd_receive(client, &dict); | ||
728 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
729 | break; | ||
730 | } | ||
731 | ret = lockdown_check_result(dict, "CUPairingCreate"); | ||
732 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
733 | break; | ||
734 | } | ||
735 | |||
736 | plist_t extresp = plist_dict_get_item(dict, "ExtendedResponse"); | ||
737 | if (!extresp) { | ||
738 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
739 | break; | ||
740 | } | ||
741 | plist_t blob = plist_dict_get_item(extresp, "Payload"); | ||
742 | if (!blob) { | ||
743 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
744 | break; | ||
745 | } | ||
746 | uint64_t data_len = 0; | ||
747 | const char* data = plist_get_data_ptr(blob, &data_len); | ||
748 | |||
749 | uint8_t state = 0; | ||
750 | if (!tlv_data_get_uint8(data, data_len, 0x06, &state)) { | ||
751 | PAIRING_ERROR("[SRP] ERROR: Could not find state in response"); | ||
752 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
753 | break; | ||
754 | } | ||
755 | if (state != current_state) { | ||
756 | PAIRING_ERROR_FMT("[SRP] ERROR: Unexpected state %d, expected %d", state, current_state); | ||
757 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
758 | break; | ||
759 | } | ||
760 | |||
761 | unsigned int errval = 0; | ||
762 | uint64_t u64val = 0; | ||
763 | tlv_data_get_uint(data, data_len, 0x07, &u64val); | ||
764 | debug_buffer(data, data_len); | ||
765 | errval = (unsigned int)u64val; | ||
766 | if (errval > 0) { | ||
767 | if (errval == 3) { | ||
768 | u64val = 0; | ||
769 | tlv_data_get_uint(data, data_len, 0x08, &u64val); | ||
770 | if (u64val > 0) { | ||
771 | uint32_t retry_delay = (uint32_t)u64val; | ||
772 | PAIRING_ERROR_FMT("[SRP] Pairing is blocked for another %u seconds", retry_delay) | ||
773 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
774 | break; | ||
775 | } | ||
776 | } else if (errval == 2 && state == 4) { | ||
777 | PAIRING_ERROR_FMT("[SRP] Invalid PIN") | ||
778 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
779 | break; | ||
780 | } else { | ||
781 | PAIRING_ERROR_FMT("[SRP] Received error %u in state %d.", errval, state); | ||
782 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
783 | break; | ||
784 | } | ||
785 | } | ||
786 | |||
787 | if (state == 2) { | ||
788 | /* receive salt and public key */ | ||
789 | if (!tlv_data_copy_data(data, data_len, 0x02, (void**)&salt, &salt_size)) { | ||
790 | PAIRING_ERROR("[SRP] ERROR: Could not find salt in response"); | ||
791 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
792 | break; | ||
793 | } | ||
794 | if (!tlv_data_copy_data(data, data_len, 0x03, (void**)&pubkey, &pubkey_size)) { | ||
795 | PAIRING_ERROR("[SRP] ERROR: Could not find public key in response"); | ||
796 | |||
797 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
798 | break; | ||
799 | } | ||
800 | |||
801 | const char PAIR_SETUP[] = "Pair-Setup"; | ||
802 | if (SRP_set_user_raw(srp, (const unsigned char*)PAIR_SETUP, sizeof(PAIR_SETUP)-1) != 0) { | ||
803 | PAIRING_ERROR("[SRP] Failed to set SRP user"); | ||
804 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
805 | break; | ||
806 | } | ||
807 | |||
808 | /* kSRPParameters_3072_SHA512 */ | ||
809 | if (SRP_set_params(srp, kSRPModulus3072, sizeof(kSRPModulus3072), &kSRPGenerator5, 1, salt, salt_size) != 0) { | ||
810 | PAIRING_ERROR("[SRP] Failed to set SRP parameters"); | ||
811 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
812 | break; | ||
813 | |||
814 | } | ||
815 | |||
816 | if (pairing_callback) { | ||
817 | char pin[64]; | ||
818 | unsigned int pin_len = sizeof(pin); | ||
819 | pairing_callback(LOCKDOWN_CU_PAIRING_PIN_REQUESTED, cb_user_data, pin, &pin_len); | ||
820 | |||
821 | SRP_set_auth_password_raw(srp, (const unsigned char*)pin, pin_len); | ||
822 | } | ||
823 | } else if (state == 4) { | ||
824 | /* receive proof */ | ||
825 | unsigned char* proof = NULL; | ||
826 | unsigned int proof_len = 0; | ||
827 | |||
828 | if (!tlv_data_copy_data(data, data_len, 0x04, (void**)&proof, &proof_len)) { | ||
829 | PAIRING_ERROR("[SRP] ERROR: Could not find proof data in response"); | ||
830 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
831 | break; | ||
832 | } | ||
833 | |||
834 | /* verify */ | ||
835 | int vrfy_result = SRP_verify(srp, proof, proof_len); | ||
836 | free(proof); | ||
837 | |||
838 | if (vrfy_result == 0) { | ||
839 | debug_info("[SRP] PIN verified successfully"); | ||
840 | } else { | ||
841 | PAIRING_ERROR("[SRP] PIN verification failure"); | ||
842 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
843 | break; | ||
844 | } | ||
845 | |||
846 | } else if (state == 6) { | ||
847 | int srp_pair_success = 0; | ||
848 | plist_t node = plist_dict_get_item(extresp, "doSRPPair"); | ||
849 | if (node) { | ||
850 | const char* strv = plist_get_string_ptr(node, NULL); | ||
851 | if (strcmp(strv, "succeed") == 0) { | ||
852 | srp_pair_success = 1; | ||
853 | } | ||
854 | } | ||
855 | if (!srp_pair_success) { | ||
856 | PAIRING_ERROR("SRP Pairing failed"); | ||
857 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
858 | break; | ||
859 | } | ||
860 | |||
861 | /* receive encrypted info */ | ||
862 | unsigned char* encrypted_buf = NULL; | ||
863 | unsigned int enc_len = 0; | ||
864 | if (!tlv_data_copy_data(data, data_len, 0x05, (void**)&encrypted_buf, &enc_len)) { | ||
865 | PAIRING_ERROR("[SRP] ERROR: Could not find encrypted data in response"); | ||
866 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
867 | break; | ||
868 | } | ||
869 | size_t plain_len = enc_len-16; | ||
870 | unsigned char* plain_buf = malloc(plain_len); | ||
871 | chacha20_poly1305_decrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg06", NULL, 0, encrypted_buf, enc_len, plain_buf, &plain_len); | ||
872 | free(encrypted_buf); | ||
873 | |||
874 | unsigned char* dev_info = NULL; | ||
875 | unsigned int dev_info_len = 0; | ||
876 | int res = tlv_data_copy_data(plain_buf, plain_len, 0x11, (void**)&dev_info, &dev_info_len); | ||
877 | free(plain_buf); | ||
878 | if (!res) { | ||
879 | PAIRING_ERROR("[SRP] ERROR: Failed to locate device info in response"); | ||
880 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
881 | break; | ||
882 | } | ||
883 | plist_t device_info = NULL; | ||
884 | opack_decode_to_plist(dev_info, dev_info_len, &device_info); | ||
885 | free(dev_info); | ||
886 | |||
887 | if (!device_info) { | ||
888 | PAIRING_ERROR("[SRP] ERROR: Failed to parse device info"); | ||
889 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
890 | break; | ||
891 | } | ||
892 | |||
893 | if (pairing_callback) { | ||
894 | pairing_callback(LOCKDOWN_CU_PAIRING_DEVICE_INFO, cb_user_data, device_info, NULL); | ||
895 | } | ||
896 | plist_free(device_info); | ||
897 | } else { | ||
898 | PAIRING_ERROR("[SRP] ERROR: Invalid state"); | ||
899 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
900 | break; | ||
901 | } | ||
902 | plist_free(dict); | ||
903 | dict = NULL; | ||
904 | |||
905 | } while (current_state != final_state); | ||
906 | |||
907 | plist_free(dict); | ||
908 | |||
909 | free(salt); | ||
910 | free(pubkey); | ||
911 | |||
912 | SRP_free(srp); | ||
913 | srp = NULL; | ||
914 | |||
915 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
916 | if (thekey) { | ||
917 | cstr_free(thekey); | ||
918 | } | ||
919 | return ret; | ||
920 | } | ||
921 | |||
922 | free(client->cu_key); | ||
923 | client->cu_key = malloc(thekey->length); | ||
924 | memcpy(client->cu_key, thekey->data, thekey->length); | ||
925 | client->cu_key_len = thekey->length; | ||
926 | cstr_free(thekey); | ||
927 | |||
928 | return LOCKDOWN_E_SUCCESS; | ||
929 | #else | ||
930 | debug_info("not supported"); | ||
931 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
932 | #endif | ||
933 | } | ||
934 | |||
935 | lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t client, const char* request, plist_t request_payload, plist_t* reply) | ||
936 | { | ||
937 | #ifdef HAVE_WIRELESS_PAIRING | ||
938 | if (!client || !request) | ||
939 | return LOCKDOWN_E_INVALID_ARG; | ||
940 | |||
941 | if (!client->cu_key) | ||
942 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
943 | |||
944 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
945 | |||
946 | /* derive keys */ | ||
947 | unsigned char cu_write_key[32]; | ||
948 | unsigned int cu_write_key_len = sizeof(cu_write_key); | ||
949 | static const char WRITE_KEY_SALT_MDLD[] = "WriteKeySaltMDLD"; | ||
950 | static const char WRITE_KEY_INFO_MDLD[] = "WriteKeyInfoMDLD"; | ||
951 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)WRITE_KEY_SALT_MDLD, sizeof(WRITE_KEY_SALT_MDLD)-1, (unsigned char*)WRITE_KEY_INFO_MDLD, sizeof(WRITE_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_write_key, &cu_write_key_len); | ||
952 | |||
953 | unsigned char cu_read_key[32]; | ||
954 | unsigned int cu_read_key_len = sizeof(cu_write_key); | ||
955 | static const char READ_KEY_SALT_MDLD[] = "ReadKeySaltMDLD"; | ||
956 | static const char READ_KEY_INFO_MDLD[] = "ReadKeyInfoMDLD"; | ||
957 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)READ_KEY_SALT_MDLD, sizeof(READ_KEY_SALT_MDLD)-1, (unsigned char*)READ_KEY_INFO_MDLD, sizeof(READ_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_read_key, &cu_read_key_len); | ||
958 | |||
959 | // Starting with iOS/tvOS 11.2 and WatchOS 4.2, this nonce is random and sent along with the request. Before, the request doesn't have a nonce and it uses hardcoded nonce "sendone01234". | ||
960 | unsigned char cu_nonce[12] = "sendone01234"; // guaranteed to be random by fair dice troll | ||
961 | if (client->device->version >= DEVICE_VERSION(11,2,0)) { | ||
962 | #if defined(HAVE_OPENSSL) | ||
963 | RAND_bytes(cu_nonce, sizeof(cu_nonce)); | ||
964 | #elif defined(HAVE_GCRYPT) | ||
965 | gcry_create_nonce(cu_nonce, sizeof(cu_nonce)); | ||
966 | #endif | ||
967 | } | ||
968 | |||
969 | debug_plist(request_payload); | ||
970 | |||
971 | /* convert request payload to binary */ | ||
972 | uint32_t bin_len = 0; | ||
973 | char* bin = NULL; | ||
974 | plist_to_bin(request_payload, &bin, &bin_len); | ||
975 | |||
976 | /* encrypt request */ | ||
977 | size_t encrypted_len = bin_len + 16; | ||
978 | unsigned char* encrypted_buf = malloc(encrypted_len); | ||
979 | chacha20_poly1305_encrypt_96(cu_write_key, cu_nonce, NULL, 0, (unsigned char*)bin, bin_len, encrypted_buf, &encrypted_len); | ||
980 | free(bin); | ||
981 | bin = NULL; | ||
982 | |||
983 | plist_t dict = plist_new_dict(); | ||
984 | plist_dict_set_item(dict,"Request", plist_new_string(request)); | ||
985 | plist_dict_set_item(dict, "Payload", plist_new_data((char*)encrypted_buf, encrypted_len)); | ||
986 | free(encrypted_buf); | ||
987 | plist_dict_set_item(dict, "Nonce", plist_new_data((char*)cu_nonce, sizeof(cu_nonce))); | ||
988 | plist_dict_set_item(dict, "Label", plist_new_string(client->label)); | ||
989 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); | ||
990 | |||
991 | /* send to device */ | ||
992 | ret = lockdownd_send(client, dict); | ||
993 | plist_free(dict); | ||
994 | dict = NULL; | ||
995 | |||
996 | if (ret != LOCKDOWN_E_SUCCESS) | ||
997 | return ret; | ||
998 | |||
999 | /* Now get device's answer */ | ||
1000 | ret = lockdownd_receive(client, &dict); | ||
1001 | if (ret != LOCKDOWN_E_SUCCESS) | ||
1002 | return ret; | ||
1003 | |||
1004 | ret = lockdown_check_result(dict, request); | ||
1005 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1006 | plist_free(dict); | ||
1007 | return ret; | ||
1008 | } | ||
1009 | |||
1010 | /* get payload */ | ||
1011 | plist_t blob = plist_dict_get_item(dict, "Payload"); | ||
1012 | if (!blob) { | ||
1013 | plist_free(dict); | ||
1014 | return LOCKDOWN_E_DICT_ERROR; | ||
1015 | } | ||
1016 | |||
1017 | uint64_t dl = 0; | ||
1018 | const char* dt = plist_get_data_ptr(blob, &dl); | ||
1019 | |||
1020 | /* see if we have a nonce */ | ||
1021 | blob = plist_dict_get_item(dict, "Nonce"); | ||
1022 | const unsigned char* rnonce = (unsigned char*)"receiveone01"; | ||
1023 | if (blob) { | ||
1024 | uint64_t rl = 0; | ||
1025 | rnonce = (const unsigned char*)plist_get_data_ptr(blob, &rl); | ||
1026 | } | ||
1027 | |||
1028 | /* decrypt payload */ | ||
1029 | size_t decrypted_len = dl-16; | ||
1030 | unsigned char* decrypted = malloc(decrypted_len); | ||
1031 | chacha20_poly1305_decrypt_96(cu_read_key, (unsigned char*)rnonce, NULL, 0, (unsigned char*)dt, dl, decrypted, &decrypted_len); | ||
1032 | plist_free(dict); | ||
1033 | dict = NULL; | ||
1034 | |||
1035 | plist_from_memory((const char*)decrypted, decrypted_len, &dict, NULL); | ||
1036 | if (!dict) { | ||
1037 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
1038 | debug_info("Failed to parse PLIST from decrypted payload:"); | ||
1039 | debug_buffer((const char*)decrypted, decrypted_len); | ||
1040 | free(decrypted); | ||
1041 | return ret; | ||
1042 | } | ||
1043 | free(decrypted); | ||
1044 | |||
1045 | debug_plist(dict); | ||
1046 | |||
1047 | if (reply) { | ||
1048 | *reply = dict; | ||
1049 | } else { | ||
1050 | plist_free(dict); | ||
1051 | } | ||
1052 | |||
1053 | return LOCKDOWN_E_SUCCESS; | ||
1054 | #else | ||
1055 | debug_info("not supported"); | ||
1056 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1057 | #endif | ||
1058 | } | ||
1059 | |||
1060 | lockdownd_error_t lockdownd_get_value_cu(lockdownd_client_t client, const char* domain, const char* key, plist_t* value) | ||
1061 | { | ||
1062 | #ifdef HAVE_WIRELESS_PAIRING | ||
1063 | if (!client) | ||
1064 | return LOCKDOWN_E_INVALID_ARG; | ||
1065 | |||
1066 | if (!client->cu_key) | ||
1067 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1068 | |||
1069 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
1070 | |||
1071 | plist_t request = plist_new_dict(); | ||
1072 | if (domain) { | ||
1073 | plist_dict_set_item(request, "Domain", plist_new_string(domain)); | ||
1074 | } | ||
1075 | if (key) { | ||
1076 | plist_dict_set_item(request, "Key", plist_new_string(key)); | ||
1077 | } | ||
1078 | |||
1079 | plist_t reply = NULL; | ||
1080 | ret = lockdownd_cu_send_request_and_get_reply(client, "GetValueCU", request, &reply); | ||
1081 | plist_free(request); | ||
1082 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1083 | return ret; | ||
1084 | } | ||
1085 | |||
1086 | plist_t value_node = plist_dict_get_item(reply, "Value"); | ||
1087 | if (value_node) { | ||
1088 | debug_info("has a value"); | ||
1089 | *value = plist_copy(value_node); | ||
1090 | } | ||
1091 | plist_free(reply); | ||
1092 | |||
1093 | return ret; | ||
1094 | #else | ||
1095 | debug_info("not supported"); | ||
1096 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1097 | #endif | ||
1098 | } | ||
1099 | |||
1100 | lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client) | ||
1101 | { | ||
1102 | #ifdef HAVE_WIRELESS_PAIRING | ||
1103 | if (!client) | ||
1104 | return LOCKDOWN_E_INVALID_ARG; | ||
1105 | |||
1106 | if (!client->cu_key) | ||
1107 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1108 | |||
1109 | lockdownd_error_t ret; | ||
1110 | |||
1111 | plist_t wifi_mac = NULL; | ||
1112 | ret = lockdownd_get_value_cu(client, NULL, "WiFiAddress", &wifi_mac); | ||
1113 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1114 | return ret; | ||
1115 | } | ||
1116 | |||
1117 | plist_t pubkey = NULL; | ||
1118 | ret = lockdownd_get_value_cu(client, NULL, "DevicePublicKey", &pubkey); | ||
1119 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1120 | plist_free(wifi_mac); | ||
1121 | return ret; | ||
1122 | } | ||
1123 | |||
1124 | key_data_t public_key = { NULL, 0 }; | ||
1125 | uint64_t data_len = 0; | ||
1126 | plist_get_data_val(pubkey, (char**)&public_key.data, &data_len); | ||
1127 | public_key.size = (unsigned int)data_len; | ||
1128 | plist_free(pubkey); | ||
1129 | |||
1130 | plist_t pair_record_plist = plist_new_dict(); | ||
1131 | pair_record_generate_keys_and_certs(pair_record_plist, public_key); | ||
1132 | |||
1133 | char* host_id = NULL; | ||
1134 | char* system_buid = NULL; | ||
1135 | |||
1136 | /* set SystemBUID */ | ||
1137 | userpref_read_system_buid(&system_buid); | ||
1138 | if (system_buid) { | ||
1139 | plist_dict_set_item(pair_record_plist, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); | ||
1140 | free(system_buid); | ||
1141 | } | ||
1142 | |||
1143 | /* set HostID */ | ||
1144 | host_id = generate_uuid(); | ||
1145 | pair_record_set_host_id(pair_record_plist, host_id); | ||
1146 | free(host_id); | ||
1147 | |||
1148 | plist_t request_pair_record = plist_copy(pair_record_plist); | ||
1149 | /* remove stuff that is private */ | ||
1150 | plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); | ||
1151 | plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); | ||
1152 | |||
1153 | plist_t request = plist_new_dict(); | ||
1154 | plist_dict_set_item(request, "PairRecord", request_pair_record); | ||
1155 | plist_t pairing_opts = plist_new_dict(); | ||
1156 | plist_dict_set_item(pairing_opts, "ExtendedPairingErrors", plist_new_bool(1)); | ||
1157 | plist_dict_set_item(request, "PairingOptions", pairing_opts); | ||
1158 | |||
1159 | plist_t reply = NULL; | ||
1160 | ret = lockdownd_cu_send_request_and_get_reply(client, "PairCU", request, &reply); | ||
1161 | plist_free(request); | ||
1162 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1163 | plist_free(wifi_mac); | ||
1164 | return ret; | ||
1165 | } | ||
1166 | |||
1167 | char *s_udid = NULL; | ||
1168 | plist_t p_udid = plist_dict_get_item(reply, "UDID"); | ||
1169 | if (p_udid) { | ||
1170 | plist_get_string_val(p_udid, &s_udid); | ||
1171 | } | ||
1172 | plist_t ebag = plist_dict_get_item(reply, "EscrowBag"); | ||
1173 | if (ebag) { | ||
1174 | plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(ebag)); | ||
1175 | } | ||
1176 | plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, wifi_mac); | ||
1177 | plist_free(reply); | ||
1178 | |||
1179 | if (userpref_save_pair_record(s_udid, 0, pair_record_plist) != 0) { | ||
1180 | printf("Failed to save pair record for UDID %s\n", s_udid); | ||
1181 | } | ||
1182 | free(s_udid); | ||
1183 | s_udid = NULL; | ||
1184 | plist_free(pair_record_plist); | ||
1185 | |||
1186 | ret = LOCKDOWN_E_SUCCESS; | ||
1187 | |||
1188 | return ret; | ||
1189 | #else | ||
1190 | debug_info("not supported"); | ||
1191 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1192 | #endif | ||
1193 | } | ||
diff --git a/src/lockdown.c b/src/lockdown.c index 935f24e..256bff0 100644 --- a/src/lockdown.c +++ b/src/lockdown.c | |||
@@ -2,6 +2,8 @@ | |||
2 | * lockdown.c | 2 | * lockdown.c |
3 | * com.apple.mobile.lockdownd service implementation. | 3 | * com.apple.mobile.lockdownd service implementation. |
4 | * | 4 | * |
5 | * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved. | ||
6 | * Copyright (c) 2014-2015 Nikias Bassen All Rights Reserved. | ||
5 | * Copyright (c) 2010 Bryan Forbes All Rights Reserved. | 7 | * Copyright (c) 2010 Bryan Forbes All Rights Reserved. |
6 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 8 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
7 | * | 9 | * |
@@ -20,36 +22,125 @@ | |||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ | 23 | */ |
22 | 24 | ||
25 | #ifdef HAVE_CONFIG_H | ||
26 | #include <config.h> | ||
27 | #endif | ||
28 | |||
23 | #include <string.h> | 29 | #include <string.h> |
24 | #include <stdlib.h> | 30 | #include <stdlib.h> |
25 | #define _GNU_SOURCE 1 | 31 | #define _GNU_SOURCE 1 |
26 | #define __USE_GNU 1 | 32 | #define __USE_GNU 1 |
27 | #include <stdio.h> | 33 | #include <stdio.h> |
28 | #include <ctype.h> | 34 | #include <ctype.h> |
29 | #include <glib.h> | 35 | #include <unistd.h> |
30 | #include <libtasn1.h> | ||
31 | #include <gnutls/x509.h> | ||
32 | #include <plist/plist.h> | 36 | #include <plist/plist.h> |
37 | #include <libimobiledevice-glue/utils.h> | ||
33 | 38 | ||
34 | #include "property_list_service.h" | 39 | #include "property_list_service.h" |
35 | #include "lockdown.h" | 40 | #include "lockdown.h" |
36 | #include "idevice.h" | 41 | #include "idevice.h" |
37 | #include "debug.h" | 42 | #include "common/debug.h" |
38 | #include "userpref.h" | 43 | #include "common/userpref.h" |
39 | 44 | #include "asprintf.h" | |
40 | #define RESULT_SUCCESS 0 | 45 | |
41 | #define RESULT_FAILURE 1 | 46 | #ifdef WIN32 |
42 | 47 | #include <windows.h> | |
43 | const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { | 48 | #define sleep(x) Sleep(x*1000) |
44 | {"PKCS1", 536872976, 0}, | 49 | #endif |
45 | {0, 1073741836, 0}, | 50 | |
46 | {"RSAPublicKey", 536870917, 0}, | 51 | struct st_lockdownd_error_str_map { |
47 | {"modulus", 1073741827, 0}, | 52 | const char *lockdown_errstr; |
48 | {"publicExponent", 3, 0}, | 53 | const char *errstr; |
49 | {0, 0, 0} | 54 | lockdownd_error_t errcode; |
55 | }; | ||
56 | |||
57 | static struct st_lockdownd_error_str_map lockdownd_error_str_map[] = { | ||
58 | { "InvalidResponse", "Invalid response", LOCKDOWN_E_INVALID_RESPONSE }, | ||
59 | { "MissingKey", "Missing key", LOCKDOWN_E_MISSING_KEY }, | ||
60 | { "MissingValue", "Missing value", LOCKDOWN_E_MISSING_VALUE }, | ||
61 | { "GetProhibited", "Get value prohibited", LOCKDOWN_E_GET_PROHIBITED }, | ||
62 | { "SetProhibited", "Set value prohibited", LOCKDOWN_E_SET_PROHIBITED }, | ||
63 | { "RemoveProhibited", "Remove value prohibited", LOCKDOWN_E_REMOVE_PROHIBITED }, | ||
64 | { "ImmutableValue", "Immutable value", LOCKDOWN_E_IMMUTABLE_VALUE }, | ||
65 | { "PasswordProtected", "Password protected", LOCKDOWN_E_PASSWORD_PROTECTED }, | ||
66 | { "UserDeniedPairing", "User denied pairing", LOCKDOWN_E_USER_DENIED_PAIRING }, | ||
67 | { "PairingDialogResponsePending", "Pairing dialog response pending", LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING }, | ||
68 | { "MissingHostID", "Missing HostID", LOCKDOWN_E_MISSING_HOST_ID }, | ||
69 | { "InvalidHostID", "Invalid HostID", LOCKDOWN_E_INVALID_HOST_ID }, | ||
70 | { "SessionActive", "Session active", LOCKDOWN_E_SESSION_ACTIVE }, | ||
71 | { "SessionInactive", "Session inactive", LOCKDOWN_E_SESSION_INACTIVE }, | ||
72 | { "MissingSessionID", "Missing session ID", LOCKDOWN_E_MISSING_SESSION_ID }, | ||
73 | { "InvalidSessionID", "Invalid session ID", LOCKDOWN_E_INVALID_SESSION_ID }, | ||
74 | { "MissingService", "Missing service", LOCKDOWN_E_MISSING_SERVICE }, | ||
75 | { "InvalidService", "Invalid service", LOCKDOWN_E_INVALID_SERVICE }, | ||
76 | { "ServiceLimit", "Service limit reached", LOCKDOWN_E_SERVICE_LIMIT }, | ||
77 | { "MissingPairRecord", "Missing pair record", LOCKDOWN_E_MISSING_PAIR_RECORD }, | ||
78 | { "SavePairRecordFailed", "Saving pair record failed", LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED }, | ||
79 | { "InvalidPairRecord", "Invalid pair record", LOCKDOWN_E_INVALID_PAIR_RECORD }, | ||
80 | { "InvalidActivationRecord", "Invalid activation record", LOCKDOWN_E_INVALID_ACTIVATION_RECORD }, | ||
81 | { "MissingActivationRecord", "Missing activation record", LOCKDOWN_E_MISSING_ACTIVATION_RECORD }, | ||
82 | { "ServiceProhibited", "Service prohibited", LOCKDOWN_E_SERVICE_PROHIBITED }, | ||
83 | { "EscrowLocked", "Escrow lockded", LOCKDOWN_E_ESCROW_LOCKED }, | ||
84 | { "PairingProhibitedOverThisConnection", "Pairing prohibited over this connection", LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION }, | ||
85 | { "FMiPProtected", "Find My iPhone/iPod/iPad protected", LOCKDOWN_E_FMIP_PROTECTED }, | ||
86 | { "MCProtected", "MC protected" , LOCKDOWN_E_MC_PROTECTED }, | ||
87 | { "MCChallengeRequired", "MC challenge required", LOCKDOWN_E_MC_CHALLENGE_REQUIRED }, | ||
88 | { NULL, NULL, 0 } | ||
50 | }; | 89 | }; |
51 | 90 | ||
52 | /** | 91 | /** |
92 | * Convert an error string identifier to a lockdownd_error_t value. | ||
93 | * Used internally to get correct error codes from a response. | ||
94 | * | ||
95 | * @param name The error name to convert. | ||
96 | * | ||
97 | * @return A matching lockdownd_error_t error code, | ||
98 | * LOCKDOWN_E_UNKNOWN_ERROR otherwise. | ||
99 | */ | ||
100 | static lockdownd_error_t lockdownd_strtoerr(const char* name) | ||
101 | { | ||
102 | lockdownd_error_t err = LOCKDOWN_E_UNKNOWN_ERROR; | ||
103 | int i = 0; | ||
104 | while (lockdownd_error_str_map[i].lockdown_errstr) { | ||
105 | if (strcmp(lockdownd_error_str_map[i].lockdown_errstr, name) == 0) { | ||
106 | return lockdownd_error_str_map[i].errcode; | ||
107 | } | ||
108 | i++; | ||
109 | } | ||
110 | return err; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Convert a property_list_service_error_t value to a lockdownd_error_t | ||
115 | * value. Used internally to get correct error codes. | ||
116 | * | ||
117 | * @param err A property_list_service_error_t error code | ||
118 | * | ||
119 | * @return A matching lockdownd_error_t error code, | ||
120 | * LOCKDOWND_E_UNKNOWN_ERROR otherwise. | ||
121 | */ | ||
122 | static lockdownd_error_t lockdownd_error(property_list_service_error_t err) | ||
123 | { | ||
124 | switch (err) { | ||
125 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | ||
126 | return LOCKDOWN_E_SUCCESS; | ||
127 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | ||
128 | return LOCKDOWN_E_INVALID_ARG; | ||
129 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | ||
130 | return LOCKDOWN_E_PLIST_ERROR; | ||
131 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | ||
132 | return LOCKDOWN_E_MUX_ERROR; | ||
133 | case PROPERTY_LIST_SERVICE_E_SSL_ERROR: | ||
134 | return LOCKDOWN_E_SSL_ERROR; | ||
135 | case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: | ||
136 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
137 | default: | ||
138 | break; | ||
139 | } | ||
140 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
141 | } | ||
142 | |||
143 | /** | ||
53 | * Internally used function for checking the result from lockdown's answer | 144 | * Internally used function for checking the result from lockdown's answer |
54 | * plist to a previously sent request. | 145 | * plist to a previously sent request. |
55 | * | 146 | * |
@@ -57,58 +148,66 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { | |||
57 | * @param query_match Name of the request to match or NULL if no match is | 148 | * @param query_match Name of the request to match or NULL if no match is |
58 | * required. | 149 | * required. |
59 | * | 150 | * |
60 | * @return RESULT_SUCCESS when the result is 'Success', | 151 | * @return LOCKDOWN_E_SUCCESS when the result is 'Success', |
61 | * RESULT_FAILURE when the result is 'Failure', | 152 | * LOCKDOWN_E_UNKNOWN_ERROR when the result is 'Failure', |
62 | * or a negative value if an error occured during evaluation. | 153 | * or a specific error code if derieved from the result. |
63 | */ | 154 | */ |
64 | static int lockdown_check_result(plist_t dict, const char *query_match) | 155 | lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match) |
65 | { | 156 | { |
66 | int ret = -1; | 157 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
67 | 158 | ||
68 | plist_t query_node = plist_dict_get_item(dict, "Request"); | 159 | plist_t query_node = plist_dict_get_item(dict, "Request"); |
69 | if (!query_node) { | 160 | if (!query_node) { |
70 | return ret; | 161 | return ret; |
71 | } | 162 | } |
163 | |||
72 | if (plist_get_node_type(query_node) != PLIST_STRING) { | 164 | if (plist_get_node_type(query_node) != PLIST_STRING) { |
73 | return ret; | 165 | return ret; |
74 | } else { | ||
75 | char *query_value = NULL; | ||
76 | plist_get_string_val(query_node, &query_value); | ||
77 | if (!query_value) { | ||
78 | return ret; | ||
79 | } | ||
80 | if (query_match && (strcmp(query_value, query_match) != 0)) { | ||
81 | free(query_value); | ||
82 | return ret; | ||
83 | } | ||
84 | free(query_value); | ||
85 | } | 166 | } |
86 | 167 | ||
87 | plist_t result_node = plist_dict_get_item(dict, "Result"); | 168 | const char *query_value = plist_get_string_ptr(query_node, NULL); |
88 | if (!result_node) { | 169 | if (!query_value) { |
89 | return ret; | 170 | return ret; |
90 | } | 171 | } |
91 | 172 | ||
92 | plist_type result_type = plist_get_node_type(result_node); | 173 | if (query_match && (strcmp(query_value, query_match) != 0)) { |
93 | 174 | return ret; | |
94 | if (result_type == PLIST_STRING) { | 175 | } |
95 | |||
96 | char *result_value = NULL; | ||
97 | 176 | ||
98 | plist_get_string_val(result_node, &result_value); | 177 | /* Check for 'Error' in reply */ |
178 | plist_t err_node = plist_dict_get_item(dict, "Error"); | ||
179 | if (err_node) { | ||
180 | if (plist_get_node_type(err_node) == PLIST_STRING) { | ||
181 | const char *err_value = plist_get_string_ptr(err_node, NULL); | ||
182 | if (err_value) { | ||
183 | debug_info("ERROR: %s", err_value); | ||
184 | ret = lockdownd_strtoerr(err_value); | ||
185 | } else { | ||
186 | debug_info("ERROR: unknown error occurred"); | ||
187 | } | ||
188 | } | ||
189 | return ret; | ||
190 | } | ||
99 | 191 | ||
192 | plist_t result_node = plist_dict_get_item(dict, "Result"); | ||
193 | if (!result_node) { | ||
194 | /* With iOS 5+ 'Result' is not present anymore. | ||
195 | If there is no 'Error', we can just assume success. */ | ||
196 | return LOCKDOWN_E_SUCCESS; | ||
197 | } | ||
198 | if (plist_get_node_type(result_node) == PLIST_STRING) { | ||
199 | const char *result_value = plist_get_string_ptr(result_node, NULL); | ||
100 | if (result_value) { | 200 | if (result_value) { |
101 | if (!strcmp(result_value, "Success")) { | 201 | if (!strcmp(result_value, "Success")) { |
102 | ret = RESULT_SUCCESS; | 202 | ret = LOCKDOWN_E_SUCCESS; |
103 | } else if (!strcmp(result_value, "Failure")) { | 203 | } else if (!strcmp(result_value, "Failure")) { |
104 | ret = RESULT_FAILURE; | 204 | ret = LOCKDOWN_E_UNKNOWN_ERROR; |
105 | } else { | 205 | } else { |
106 | debug_info("ERROR: unknown result value '%s'", result_value); | 206 | debug_info("ERROR: unknown result value '%s'", result_value); |
107 | } | 207 | } |
108 | } | 208 | } |
109 | if (result_value) | ||
110 | free(result_value); | ||
111 | } | 209 | } |
210 | |||
112 | return ret; | 211 | return ret; |
113 | } | 212 | } |
114 | 213 | ||
@@ -123,20 +222,10 @@ static void plist_dict_add_label(plist_t plist, const char *label) | |||
123 | { | 222 | { |
124 | if (plist && label) { | 223 | if (plist && label) { |
125 | if (plist_get_node_type(plist) == PLIST_DICT) | 224 | if (plist_get_node_type(plist) == PLIST_DICT) |
126 | plist_dict_insert_item(plist, "Label", plist_new_string(label)); | 225 | plist_dict_set_item(plist, "Label", plist_new_string(label)); |
127 | } | 226 | } |
128 | } | 227 | } |
129 | 228 | ||
130 | /** | ||
131 | * Closes the lockdownd session by sending the StopSession request. | ||
132 | * | ||
133 | * @see lockdownd_start_session | ||
134 | * | ||
135 | * @param client The lockdown client | ||
136 | * @param session_id The id of a running session | ||
137 | * | ||
138 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
139 | */ | ||
140 | lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) | 229 | lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) |
141 | { | 230 | { |
142 | if (!client) | 231 | if (!client) |
@@ -151,8 +240,8 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char * | |||
151 | 240 | ||
152 | plist_t dict = plist_new_dict(); | 241 | plist_t dict = plist_new_dict(); |
153 | plist_dict_add_label(dict, client->label); | 242 | plist_dict_add_label(dict, client->label); |
154 | plist_dict_insert_item(dict,"Request", plist_new_string("StopSession")); | 243 | plist_dict_set_item(dict,"Request", plist_new_string("StopSession")); |
155 | plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id)); | 244 | plist_dict_set_item(dict,"SessionID", plist_new_string(session_id)); |
156 | 245 | ||
157 | debug_info("stopping session %s", session_id); | 246 | debug_info("stopping session %s", session_id); |
158 | 247 | ||
@@ -168,64 +257,74 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char * | |||
168 | return LOCKDOWN_E_PLIST_ERROR; | 257 | return LOCKDOWN_E_PLIST_ERROR; |
169 | } | 258 | } |
170 | 259 | ||
171 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 260 | ret = lockdown_check_result(dict, "StopSession"); |
172 | if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) { | 261 | if (ret == LOCKDOWN_E_SUCCESS) { |
173 | debug_info("success"); | 262 | debug_info("success"); |
174 | ret = LOCKDOWN_E_SUCCESS; | ||
175 | } | 263 | } |
264 | |||
176 | plist_free(dict); | 265 | plist_free(dict); |
177 | dict = NULL; | 266 | dict = NULL; |
267 | |||
268 | if (client->session_id) { | ||
269 | free(client->session_id); | ||
270 | client->session_id = NULL; | ||
271 | } | ||
272 | |||
178 | if (client->ssl_enabled) { | 273 | if (client->ssl_enabled) { |
179 | property_list_service_disable_ssl(client->parent); | 274 | property_list_service_disable_ssl(client->parent); |
275 | client->ssl_enabled = 0; | ||
180 | } | 276 | } |
277 | |||
181 | return ret; | 278 | return ret; |
182 | } | 279 | } |
183 | 280 | ||
184 | /** | 281 | static 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 | */ | ||
192 | lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) | ||
193 | { | 282 | { |
194 | if (!client) | 283 | if (!client) |
195 | return LOCKDOWN_E_INVALID_ARG; | 284 | return LOCKDOWN_E_INVALID_ARG; |
196 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
197 | 285 | ||
198 | if (client->session_id) { | 286 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
199 | lockdownd_stop_session(client, client->session_id); | ||
200 | free(client->session_id); | ||
201 | } | ||
202 | 287 | ||
203 | if (client->parent) { | 288 | if (client->parent) { |
204 | lockdownd_goodbye(client); | ||
205 | |||
206 | if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { | 289 | if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { |
207 | ret = LOCKDOWN_E_SUCCESS; | 290 | ret = LOCKDOWN_E_SUCCESS; |
208 | } | 291 | } |
209 | } | 292 | } |
210 | 293 | ||
211 | if (client->uuid) { | 294 | if (client->session_id) { |
212 | free(client->uuid); | 295 | free(client->session_id); |
296 | client->session_id = NULL; | ||
213 | } | 297 | } |
214 | if (client->label) { | 298 | if (client->label) { |
215 | free(client->label); | 299 | free(client->label); |
216 | } | 300 | } |
301 | if (client->cu_key) { | ||
302 | free(client->cu_key); | ||
303 | client->cu_key = NULL; | ||
304 | } | ||
217 | 305 | ||
218 | free(client); | 306 | free(client); |
307 | client = NULL; | ||
308 | |||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) | ||
313 | { | ||
314 | if (!client) | ||
315 | return LOCKDOWN_E_INVALID_ARG; | ||
316 | |||
317 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
318 | |||
319 | if (client->session_id) { | ||
320 | lockdownd_stop_session(client, client->session_id); | ||
321 | } | ||
322 | |||
323 | ret = lockdownd_client_free_simple(client); | ||
324 | |||
219 | return ret; | 325 | return ret; |
220 | } | 326 | } |
221 | 327 | ||
222 | /** | ||
223 | * Sets the label to send for requests to lockdownd. | ||
224 | * | ||
225 | * @param client The lockdown client | ||
226 | * @param label The label to set or NULL to disable sending a label | ||
227 | * | ||
228 | */ | ||
229 | void lockdownd_client_set_label(lockdownd_client_t client, const char *label) | 328 | void lockdownd_client_set_label(lockdownd_client_t client, const char *label) |
230 | { | 329 | { |
231 | if (client) { | 330 | if (client) { |
@@ -236,69 +335,22 @@ void lockdownd_client_set_label(lockdownd_client_t client, const char *label) | |||
236 | } | 335 | } |
237 | } | 336 | } |
238 | 337 | ||
239 | /** | ||
240 | * Receives a plist from lockdownd. | ||
241 | * | ||
242 | * @param client The lockdownd client | ||
243 | * @param plist The plist to store the received data | ||
244 | * | ||
245 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | ||
246 | * plist is NULL | ||
247 | */ | ||
248 | lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) | 338 | lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) |
249 | { | 339 | { |
250 | if (!client || !plist || (plist && *plist)) | 340 | if (!client || !plist || (plist && *plist)) |
251 | return LOCKDOWN_E_INVALID_ARG; | 341 | return LOCKDOWN_E_INVALID_ARG; |
252 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | ||
253 | property_list_service_error_t err; | ||
254 | |||
255 | err = property_list_service_receive_plist(client->parent, plist); | ||
256 | if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { | ||
257 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
258 | } | ||
259 | |||
260 | if (!*plist) | ||
261 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
262 | 342 | ||
263 | return ret; | 343 | return lockdownd_error(property_list_service_receive_plist(client->parent, plist)); |
264 | } | 344 | } |
265 | 345 | ||
266 | /** | ||
267 | * Sends a plist to lockdownd. | ||
268 | * | ||
269 | * @note This function is low-level and should only be used if you need to send | ||
270 | * a new type of message. | ||
271 | * | ||
272 | * @param client The lockdownd client | ||
273 | * @param plist The plist to send | ||
274 | * | ||
275 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | ||
276 | * plist is NULL | ||
277 | */ | ||
278 | lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) | 346 | lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) |
279 | { | 347 | { |
280 | if (!client || !plist) | 348 | if (!client || !plist) |
281 | return LOCKDOWN_E_INVALID_ARG; | 349 | return LOCKDOWN_E_INVALID_ARG; |
282 | 350 | ||
283 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | 351 | return lockdownd_error(property_list_service_send_xml_plist(client->parent, plist)); |
284 | idevice_error_t err; | ||
285 | |||
286 | err = property_list_service_send_xml_plist(client->parent, plist); | ||
287 | if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { | ||
288 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
289 | } | ||
290 | return ret; | ||
291 | } | 352 | } |
292 | 353 | ||
293 | /** | ||
294 | * Query the type of the service daemon. Depending on whether the device is | ||
295 | * queried in normal mode or restore mode, different types will be returned. | ||
296 | * | ||
297 | * @param client The lockdownd client | ||
298 | * @param type The type returned by the service daemon. Pass NULL to ignore. | ||
299 | * | ||
300 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
301 | */ | ||
302 | lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | 354 | lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) |
303 | { | 355 | { |
304 | if (!client) | 356 | if (!client) |
@@ -308,7 +360,7 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | |||
308 | 360 | ||
309 | plist_t dict = plist_new_dict(); | 361 | plist_t dict = plist_new_dict(); |
310 | plist_dict_add_label(dict, client->label); | 362 | plist_dict_add_label(dict, client->label); |
311 | plist_dict_insert_item(dict,"Request", plist_new_string("QueryType")); | 363 | plist_dict_set_item(dict,"Request", plist_new_string("QueryType")); |
312 | 364 | ||
313 | debug_info("called"); | 365 | debug_info("called"); |
314 | ret = lockdownd_send(client, dict); | 366 | ret = lockdownd_send(client, dict); |
@@ -322,14 +374,21 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | |||
322 | return ret; | 374 | return ret; |
323 | 375 | ||
324 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 376 | ret = LOCKDOWN_E_UNKNOWN_ERROR; |
325 | if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) { | 377 | plist_t type_node = plist_dict_get_item(dict, "Type"); |
378 | if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) { | ||
379 | char* typestr = NULL; | ||
380 | plist_get_string_val(type_node, &typestr); | ||
381 | debug_info("success with type %s", typestr); | ||
326 | /* return the type if requested */ | 382 | /* return the type if requested */ |
327 | if (type != NULL) { | 383 | if (type != NULL) { |
328 | plist_t type_node = plist_dict_get_item(dict, "Type"); | 384 | *type = typestr; |
329 | plist_get_string_val(type_node, type); | 385 | } else { |
386 | free(typestr); | ||
330 | } | 387 | } |
331 | debug_info("success with type %s", *type); | ||
332 | ret = LOCKDOWN_E_SUCCESS; | 388 | ret = LOCKDOWN_E_SUCCESS; |
389 | } else { | ||
390 | debug_info("hmm. QueryType response does not contain a type?!"); | ||
391 | debug_plist(dict); | ||
333 | } | 392 | } |
334 | plist_free(dict); | 393 | plist_free(dict); |
335 | dict = NULL; | 394 | dict = NULL; |
@@ -337,16 +396,6 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | |||
337 | return ret; | 396 | return ret; |
338 | } | 397 | } |
339 | 398 | ||
340 | /** | ||
341 | * Retrieves a preferences plist using an optional domain and/or key name. | ||
342 | * | ||
343 | * @param client An initialized lockdownd client. | ||
344 | * @param domain The domain to query on or NULL for global domain | ||
345 | * @param key The key name to request or NULL to query for all keys | ||
346 | * @param value A plist node representing the result value node | ||
347 | * | ||
348 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
349 | */ | ||
350 | lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) | 399 | lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) |
351 | { | 400 | { |
352 | if (!client) | 401 | if (!client) |
@@ -359,12 +408,12 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom | |||
359 | dict = plist_new_dict(); | 408 | dict = plist_new_dict(); |
360 | plist_dict_add_label(dict, client->label); | 409 | plist_dict_add_label(dict, client->label); |
361 | if (domain) { | 410 | if (domain) { |
362 | plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); | 411 | plist_dict_set_item(dict,"Domain", plist_new_string(domain)); |
363 | } | 412 | } |
364 | if (key) { | 413 | if (key) { |
365 | plist_dict_insert_item(dict,"Key", plist_new_string(key)); | 414 | plist_dict_set_item(dict,"Key", plist_new_string(key)); |
366 | } | 415 | } |
367 | plist_dict_insert_item(dict,"Request", plist_new_string("GetValue")); | 416 | plist_dict_set_item(dict,"Request", plist_new_string("GetValue")); |
368 | 417 | ||
369 | /* send to device */ | 418 | /* send to device */ |
370 | ret = lockdownd_send(client, dict); | 419 | ret = lockdownd_send(client, dict); |
@@ -380,10 +429,11 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom | |||
380 | if (ret != LOCKDOWN_E_SUCCESS) | 429 | if (ret != LOCKDOWN_E_SUCCESS) |
381 | return ret; | 430 | return ret; |
382 | 431 | ||
383 | if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) { | 432 | ret = lockdown_check_result(dict, "GetValue"); |
433 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
384 | debug_info("success"); | 434 | debug_info("success"); |
385 | ret = LOCKDOWN_E_SUCCESS; | ||
386 | } | 435 | } |
436 | |||
387 | if (ret != LOCKDOWN_E_SUCCESS) { | 437 | if (ret != LOCKDOWN_E_SUCCESS) { |
388 | plist_free(dict); | 438 | plist_free(dict); |
389 | return ret; | 439 | return ret; |
@@ -400,17 +450,6 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom | |||
400 | return ret; | 450 | return ret; |
401 | } | 451 | } |
402 | 452 | ||
403 | /** | ||
404 | * Sets a preferences value using a plist and optional by domain and/or key name. | ||
405 | * | ||
406 | * @param client an initialized lockdownd client. | ||
407 | * @param domain the domain to query on or NULL for global domain | ||
408 | * @param key the key name to set the value or NULL to set a value dict plist | ||
409 | * @param value a plist node of any node type representing the value to set | ||
410 | * | ||
411 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | ||
412 | * value is NULL | ||
413 | */ | ||
414 | lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) | 453 | lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) |
415 | { | 454 | { |
416 | if (!client || !value) | 455 | if (!client || !value) |
@@ -423,13 +462,13 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom | |||
423 | dict = plist_new_dict(); | 462 | dict = plist_new_dict(); |
424 | plist_dict_add_label(dict, client->label); | 463 | plist_dict_add_label(dict, client->label); |
425 | if (domain) { | 464 | if (domain) { |
426 | plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); | 465 | plist_dict_set_item(dict,"Domain", plist_new_string(domain)); |
427 | } | 466 | } |
428 | if (key) { | 467 | if (key) { |
429 | plist_dict_insert_item(dict,"Key", plist_new_string(key)); | 468 | plist_dict_set_item(dict,"Key", plist_new_string(key)); |
430 | } | 469 | } |
431 | plist_dict_insert_item(dict,"Request", plist_new_string("SetValue")); | 470 | plist_dict_set_item(dict,"Request", plist_new_string("SetValue")); |
432 | plist_dict_insert_item(dict,"Value", value); | 471 | plist_dict_set_item(dict,"Value", value); |
433 | 472 | ||
434 | /* send to device */ | 473 | /* send to device */ |
435 | ret = lockdownd_send(client, dict); | 474 | ret = lockdownd_send(client, dict); |
@@ -445,9 +484,9 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom | |||
445 | if (ret != LOCKDOWN_E_SUCCESS) | 484 | if (ret != LOCKDOWN_E_SUCCESS) |
446 | return ret; | 485 | return ret; |
447 | 486 | ||
448 | if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) { | 487 | ret = lockdown_check_result(dict, "SetValue"); |
488 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
449 | debug_info("success"); | 489 | debug_info("success"); |
450 | ret = LOCKDOWN_E_SUCCESS; | ||
451 | } | 490 | } |
452 | 491 | ||
453 | if (ret != LOCKDOWN_E_SUCCESS) { | 492 | if (ret != LOCKDOWN_E_SUCCESS) { |
@@ -459,17 +498,6 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom | |||
459 | return ret; | 498 | return ret; |
460 | } | 499 | } |
461 | 500 | ||
462 | /** | ||
463 | * Removes a preference node by domain and/or key name. | ||
464 | * | ||
465 | * @note: Use with caution as this could remove vital information on the device | ||
466 | * | ||
467 | * @param client An initialized lockdownd client. | ||
468 | * @param domain The domain to query on or NULL for global domain | ||
469 | * @param key The key name to remove or NULL remove all keys for the current domain | ||
470 | * | ||
471 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
472 | */ | ||
473 | lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) | 501 | lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) |
474 | { | 502 | { |
475 | if (!client) | 503 | if (!client) |
@@ -482,12 +510,12 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * | |||
482 | dict = plist_new_dict(); | 510 | dict = plist_new_dict(); |
483 | plist_dict_add_label(dict, client->label); | 511 | plist_dict_add_label(dict, client->label); |
484 | if (domain) { | 512 | if (domain) { |
485 | plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); | 513 | plist_dict_set_item(dict,"Domain", plist_new_string(domain)); |
486 | } | 514 | } |
487 | if (key) { | 515 | if (key) { |
488 | plist_dict_insert_item(dict,"Key", plist_new_string(key)); | 516 | plist_dict_set_item(dict,"Key", plist_new_string(key)); |
489 | } | 517 | } |
490 | plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue")); | 518 | plist_dict_set_item(dict,"Request", plist_new_string("RemoveValue")); |
491 | 519 | ||
492 | /* send to device */ | 520 | /* send to device */ |
493 | ret = lockdownd_send(client, dict); | 521 | ret = lockdownd_send(client, dict); |
@@ -503,9 +531,9 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * | |||
503 | if (ret != LOCKDOWN_E_SUCCESS) | 531 | if (ret != LOCKDOWN_E_SUCCESS) |
504 | return ret; | 532 | return ret; |
505 | 533 | ||
506 | if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) { | 534 | ret = lockdown_check_result(dict, "RemoveValue"); |
535 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
507 | debug_info("success"); | 536 | debug_info("success"); |
508 | ret = LOCKDOWN_E_SUCCESS; | ||
509 | } | 537 | } |
510 | 538 | ||
511 | if (ret != LOCKDOWN_E_SUCCESS) { | 539 | if (ret != LOCKDOWN_E_SUCCESS) { |
@@ -517,16 +545,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * | |||
517 | return ret; | 545 | return ret; |
518 | } | 546 | } |
519 | 547 | ||
520 | /** | 548 | lockdownd_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 | */ | ||
529 | lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uuid) | ||
530 | { | 549 | { |
531 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 550 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
532 | plist_t value = NULL; | 551 | plist_t value = NULL; |
@@ -535,7 +554,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu | |||
535 | if (ret != LOCKDOWN_E_SUCCESS) { | 554 | if (ret != LOCKDOWN_E_SUCCESS) { |
536 | return ret; | 555 | return ret; |
537 | } | 556 | } |
538 | plist_get_string_val(value, uuid); | 557 | plist_get_string_val(value, udid); |
539 | 558 | ||
540 | plist_free(value); | 559 | plist_free(value); |
541 | value = NULL; | 560 | value = NULL; |
@@ -551,7 +570,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu | |||
551 | * | 570 | * |
552 | * @return LOCKDOWN_E_SUCCESS on success | 571 | * @return LOCKDOWN_E_SUCCESS on success |
553 | */ | 572 | */ |
554 | lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key) | 573 | static lockdownd_error_t lockdownd_get_device_public_key_as_key_data(lockdownd_client_t client, key_data_t *public_key) |
555 | { | 574 | { |
556 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 575 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
557 | plist_t value = NULL; | 576 | plist_t value = NULL; |
@@ -572,15 +591,6 @@ lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnu | |||
572 | return ret; | 591 | return ret; |
573 | } | 592 | } |
574 | 593 | ||
575 | /** | ||
576 | * Retrieves the name of the device from lockdownd set by the user. | ||
577 | * | ||
578 | * @param client An initialized lockdownd client. | ||
579 | * @param device_name Holds the name of the device. The caller is | ||
580 | * responsible for freeing the memory. | ||
581 | * | ||
582 | * @return LOCKDOWN_E_SUCCESS on success | ||
583 | */ | ||
584 | lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) | 594 | lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) |
585 | { | 595 | { |
586 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 596 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
@@ -598,29 +608,19 @@ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **de | |||
598 | return ret; | 608 | return ret; |
599 | } | 609 | } |
600 | 610 | ||
601 | /** | ||
602 | * Creates a new lockdownd client for the device. | ||
603 | * | ||
604 | * @note This function does not pair with the device or start a session. This | ||
605 | * has to be done manually by the caller after the client is created. | ||
606 | * The device disconnects automatically if the lockdown connection idles | ||
607 | * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon | ||
608 | * as the connection is no longer needed. | ||
609 | * | ||
610 | * @param device The device to create a lockdownd client for | ||
611 | * @param client The pointer to the location of the new lockdownd_client | ||
612 | * @param label The label to use for communication. Usually the program name. | ||
613 | * | ||
614 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
615 | */ | ||
616 | lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) | 611 | lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) |
617 | { | 612 | { |
618 | if (!client) | 613 | if (!device || !client) |
619 | return LOCKDOWN_E_INVALID_ARG; | 614 | return LOCKDOWN_E_INVALID_ARG; |
620 | 615 | ||
616 | static struct lockdownd_service_descriptor service = { | ||
617 | .port = 0xf27e, | ||
618 | .ssl_enabled = 0 | ||
619 | }; | ||
620 | |||
621 | property_list_service_client_t plistclient = NULL; | 621 | property_list_service_client_t plistclient = NULL; |
622 | if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 622 | if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { |
623 | debug_info("could not connect to lockdownd (device %s)", device->uuid); | 623 | debug_info("could not connect to lockdownd (device %s)", device->udid); |
624 | return LOCKDOWN_E_MUX_ERROR; | 624 | return LOCKDOWN_E_MUX_ERROR; |
625 | } | 625 | } |
626 | 626 | ||
@@ -628,7 +628,14 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli | |||
628 | client_loc->parent = plistclient; | 628 | client_loc->parent = plistclient; |
629 | client_loc->ssl_enabled = 0; | 629 | client_loc->ssl_enabled = 0; |
630 | client_loc->session_id = NULL; | 630 | client_loc->session_id = NULL; |
631 | client_loc->uuid = NULL; | 631 | client_loc->device = device; |
632 | client_loc->cu_key = NULL; | ||
633 | client_loc->cu_key_len = 0; | ||
634 | |||
635 | if (device->udid) { | ||
636 | debug_info("device udid: %s", device->udid); | ||
637 | } | ||
638 | |||
632 | client_loc->label = label ? strdup(label) : NULL; | 639 | client_loc->label = label ? strdup(label) : NULL; |
633 | 640 | ||
634 | *client = client_loc; | 641 | *client = client_loc; |
@@ -636,23 +643,6 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli | |||
636 | return LOCKDOWN_E_SUCCESS; | 643 | return LOCKDOWN_E_SUCCESS; |
637 | } | 644 | } |
638 | 645 | ||
639 | /** | ||
640 | * Creates a new lockdownd client for the device and starts initial handshake. | ||
641 | * The handshake consists out of query_type, validate_pair, pair and | ||
642 | * start_session calls. It uses the internal pairing record management. | ||
643 | * | ||
644 | * @note The device disconnects automatically if the lockdown connection idles | ||
645 | * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon | ||
646 | * as the connection is no longer needed. | ||
647 | * | ||
648 | * @param device The device to create a lockdownd client for | ||
649 | * @param client The pointer to the location of the new lockdownd_client | ||
650 | * @param label The label to use for communication. Usually the program name. | ||
651 | * Pass NULL to disable sending the label in requests to lockdownd. | ||
652 | * | ||
653 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
654 | * LOCKDOWN_E_INVALID_CONF if configuration data is wrong | ||
655 | */ | ||
656 | lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) | 646 | lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) |
657 | { | 647 | { |
658 | if (!client) | 648 | if (!client) |
@@ -660,6 +650,7 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown | |||
660 | 650 | ||
661 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | 651 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; |
662 | lockdownd_client_t client_loc = NULL; | 652 | lockdownd_client_t client_loc = NULL; |
653 | plist_t pair_record = NULL; | ||
663 | char *host_id = NULL; | 654 | char *host_id = NULL; |
664 | char *type = NULL; | 655 | char *type = NULL; |
665 | 656 | ||
@@ -670,60 +661,125 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown | |||
670 | } | 661 | } |
671 | 662 | ||
672 | /* perform handshake */ | 663 | /* perform handshake */ |
673 | if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) { | 664 | ret = lockdownd_query_type(client_loc, &type); |
665 | if (LOCKDOWN_E_SUCCESS != ret) { | ||
674 | debug_info("QueryType failed in the lockdownd client."); | 666 | debug_info("QueryType failed in the lockdownd client."); |
675 | ret = LOCKDOWN_E_NOT_ENOUGH_DATA; | 667 | } else if (strcmp("com.apple.mobile.lockdown", type) != 0) { |
676 | } else { | 668 | debug_info("Warning QueryType request returned \"%s\".", type); |
677 | if (strcmp("com.apple.mobile.lockdown", type)) { | 669 | } |
678 | debug_info("Warning QueryType request returned \"%s\".", type); | 670 | free(type); |
671 | |||
672 | if (device->version == 0) { | ||
673 | plist_t p_version = NULL; | ||
674 | if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { | ||
675 | int vers[3] = {0, 0, 0}; | ||
676 | char *s_version = NULL; | ||
677 | plist_get_string_val(p_version, &s_version); | ||
678 | if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { | ||
679 | device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); | ||
680 | } | ||
681 | free(s_version); | ||
679 | } | 682 | } |
680 | if (type) | 683 | plist_free(p_version); |
681 | free(type); | ||
682 | } | 684 | } |
683 | 685 | if (device->device_class == 0) { | |
684 | ret = idevice_get_uuid(device, &client_loc->uuid); | 686 | plist_t p_device_class = NULL; |
685 | if (LOCKDOWN_E_SUCCESS != ret) { | 687 | if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) { |
686 | debug_info("failed to get device uuid."); | 688 | char* s_device_class = NULL; |
689 | plist_get_string_val(p_device_class, &s_device_class); | ||
690 | if (s_device_class != NULL) { | ||
691 | if (!strcmp(s_device_class, "iPhone")) { | ||
692 | device->device_class = DEVICE_CLASS_IPHONE; | ||
693 | } else if (!strcmp(s_device_class, "iPad")) { | ||
694 | device->device_class = DEVICE_CLASS_IPAD; | ||
695 | } else if (!strcmp(s_device_class, "iPod")) { | ||
696 | device->device_class = DEVICE_CLASS_IPOD; | ||
697 | } else if (!strcmp(s_device_class, "Watch")) { | ||
698 | device->device_class = DEVICE_CLASS_WATCH; | ||
699 | } else if (!strcmp(s_device_class, "AppleTV")) { | ||
700 | device->device_class = DEVICE_CLASS_APPLETV; | ||
701 | } else { | ||
702 | device->device_class = DEVICE_CLASS_UNKNOWN; | ||
703 | } | ||
704 | free(s_device_class); | ||
705 | } | ||
706 | } | ||
707 | plist_free(p_device_class); | ||
687 | } | 708 | } |
688 | debug_info("device uuid: %s", client_loc->uuid); | ||
689 | 709 | ||
690 | userpref_get_host_id(&host_id); | 710 | userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); |
691 | if (LOCKDOWN_E_SUCCESS == ret && !host_id) { | 711 | if (uerr == USERPREF_E_READ_ERROR) { |
712 | debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); | ||
713 | lockdownd_client_free(client_loc); | ||
714 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
715 | } | ||
716 | if (pair_record) { | ||
717 | pair_record_get_host_id(pair_record, &host_id); | ||
718 | } | ||
719 | if (LOCKDOWN_E_SUCCESS == ret && pair_record && !host_id) { | ||
692 | ret = LOCKDOWN_E_INVALID_CONF; | 720 | ret = LOCKDOWN_E_INVALID_CONF; |
693 | } | 721 | } |
694 | 722 | ||
695 | if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid)) | 723 | if (LOCKDOWN_E_SUCCESS == ret && !pair_record) { |
724 | /* attempt pairing */ | ||
725 | free(host_id); | ||
726 | host_id = NULL; | ||
696 | ret = lockdownd_pair(client_loc, NULL); | 727 | ret = lockdownd_pair(client_loc, NULL); |
728 | } | ||
697 | 729 | ||
698 | /* in any case, we need to validate pairing to receive trusted host status */ | 730 | plist_free(pair_record); |
699 | ret = lockdownd_validate_pair(client_loc, NULL); | 731 | pair_record = NULL; |
700 | 732 | ||
701 | /* if not paired yet, let's do it now */ | 733 | if (device->version < DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) { |
702 | if (LOCKDOWN_E_INVALID_HOST_ID == ret) { | 734 | /* for older devices, we need to validate pairing to receive trusted host status */ |
703 | ret = lockdownd_pair(client_loc, NULL); | 735 | ret = lockdownd_validate_pair(client_loc, NULL); |
704 | if (LOCKDOWN_E_SUCCESS == ret) { | 736 | |
705 | ret = lockdownd_validate_pair(client_loc, NULL); | 737 | /* if not paired yet, let's do it now */ |
738 | if (LOCKDOWN_E_INVALID_HOST_ID == ret) { | ||
739 | free(host_id); | ||
740 | host_id = NULL; | ||
741 | ret = lockdownd_pair(client_loc, NULL); | ||
742 | if (LOCKDOWN_E_SUCCESS == ret) { | ||
743 | ret = lockdownd_validate_pair(client_loc, NULL); | ||
744 | } | ||
706 | } | 745 | } |
707 | } | 746 | } |
708 | 747 | ||
709 | if (LOCKDOWN_E_SUCCESS == ret) { | 748 | if (LOCKDOWN_E_SUCCESS == ret) { |
749 | if (!host_id) { | ||
750 | uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); | ||
751 | if (uerr == USERPREF_E_READ_ERROR) { | ||
752 | debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); | ||
753 | lockdownd_client_free(client_loc); | ||
754 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
755 | } else if (uerr == USERPREF_E_NOENT) { | ||
756 | debug_info("ERROR: No pair record for %s", client_loc->device->udid); | ||
757 | lockdownd_client_free(client_loc); | ||
758 | return LOCKDOWN_E_INVALID_CONF; | ||
759 | } else if (uerr != USERPREF_E_SUCCESS) { | ||
760 | debug_info("ERROR: Failed to retrieve or parse pair record for %s", client_loc->device->udid); | ||
761 | lockdownd_client_free(client_loc); | ||
762 | return LOCKDOWN_E_INVALID_CONF; | ||
763 | } | ||
764 | if (pair_record) { | ||
765 | pair_record_get_host_id(pair_record, &host_id); | ||
766 | plist_free(pair_record); | ||
767 | } | ||
768 | } | ||
769 | |||
710 | ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); | 770 | ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); |
711 | if (LOCKDOWN_E_SUCCESS != ret) { | 771 | if (LOCKDOWN_E_SUCCESS != ret) { |
712 | debug_info("Session opening failed."); | 772 | debug_info("Session opening failed."); |
713 | } | 773 | } |
714 | 774 | ||
715 | if (host_id) { | ||
716 | free(host_id); | ||
717 | host_id = NULL; | ||
718 | } | ||
719 | } | 775 | } |
720 | 776 | ||
721 | if (LOCKDOWN_E_SUCCESS == ret) { | 777 | if (LOCKDOWN_E_SUCCESS == ret) { |
722 | *client = client_loc; | 778 | *client = client_loc; |
723 | } else { | 779 | } else { |
724 | lockdownd_client_free(client_loc); | 780 | lockdownd_client_free(client_loc); |
725 | } | 781 | } |
726 | 782 | free(host_id); | |
727 | return ret; | 783 | return ret; |
728 | } | 784 | } |
729 | 785 | ||
@@ -740,68 +796,77 @@ static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_recor | |||
740 | if (!pair_record) | 796 | if (!pair_record) |
741 | return NULL; | 797 | return NULL; |
742 | 798 | ||
743 | char *host_id_loc = pair_record->host_id; | ||
744 | |||
745 | /* setup request plist */ | 799 | /* setup request plist */ |
746 | plist_t dict = plist_new_dict(); | 800 | plist_t dict = plist_new_dict(); |
747 | plist_dict_insert_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); | 801 | plist_dict_set_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); |
748 | plist_dict_insert_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); | 802 | plist_dict_set_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); |
749 | if (!pair_record->host_id) | 803 | plist_dict_set_item(dict, "HostID", plist_new_string(pair_record->host_id)); |
750 | userpref_get_host_id(&host_id_loc); | 804 | plist_dict_set_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate))); |
751 | plist_dict_insert_item(dict, "HostID", plist_new_string(host_id_loc)); | 805 | plist_dict_set_item(dict, "SystemBUID", plist_new_string(pair_record->system_buid)); |
752 | plist_dict_insert_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate))); | ||
753 | |||
754 | if (!pair_record->host_id) | ||
755 | free(host_id_loc); | ||
756 | 806 | ||
757 | return dict; | 807 | return dict; |
758 | } | 808 | } |
759 | 809 | ||
760 | /** | 810 | /** |
761 | * Generates a new pairing record plist and required certificates for the | 811 | * Generates a pair record plist with required certificates for a specific |
762 | * supplied public key of the device and the host_id of the caller's host | 812 | * device. If a pairing exists, it is loaded from the computer instead of being |
763 | * computer. | 813 | * generated. |
764 | * | 814 | * |
765 | * @param public_key The public key of the device. | 815 | * @param pair_record_plist Holds the pair record. |
766 | * @param host_id The HostID to use for the pair record plist. | ||
767 | * @param pair_record_plist Holds the generated pair record. | ||
768 | * | 816 | * |
769 | * @return LOCKDOWN_E_SUCCESS on success | 817 | * @return LOCKDOWN_E_SUCCESS on success |
770 | */ | 818 | */ |
771 | static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist) | 819 | static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t *pair_record) |
772 | { | 820 | { |
773 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 821 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
774 | 822 | ||
775 | gnutls_datum_t device_cert = { NULL, 0 }; | 823 | key_data_t public_key = { NULL, 0 }; |
776 | gnutls_datum_t host_cert = { NULL, 0 }; | 824 | char* host_id = NULL; |
777 | gnutls_datum_t root_cert = { NULL, 0 }; | 825 | char* system_buid = NULL; |
778 | 826 | ||
779 | ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); | 827 | /* retrieve device public key */ |
828 | ret = lockdownd_get_device_public_key_as_key_data(client, &public_key); | ||
780 | if (ret != LOCKDOWN_E_SUCCESS) { | 829 | if (ret != LOCKDOWN_E_SUCCESS) { |
781 | return ret; | 830 | debug_info("device refused to send public key."); |
831 | goto leave; | ||
832 | } | ||
833 | debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); | ||
834 | |||
835 | *pair_record = plist_new_dict(); | ||
836 | |||
837 | /* generate keys and certificates into pair record */ | ||
838 | userpref_error_t uret = USERPREF_E_SUCCESS; | ||
839 | uret = pair_record_generate_keys_and_certs(*pair_record, public_key); | ||
840 | switch(uret) { | ||
841 | case USERPREF_E_INVALID_ARG: | ||
842 | ret = LOCKDOWN_E_INVALID_ARG; | ||
843 | break; | ||
844 | case USERPREF_E_INVALID_CONF: | ||
845 | ret = LOCKDOWN_E_INVALID_CONF; | ||
846 | break; | ||
847 | case USERPREF_E_SSL_ERROR: | ||
848 | ret = LOCKDOWN_E_SSL_ERROR; | ||
849 | default: | ||
850 | break; | ||
782 | } | 851 | } |
783 | 852 | ||
784 | char *host_id_loc = host_id; | 853 | /* set SystemBUID */ |
854 | userpref_read_system_buid(&system_buid); | ||
855 | if (system_buid) { | ||
856 | plist_dict_set_item(*pair_record, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); | ||
857 | } | ||
785 | 858 | ||
786 | if (!host_id) | 859 | /* set HostID */ |
787 | userpref_get_host_id(&host_id_loc); | 860 | host_id = generate_uuid(); |
861 | pair_record_set_host_id(*pair_record, host_id); | ||
788 | 862 | ||
789 | /* setup request plist */ | 863 | leave: |
790 | *pair_record_plist = plist_new_dict(); | 864 | if (host_id) |
791 | plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size)); | 865 | free(host_id); |
792 | plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size)); | 866 | if (system_buid) |
793 | plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc)); | 867 | free(system_buid); |
794 | plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size)); | 868 | if (public_key.data) |
795 | 869 | free(public_key.data); | |
796 | if (!host_id) | ||
797 | free(host_id_loc); | ||
798 | |||
799 | if (device_cert.data) | ||
800 | free(device_cert.data); | ||
801 | if (host_cert.data) | ||
802 | free(host_cert.data); | ||
803 | if (root_cert.data) | ||
804 | free(root_cert.data); | ||
805 | 870 | ||
806 | return ret; | 871 | return ret; |
807 | } | 872 | } |
@@ -809,11 +874,13 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c | |||
809 | /** | 874 | /** |
810 | * Function used internally by lockdownd_pair() and lockdownd_validate_pair() | 875 | * Function used internally by lockdownd_pair() and lockdownd_validate_pair() |
811 | * | 876 | * |
812 | * @param client The lockdown client to pair with. | 877 | * @param client The lockdown client |
813 | * @param pair_record The pair record to use for pairing. If NULL is passed, then | 878 | * @param pair_record The pair record to use for pairing. If NULL is passed, then |
814 | * the pair records from the current machine are used. New records will be | 879 | * the pair records from the current machine are used. New records will be |
815 | * generated automatically when pairing is done for the first time. | 880 | * generated automatically when pairing is done for the first time. |
816 | * @param verb This is either "Pair", "ValidatePair" or "Unpair". | 881 | * @param verb This is either "Pair", "ValidatePair" or "Unpair". |
882 | * @param options The pairing options to pass. | ||
883 | * @param response If non-NULL a pointer to lockdownd's response dictionary is returned. | ||
817 | * | 884 | * |
818 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | 885 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, |
819 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | 886 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, |
@@ -821,73 +888,103 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c | |||
821 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | 888 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, |
822 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | 889 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id |
823 | */ | 890 | */ |
824 | static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb) | 891 | static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb, plist_t options, plist_t *result) |
825 | { | 892 | { |
826 | if (!client) | 893 | if (!client) |
827 | return LOCKDOWN_E_INVALID_ARG; | 894 | return LOCKDOWN_E_INVALID_ARG; |
828 | 895 | ||
829 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 896 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
830 | plist_t dict = NULL; | 897 | plist_t dict = NULL; |
831 | plist_t dict_record = NULL; | 898 | plist_t pair_record_plist = NULL; |
832 | gnutls_datum_t public_key = { NULL, 0 }; | 899 | plist_t wifi_node = NULL; |
833 | int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ | 900 | int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ |
834 | 901 | ||
835 | if (pair_record && pair_record->host_id) { | 902 | if (pair_record && pair_record->system_buid && pair_record->host_id) { |
836 | /* valid pair_record passed? */ | 903 | /* valid pair_record passed? */ |
837 | if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { | 904 | if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { |
838 | return LOCKDOWN_E_PLIST_ERROR; | 905 | return LOCKDOWN_E_PLIST_ERROR; |
839 | } | 906 | } |
840 | 907 | ||
841 | /* use passed pair_record */ | 908 | /* use passed pair_record */ |
842 | dict_record = lockdownd_pair_record_to_plist(pair_record); | 909 | pair_record_plist = lockdownd_pair_record_to_plist(pair_record); |
843 | 910 | ||
844 | pairing_mode = 1; | 911 | pairing_mode = 1; |
845 | } else { | 912 | } else { |
846 | ret = lockdownd_get_device_public_key(client, &public_key); | 913 | /* generate a new pair record if pairing */ |
847 | if (ret != LOCKDOWN_E_SUCCESS) { | 914 | if (!strcmp("Pair", verb)) { |
848 | if (public_key.data) | 915 | ret = pair_record_generate(client, &pair_record_plist); |
849 | free(public_key.data); | 916 | |
850 | debug_info("device refused to send public key."); | 917 | if (ret != LOCKDOWN_E_SUCCESS) { |
851 | return ret; | 918 | if (pair_record_plist) |
852 | } | 919 | plist_free(pair_record_plist); |
853 | debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); | 920 | return ret; |
854 | /* get libimobiledevice pair_record */ | 921 | } |
855 | ret = generate_pair_record_plist(public_key, NULL, &dict_record); | 922 | |
856 | if (ret != LOCKDOWN_E_SUCCESS) { | 923 | /* get wifi mac now, if we get it later we fail on iOS 7 which causes a reconnect */ |
857 | if (dict_record) | 924 | lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node); |
858 | plist_free(dict_record); | 925 | } else { |
859 | return ret; | 926 | /* use existing pair record */ |
927 | userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record_plist); | ||
928 | if (uerr == USERPREF_E_READ_ERROR) { | ||
929 | debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); | ||
930 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
931 | } else if (uerr == USERPREF_E_NOENT) { | ||
932 | debug_info("ERROR: No pair record for %s", client->device->udid); | ||
933 | return LOCKDOWN_E_INVALID_CONF; | ||
934 | } else if (uerr != USERPREF_E_SUCCESS) { | ||
935 | debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); | ||
936 | return LOCKDOWN_E_INVALID_CONF; | ||
937 | } | ||
860 | } | 938 | } |
861 | } | 939 | } |
862 | 940 | ||
863 | /* Setup Pair request plist */ | 941 | plist_t request_pair_record = plist_copy(pair_record_plist); |
942 | |||
943 | /* remove stuff that is private */ | ||
944 | plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); | ||
945 | plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); | ||
946 | |||
947 | /* setup pair request plist */ | ||
864 | dict = plist_new_dict(); | 948 | dict = plist_new_dict(); |
865 | plist_dict_add_label(dict, client->label); | 949 | plist_dict_add_label(dict, client->label); |
866 | plist_dict_insert_item(dict,"PairRecord", dict_record); | 950 | plist_dict_set_item(dict, "PairRecord", request_pair_record); |
867 | plist_dict_insert_item(dict, "Request", plist_new_string(verb)); | 951 | plist_dict_set_item(dict, "Request", plist_new_string(verb)); |
952 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION)); | ||
953 | |||
954 | if (options) { | ||
955 | plist_dict_set_item(dict, "PairingOptions", plist_copy(options)); | ||
956 | } | ||
868 | 957 | ||
869 | /* send to device */ | 958 | /* send to device */ |
870 | ret = lockdownd_send(client, dict); | 959 | ret = lockdownd_send(client, dict); |
871 | plist_free(dict); | 960 | plist_free(dict); |
872 | dict = NULL; | 961 | dict = NULL; |
873 | 962 | ||
874 | if (ret != LOCKDOWN_E_SUCCESS) | 963 | if (ret != LOCKDOWN_E_SUCCESS) { |
964 | plist_free(pair_record_plist); | ||
965 | if (wifi_node) | ||
966 | plist_free(wifi_node); | ||
875 | return ret; | 967 | return ret; |
968 | } | ||
876 | 969 | ||
877 | /* Now get device's answer */ | 970 | /* Now get device's answer */ |
878 | ret = lockdownd_receive(client, &dict); | 971 | ret = lockdownd_receive(client, &dict); |
879 | 972 | ||
880 | if (ret != LOCKDOWN_E_SUCCESS) | 973 | if (ret != LOCKDOWN_E_SUCCESS) { |
974 | plist_free(pair_record_plist); | ||
975 | if (wifi_node) | ||
976 | plist_free(wifi_node); | ||
881 | return ret; | 977 | return ret; |
978 | } | ||
882 | 979 | ||
883 | if (strcmp(verb, "Unpair") == 0) { | 980 | if (strcmp(verb, "Unpair") == 0) { |
884 | /* workaround for Unpair giving back ValidatePair, | 981 | /* workaround for Unpair giving back ValidatePair, |
885 | * seems to be a bug in the device's fw */ | 982 | * seems to be a bug in the device's fw */ |
886 | if (lockdown_check_result(dict, NULL) != RESULT_SUCCESS) { | 983 | if (lockdown_check_result(dict, NULL) != LOCKDOWN_E_SUCCESS) { |
887 | ret = LOCKDOWN_E_PAIRING_FAILED; | 984 | ret = LOCKDOWN_E_PAIRING_FAILED; |
888 | } | 985 | } |
889 | } else { | 986 | } else { |
890 | if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) { | 987 | if (lockdown_check_result(dict, verb) != LOCKDOWN_E_SUCCESS) { |
891 | ret = LOCKDOWN_E_PAIRING_FAILED; | 988 | ret = LOCKDOWN_E_PAIRING_FAILED; |
892 | } | 989 | } |
893 | } | 990 | } |
@@ -896,13 +993,32 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ | |||
896 | if (ret == LOCKDOWN_E_SUCCESS) { | 993 | if (ret == LOCKDOWN_E_SUCCESS) { |
897 | debug_info("%s success", verb); | 994 | debug_info("%s success", verb); |
898 | if (!pairing_mode) { | 995 | if (!pairing_mode) { |
996 | debug_info("internal pairing mode"); | ||
899 | if (!strcmp("Unpair", verb)) { | 997 | if (!strcmp("Unpair", verb)) { |
900 | /* remove public key from config */ | 998 | /* remove public key from config */ |
901 | userpref_remove_device_public_key(client->uuid); | 999 | userpref_delete_pair_record(client->device->udid); |
902 | } else { | 1000 | } else { |
903 | /* store public key in config */ | 1001 | if (!strcmp("Pair", verb)) { |
904 | userpref_set_device_public_key(client->uuid, public_key); | 1002 | /* add returned escrow bag if available */ |
1003 | plist_t extra_node = plist_dict_get_item(dict, USERPREF_ESCROW_BAG_KEY); | ||
1004 | if (extra_node && plist_get_node_type(extra_node) == PLIST_DATA) { | ||
1005 | debug_info("Saving EscrowBag from response in pair record"); | ||
1006 | plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(extra_node)); | ||
1007 | } | ||
1008 | |||
1009 | /* save previously retrieved wifi mac address in pair record */ | ||
1010 | if (wifi_node) { | ||
1011 | debug_info("Saving WiFiAddress from device in pair record"); | ||
1012 | plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_node)); | ||
1013 | plist_free(wifi_node); | ||
1014 | wifi_node = NULL; | ||
1015 | } | ||
1016 | |||
1017 | userpref_save_pair_record(client->device->udid, client->device->mux_id, pair_record_plist); | ||
1018 | } | ||
905 | } | 1019 | } |
1020 | } else { | ||
1021 | debug_info("external pairing mode"); | ||
906 | } | 1022 | } |
907 | } else { | 1023 | } else { |
908 | debug_info("%s failure", verb); | 1024 | debug_info("%s failure", verb); |
@@ -914,92 +1030,60 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ | |||
914 | plist_get_string_val(error_node, &value); | 1030 | plist_get_string_val(error_node, &value); |
915 | if (value) { | 1031 | if (value) { |
916 | /* the first pairing fails if the device is password protected */ | 1032 | /* the first pairing fails if the device is password protected */ |
917 | if (!strcmp(value, "PasswordProtected")) { | 1033 | ret = lockdownd_strtoerr(value); |
918 | ret = LOCKDOWN_E_PASSWORD_PROTECTED; | ||
919 | } else if (!strcmp(value, "InvalidHostID")) { | ||
920 | ret = LOCKDOWN_E_INVALID_HOST_ID; | ||
921 | } | ||
922 | free(value); | 1034 | free(value); |
923 | } | 1035 | } |
924 | |||
925 | plist_free(error_node); | ||
926 | error_node = NULL; | ||
927 | } | 1036 | } |
928 | } | 1037 | } |
929 | plist_free(dict); | 1038 | |
930 | dict = NULL; | 1039 | if (pair_record_plist) { |
931 | if (public_key.data) | 1040 | plist_free(pair_record_plist); |
932 | free(public_key.data); | 1041 | pair_record_plist = NULL; |
1042 | } | ||
1043 | |||
1044 | if (wifi_node) { | ||
1045 | plist_free(wifi_node); | ||
1046 | wifi_node = NULL; | ||
1047 | } | ||
1048 | |||
1049 | if (result) { | ||
1050 | *result = dict; | ||
1051 | } else { | ||
1052 | plist_free(dict); | ||
1053 | dict = NULL; | ||
1054 | } | ||
1055 | |||
933 | return ret; | 1056 | return ret; |
934 | } | 1057 | } |
935 | 1058 | ||
936 | /** | ||
937 | * Pairs the device using the supplied pair record. | ||
938 | * | ||
939 | * @param client The lockdown client to pair with. | ||
940 | * @param pair_record The pair record to use for pairing. If NULL is passed, then | ||
941 | * the pair records from the current machine are used. New records will be | ||
942 | * generated automatically when pairing is done for the first time. | ||
943 | * | ||
944 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
945 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | ||
946 | * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, | ||
947 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | ||
948 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | ||
949 | */ | ||
950 | lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) | 1059 | lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) |
951 | { | 1060 | { |
952 | return lockdownd_do_pair(client, pair_record, "Pair"); | 1061 | |
1062 | plist_t options = plist_new_dict(); | ||
1063 | plist_dict_set_item(options, "ExtendedPairingErrors", plist_new_bool(1)); | ||
1064 | |||
1065 | lockdownd_error_t ret = lockdownd_do_pair(client, pair_record, "Pair", options, NULL); | ||
1066 | |||
1067 | plist_free(options); | ||
1068 | |||
1069 | return ret; | ||
1070 | } | ||
1071 | |||
1072 | lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response) | ||
1073 | { | ||
1074 | return lockdownd_do_pair(client, pair_record, "Pair", options, response); | ||
953 | } | 1075 | } |
954 | 1076 | ||
955 | /** | ||
956 | * Validates if the device is paired with the given HostID. If succeeded them | ||
957 | * specified host will become trusted host of the device indicated by the | ||
958 | * lockdownd preference named TrustedHostAttached. Otherwise the host must because | ||
959 | * paired using lockdownd_pair() first. | ||
960 | * | ||
961 | * @param client The lockdown client to pair with. | ||
962 | * @param pair_record The pair record to validate pairing with. If NULL is | ||
963 | * passed, then the pair record is read from the internal pairing record | ||
964 | * management. | ||
965 | * | ||
966 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
967 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | ||
968 | * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, | ||
969 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | ||
970 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | ||
971 | */ | ||
972 | lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) | 1077 | lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) |
973 | { | 1078 | { |
974 | return lockdownd_do_pair(client, pair_record, "ValidatePair"); | 1079 | return lockdownd_do_pair(client, pair_record, "ValidatePair", NULL, NULL); |
975 | } | 1080 | } |
976 | 1081 | ||
977 | /** | ||
978 | * Unpairs the device with the given HostID and removes the pairing records | ||
979 | * from the device and host if the internal pairing record management is used. | ||
980 | * | ||
981 | * @param client The lockdown client to pair with. | ||
982 | * @param pair_record The pair record to use for unpair. If NULL is passed, then | ||
983 | * the pair records from the current machine are used. | ||
984 | * | ||
985 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
986 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | ||
987 | * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, | ||
988 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | ||
989 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | ||
990 | */ | ||
991 | lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) | 1082 | lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) |
992 | { | 1083 | { |
993 | return lockdownd_do_pair(client, pair_record, "Unpair"); | 1084 | return lockdownd_do_pair(client, pair_record, "Unpair", NULL, NULL); |
994 | } | 1085 | } |
995 | 1086 | ||
996 | /** | ||
997 | * Tells the device to immediately enter recovery mode. | ||
998 | * | ||
999 | * @param client The lockdown client | ||
1000 | * | ||
1001 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
1002 | */ | ||
1003 | lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) | 1087 | lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) |
1004 | { | 1088 | { |
1005 | if (!client) | 1089 | if (!client) |
@@ -1009,7 +1093,7 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) | |||
1009 | 1093 | ||
1010 | plist_t dict = plist_new_dict(); | 1094 | plist_t dict = plist_new_dict(); |
1011 | plist_dict_add_label(dict, client->label); | 1095 | plist_dict_add_label(dict, client->label); |
1012 | plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery")); | 1096 | plist_dict_set_item(dict,"Request", plist_new_string("EnterRecovery")); |
1013 | 1097 | ||
1014 | debug_info("telling device to enter recovery mode"); | 1098 | debug_info("telling device to enter recovery mode"); |
1015 | 1099 | ||
@@ -1019,23 +1103,17 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) | |||
1019 | 1103 | ||
1020 | ret = lockdownd_receive(client, &dict); | 1104 | ret = lockdownd_receive(client, &dict); |
1021 | 1105 | ||
1022 | if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) { | 1106 | ret = lockdown_check_result(dict, "EnterRecovery"); |
1107 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
1023 | debug_info("success"); | 1108 | debug_info("success"); |
1024 | ret = LOCKDOWN_E_SUCCESS; | ||
1025 | } | 1109 | } |
1110 | |||
1026 | plist_free(dict); | 1111 | plist_free(dict); |
1027 | dict = NULL; | 1112 | dict = NULL; |
1113 | |||
1028 | return ret; | 1114 | return ret; |
1029 | } | 1115 | } |
1030 | 1116 | ||
1031 | /** | ||
1032 | * Sends the Goodbye request to lockdownd signaling the end of communication. | ||
1033 | * | ||
1034 | * @param client The lockdown client | ||
1035 | * | ||
1036 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
1037 | * LOCKDOWN_E_PLIST_ERROR if the device did not acknowledge the request | ||
1038 | */ | ||
1039 | lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) | 1117 | lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) |
1040 | { | 1118 | { |
1041 | if (!client) | 1119 | if (!client) |
@@ -1045,7 +1123,7 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) | |||
1045 | 1123 | ||
1046 | plist_t dict = plist_new_dict(); | 1124 | plist_t dict = plist_new_dict(); |
1047 | plist_dict_add_label(dict, client->label); | 1125 | plist_dict_add_label(dict, client->label); |
1048 | plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); | 1126 | plist_dict_set_item(dict,"Request", plist_new_string("Goodbye")); |
1049 | 1127 | ||
1050 | debug_info("called"); | 1128 | debug_info("called"); |
1051 | 1129 | ||
@@ -1059,187 +1137,17 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) | |||
1059 | return LOCKDOWN_E_PLIST_ERROR; | 1137 | return LOCKDOWN_E_PLIST_ERROR; |
1060 | } | 1138 | } |
1061 | 1139 | ||
1062 | if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) { | 1140 | ret = lockdown_check_result(dict, "Goodbye"); |
1141 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
1063 | debug_info("success"); | 1142 | debug_info("success"); |
1064 | ret = LOCKDOWN_E_SUCCESS; | ||
1065 | } | 1143 | } |
1144 | |||
1066 | plist_free(dict); | 1145 | plist_free(dict); |
1067 | dict = NULL; | 1146 | dict = NULL; |
1068 | return ret; | ||
1069 | } | ||
1070 | |||
1071 | /** | ||
1072 | * Generates the device certificate from the public key as well as the host | ||
1073 | * and root certificates. | ||
1074 | * | ||
1075 | * @param public_key The public key of the device to use for generation. | ||
1076 | * @param odevice_cert Holds the generated device certificate. | ||
1077 | * @param ohost_cert Holds the generated host certificate. | ||
1078 | * @param oroot_cert Holds the generated root certificate. | ||
1079 | * | ||
1080 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a parameter is NULL, | ||
1081 | * LOCKDOWN_E_INVALID_CONF if the internal configuration system failed, | ||
1082 | * LOCKDOWN_E_SSL_ERROR if the certificates could not be generated | ||
1083 | */ | ||
1084 | lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * odevice_cert, | ||
1085 | gnutls_datum_t * ohost_cert, gnutls_datum_t * oroot_cert) | ||
1086 | { | ||
1087 | if (!public_key.data || !odevice_cert || !ohost_cert || !oroot_cert) | ||
1088 | return LOCKDOWN_E_INVALID_ARG; | ||
1089 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
1090 | userpref_error_t uret = USERPREF_E_UNKNOWN_ERROR; | ||
1091 | |||
1092 | gnutls_datum_t modulus = { NULL, 0 }; | ||
1093 | gnutls_datum_t exponent = { NULL, 0 }; | ||
1094 | |||
1095 | /* now decode the PEM encoded key */ | ||
1096 | gnutls_datum_t der_pub_key; | ||
1097 | if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) { | ||
1098 | |||
1099 | /* initalize asn.1 parser */ | ||
1100 | ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY; | ||
1101 | if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) { | ||
1102 | |||
1103 | ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY; | ||
1104 | asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key); | ||
1105 | |||
1106 | if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) { | ||
1107 | |||
1108 | /* get size to read */ | ||
1109 | int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, (int*)&modulus.size); | ||
1110 | int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, (int*)&exponent.size); | ||
1111 | |||
1112 | modulus.data = gnutls_malloc(modulus.size); | ||
1113 | exponent.data = gnutls_malloc(exponent.size); | ||
1114 | |||
1115 | ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, (int*)&modulus.size); | ||
1116 | ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, (int*)&exponent.size); | ||
1117 | if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2) | ||
1118 | ret = LOCKDOWN_E_SUCCESS; | ||
1119 | } | ||
1120 | if (asn1_pub_key) | ||
1121 | asn1_delete_structure(&asn1_pub_key); | ||
1122 | } | ||
1123 | if (pkcs1) | ||
1124 | asn1_delete_structure(&pkcs1); | ||
1125 | } | ||
1126 | |||
1127 | /* now generate certificates */ | ||
1128 | if (LOCKDOWN_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) { | ||
1129 | |||
1130 | gnutls_global_init(); | ||
1131 | gnutls_datum_t essentially_null = { (unsigned char*)strdup("abababababababab"), strlen("abababababababab") }; | ||
1132 | |||
1133 | gnutls_x509_privkey_t fake_privkey, root_privkey, host_privkey; | ||
1134 | gnutls_x509_crt_t dev_cert, root_cert, host_cert; | ||
1135 | |||
1136 | gnutls_x509_privkey_init(&fake_privkey); | ||
1137 | gnutls_x509_crt_init(&dev_cert); | ||
1138 | gnutls_x509_crt_init(&root_cert); | ||
1139 | gnutls_x509_crt_init(&host_cert); | ||
1140 | |||
1141 | if (GNUTLS_E_SUCCESS == | ||
1142 | gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null, | ||
1143 | &essentially_null, &essentially_null)) { | ||
1144 | |||
1145 | gnutls_x509_privkey_init(&root_privkey); | ||
1146 | gnutls_x509_privkey_init(&host_privkey); | ||
1147 | |||
1148 | uret = userpref_get_keys_and_certs(root_privkey, root_cert, host_privkey, host_cert); | ||
1149 | |||
1150 | if (USERPREF_E_SUCCESS == uret) { | ||
1151 | /* generate device certificate */ | ||
1152 | gnutls_x509_crt_set_key(dev_cert, fake_privkey); | ||
1153 | gnutls_x509_crt_set_serial(dev_cert, "\x00", 1); | ||
1154 | gnutls_x509_crt_set_version(dev_cert, 3); | ||
1155 | gnutls_x509_crt_set_ca_status(dev_cert, 0); | ||
1156 | gnutls_x509_crt_set_activation_time(dev_cert, time(NULL)); | ||
1157 | gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
1158 | gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey); | ||
1159 | |||
1160 | if (LOCKDOWN_E_SUCCESS == ret) { | ||
1161 | /* if everything went well, export in PEM format */ | ||
1162 | size_t export_size = 0; | ||
1163 | gnutls_datum_t dev_pem = { NULL, 0 }; | ||
1164 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &export_size); | ||
1165 | dev_pem.data = gnutls_malloc(export_size); | ||
1166 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &export_size); | ||
1167 | dev_pem.size = export_size; | ||
1168 | |||
1169 | gnutls_datum_t pem_root_cert = { NULL, 0 }; | ||
1170 | gnutls_datum_t pem_host_cert = { NULL, 0 }; | ||
1171 | |||
1172 | uret = userpref_get_certs_as_pem(&pem_root_cert, &pem_host_cert); | ||
1173 | |||
1174 | if (USERPREF_E_SUCCESS == uret) { | ||
1175 | /* copy buffer for output */ | ||
1176 | odevice_cert->data = malloc(dev_pem.size); | ||
1177 | memcpy(odevice_cert->data, dev_pem.data, dev_pem.size); | ||
1178 | odevice_cert->size = dev_pem.size; | ||
1179 | |||
1180 | ohost_cert->data = malloc(pem_host_cert.size); | ||
1181 | memcpy(ohost_cert->data, pem_host_cert.data, pem_host_cert.size); | ||
1182 | ohost_cert->size = pem_host_cert.size; | ||
1183 | |||
1184 | oroot_cert->data = malloc(pem_root_cert.size); | ||
1185 | memcpy(oroot_cert->data, pem_root_cert.data, pem_root_cert.size); | ||
1186 | oroot_cert->size = pem_root_cert.size; | ||
1187 | |||
1188 | g_free(pem_root_cert.data); | ||
1189 | g_free(pem_host_cert.data); | ||
1190 | |||
1191 | if (dev_pem.data) | ||
1192 | gnutls_free(dev_pem.data); | ||
1193 | } | ||
1194 | } | ||
1195 | } | ||
1196 | |||
1197 | switch(uret) { | ||
1198 | case USERPREF_E_INVALID_ARG: | ||
1199 | ret = LOCKDOWN_E_INVALID_ARG; | ||
1200 | break; | ||
1201 | case USERPREF_E_INVALID_CONF: | ||
1202 | ret = LOCKDOWN_E_INVALID_CONF; | ||
1203 | break; | ||
1204 | case USERPREF_E_SSL_ERROR: | ||
1205 | ret = LOCKDOWN_E_SSL_ERROR; | ||
1206 | default: | ||
1207 | break; | ||
1208 | } | ||
1209 | } | ||
1210 | |||
1211 | if (essentially_null.data) | ||
1212 | free(essentially_null.data); | ||
1213 | gnutls_x509_crt_deinit(dev_cert); | ||
1214 | gnutls_x509_crt_deinit(root_cert); | ||
1215 | gnutls_x509_crt_deinit(host_cert); | ||
1216 | gnutls_x509_privkey_deinit(fake_privkey); | ||
1217 | gnutls_x509_privkey_deinit(root_privkey); | ||
1218 | gnutls_x509_privkey_deinit(host_privkey); | ||
1219 | |||
1220 | } | ||
1221 | |||
1222 | gnutls_free(modulus.data); | ||
1223 | gnutls_free(exponent.data); | ||
1224 | |||
1225 | gnutls_free(der_pub_key.data); | ||
1226 | 1147 | ||
1227 | return ret; | 1148 | return ret; |
1228 | } | 1149 | } |
1229 | 1150 | ||
1230 | /** | ||
1231 | * Opens a session with lockdownd and switches to SSL mode if device wants it. | ||
1232 | * | ||
1233 | * @param client The lockdownd client | ||
1234 | * @param host_id The HostID of the computer | ||
1235 | * @param session_id The new session_id of the created session | ||
1236 | * @param ssl_enabled Whether SSL communication is used in the session | ||
1237 | * | ||
1238 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a client or | ||
1239 | * host_id is NULL, LOCKDOWN_E_PLIST_ERROR if the response plist had errors, | ||
1240 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the supplied HostID, | ||
1241 | * LOCKDOWN_E_SSL_ERROR if enabling SSL communication failed | ||
1242 | */ | ||
1243 | lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) | 1151 | lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) |
1244 | { | 1152 | { |
1245 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | 1153 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; |
@@ -1251,14 +1159,28 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1251 | /* if we have a running session, stop current one first */ | 1159 | /* if we have a running session, stop current one first */ |
1252 | if (client->session_id) { | 1160 | if (client->session_id) { |
1253 | lockdownd_stop_session(client, client->session_id); | 1161 | lockdownd_stop_session(client, client->session_id); |
1254 | free(client->session_id); | ||
1255 | } | 1162 | } |
1256 | 1163 | ||
1257 | /* setup request plist */ | 1164 | /* setup request plist */ |
1258 | dict = plist_new_dict(); | 1165 | dict = plist_new_dict(); |
1259 | plist_dict_add_label(dict, client->label); | 1166 | plist_dict_add_label(dict, client->label); |
1260 | plist_dict_insert_item(dict,"HostID", plist_new_string(host_id)); | 1167 | plist_dict_set_item(dict,"Request", plist_new_string("StartSession")); |
1261 | plist_dict_insert_item(dict,"Request", plist_new_string("StartSession")); | 1168 | |
1169 | /* add host id */ | ||
1170 | if (host_id) { | ||
1171 | plist_dict_set_item(dict, "HostID", plist_new_string(host_id)); | ||
1172 | } | ||
1173 | |||
1174 | /* add system buid */ | ||
1175 | char *system_buid = NULL; | ||
1176 | userpref_read_system_buid(&system_buid); | ||
1177 | if (system_buid) { | ||
1178 | plist_dict_set_item(dict, "SystemBUID", plist_new_string(system_buid)); | ||
1179 | if (system_buid) { | ||
1180 | free(system_buid); | ||
1181 | system_buid = NULL; | ||
1182 | } | ||
1183 | } | ||
1262 | 1184 | ||
1263 | ret = lockdownd_send(client, dict); | 1185 | ret = lockdownd_send(client, dict); |
1264 | plist_free(dict); | 1186 | plist_free(dict); |
@@ -1272,17 +1194,8 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1272 | if (!dict) | 1194 | if (!dict) |
1273 | return LOCKDOWN_E_PLIST_ERROR; | 1195 | return LOCKDOWN_E_PLIST_ERROR; |
1274 | 1196 | ||
1275 | if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) { | 1197 | ret = lockdown_check_result(dict, "StartSession"); |
1276 | plist_t error_node = plist_dict_get_item(dict, "Error"); | 1198 | if (ret == LOCKDOWN_E_SUCCESS) { |
1277 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { | ||
1278 | char *error = NULL; | ||
1279 | plist_get_string_val(error_node, &error); | ||
1280 | if (!strcmp(error, "InvalidHostID")) { | ||
1281 | ret = LOCKDOWN_E_INVALID_HOST_ID; | ||
1282 | } | ||
1283 | free(error); | ||
1284 | } | ||
1285 | } else { | ||
1286 | uint8_t use_ssl = 0; | 1199 | uint8_t use_ssl = 0; |
1287 | 1200 | ||
1288 | plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); | 1201 | plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); |
@@ -1299,6 +1212,7 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1299 | if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) { | 1212 | if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) { |
1300 | plist_get_string_val(session_node, &client->session_id); | 1213 | plist_get_string_val(session_node, &client->session_id); |
1301 | } | 1214 | } |
1215 | |||
1302 | if (client->session_id) { | 1216 | if (client->session_id) { |
1303 | debug_info("SessionID: %s", client->session_id); | 1217 | debug_info("SessionID: %s", client->session_id); |
1304 | if (session_id != NULL) | 1218 | if (session_id != NULL) |
@@ -1306,18 +1220,15 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1306 | } else { | 1220 | } else { |
1307 | debug_info("Failed to get SessionID!"); | 1221 | debug_info("Failed to get SessionID!"); |
1308 | } | 1222 | } |
1309 | debug_info("Enable SSL Session: %s", (use_ssl?"true":"false")); | 1223 | |
1224 | debug_info("Enable SSL Session: %s", (use_ssl ? "true" : "false")); | ||
1225 | |||
1310 | if (use_ssl) { | 1226 | if (use_ssl) { |
1311 | ret = property_list_service_enable_ssl(client->parent); | 1227 | ret = lockdownd_error(property_list_service_enable_ssl(client->parent)); |
1312 | if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) { | 1228 | client->ssl_enabled = (ret == LOCKDOWN_E_SUCCESS ? 1 : 0); |
1313 | client->ssl_enabled = 1; | ||
1314 | } else { | ||
1315 | ret = LOCKDOWN_E_SSL_ERROR; | ||
1316 | client->ssl_enabled = 0; | ||
1317 | } | ||
1318 | } else { | 1229 | } else { |
1319 | client->ssl_enabled = 0; | ||
1320 | ret = LOCKDOWN_E_SUCCESS; | 1230 | ret = LOCKDOWN_E_SUCCESS; |
1231 | client->ssl_enabled = 0; | ||
1321 | } | 1232 | } |
1322 | } | 1233 | } |
1323 | 1234 | ||
@@ -1328,40 +1239,96 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1328 | } | 1239 | } |
1329 | 1240 | ||
1330 | /** | 1241 | /** |
1331 | * Requests to start a service and retrieve it's port on success. | 1242 | * Internal function used by lockdownd_do_start_service to create the |
1243 | * StartService request's plist. | ||
1332 | * | 1244 | * |
1333 | * @param client The lockdownd client | 1245 | * @param client The lockdownd client |
1334 | * @param service The name of the service to start | 1246 | * @param identifier The identifier of the service to start |
1335 | * @param port The port number the service was started on | 1247 | * @param send_escrow_bag Should we send the device's escrow bag with the request |
1336 | 1248 | * @param request The request's plist on success, NULL on failure | |
1337 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter | 1249 | * |
1250 | * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_CONF on failure | ||
1251 | * to read the escrow bag from the device's record (when used). | ||
1252 | */ | ||
1253 | static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_t client, const char *identifier, int send_escrow_bag, plist_t *request) | ||
1254 | { | ||
1255 | plist_t dict = plist_new_dict(); | ||
1256 | |||
1257 | /* create the basic request params */ | ||
1258 | plist_dict_add_label(dict, client->label); | ||
1259 | plist_dict_set_item(dict, "Request", plist_new_string("StartService")); | ||
1260 | plist_dict_set_item(dict, "Service", plist_new_string(identifier)); | ||
1261 | |||
1262 | /* if needed - get the escrow bag for the device and send it with the request */ | ||
1263 | if (send_escrow_bag) { | ||
1264 | /* get the pairing record */ | ||
1265 | plist_t pair_record = NULL; | ||
1266 | userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record); | ||
1267 | if (uerr == USERPREF_E_READ_ERROR) { | ||
1268 | debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); | ||
1269 | plist_free(dict); | ||
1270 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
1271 | } else if (uerr == USERPREF_E_NOENT) { | ||
1272 | debug_info("ERROR: No pair record for %s", client->device->udid); | ||
1273 | plist_free(dict); | ||
1274 | return LOCKDOWN_E_INVALID_CONF; | ||
1275 | } else if (uerr != USERPREF_E_SUCCESS) { | ||
1276 | debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); | ||
1277 | plist_free(dict); | ||
1278 | return LOCKDOWN_E_INVALID_CONF; | ||
1279 | } | ||
1280 | |||
1281 | /* try to read the escrow bag from the record */ | ||
1282 | plist_t escrow_bag = plist_dict_get_item(pair_record, USERPREF_ESCROW_BAG_KEY); | ||
1283 | if (!escrow_bag || (PLIST_DATA != plist_get_node_type(escrow_bag))) { | ||
1284 | debug_info("ERROR: Failed to retrieve the escrow bag from the device's record"); | ||
1285 | plist_free(dict); | ||
1286 | plist_free(pair_record); | ||
1287 | return LOCKDOWN_E_INVALID_CONF; | ||
1288 | } | ||
1289 | |||
1290 | debug_info("Adding escrow bag to StartService for %s", identifier); | ||
1291 | plist_dict_set_item(dict, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag)); | ||
1292 | plist_free(pair_record); | ||
1293 | } | ||
1294 | |||
1295 | *request = dict; | ||
1296 | return LOCKDOWN_E_SUCCESS; | ||
1297 | } | ||
1298 | |||
1299 | /** | ||
1300 | * Function used internally by lockdownd_start_service and lockdownd_start_service_with_escrow_bag. | ||
1301 | * | ||
1302 | * @param client The lockdownd client | ||
1303 | * @param identifier The identifier of the service to start | ||
1304 | * @param send_escrow_bag Should we send the device's escrow bag with the request | ||
1305 | * @param descriptor The service descriptor on success or NULL on failure | ||
1306 | * | ||
1307 | * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_ARG if a parameter | ||
1338 | * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known | 1308 | * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known |
1339 | * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because | 1309 | * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because |
1340 | * started by the device | 1310 | * started by the device, LOCKDOWN_E_INVALID_CONF if the host id or escrow bag (when |
1311 | * used) are missing from the device record. | ||
1341 | */ | 1312 | */ |
1342 | lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port) | 1313 | static lockdownd_error_t lockdownd_do_start_service(lockdownd_client_t client, const char *identifier, int send_escrow_bag, lockdownd_service_descriptor_t *service) |
1343 | { | 1314 | { |
1344 | if (!client || !service || !port) | 1315 | if (!client || !identifier || !service) |
1345 | return LOCKDOWN_E_INVALID_ARG; | 1316 | return LOCKDOWN_E_INVALID_ARG; |
1346 | 1317 | ||
1347 | char *host_id = NULL; | 1318 | if (*service) { |
1348 | userpref_get_host_id(&host_id); | 1319 | // reset fields if service descriptor is reused |
1349 | if (!host_id) | 1320 | (*service)->port = 0; |
1350 | return LOCKDOWN_E_INVALID_CONF; | 1321 | (*service)->ssl_enabled = 0; |
1351 | if (!client->session_id) | 1322 | } |
1352 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1353 | 1323 | ||
1354 | plist_t dict = NULL; | 1324 | plist_t dict = NULL; |
1355 | uint16_t port_loc = 0; | 1325 | uint16_t port_loc = 0; |
1356 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 1326 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
1357 | 1327 | ||
1358 | free(host_id); | 1328 | /* create StartService request */ |
1359 | host_id = NULL; | 1329 | ret = lockdownd_build_start_service_request(client, identifier, send_escrow_bag, &dict); |
1360 | 1330 | if (LOCKDOWN_E_SUCCESS != ret) | |
1361 | dict = plist_new_dict(); | 1331 | return ret; |
1362 | plist_dict_add_label(dict, client->label); | ||
1363 | plist_dict_insert_item(dict,"Request", plist_new_string("StartService")); | ||
1364 | plist_dict_insert_item(dict,"Service", plist_new_string(service)); | ||
1365 | 1332 | ||
1366 | /* send to device */ | 1333 | /* send to device */ |
1367 | ret = lockdownd_send(client, dict); | 1334 | ret = lockdownd_send(client, dict); |
@@ -1379,57 +1346,63 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char | |||
1379 | if (!dict) | 1346 | if (!dict) |
1380 | return LOCKDOWN_E_PLIST_ERROR; | 1347 | return LOCKDOWN_E_PLIST_ERROR; |
1381 | 1348 | ||
1382 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 1349 | ret = lockdown_check_result(dict, "StartService"); |
1383 | if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) { | 1350 | if (ret == LOCKDOWN_E_SUCCESS) { |
1384 | plist_t port_value_node = plist_dict_get_item(dict, "Port"); | 1351 | if (*service == NULL) |
1385 | 1352 | *service = (lockdownd_service_descriptor_t)malloc(sizeof(struct lockdownd_service_descriptor)); | |
1386 | if (port_value_node && (plist_get_node_type(port_value_node) == PLIST_UINT)) { | 1353 | (*service)->port = 0; |
1354 | (*service)->ssl_enabled = 0; | ||
1355 | (*service)->identifier = strdup(identifier); | ||
1356 | |||
1357 | /* read service port number */ | ||
1358 | plist_t node = plist_dict_get_item(dict, "Port"); | ||
1359 | if (node && (plist_get_node_type(node) == PLIST_UINT)) { | ||
1387 | uint64_t port_value = 0; | 1360 | uint64_t port_value = 0; |
1388 | plist_get_uint_val(port_value_node, &port_value); | 1361 | plist_get_uint_val(node, &port_value); |
1389 | 1362 | ||
1390 | if (port_value) { | 1363 | if (port_value) { |
1391 | port_loc = port_value; | 1364 | port_loc = port_value; |
1392 | ret = LOCKDOWN_E_SUCCESS; | 1365 | ret = LOCKDOWN_E_SUCCESS; |
1393 | } | 1366 | } |
1394 | if (port && ret == LOCKDOWN_E_SUCCESS) | 1367 | if (port_loc && ret == LOCKDOWN_E_SUCCESS) { |
1395 | *port = port_loc; | 1368 | (*service)->port = port_loc; |
1369 | } | ||
1370 | } | ||
1371 | |||
1372 | /* check if the service requires SSL */ | ||
1373 | node = plist_dict_get_item(dict, "EnableServiceSSL"); | ||
1374 | if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { | ||
1375 | uint8_t b = 0; | ||
1376 | plist_get_bool_val(node, &b); | ||
1377 | (*service)->ssl_enabled = b; | ||
1396 | } | 1378 | } |
1397 | } else { | 1379 | } else { |
1398 | ret = LOCKDOWN_E_START_SERVICE_FAILED; | ||
1399 | plist_t error_node = plist_dict_get_item(dict, "Error"); | 1380 | plist_t error_node = plist_dict_get_item(dict, "Error"); |
1400 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { | 1381 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { |
1401 | char *error = NULL; | 1382 | char *error = NULL; |
1402 | plist_get_string_val(error_node, &error); | 1383 | plist_get_string_val(error_node, &error); |
1403 | if (!strcmp(error, "InvalidService")) { | 1384 | ret = lockdownd_strtoerr(error); |
1404 | ret = LOCKDOWN_E_INVALID_SERVICE; | ||
1405 | } | ||
1406 | free(error); | 1385 | free(error); |
1407 | } | 1386 | } |
1408 | } | 1387 | } |
1409 | 1388 | ||
1410 | plist_free(dict); | 1389 | plist_free(dict); |
1411 | dict = NULL; | 1390 | dict = NULL; |
1391 | |||
1412 | return ret; | 1392 | return ret; |
1413 | } | 1393 | } |
1414 | 1394 | ||
1415 | /** | 1395 | lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) |
1416 | * Activates the device. Only works within an open session. | 1396 | { |
1417 | * The ActivationRecord plist dictionary must be obtained using the | 1397 | return lockdownd_do_start_service(client, identifier, 0, service); |
1418 | * activation protocol requesting from Apple's https webservice. | 1398 | } |
1419 | * | 1399 | |
1420 | * @see http://iphone-docs.org/doku.php?id=docs:protocols:activation | 1400 | lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) |
1421 | * | 1401 | { |
1422 | * @param client The lockdown client | 1402 | return lockdownd_do_start_service(client, identifier, 1, service); |
1423 | * @param activation_record The activation record plist dictionary | 1403 | } |
1424 | * | 1404 | |
1425 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | 1405 | lockdownd_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 | */ | ||
1432 | lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activation_record) | ||
1433 | { | 1406 | { |
1434 | if (!client) | 1407 | if (!client) |
1435 | return LOCKDOWN_E_INVALID_ARG; | 1408 | return LOCKDOWN_E_INVALID_ARG; |
@@ -1444,8 +1417,8 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati | |||
1444 | 1417 | ||
1445 | plist_t dict = plist_new_dict(); | 1418 | plist_t dict = plist_new_dict(); |
1446 | plist_dict_add_label(dict, client->label); | 1419 | plist_dict_add_label(dict, client->label); |
1447 | plist_dict_insert_item(dict,"Request", plist_new_string("Activate")); | 1420 | plist_dict_set_item(dict,"Request", plist_new_string("Activate")); |
1448 | plist_dict_insert_item(dict,"ActivationRecord", plist_copy(activation_record)); | 1421 | plist_dict_set_item(dict,"ActivationRecord", plist_copy(activation_record)); |
1449 | 1422 | ||
1450 | ret = lockdownd_send(client, dict); | 1423 | ret = lockdownd_send(client, dict); |
1451 | plist_free(dict); | 1424 | plist_free(dict); |
@@ -1457,39 +1430,17 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati | |||
1457 | return LOCKDOWN_E_PLIST_ERROR; | 1430 | return LOCKDOWN_E_PLIST_ERROR; |
1458 | } | 1431 | } |
1459 | 1432 | ||
1460 | ret = LOCKDOWN_E_ACTIVATION_FAILED; | 1433 | ret = lockdown_check_result(dict, "Activate"); |
1461 | if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) { | 1434 | if (ret == LOCKDOWN_E_SUCCESS) { |
1462 | debug_info("success"); | 1435 | debug_info("success"); |
1463 | ret = LOCKDOWN_E_SUCCESS; | ||
1464 | |||
1465 | } else { | ||
1466 | plist_t error_node = plist_dict_get_item(dict, "Error"); | ||
1467 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { | ||
1468 | char *error = NULL; | ||
1469 | plist_get_string_val(error_node, &error); | ||
1470 | if (!strcmp(error, "InvalidActivationRecord")) { | ||
1471 | ret = LOCKDOWN_E_INVALID_ACTIVATION_RECORD; | ||
1472 | } | ||
1473 | free(error); | ||
1474 | } | ||
1475 | } | 1436 | } |
1476 | 1437 | ||
1477 | plist_free(dict); | 1438 | plist_free(dict); |
1478 | dict = NULL; | 1439 | dict = NULL; |
1479 | 1440 | ||
1480 | return ret; | 1441 | return ret; |
1481 | } | 1442 | } |
1482 | 1443 | ||
1483 | /** | ||
1484 | * Deactivates the device, returning it to the locked “Activate with iTunes” | ||
1485 | * screen. | ||
1486 | * | ||
1487 | * @param client The lockdown client | ||
1488 | * | ||
1489 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
1490 | * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open, | ||
1491 | * LOCKDOWN_E_PLIST_ERROR if the received plist is broken | ||
1492 | */ | ||
1493 | lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) | 1444 | lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) |
1494 | { | 1445 | { |
1495 | if (!client) | 1446 | if (!client) |
@@ -1502,7 +1453,7 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) | |||
1502 | 1453 | ||
1503 | plist_t dict = plist_new_dict(); | 1454 | plist_t dict = plist_new_dict(); |
1504 | plist_dict_add_label(dict, client->label); | 1455 | plist_dict_add_label(dict, client->label); |
1505 | plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate")); | 1456 | plist_dict_set_item(dict,"Request", plist_new_string("Deactivate")); |
1506 | 1457 | ||
1507 | ret = lockdownd_send(client, dict); | 1458 | ret = lockdownd_send(client, dict); |
1508 | plist_free(dict); | 1459 | plist_free(dict); |
@@ -1514,11 +1465,11 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) | |||
1514 | return LOCKDOWN_E_PLIST_ERROR; | 1465 | return LOCKDOWN_E_PLIST_ERROR; |
1515 | } | 1466 | } |
1516 | 1467 | ||
1517 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 1468 | ret = lockdown_check_result(dict, "Deactivate"); |
1518 | if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) { | 1469 | if (ret == LOCKDOWN_E_SUCCESS) { |
1519 | debug_info("success"); | 1470 | debug_info("success"); |
1520 | ret = LOCKDOWN_E_SUCCESS; | ||
1521 | } | 1471 | } |
1472 | |||
1522 | plist_free(dict); | 1473 | plist_free(dict); |
1523 | dict = NULL; | 1474 | dict = NULL; |
1524 | 1475 | ||
@@ -1537,19 +1488,6 @@ static void str_remove_spaces(char *source) | |||
1537 | *dest = 0; | 1488 | *dest = 0; |
1538 | } | 1489 | } |
1539 | 1490 | ||
1540 | /** | ||
1541 | * Calculates and returns the data classes the device supports from lockdownd. | ||
1542 | * | ||
1543 | * @param client An initialized lockdownd client. | ||
1544 | * @param classes A pointer to store an array of class names. The caller is responsible | ||
1545 | * for freeing the memory which can be done using mobilesync_data_classes_free(). | ||
1546 | * @param count The number of items in the classes array. | ||
1547 | * | ||
1548 | * @return LOCKDOWN_E_SUCCESS on success, | ||
1549 | * LOCKDOWN_E_INVALID_ARG when client is NULL, | ||
1550 | * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open, | ||
1551 | * LOCKDOWN_E_PLIST_ERROR if the received plist is broken | ||
1552 | */ | ||
1553 | lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) | 1491 | lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) |
1554 | { | 1492 | { |
1555 | if (!client) | 1493 | if (!client) |
@@ -1583,14 +1521,16 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha | |||
1583 | } | 1521 | } |
1584 | 1522 | ||
1585 | while((value = plist_array_get_item(dict, *count)) != NULL) { | 1523 | while((value = plist_array_get_item(dict, *count)) != NULL) { |
1586 | plist_get_string_val(value, &val); | 1524 | plist_get_string_val(value, &val); |
1587 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); | 1525 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); |
1588 | str_remove_spaces(val); | 1526 | str_remove_spaces(val); |
1589 | asprintf(&newlist[*count], "com.apple.%s", val); | 1527 | if (asprintf(&newlist[*count], "com.apple.%s", val) < 0) { |
1590 | free(val); | 1528 | debug_info("ERROR: asprintf failed"); |
1591 | val = NULL; | 1529 | } |
1592 | *classes = newlist; | 1530 | free(val); |
1593 | *count = *count+1; | 1531 | val = NULL; |
1532 | *classes = newlist; | ||
1533 | *count = *count+1; | ||
1594 | } | 1534 | } |
1595 | 1535 | ||
1596 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); | 1536 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); |
@@ -1603,14 +1543,6 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha | |||
1603 | return LOCKDOWN_E_SUCCESS; | 1543 | return LOCKDOWN_E_SUCCESS; |
1604 | } | 1544 | } |
1605 | 1545 | ||
1606 | |||
1607 | /** | ||
1608 | * Frees memory of an allocated array of data classes as returned by lockdownd_get_sync_data_classes() | ||
1609 | * | ||
1610 | * @param classes An array of class names to free. | ||
1611 | * | ||
1612 | * @return LOCKDOWN_E_SUCCESS on success | ||
1613 | */ | ||
1614 | lockdownd_error_t lockdownd_data_classes_free(char **classes) | 1546 | lockdownd_error_t lockdownd_data_classes_free(char **classes) |
1615 | { | 1547 | { |
1616 | if (classes) { | 1548 | if (classes) { |
@@ -1622,3 +1554,51 @@ lockdownd_error_t lockdownd_data_classes_free(char **classes) | |||
1622 | } | 1554 | } |
1623 | return LOCKDOWN_E_SUCCESS; | 1555 | return LOCKDOWN_E_SUCCESS; |
1624 | } | 1556 | } |
1557 | |||
1558 | lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service) | ||
1559 | { | ||
1560 | if (service) { | ||
1561 | free(service->identifier); | ||
1562 | free(service); | ||
1563 | } | ||
1564 | |||
1565 | return LOCKDOWN_E_SUCCESS; | ||
1566 | } | ||
1567 | |||
1568 | const char* lockdownd_strerror(lockdownd_error_t err) | ||
1569 | { | ||
1570 | switch (err) { | ||
1571 | case LOCKDOWN_E_SUCCESS: | ||
1572 | return "Success"; | ||
1573 | case LOCKDOWN_E_INVALID_ARG: | ||
1574 | return "Invalid argument"; | ||
1575 | case LOCKDOWN_E_INVALID_CONF: | ||
1576 | return "Invalid configuration"; | ||
1577 | case LOCKDOWN_E_PLIST_ERROR: | ||
1578 | return "PropertyList error"; | ||
1579 | case LOCKDOWN_E_PAIRING_FAILED: | ||
1580 | return "Pairing failed"; | ||
1581 | case LOCKDOWN_E_SSL_ERROR: | ||
1582 | return "SSL error"; | ||
1583 | case LOCKDOWN_E_DICT_ERROR: | ||
1584 | return "Invalid dictionary"; | ||
1585 | case LOCKDOWN_E_RECEIVE_TIMEOUT: | ||
1586 | return "Receive timeout"; | ||
1587 | case LOCKDOWN_E_MUX_ERROR: | ||
1588 | return "Mux error"; | ||
1589 | case LOCKDOWN_E_NO_RUNNING_SESSION: | ||
1590 | return "No running session"; | ||
1591 | case LOCKDOWN_E_UNKNOWN_ERROR: | ||
1592 | return "Unknown Error"; | ||
1593 | default: { | ||
1594 | int i = 0; | ||
1595 | while (lockdownd_error_str_map[i].lockdown_errstr) { | ||
1596 | if (lockdownd_error_str_map[i].errcode == err) { | ||
1597 | return lockdownd_error_str_map[i].errstr; | ||
1598 | } | ||
1599 | i++; | ||
1600 | } | ||
1601 | } break; | ||
1602 | } | ||
1603 | return "Unknown Error"; | ||
1604 | } | ||
diff --git a/src/lockdown.h b/src/lockdown.h index a25e59d..ba291ec 100644 --- a/src/lockdown.h +++ b/src/lockdown.h | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * lockdownd.h | 2 | * lockdown.h |
3 | * Defines lockdown stuff, like the client struct. | 3 | * Defines lockdown stuff, like the client struct. |
4 | * | 4 | * |
5 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 6 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
6 | * | 7 | * |
7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or |
@@ -19,24 +20,25 @@ | |||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 21 | */ |
21 | 22 | ||
22 | #ifndef LOCKDOWND_H | 23 | #ifndef __LOCKDOWND_H |
23 | #define LOCKDOWND_H | 24 | #define __LOCKDOWND_H |
24 | |||
25 | #include <gnutls/gnutls.h> | ||
26 | 25 | ||
26 | #include "idevice.h" | ||
27 | #include "libimobiledevice/lockdown.h" | 27 | #include "libimobiledevice/lockdown.h" |
28 | #include "property_list_service.h" | 28 | #include "property_list_service.h" |
29 | 29 | ||
30 | #define LOCKDOWN_PROTOCOL_VERSION "2" | ||
31 | |||
30 | struct lockdownd_client_private { | 32 | struct 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 | ||
38 | lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key); | 42 | lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match); |
39 | lockdownd_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * device_cert, | ||
40 | gnutls_datum_t * host_cert, gnutls_datum_t * root_cert); | ||
41 | 43 | ||
42 | #endif | 44 | #endif |
diff --git a/src/misagent.c b/src/misagent.c new file mode 100644 index 0000000..e3da997 --- /dev/null +++ b/src/misagent.c | |||
@@ -0,0 +1,290 @@ | |||
1 | /* | ||
2 | * misagent.c | ||
3 | * com.apple.misagent service implementation. | ||
4 | * | ||
5 | * Copyright (c) 2012 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <unistd.h> | ||
28 | #include <plist/plist.h> | ||
29 | #include <stdio.h> | ||
30 | |||
31 | #include "misagent.h" | ||
32 | #include "property_list_service.h" | ||
33 | #include "common/debug.h" | ||
34 | |||
35 | /** | ||
36 | * Convert a property_list_service_error_t value to a misagent_error_t | ||
37 | * value. Used internally to get correct error codes. | ||
38 | * | ||
39 | * @param err A property_list_service_error_t error code | ||
40 | * | ||
41 | * @return A matching misagent_error_t error code, | ||
42 | * MISAGENT_E_UNKNOWN_ERROR otherwise. | ||
43 | */ | ||
44 | static misagent_error_t misagent_error(property_list_service_error_t err) | ||
45 | { | ||
46 | switch (err) { | ||
47 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | ||
48 | return MISAGENT_E_SUCCESS; | ||
49 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | ||
50 | return MISAGENT_E_INVALID_ARG; | ||
51 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | ||
52 | return MISAGENT_E_PLIST_ERROR; | ||
53 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | ||
54 | return MISAGENT_E_CONN_FAILED; | ||
55 | default: | ||
56 | break; | ||
57 | } | ||
58 | return MISAGENT_E_UNKNOWN_ERROR; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Checks the response from misagent to determine if the operation | ||
63 | * was successful or an error occurred. Internally used only. | ||
64 | * | ||
65 | * @param response a PLIST_DICT received from device's misagent | ||
66 | * @param status_code pointer to an int that will be set to the status code | ||
67 | * contained in the response | ||
68 | */ | ||
69 | static misagent_error_t misagent_check_result(plist_t response, int* status_code) | ||
70 | { | ||
71 | if (plist_get_node_type(response) != PLIST_DICT) { | ||
72 | return MISAGENT_E_PLIST_ERROR; | ||
73 | } | ||
74 | |||
75 | plist_t node = plist_dict_get_item(response, "Status"); | ||
76 | if (!node || (plist_get_node_type(node) != PLIST_UINT)) { | ||
77 | return MISAGENT_E_PLIST_ERROR; | ||
78 | } | ||
79 | |||
80 | uint64_t val = -1LL; | ||
81 | plist_get_uint_val(node, &val); | ||
82 | if ((int64_t)val == -1LL) { | ||
83 | return MISAGENT_E_PLIST_ERROR; | ||
84 | } | ||
85 | *status_code = (int)(val & 0xFFFFFFFF); | ||
86 | if (*status_code == 0) { | ||
87 | return MISAGENT_E_SUCCESS; | ||
88 | } | ||
89 | return MISAGENT_E_REQUEST_FAILED; | ||
90 | } | ||
91 | |||
92 | misagent_error_t misagent_client_new(idevice_t device, lockdownd_service_descriptor_t service, misagent_client_t *client) | ||
93 | { | ||
94 | property_list_service_client_t plistclient = NULL; | ||
95 | misagent_error_t err = misagent_error(property_list_service_client_new(device, service, &plistclient)); | ||
96 | if (err != MISAGENT_E_SUCCESS) { | ||
97 | return err; | ||
98 | } | ||
99 | |||
100 | misagent_client_t client_loc = (misagent_client_t) malloc(sizeof(struct misagent_client_private)); | ||
101 | client_loc->parent = plistclient; | ||
102 | client_loc->last_error = 0; | ||
103 | |||
104 | *client = client_loc; | ||
105 | return MISAGENT_E_SUCCESS; | ||
106 | } | ||
107 | |||
108 | misagent_error_t misagent_client_start_service(idevice_t device, misagent_client_t * client, const char* label) | ||
109 | { | ||
110 | misagent_error_t err = MISAGENT_E_UNKNOWN_ERROR; | ||
111 | service_client_factory_start_service(device, MISAGENT_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(misagent_client_new), &err); | ||
112 | return err; | ||
113 | } | ||
114 | |||
115 | misagent_error_t misagent_client_free(misagent_client_t client) | ||
116 | { | ||
117 | if (!client) | ||
118 | return MISAGENT_E_INVALID_ARG; | ||
119 | |||
120 | misagent_error_t err = MISAGENT_E_SUCCESS; | ||
121 | if (client->parent && client->parent->parent) { | ||
122 | misagent_error(property_list_service_client_free(client->parent)); | ||
123 | } | ||
124 | client->parent = NULL; | ||
125 | free(client); | ||
126 | |||
127 | return err; | ||
128 | } | ||
129 | |||
130 | misagent_error_t misagent_install(misagent_client_t client, plist_t profile) | ||
131 | { | ||
132 | if (!client || !client->parent || !profile || (plist_get_node_type(profile) != PLIST_DATA)) | ||
133 | return MISAGENT_E_INVALID_ARG; | ||
134 | |||
135 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
136 | |||
137 | plist_t dict = plist_new_dict(); | ||
138 | plist_dict_set_item(dict, "MessageType", plist_new_string("Install")); | ||
139 | plist_dict_set_item(dict, "Profile", plist_copy(profile)); | ||
140 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
141 | |||
142 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
143 | plist_free(dict); | ||
144 | dict = NULL; | ||
145 | |||
146 | if (res != MISAGENT_E_SUCCESS) { | ||
147 | debug_info("could not send plist, error %d", res); | ||
148 | return res; | ||
149 | } | ||
150 | |||
151 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
152 | if (res != MISAGENT_E_SUCCESS) { | ||
153 | debug_info("could not receive response, error %d", res); | ||
154 | return res; | ||
155 | } | ||
156 | if (!dict) { | ||
157 | debug_info("could not get response plist"); | ||
158 | return MISAGENT_E_UNKNOWN_ERROR; | ||
159 | } | ||
160 | |||
161 | res = misagent_check_result(dict, &client->last_error); | ||
162 | plist_free(dict); | ||
163 | |||
164 | return res; | ||
165 | } | ||
166 | |||
167 | misagent_error_t misagent_copy(misagent_client_t client, plist_t* profiles) | ||
168 | { | ||
169 | if (!client || !client->parent || !profiles) | ||
170 | return MISAGENT_E_INVALID_ARG; | ||
171 | |||
172 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
173 | |||
174 | plist_t dict = plist_new_dict(); | ||
175 | plist_dict_set_item(dict, "MessageType", plist_new_string("Copy")); | ||
176 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
177 | |||
178 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
179 | plist_free(dict); | ||
180 | dict = NULL; | ||
181 | |||
182 | if (res != MISAGENT_E_SUCCESS) { | ||
183 | debug_info("could not send plist, error %d", res); | ||
184 | return res; | ||
185 | } | ||
186 | |||
187 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
188 | if (res != MISAGENT_E_SUCCESS) { | ||
189 | debug_info("could not receive response, error %d", res); | ||
190 | return res; | ||
191 | } | ||
192 | if (!dict) { | ||
193 | debug_info("could not get response plist"); | ||
194 | return MISAGENT_E_UNKNOWN_ERROR; | ||
195 | } | ||
196 | |||
197 | res = misagent_check_result(dict, &client->last_error); | ||
198 | if (res == MISAGENT_E_SUCCESS) { | ||
199 | *profiles = plist_copy(plist_dict_get_item(dict, "Payload")); | ||
200 | } | ||
201 | plist_free(dict); | ||
202 | |||
203 | return res; | ||
204 | |||
205 | } | ||
206 | |||
207 | misagent_error_t misagent_copy_all(misagent_client_t client, plist_t* profiles) | ||
208 | { | ||
209 | if (!client || !client->parent || !profiles) | ||
210 | return MISAGENT_E_INVALID_ARG; | ||
211 | |||
212 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
213 | |||
214 | plist_t dict = plist_new_dict(); | ||
215 | plist_dict_set_item(dict, "MessageType", plist_new_string("CopyAll")); | ||
216 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
217 | |||
218 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
219 | plist_free(dict); | ||
220 | dict = NULL; | ||
221 | |||
222 | if (res != MISAGENT_E_SUCCESS) { | ||
223 | debug_info("could not send plist, error %d", res); | ||
224 | return res; | ||
225 | } | ||
226 | |||
227 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
228 | if (res != MISAGENT_E_SUCCESS) { | ||
229 | debug_info("could not receive response, error %d", res); | ||
230 | return res; | ||
231 | } | ||
232 | if (!dict) { | ||
233 | debug_info("could not get response plist"); | ||
234 | return MISAGENT_E_UNKNOWN_ERROR; | ||
235 | } | ||
236 | |||
237 | res = misagent_check_result(dict, &client->last_error); | ||
238 | if (res == MISAGENT_E_SUCCESS) { | ||
239 | *profiles = plist_copy(plist_dict_get_item(dict, "Payload")); | ||
240 | } | ||
241 | plist_free(dict); | ||
242 | |||
243 | return res; | ||
244 | |||
245 | } | ||
246 | |||
247 | misagent_error_t misagent_remove(misagent_client_t client, const char* profileID) | ||
248 | { | ||
249 | if (!client || !client->parent || !profileID) | ||
250 | return MISAGENT_E_INVALID_ARG; | ||
251 | |||
252 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
253 | |||
254 | plist_t dict = plist_new_dict(); | ||
255 | plist_dict_set_item(dict, "MessageType", plist_new_string("Remove")); | ||
256 | plist_dict_set_item(dict, "ProfileID", plist_new_string(profileID)); | ||
257 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
258 | |||
259 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
260 | plist_free(dict); | ||
261 | dict = NULL; | ||
262 | |||
263 | if (res != MISAGENT_E_SUCCESS) { | ||
264 | debug_info("could not send plist, error %d", res); | ||
265 | return res; | ||
266 | } | ||
267 | |||
268 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
269 | if (res != MISAGENT_E_SUCCESS) { | ||
270 | debug_info("could not receive response, error %d", res); | ||
271 | return res; | ||
272 | } | ||
273 | if (!dict) { | ||
274 | debug_info("could not get response plist"); | ||
275 | return MISAGENT_E_UNKNOWN_ERROR; | ||
276 | } | ||
277 | |||
278 | res = misagent_check_result(dict, &client->last_error); | ||
279 | plist_free(dict); | ||
280 | |||
281 | return res; | ||
282 | } | ||
283 | |||
284 | int misagent_get_status_code(misagent_client_t client) | ||
285 | { | ||
286 | if (!client) { | ||
287 | return -1; | ||
288 | } | ||
289 | return client->last_error; | ||
290 | } | ||
diff --git a/src/misagent.h b/src/misagent.h new file mode 100644 index 0000000..e394087 --- /dev/null +++ b/src/misagent.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * misagent.h | ||
3 | * com.apple.misagent service header file. | ||
4 | * | ||
5 | * Copyright (c) 2012 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifndef __MISAGENT_H | ||
23 | #define __MISAGENT_H | ||
24 | |||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/misagent.h" | ||
27 | #include "property_list_service.h" | ||
28 | |||
29 | struct misagent_client_private { | ||
30 | property_list_service_client_t parent; | ||
31 | int last_error; | ||
32 | }; | ||
33 | |||
34 | #endif | ||
diff --git a/src/mobile_image_mounter.c b/src/mobile_image_mounter.c index 367bee0..6df50c4 100644 --- a/src/mobile_image_mounter.c +++ b/src/mobile_image_mounter.c | |||
@@ -2,23 +2,26 @@ | |||
2 | * mobile_image_mounter.c | 2 | * mobile_image_mounter.c |
3 | * com.apple.mobile.mobile_image_mounter service implementation. | 3 | * com.apple.mobile.mobile_image_mounter service implementation. |
4 | * | 4 | * |
5 | * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved. |
6 | * | 6 | * |
7 | * This library is free software; you can redistribute it and/or | 7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
24 | #include <unistd.h> | 27 | #include <unistd.h> |
@@ -26,7 +29,7 @@ | |||
26 | 29 | ||
27 | #include "mobile_image_mounter.h" | 30 | #include "mobile_image_mounter.h" |
28 | #include "property_list_service.h" | 31 | #include "property_list_service.h" |
29 | #include "debug.h" | 32 | #include "common/debug.h" |
30 | 33 | ||
31 | /** | 34 | /** |
32 | * Locks a mobile_image_mounter client, used for thread safety. | 35 | * Locks a mobile_image_mounter client, used for thread safety. |
@@ -35,17 +38,17 @@ | |||
35 | */ | 38 | */ |
36 | static void mobile_image_mounter_lock(mobile_image_mounter_client_t client) | 39 | static void mobile_image_mounter_lock(mobile_image_mounter_client_t client) |
37 | { | 40 | { |
38 | g_mutex_lock(client->mutex); | 41 | mutex_lock(&client->mutex); |
39 | } | 42 | } |
40 | 43 | ||
41 | /** | 44 | /** |
42 | * Unlocks a mobile_image_mounter client, used for thread safety. | 45 | * Unlocks a mobile_image_mounter client, used for thread safety. |
43 | * | 46 | * |
44 | * @param client mobile_image_mounter client to unlock | 47 | * @param client mobile_image_mounter client to unlock |
45 | */ | 48 | */ |
46 | static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client) | 49 | static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client) |
47 | { | 50 | { |
48 | g_mutex_unlock(client->mutex); | 51 | mutex_unlock(&client->mutex); |
49 | } | 52 | } |
50 | 53 | ||
51 | /** | 54 | /** |
@@ -75,51 +78,30 @@ static mobile_image_mounter_error_t mobile_image_mounter_error(property_list_ser | |||
75 | return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; | 78 | return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; |
76 | } | 79 | } |
77 | 80 | ||
78 | /** | 81 | mobile_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 | */ | ||
91 | mobile_image_mounter_error_t mobile_image_mounter_new(idevice_t device, uint16_t port, mobile_image_mounter_client_t *client) | ||
92 | { | 82 | { |
93 | /* makes sure thread environment is available */ | ||
94 | if (!g_thread_supported()) | ||
95 | g_thread_init(NULL); | ||
96 | |||
97 | if (!device) | ||
98 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
99 | |||
100 | property_list_service_client_t plistclient = NULL; | 83 | property_list_service_client_t plistclient = NULL; |
101 | if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 84 | mobile_image_mounter_error_t err = mobile_image_mounter_error(property_list_service_client_new(device, service, &plistclient)); |
102 | return MOBILE_IMAGE_MOUNTER_E_CONN_FAILED; | 85 | if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { |
86 | return err; | ||
103 | } | 87 | } |
104 | 88 | ||
105 | mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_private)); | 89 | mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_private)); |
106 | client_loc->parent = plistclient; | 90 | client_loc->parent = plistclient; |
107 | 91 | ||
108 | client_loc->mutex = g_mutex_new(); | 92 | mutex_init(&client_loc->mutex); |
109 | 93 | ||
110 | *client = client_loc; | 94 | *client = client_loc; |
111 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; | 95 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; |
112 | } | 96 | } |
113 | 97 | ||
114 | /** | 98 | mobile_image_mounter_error_t mobile_image_mounter_start_service(idevice_t device, mobile_image_mounter_client_t * client, const char* label) |
115 | * Disconnects a mobile_image_mounter client from the device and frees up the | 99 | { |
116 | * mobile_image_mounter client data. | 100 | mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; |
117 | * | 101 | service_client_factory_start_service(device, MOBILE_IMAGE_MOUNTER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobile_image_mounter_new), &err); |
118 | * @param client The mobile_image_mounter client to disconnect and free. | 102 | return err; |
119 | * | 103 | } |
120 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, | 104 | |
121 | * or MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is NULL. | ||
122 | */ | ||
123 | mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) | 105 | mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) |
124 | { | 106 | { |
125 | if (!client) | 107 | if (!client) |
@@ -127,27 +109,12 @@ mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_clie | |||
127 | 109 | ||
128 | property_list_service_client_free(client->parent); | 110 | property_list_service_client_free(client->parent); |
129 | client->parent = NULL; | 111 | client->parent = NULL; |
130 | if (client->mutex) { | 112 | mutex_destroy(&client->mutex); |
131 | g_mutex_free(client->mutex); | ||
132 | } | ||
133 | free(client); | 113 | free(client); |
134 | 114 | ||
135 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; | 115 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; |
136 | } | 116 | } |
137 | 117 | ||
138 | /** | ||
139 | * Tells if the image of ImageType is already mounted. | ||
140 | * | ||
141 | * @param client The client use | ||
142 | * @param image_type The type of the image to look up | ||
143 | * @param result Pointer to a plist that will receive the result of the | ||
144 | * operation. | ||
145 | * | ||
146 | * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the | ||
147 | * operation has failed. Check the resulting plist for further information. | ||
148 | * | ||
149 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, or an error code on error | ||
150 | */ | ||
151 | mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result) | 118 | mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result) |
152 | { | 119 | { |
153 | if (!client || !image_type || !result) { | 120 | if (!client || !image_type || !result) { |
@@ -156,8 +123,8 @@ mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_moun | |||
156 | mobile_image_mounter_lock(client); | 123 | mobile_image_mounter_lock(client); |
157 | 124 | ||
158 | plist_t dict = plist_new_dict(); | 125 | plist_t dict = plist_new_dict(); |
159 | plist_dict_insert_item(dict,"Command", plist_new_string("LookupImage")); | 126 | plist_dict_set_item(dict,"Command", plist_new_string("LookupImage")); |
160 | plist_dict_insert_item(dict,"ImageType", plist_new_string(image_type)); | 127 | plist_dict_set_item(dict,"ImageType", plist_new_string(image_type)); |
161 | 128 | ||
162 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | 129 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); |
163 | plist_free(dict); | 130 | plist_free(dict); |
@@ -177,39 +144,139 @@ leave_unlock: | |||
177 | return res; | 144 | return res; |
178 | } | 145 | } |
179 | 146 | ||
180 | /** | 147 | static mobile_image_mounter_error_t process_result(plist_t result, const char *expected_status) |
181 | * Mounts an image on the device. | 148 | { |
182 | * | 149 | mobile_image_mounter_error_t res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; |
183 | * @param client The connected mobile_image_mounter client. | 150 | char* strval = NULL; |
184 | * @param image_path The absolute path of the image to mount. The image must | 151 | plist_t node; |
185 | * be present before calling this function. | 152 | |
186 | * @param image_signature Pointer to a buffer holding the images' signature | 153 | node = plist_dict_get_item(result, "Error"); |
187 | * @param signature_length Length of the signature image_signature points to | 154 | if (node && plist_get_node_type(node) == PLIST_STRING) { |
188 | * @param image_type Type of image to mount | 155 | plist_get_string_val(node, &strval); |
189 | * @param result Pointer to a plist that will receive the result of the | 156 | } |
190 | * operation. | 157 | if (strval) { |
191 | * | 158 | if (!strcmp(strval, "DeviceLocked")) { |
192 | * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the | 159 | debug_info("Device is locked, can't mount"); |
193 | * operation has failed. Check the resulting plist for further information. | 160 | res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED; |
194 | * Note that there is no unmounting function. The mount persists until the | 161 | } else { |
195 | * device is rebooted. | 162 | debug_info("Unhandled error '%s' received", strval); |
196 | * | 163 | } |
197 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, | 164 | free(strval); |
198 | * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if on ore more parameters are | 165 | return res; |
199 | * invalid, or another error code otherwise. | 166 | } |
200 | */ | 167 | |
201 | mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const char *image_signature, uint16_t signature_length, const char *image_type, plist_t *result) | 168 | node = plist_dict_get_item(result, "Status"); |
169 | if (node && plist_get_node_type(node) == PLIST_STRING) { | ||
170 | plist_get_string_val(node, &strval); | ||
171 | } | ||
172 | if (!strval) { | ||
173 | debug_info("Error: Unexpected response received!"); | ||
174 | } else if (strcmp(strval, expected_status) == 0) { | ||
175 | res = MOBILE_IMAGE_MOUNTER_E_SUCCESS; | ||
176 | } else { | ||
177 | debug_info("Error: didn't get %s but %s", expected_status, strval); | ||
178 | } | ||
179 | free(strval); | ||
180 | |||
181 | return res; | ||
182 | } | ||
183 | |||
184 | mobile_image_mounter_error_t mobile_image_mounter_upload_image(mobile_image_mounter_client_t client, const char *image_type, size_t image_size, const unsigned char *signature, unsigned int signature_size, mobile_image_mounter_upload_cb_t upload_cb, void* userdata) | ||
185 | { | ||
186 | if (!client || !image_type || (image_size == 0) || !upload_cb) { | ||
187 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
188 | } | ||
189 | mobile_image_mounter_lock(client); | ||
190 | plist_t result = NULL; | ||
191 | |||
192 | plist_t dict = plist_new_dict(); | ||
193 | plist_dict_set_item(dict, "Command", plist_new_string("ReceiveBytes")); | ||
194 | if (signature && signature_size != 0) | ||
195 | plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size)); | ||
196 | plist_dict_set_item(dict, "ImageSize", plist_new_uint(image_size)); | ||
197 | plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); | ||
198 | |||
199 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
200 | plist_free(dict); | ||
201 | |||
202 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
203 | debug_info("Error sending XML plist to device!"); | ||
204 | goto leave_unlock; | ||
205 | } | ||
206 | |||
207 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
208 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
209 | debug_info("Error receiving response from device!"); | ||
210 | goto leave_unlock; | ||
211 | } | ||
212 | res = process_result(result, "ReceiveBytesAck"); | ||
213 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
214 | goto leave_unlock; | ||
215 | } | ||
216 | |||
217 | size_t tx = 0; | ||
218 | size_t bufsize = 65536; | ||
219 | unsigned char *buf = (unsigned char*)malloc(bufsize); | ||
220 | if (!buf) { | ||
221 | debug_info("Out of memory"); | ||
222 | res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; | ||
223 | goto leave_unlock; | ||
224 | } | ||
225 | debug_info("uploading image (%d bytes)", (int)image_size); | ||
226 | while (tx < image_size) { | ||
227 | size_t remaining = image_size - tx; | ||
228 | size_t amount = (remaining < bufsize) ? remaining : bufsize; | ||
229 | ssize_t r = upload_cb(buf, amount, userdata); | ||
230 | if (r < 0) { | ||
231 | debug_info("upload_cb returned %d", (int)r); | ||
232 | break; | ||
233 | } | ||
234 | uint32_t sent = 0; | ||
235 | if (service_send(client->parent->parent, (const char*)buf, (uint32_t)r, &sent) != SERVICE_E_SUCCESS) { | ||
236 | debug_info("service_send failed"); | ||
237 | break; | ||
238 | } | ||
239 | tx += r; | ||
240 | } | ||
241 | free(buf); | ||
242 | if (tx < image_size) { | ||
243 | debug_info("Error: failed to upload image"); | ||
244 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
245 | goto leave_unlock; | ||
246 | } | ||
247 | debug_info("image uploaded"); | ||
248 | |||
249 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
250 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
251 | debug_info("Error receiving response from device!"); | ||
252 | goto leave_unlock; | ||
253 | } | ||
254 | res = process_result(result, "Complete"); | ||
255 | |||
256 | leave_unlock: | ||
257 | mobile_image_mounter_unlock(client); | ||
258 | if (result) | ||
259 | plist_free(result); | ||
260 | return res; | ||
261 | |||
262 | } | ||
263 | |||
264 | mobile_image_mounter_error_t mobile_image_mounter_mount_image_with_options(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t options, plist_t *result) | ||
202 | { | 265 | { |
203 | if (!client || !image_path || !image_signature || (signature_length == 0) || !image_type || !result) { | 266 | if (!client || !image_path || !image_type || !result) { |
204 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | 267 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; |
205 | } | 268 | } |
206 | mobile_image_mounter_lock(client); | 269 | mobile_image_mounter_lock(client); |
207 | 270 | ||
208 | plist_t dict = plist_new_dict(); | 271 | plist_t dict = plist_new_dict(); |
209 | plist_dict_insert_item(dict, "Command", plist_new_string("MountImage")); | 272 | plist_dict_set_item(dict, "Command", plist_new_string("MountImage")); |
210 | plist_dict_insert_item(dict, "ImagePath", plist_new_string(image_path)); | 273 | plist_dict_set_item(dict, "ImagePath", plist_new_string(image_path)); |
211 | plist_dict_insert_item(dict, "ImageSignature", plist_new_data(image_signature, signature_length)); | 274 | if (signature && signature_size != 0) |
212 | plist_dict_insert_item(dict, "ImageType", plist_new_string(image_type)); | 275 | plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size)); |
276 | plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); | ||
277 | if (PLIST_IS_DICT(options)) { | ||
278 | plist_dict_merge(&dict, options); | ||
279 | } | ||
213 | 280 | ||
214 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | 281 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); |
215 | plist_free(dict); | 282 | plist_free(dict); |
@@ -229,17 +296,56 @@ leave_unlock: | |||
229 | return res; | 296 | return res; |
230 | } | 297 | } |
231 | 298 | ||
232 | /** | 299 | mobile_image_mounter_error_t mobile_image_mounter_mount_image(mobile_image_mounter_client_t client, const char *image_path, const unsigned char *signature, unsigned int signature_size, const char *image_type, plist_t *result) |
233 | * Hangs up the connection to the mobile_image_mounter service. | 300 | { |
234 | * This functions has to be called before freeing up a mobile_image_mounter | 301 | return mobile_image_mounter_mount_image_with_options(client, image_path, signature, signature_size, image_type, NULL, result); |
235 | * instance. If not, errors appear in the device's syslog. | 302 | } |
236 | * | 303 | |
237 | * @param client The client to hang up | 304 | mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path) |
238 | * | 305 | { |
239 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, | 306 | if (!client || !mount_path) { |
240 | * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is invalid, | 307 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; |
241 | * or another error code otherwise. | 308 | } |
242 | */ | 309 | mobile_image_mounter_lock(client); |
310 | |||
311 | plist_t dict = plist_new_dict(); | ||
312 | plist_dict_set_item(dict, "Command", plist_new_string("UnmountImage")); | ||
313 | plist_dict_set_item(dict, "MountPath", plist_new_string(mount_path)); | ||
314 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
315 | plist_free(dict); | ||
316 | |||
317 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
318 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
319 | goto leave_unlock; | ||
320 | } | ||
321 | |||
322 | plist_t result = NULL; | ||
323 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
324 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
325 | debug_info("%s: Error receiving response from device!", __func__); | ||
326 | } else { | ||
327 | plist_t p_error = plist_dict_get_item(result, "Error"); | ||
328 | if (p_error) { | ||
329 | plist_t p_detailed = plist_dict_get_item(result, "DetailedError"); | ||
330 | const char* detailederr = (p_detailed) ? plist_get_string_ptr(p_detailed, NULL) : ""; | ||
331 | const char* errstr = plist_get_string_ptr(p_error, NULL); | ||
332 | if (errstr && !strcmp(errstr, "UnknownCommand")) { | ||
333 | res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; | ||
334 | } else if (errstr && !strcmp(errstr, "DeviceLocked")) { | ||
335 | res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED; | ||
336 | } else if (strstr(detailederr, "no matching entry")) { | ||
337 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
338 | } else { | ||
339 | res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; | ||
340 | } | ||
341 | } | ||
342 | } | ||
343 | |||
344 | leave_unlock: | ||
345 | mobile_image_mounter_unlock(client); | ||
346 | return res; | ||
347 | } | ||
348 | |||
243 | mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) | 349 | mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) |
244 | { | 350 | { |
245 | if (!client) { | 351 | if (!client) { |
@@ -248,7 +354,7 @@ mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_cl | |||
248 | mobile_image_mounter_lock(client); | 354 | mobile_image_mounter_lock(client); |
249 | 355 | ||
250 | plist_t dict = plist_new_dict(); | 356 | plist_t dict = plist_new_dict(); |
251 | plist_dict_insert_item(dict, "Command", plist_new_string("Hangup")); | 357 | plist_dict_set_item(dict, "Command", plist_new_string("Hangup")); |
252 | 358 | ||
253 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | 359 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); |
254 | plist_free(dict); | 360 | plist_free(dict); |
@@ -272,3 +378,215 @@ leave_unlock: | |||
272 | mobile_image_mounter_unlock(client); | 378 | mobile_image_mounter_unlock(client); |
273 | return res; | 379 | return res; |
274 | } | 380 | } |
381 | |||
382 | mobile_image_mounter_error_t mobile_image_mounter_query_developer_mode_status(mobile_image_mounter_client_t client, plist_t *result) | ||
383 | { | ||
384 | if (!client || !result) { | ||
385 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
386 | } | ||
387 | mobile_image_mounter_lock(client); | ||
388 | |||
389 | plist_t dict = plist_new_dict(); | ||
390 | plist_dict_set_item(dict, "Command", plist_new_string("QueryDeveloperModeStatus")); | ||
391 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
392 | plist_free(dict); | ||
393 | |||
394 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
395 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
396 | goto leave_unlock; | ||
397 | } | ||
398 | |||
399 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result)); | ||
400 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
401 | debug_info("%s: Error receiving response from device!", __func__); | ||
402 | } | ||
403 | |||
404 | leave_unlock: | ||
405 | mobile_image_mounter_unlock(client); | ||
406 | return res; | ||
407 | } | ||
408 | |||
409 | mobile_image_mounter_error_t mobile_image_mounter_query_nonce(mobile_image_mounter_client_t client, const char* image_type, unsigned char** nonce, unsigned int* nonce_size) | ||
410 | { | ||
411 | if (!client || !nonce || !nonce_size) { | ||
412 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
413 | } | ||
414 | mobile_image_mounter_lock(client); | ||
415 | |||
416 | plist_t dict = plist_new_dict(); | ||
417 | plist_dict_set_item(dict, "Command", plist_new_string("QueryNonce")); | ||
418 | if (image_type) { | ||
419 | plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); | ||
420 | } | ||
421 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
422 | plist_free(dict); | ||
423 | |||
424 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
425 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
426 | goto leave_unlock; | ||
427 | } | ||
428 | |||
429 | plist_t result = NULL; | ||
430 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
431 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
432 | debug_info("%s: Error receiving response from device!", __func__); | ||
433 | } else { | ||
434 | plist_t p_nonce = plist_dict_get_item(result, "PersonalizationNonce"); | ||
435 | if (!p_nonce) { | ||
436 | res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; | ||
437 | } else { | ||
438 | uint64_t nonce_size_ = 0; | ||
439 | plist_get_data_val(p_nonce, (char**)nonce, &nonce_size_); | ||
440 | if (*nonce) { | ||
441 | *nonce_size = (unsigned int)nonce_size_; | ||
442 | } else { | ||
443 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
444 | } | ||
445 | } | ||
446 | } | ||
447 | plist_free(result); | ||
448 | |||
449 | leave_unlock: | ||
450 | mobile_image_mounter_unlock(client); | ||
451 | return res; | ||
452 | } | ||
453 | |||
454 | mobile_image_mounter_error_t mobile_image_mounter_query_personalization_identifiers(mobile_image_mounter_client_t client, const char* image_type, plist_t *result) | ||
455 | { | ||
456 | if (!client || !result) { | ||
457 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
458 | } | ||
459 | mobile_image_mounter_lock(client); | ||
460 | |||
461 | plist_t dict = plist_new_dict(); | ||
462 | plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationIdentifiers")); | ||
463 | if (image_type) { | ||
464 | plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); | ||
465 | } | ||
466 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
467 | plist_free(dict); | ||
468 | |||
469 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
470 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
471 | goto leave_unlock; | ||
472 | } | ||
473 | |||
474 | plist_t _result = NULL; | ||
475 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &_result)); | ||
476 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
477 | debug_info("%s: Error receiving response from device!", __func__); | ||
478 | } | ||
479 | *result = plist_copy(plist_dict_get_item(_result, "PersonalizationIdentifiers")); | ||
480 | if (!*result) { | ||
481 | debug_info("%s: Response did not contain PersonalizationIdentifiers!", __func__); | ||
482 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
483 | } | ||
484 | |||
485 | leave_unlock: | ||
486 | mobile_image_mounter_unlock(client); | ||
487 | return res; | ||
488 | } | ||
489 | |||
490 | mobile_image_mounter_error_t mobile_image_mounter_query_personalization_manifest(mobile_image_mounter_client_t client, const char* image_type, const unsigned char* signature, unsigned int signature_size, unsigned char** manifest, unsigned int* manifest_size) | ||
491 | { | ||
492 | if (!client || !image_type || !signature || !signature_size || !manifest || !manifest_size) { | ||
493 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
494 | } | ||
495 | mobile_image_mounter_lock(client); | ||
496 | |||
497 | plist_t dict = plist_new_dict(); | ||
498 | plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationManifest")); | ||
499 | plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); | ||
500 | plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); | ||
501 | plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size)); | ||
502 | |||
503 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
504 | plist_free(dict); | ||
505 | |||
506 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
507 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
508 | goto leave_unlock; | ||
509 | } | ||
510 | |||
511 | plist_t result = NULL; | ||
512 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
513 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
514 | debug_info("%s: Error receiving response from device!", __func__); | ||
515 | } else { | ||
516 | plist_t p_manifest = plist_dict_get_item(result, "ImageSignature"); | ||
517 | if (!p_manifest) { | ||
518 | res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; | ||
519 | } else { | ||
520 | uint64_t manifest_size_ = 0; | ||
521 | plist_get_data_val(p_manifest, (char**)manifest, &manifest_size_); | ||
522 | if (*manifest) { | ||
523 | *manifest_size = (unsigned int)manifest_size_; | ||
524 | } else { | ||
525 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
526 | } | ||
527 | } | ||
528 | } | ||
529 | plist_free(result); | ||
530 | |||
531 | leave_unlock: | ||
532 | mobile_image_mounter_unlock(client); | ||
533 | return res; | ||
534 | } | ||
535 | |||
536 | mobile_image_mounter_error_t mobile_image_mounter_roll_personalization_nonce(mobile_image_mounter_client_t client) | ||
537 | { | ||
538 | if (!client) { | ||
539 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
540 | } | ||
541 | mobile_image_mounter_lock(client); | ||
542 | |||
543 | plist_t dict = plist_new_dict(); | ||
544 | plist_dict_set_item(dict, "Command", plist_new_string("RollPersonalizationNonce")); | ||
545 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
546 | plist_free(dict); | ||
547 | |||
548 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
549 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
550 | goto leave_unlock; | ||
551 | } | ||
552 | |||
553 | plist_t result = NULL; | ||
554 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
555 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
556 | debug_info("%s: Error receiving response from device!", __func__); | ||
557 | } | ||
558 | plist_free(result); | ||
559 | |||
560 | leave_unlock: | ||
561 | mobile_image_mounter_unlock(client); | ||
562 | return res; | ||
563 | } | ||
564 | |||
565 | mobile_image_mounter_error_t mobile_image_mounter_roll_cryptex_nonce(mobile_image_mounter_client_t client) | ||
566 | { | ||
567 | if (!client) { | ||
568 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
569 | } | ||
570 | mobile_image_mounter_lock(client); | ||
571 | |||
572 | plist_t dict = plist_new_dict(); | ||
573 | plist_dict_set_item(dict, "Command", plist_new_string("RollCryptexNonce")); | ||
574 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
575 | plist_free(dict); | ||
576 | |||
577 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
578 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
579 | goto leave_unlock; | ||
580 | } | ||
581 | |||
582 | plist_t result = NULL; | ||
583 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
584 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
585 | debug_info("%s: Error receiving response from device!", __func__); | ||
586 | } | ||
587 | plist_free(result); | ||
588 | |||
589 | leave_unlock: | ||
590 | mobile_image_mounter_unlock(client); | ||
591 | return res; | ||
592 | } | ||
diff --git a/src/mobile_image_mounter.h b/src/mobile_image_mounter.h index 2615dbc..9a8fcdd 100644 --- a/src/mobile_image_mounter.h +++ b/src/mobile_image_mounter.h | |||
@@ -8,27 +8,28 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef IMOBILE_IMAGE_MOUNTER_H | ||
22 | #define IMOBILE_IMAGE_MOUNTER_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __MOBILE_IMAGE_MOUNTER_H |
23 | #define __MOBILE_IMAGE_MOUNTER_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/mobile_image_mounter.h" | 26 | #include "libimobiledevice/mobile_image_mounter.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | #include <libimobiledevice-glue/thread.h> | ||
28 | 29 | ||
29 | struct mobile_image_mounter_client_private { | 30 | struct 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 | */ | ||
40 | static 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 | |||
57 | mobileactivation_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 | |||
77 | mobileactivation_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 | |||
84 | mobileactivation_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 | |||
96 | static 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 | |||
110 | static 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 | |||
128 | static 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 | |||
160 | static 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 | |||
179 | mobileactivation_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 | |||
201 | mobileactivation_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 | |||
221 | mobileactivation_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 | |||
243 | mobileactivation_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 | |||
267 | mobileactivation_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 | |||
280 | mobileactivation_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 | |||
303 | mobileactivation_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 | |||
29 | struct 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 | /** | 72 | mobilebackup_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 | */ | ||
75 | mobilebackup_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 | /** | 99 | mobilebackup_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 | */ | ||
112 | mobilebackup_error_t mobilebackup_client_free(mobilebackup_client_t client) | 106 | mobilebackup_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 | */ | ||
133 | mobilebackup_error_t mobilebackup_receive(mobilebackup_client_t client, plist_t * plist) | 119 | mobilebackup_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 | */ | ||
152 | mobilebackup_error_t mobilebackup_send(mobilebackup_client_t client, plist_t plist) | 127 | mobilebackup_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 | */ | ||
286 | mobilebackup_error_t mobilebackup_request_backup(mobilebackup_client_t client, plist_t backup_manifest, const char *base_path, const char *proto_version) | 244 | mobilebackup_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 | */ | ||
355 | mobilebackup_error_t mobilebackup_send_backup_file_received(mobilebackup_client_t client) | 312 | mobilebackup_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 | */ | ||
381 | mobilebackup_error_t mobilebackup_request_restore(mobilebackup_client_t client, plist_t backup_manifest, mobilebackup_flags_t flags, const char *proto_version) | 317 | mobilebackup_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 | */ | ||
453 | mobilebackup_error_t mobilebackup_receive_restore_file_received(mobilebackup_client_t client, plist_t *result) | 379 | mobilebackup_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 | */ | ||
476 | mobilebackup_error_t mobilebackup_receive_restore_application_received(mobilebackup_client_t client, plist_t *result) | 384 | mobilebackup_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 | */ | ||
492 | mobilebackup_error_t mobilebackup_send_restore_complete(mobilebackup_client_t client) | 389 | mobilebackup_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 | */ | ||
547 | mobilebackup_error_t mobilebackup_send_error(mobilebackup_client_t client, const char *reason) | 434 | mobilebackup_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 | */ | ||
48 | static 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 | |||
71 | mobilebackup2_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 | |||
99 | mobilebackup2_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 | |||
106 | mobilebackup2_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 | |||
119 | mobilebackup2_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 | */ | ||
170 | static 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 | } | ||
209 | leave: | ||
210 | if (dict) { | ||
211 | plist_free(dict); | ||
212 | } | ||
213 | |||
214 | return err; | ||
215 | } | ||
216 | |||
217 | mobilebackup2_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 | |||
222 | mobilebackup2_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 | |||
247 | mobilebackup2_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 | |||
274 | mobilebackup2_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); | ||
326 | leave: | ||
327 | if (dict) | ||
328 | plist_free(dict); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | mobilebackup2_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 | |||
363 | mobilebackup2_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 | |||
29 | struct 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 | /** | 74 | mobilesync_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 | */ | ||
82 | mobilesync_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 | /** | 104 | mobilesync_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 | */ | ||
121 | mobilesync_error_t mobilesync_client_free(mobilesync_client_t client) | 111 | mobilesync_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 | */ | ||
139 | mobilesync_error_t mobilesync_receive(mobilesync_client_t client, plist_t * plist) | 121 | mobilesync_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 | */ | ||
158 | mobilesync_error_t mobilesync_send(mobilesync_client_t client, plist_t plist) | 129 | mobilesync_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 | /** | 136 | mobilesync_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 | */ | ||
184 | mobilesync_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 | */ | ||
323 | mobilesync_error_t mobilesync_finish(mobilesync_client_t client) | 262 | mobilesync_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 | */ | ||
418 | mobilesync_error_t mobilesync_get_all_records_from_device(mobilesync_client_t client) | 347 | mobilesync_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 | */ | ||
433 | mobilesync_error_t mobilesync_get_changes_from_device(mobilesync_client_t client) | 352 | mobilesync_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 | */ | ||
451 | mobilesync_error_t mobilesync_receive_changes(mobilesync_client_t client, plist_t *entities, uint8_t *is_last_record, plist_t *actions) | 357 | mobilesync_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 | */ | ||
529 | mobilesync_error_t mobilesync_clear_all_records_on_device(mobilesync_client_t client) | 424 | mobilesync_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 | */ | ||
606 | mobilesync_error_t mobilesync_acknowledge_changes_from_device(mobilesync_client_t client) | 493 | mobilesync_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 | */ | ||
657 | mobilesync_error_t mobilesync_ready_to_send_changes_from_computer(mobilesync_client_t client) | 527 | mobilesync_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 | */ | ||
738 | mobilesync_error_t mobilesync_send_changes(mobilesync_client_t client, plist_t entities, uint8_t is_last_record, plist_t actions) | 594 | mobilesync_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 | */ | ||
781 | mobilesync_error_t mobilesync_remap_identifiers(mobilesync_client_t client, plist_t *mapping) | 622 | mobilesync_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 | */ | ||
859 | mobilesync_error_t mobilesync_cancel(mobilesync_client_t client, const char* reason) | 691 | mobilesync_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 | */ | ||
894 | mobilesync_anchors_t mobilesync_anchors_new(const char *device_anchor, const char *computer_anchor) | 717 | mobilesync_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 | */ | ||
916 | void mobilesync_anchors_free(mobilesync_anchors_t anchors) | 734 | void 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 | /** | 748 | plist_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 | */ | ||
935 | plist_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 | */ | ||
952 | void mobilesync_actions_add(plist_t actions, ...) | 753 | void 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 | */ | ||
989 | void mobilesync_actions_free(plist_t actions) | 785 | void mobilesync_actions_free(plist_t actions) |
990 | { | 786 | { |
991 | if (actions) { | 787 | if (actions) { |
diff --git a/src/mobilesync.h b/src/mobilesync.h index 24e61af..3b5ece9 100644 --- a/src/mobilesync.h +++ b/src/mobilesync.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * mobilesync.h | 2 | * mobilesync.h |
3 | * Definitions for the built-in MobileSync client | 3 | * Definitions for the built-in MobileSync client |
4 | * | 4 | * |
5 | * Copyright (c) 2010 Bryan Forbes All Rights Reserved. | 5 | * Copyright (c) 2010 Bryan Forbes All Rights Reserved. |
6 | * Copyright (c) 2009 Jonathan Beck All Rights Reserved. | 6 | * Copyright (c) 2009 Jonathan Beck All Rights Reserved. |
7 | * | 7 | * |
@@ -9,19 +9,21 @@ | |||
9 | * modify it under the terms of the GNU Lesser General Public | 9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either | 10 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. | 11 | * version 2.1 of the License, or (at your option) any later version. |
12 | * | 12 | * |
13 | * This library is distributed in the hope that it will be useful, | 13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. | 16 | * Lesser General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU Lesser General Public | 18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, write to the Free Software | 19 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ | 21 | */ |
22 | #ifndef MOBILESYNC_H | ||
23 | #define MOBILESYNC_H | ||
24 | 22 | ||
23 | #ifndef __MOBILESYNC_H | ||
24 | #define __MOBILESYNC_H | ||
25 | |||
26 | #include "idevice.h" | ||
25 | #include "libimobiledevice/mobilesync.h" | 27 | #include "libimobiledevice/mobilesync.h" |
26 | #include "device_link_service.h" | 28 | #include "device_link_service.h" |
27 | 29 | ||
diff --git a/src/notification_proxy.c b/src/notification_proxy.c index 80a82c4..60b2e03 100644 --- a/src/notification_proxy.c +++ b/src/notification_proxy.c | |||
@@ -8,17 +8,20 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
24 | #include <unistd.h> | 27 | #include <unistd.h> |
@@ -26,7 +29,11 @@ | |||
26 | 29 | ||
27 | #include "notification_proxy.h" | 30 | #include "notification_proxy.h" |
28 | #include "property_list_service.h" | 31 | #include "property_list_service.h" |
29 | #include "debug.h" | 32 | #include "common/debug.h" |
33 | |||
34 | #ifdef WIN32 | ||
35 | #define sleep(x) Sleep(x*1000) | ||
36 | #endif | ||
30 | 37 | ||
31 | struct np_thread { | 38 | struct np_thread { |
32 | np_client_t client; | 39 | np_client_t client; |
@@ -41,19 +48,19 @@ struct np_thread { | |||
41 | */ | 48 | */ |
42 | static void np_lock(np_client_t client) | 49 | static void np_lock(np_client_t client) |
43 | { | 50 | { |
44 | debug_info("NP: Locked"); | 51 | debug_info("Locked"); |
45 | g_mutex_lock(client->mutex); | 52 | mutex_lock(&client->mutex); |
46 | } | 53 | } |
47 | 54 | ||
48 | /** | 55 | /** |
49 | * Unlocks a notification_proxy client, used for thread safety. | 56 | * Unlocks a notification_proxy client, used for thread safety. |
50 | * | 57 | * |
51 | * @param client notification_proxy client to unlock | 58 | * @param client notification_proxy client to unlock |
52 | */ | 59 | */ |
53 | static void np_unlock(np_client_t client) | 60 | static void np_unlock(np_client_t client) |
54 | { | 61 | { |
55 | debug_info("NP: Unlocked"); | 62 | debug_info("Unlocked"); |
56 | g_mutex_unlock(client->mutex); | 63 | mutex_unlock(&client->mutex); |
57 | } | 64 | } |
58 | 65 | ||
59 | /** | 66 | /** |
@@ -82,78 +89,85 @@ static np_error_t np_error(property_list_service_error_t err) | |||
82 | return NP_E_UNKNOWN_ERROR; | 89 | return NP_E_UNKNOWN_ERROR; |
83 | } | 90 | } |
84 | 91 | ||
85 | /** | 92 | np_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 | */ | ||
97 | np_error_t np_client_new(idevice_t device, uint16_t port, np_client_t *client) | ||
98 | { | 93 | { |
99 | /* makes sure thread environment is available */ | ||
100 | if (!g_thread_supported()) | ||
101 | g_thread_init(NULL); | ||
102 | |||
103 | if (!device) | ||
104 | return NP_E_INVALID_ARG; | ||
105 | |||
106 | property_list_service_client_t plistclient = NULL; | 94 | property_list_service_client_t plistclient = NULL; |
107 | if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 95 | np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient)); |
108 | return NP_E_CONN_FAILED; | 96 | if (err != NP_E_SUCCESS) { |
97 | return err; | ||
109 | } | 98 | } |
110 | 99 | ||
111 | np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); | 100 | np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); |
112 | client_loc->parent = plistclient; | 101 | client_loc->parent = plistclient; |
113 | 102 | ||
114 | client_loc->mutex = g_mutex_new(); | 103 | mutex_init(&client_loc->mutex); |
115 | 104 | client_loc->notifier = THREAD_T_NULL; | |
116 | client_loc->notifier = NULL; | ||
117 | 105 | ||
118 | *client = client_loc; | 106 | *client = client_loc; |
119 | return NP_E_SUCCESS; | 107 | return NP_E_SUCCESS; |
120 | } | 108 | } |
121 | 109 | ||
122 | /** | 110 | np_error_t np_client_start_service(idevice_t device, np_client_t* client, const char* label) |
123 | * Disconnects a notification_proxy client from the device and frees up the | 111 | { |
124 | * notification_proxy client data. | 112 | np_error_t err = NP_E_UNKNOWN_ERROR; |
125 | * | 113 | service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err); |
126 | * @param client The notification_proxy client to disconnect and free. | 114 | return err; |
127 | * | 115 | } |
128 | * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL. | 116 | |
129 | */ | ||
130 | np_error_t np_client_free(np_client_t client) | 117 | np_error_t np_client_free(np_client_t client) |
131 | { | 118 | { |
119 | plist_t dict; | ||
120 | property_list_service_client_t parent; | ||
121 | |||
132 | if (!client) | 122 | if (!client) |
133 | return NP_E_INVALID_ARG; | 123 | return NP_E_INVALID_ARG; |
134 | 124 | ||
135 | property_list_service_client_free(client->parent); | 125 | dict = plist_new_dict(); |
126 | plist_dict_set_item(dict,"Command", plist_new_string("Shutdown")); | ||
127 | property_list_service_send_xml_plist(client->parent, dict); | ||
128 | plist_free(dict); | ||
129 | |||
130 | parent = client->parent; | ||
131 | /* notifies the client->notifier thread that it should terminate */ | ||
136 | client->parent = NULL; | 132 | client->parent = NULL; |
133 | |||
137 | if (client->notifier) { | 134 | if (client->notifier) { |
138 | debug_info("joining np callback"); | 135 | debug_info("joining np callback"); |
139 | g_thread_join(client->notifier); | 136 | thread_join(client->notifier); |
140 | } | 137 | thread_free(client->notifier); |
141 | if (client->mutex) { | 138 | client->notifier = THREAD_T_NULL; |
142 | g_mutex_free(client->mutex); | 139 | } else { |
140 | dict = NULL; | ||
141 | property_list_service_receive_plist(parent, &dict); | ||
142 | if (dict) { | ||
143 | #ifndef STRIP_DEBUG_CODE | ||
144 | char *cmd_value = NULL; | ||
145 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); | ||
146 | if (plist_get_node_type(cmd_value_node) == PLIST_STRING) { | ||
147 | plist_get_string_val(cmd_value_node, &cmd_value); | ||
148 | } | ||
149 | if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { | ||
150 | // this is the expected answer | ||
151 | } else { | ||
152 | debug_info("Did not get ProxyDeath but:"); | ||
153 | debug_plist(dict); | ||
154 | } | ||
155 | if (cmd_value) { | ||
156 | free(cmd_value); | ||
157 | } | ||
158 | #endif | ||
159 | plist_free(dict); | ||
160 | } | ||
143 | } | 161 | } |
162 | |||
163 | property_list_service_client_free(parent); | ||
164 | |||
165 | mutex_destroy(&client->mutex); | ||
144 | free(client); | 166 | free(client); |
145 | 167 | ||
146 | return NP_E_SUCCESS; | 168 | return NP_E_SUCCESS; |
147 | } | 169 | } |
148 | 170 | ||
149 | /** | ||
150 | * Sends a notification to the device's notification_proxy. | ||
151 | * | ||
152 | * @param client The client to send to | ||
153 | * @param notification The notification message to send | ||
154 | * | ||
155 | * @return NP_E_SUCCESS on success, or an error returned by np_plist_send | ||
156 | */ | ||
157 | np_error_t np_post_notification(np_client_t client, const char *notification) | 171 | np_error_t np_post_notification(np_client_t client, const char *notification) |
158 | { | 172 | { |
159 | if (!client || !notification) { | 173 | if (!client || !notification) { |
@@ -162,66 +176,24 @@ np_error_t np_post_notification(np_client_t client, const char *notification) | |||
162 | np_lock(client); | 176 | np_lock(client); |
163 | 177 | ||
164 | plist_t dict = plist_new_dict(); | 178 | plist_t dict = plist_new_dict(); |
165 | plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification")); | 179 | plist_dict_set_item(dict,"Command", plist_new_string("PostNotification")); |
166 | plist_dict_insert_item(dict,"Name", plist_new_string(notification)); | 180 | plist_dict_set_item(dict,"Name", plist_new_string(notification)); |
167 | 181 | ||
168 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); | 182 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); |
169 | plist_free(dict); | 183 | plist_free(dict); |
170 | 184 | ||
171 | dict = plist_new_dict(); | ||
172 | plist_dict_insert_item(dict,"Command", plist_new_string("Shutdown")); | ||
173 | |||
174 | res = np_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
175 | plist_free(dict); | ||
176 | |||
177 | if (res != NP_E_SUCCESS) { | 185 | if (res != NP_E_SUCCESS) { |
178 | debug_info("Error sending XML plist to device!"); | 186 | debug_info("Error sending XML plist to device!"); |
179 | } | 187 | } |
180 | |||
181 | // try to read an answer, we just ignore errors here | ||
182 | dict = NULL; | ||
183 | property_list_service_receive_plist(client->parent, &dict); | ||
184 | if (dict) { | ||
185 | #ifndef STRIP_DEBUG_CODE | ||
186 | char *cmd_value = NULL; | ||
187 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); | ||
188 | if (plist_get_node_type(cmd_value_node) == PLIST_STRING) { | ||
189 | plist_get_string_val(cmd_value_node, &cmd_value); | ||
190 | } | ||
191 | |||
192 | if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { | ||
193 | // this is the expected answer | ||
194 | } else { | ||
195 | debug_plist(dict); | ||
196 | } | ||
197 | g_free(cmd_value); | ||
198 | #endif | ||
199 | plist_free(dict); | ||
200 | } | ||
201 | |||
202 | np_unlock(client); | 188 | np_unlock(client); |
203 | return res; | 189 | return res; |
204 | } | 190 | } |
205 | 191 | ||
206 | /** | 192 | static 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 | */ | ||
215 | np_error_t np_observe_notification( np_client_t client, const char *notification ) | ||
216 | { | 193 | { |
217 | if (!client || !notification) { | ||
218 | return NP_E_INVALID_ARG; | ||
219 | } | ||
220 | np_lock(client); | ||
221 | |||
222 | plist_t dict = plist_new_dict(); | 194 | plist_t dict = plist_new_dict(); |
223 | plist_dict_insert_item(dict,"Command", plist_new_string("ObserveNotification")); | 195 | plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification")); |
224 | plist_dict_insert_item(dict,"Name", plist_new_string(notification)); | 196 | plist_dict_set_item(dict,"Name", plist_new_string(notification)); |
225 | 197 | ||
226 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); | 198 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); |
227 | if (res != NP_E_SUCCESS) { | 199 | if (res != NP_E_SUCCESS) { |
@@ -229,21 +201,20 @@ np_error_t np_observe_notification( np_client_t client, const char *notification | |||
229 | } | 201 | } |
230 | plist_free(dict); | 202 | plist_free(dict); |
231 | 203 | ||
204 | return res; | ||
205 | } | ||
206 | |||
207 | np_error_t np_observe_notification( np_client_t client, const char *notification ) | ||
208 | { | ||
209 | if (!client || !notification) { | ||
210 | return NP_E_INVALID_ARG; | ||
211 | } | ||
212 | np_lock(client); | ||
213 | np_error_t res = internal_np_observe_notification(client, notification); | ||
232 | np_unlock(client); | 214 | np_unlock(client); |
233 | return res; | 215 | return res; |
234 | } | 216 | } |
235 | 217 | ||
236 | /** | ||
237 | * Tells the device to send a notification on specified events. | ||
238 | * | ||
239 | * @param client The client to send to | ||
240 | * @param notification_spec Specification of the notifications that should be | ||
241 | * observed. This is expected to be an array of const char* that MUST have a | ||
242 | * terminating NULL entry. | ||
243 | * | ||
244 | * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client is null, | ||
245 | * or an error returned by np_observe_notification. | ||
246 | */ | ||
247 | np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) | 218 | np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) |
248 | { | 219 | { |
249 | int i = 0; | 220 | int i = 0; |
@@ -258,13 +229,15 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio | |||
258 | return NP_E_INVALID_ARG; | 229 | return NP_E_INVALID_ARG; |
259 | } | 230 | } |
260 | 231 | ||
232 | np_lock(client); | ||
261 | while (notifications[i]) { | 233 | while (notifications[i]) { |
262 | res = np_observe_notification(client, notifications[i]); | 234 | res = internal_np_observe_notification(client, notifications[i]); |
263 | if (res != NP_E_SUCCESS) { | 235 | if (res != NP_E_SUCCESS) { |
264 | break; | 236 | break; |
265 | } | 237 | } |
266 | i++; | 238 | i++; |
267 | } | 239 | } |
240 | np_unlock(client); | ||
268 | 241 | ||
269 | return res; | 242 | return res; |
270 | } | 243 | } |
@@ -277,7 +250,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio | |||
277 | * with the notification that has been received. | 250 | * with the notification that has been received. |
278 | * | 251 | * |
279 | * @return 0 if a notification has been received or nothing has been received, | 252 | * @return 0 if a notification has been received or nothing has been received, |
280 | * or a negative value if an error occured. | 253 | * or a negative value if an error occurred. |
281 | * | 254 | * |
282 | * @note You probably want to check out np_set_notify_callback | 255 | * @note You probably want to check out np_set_notify_callback |
283 | * @see np_set_notify_callback | 256 | * @see np_set_notify_callback |
@@ -292,11 +265,15 @@ static int np_get_notification(np_client_t client, char **notification) | |||
292 | 265 | ||
293 | np_lock(client); | 266 | np_lock(client); |
294 | 267 | ||
295 | property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); | 268 | property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); |
296 | if (!dict) { | 269 | if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) { |
297 | debug_info("NotificationProxy: no notification received!"); | 270 | debug_info("NotificationProxy: no notification received!"); |
298 | res = 0; | 271 | res = 0; |
299 | } else { | 272 | } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) { |
273 | debug_info("NotificationProxy: error %d occurred!", perr); | ||
274 | res = perr; | ||
275 | } | ||
276 | if (dict) { | ||
300 | char *cmd_value = NULL; | 277 | char *cmd_value = NULL; |
301 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); | 278 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); |
302 | 279 | ||
@@ -315,11 +292,11 @@ static int np_get_notification(np_client_t client, char **notification) | |||
315 | res = -2; | 292 | res = -2; |
316 | if (name_value_node && name_value) { | 293 | if (name_value_node && name_value) { |
317 | *notification = name_value; | 294 | *notification = name_value; |
318 | debug_info("got notification %s\n", __func__, name_value); | 295 | debug_info("got notification %s", __func__, name_value); |
319 | res = 0; | 296 | res = 0; |
320 | } | 297 | } |
321 | } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { | 298 | } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { |
322 | debug_info("ERROR: NotificationProxy died!"); | 299 | debug_info("NotificationProxy died!"); |
323 | res = -1; | 300 | res = -1; |
324 | } else if (cmd_value) { | 301 | } else if (cmd_value) { |
325 | debug_info("unknown NotificationProxy command '%s' received!", cmd_value); | 302 | debug_info("unknown NotificationProxy command '%s' received!", cmd_value); |
@@ -342,7 +319,7 @@ static int np_get_notification(np_client_t client, char **notification) | |||
342 | /** | 319 | /** |
343 | * Internally used thread function. | 320 | * Internally used thread function. |
344 | */ | 321 | */ |
345 | gpointer np_notifier( gpointer arg ) | 322 | void* np_notifier( void* arg ) |
346 | { | 323 | { |
347 | char *notification = NULL; | 324 | char *notification = NULL; |
348 | struct np_thread *npt = (struct np_thread*)arg; | 325 | struct np_thread *npt = (struct np_thread*)arg; |
@@ -351,7 +328,10 @@ gpointer np_notifier( gpointer arg ) | |||
351 | 328 | ||
352 | debug_info("starting callback."); | 329 | debug_info("starting callback."); |
353 | while (npt->client->parent) { | 330 | while (npt->client->parent) { |
354 | np_get_notification(npt->client, ¬ification); | 331 | if (np_get_notification(npt->client, ¬ification) < 0) { |
332 | npt->cbfunc("", npt->user_data); | ||
333 | break; | ||
334 | } | ||
355 | if (notification) { | 335 | if (notification) { |
356 | npt->cbfunc(notification, npt->user_data); | 336 | npt->cbfunc(notification, npt->user_data); |
357 | free(notification); | 337 | free(notification); |
@@ -366,25 +346,6 @@ gpointer np_notifier( gpointer arg ) | |||
366 | return NULL; | 346 | return NULL; |
367 | } | 347 | } |
368 | 348 | ||
369 | /** | ||
370 | * This function allows an application to define a callback function that will | ||
371 | * be called when a notification has been received. | ||
372 | * It will start a thread that polls for notifications and calls the callback | ||
373 | * function if a notification has been received. | ||
374 | * | ||
375 | * @param client the NP client | ||
376 | * @param notify_cb pointer to a callback function or NULL to de-register a | ||
377 | * previously set callback function. | ||
378 | * @param user_data Pointer that will be passed to the callback function as | ||
379 | * user data. If notify_cb is NULL, this parameter is ignored. | ||
380 | * | ||
381 | * @note Only one callback function can be registered at the same time; | ||
382 | * any previously set callback function will be removed automatically. | ||
383 | * | ||
384 | * @return NP_E_SUCCESS when the callback was successfully registered, | ||
385 | * NP_E_INVALID_ARG when client is NULL, or NP_E_UNKNOWN_ERROR when | ||
386 | * the callback thread could no be created. | ||
387 | */ | ||
388 | np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) | 349 | np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) |
389 | { | 350 | { |
390 | if (!client) | 351 | if (!client) |
@@ -394,11 +355,12 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, | |||
394 | 355 | ||
395 | np_lock(client); | 356 | np_lock(client); |
396 | if (client->notifier) { | 357 | if (client->notifier) { |
397 | debug_info("callback already set, removing\n"); | 358 | debug_info("callback already set, removing"); |
398 | property_list_service_client_t parent = client->parent; | 359 | property_list_service_client_t parent = client->parent; |
399 | client->parent = NULL; | 360 | client->parent = NULL; |
400 | g_thread_join(client->notifier); | 361 | thread_join(client->notifier); |
401 | client->notifier = NULL; | 362 | thread_free(client->notifier); |
363 | client->notifier = THREAD_T_NULL; | ||
402 | client->parent = parent; | 364 | client->parent = parent; |
403 | } | 365 | } |
404 | 366 | ||
@@ -409,8 +371,7 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, | |||
409 | npt->cbfunc = notify_cb; | 371 | npt->cbfunc = notify_cb; |
410 | npt->user_data = user_data; | 372 | npt->user_data = user_data; |
411 | 373 | ||
412 | client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL); | 374 | if (thread_new(&client->notifier, np_notifier, npt) == 0) { |
413 | if (client->notifier) { | ||
414 | res = NP_E_SUCCESS; | 375 | res = NP_E_SUCCESS; |
415 | } | 376 | } |
416 | } | 377 | } |
diff --git a/src/notification_proxy.h b/src/notification_proxy.h index 8d5cd24..595cb01 100644 --- a/src/notification_proxy.h +++ b/src/notification_proxy.h | |||
@@ -8,30 +8,31 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef INOTIFICATION_PROXY_H | ||
22 | #define INOTIFICATION_PROXY_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __NOTIFICATION_PROXY_H |
23 | #define __NOTIFICATION_PROXY_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/notification_proxy.h" | 26 | #include "libimobiledevice/notification_proxy.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | #include <libimobiledevice-glue/thread.h> | ||
28 | 29 | ||
29 | struct np_client_private { | 30 | struct 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 | ||
35 | gpointer np_notifier(gpointer arg); | 36 | void* np_notifier(void* arg); |
36 | 37 | ||
37 | #endif | 38 | #endif |
diff --git a/src/preboard.c b/src/preboard.c new file mode 100644 index 0000000..c3eff02 --- /dev/null +++ b/src/preboard.c | |||
@@ -0,0 +1,256 @@ | |||
1 | /* | ||
2 | * preboard.c | ||
3 | * com.apple.preboardservice_v2 service implementation. | ||
4 | * | ||
5 | * Copyright (c) 2019 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <plist/plist.h> | ||
28 | |||
29 | #include "preboard.h" | ||
30 | #include "lockdown.h" | ||
31 | #include "common/debug.h" | ||
32 | |||
33 | /** | ||
34 | * Convert a property_list_service_error_t value to a preboard_error_t value. | ||
35 | * Used internally to get correct error codes. | ||
36 | * | ||
37 | * @param err An property_list_service_error_t error code | ||
38 | * | ||
39 | * @return A matching preboard_error_t error code, | ||
40 | * PREBOARD_E_UNKNOWN_ERROR otherwise. | ||
41 | */ | ||
42 | static 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 | |||
65 | preboard_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 | |||
93 | preboard_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 | |||
100 | preboard_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 | |||
119 | preboard_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 | |||
130 | preboard_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 | |||
144 | preboard_error_t preboard_receive(preboard_client_t client, plist_t * plist) | ||
145 | { | ||
146 | return preboard_receive_with_timeout(client, plist, 5000); | ||
147 | } | ||
148 | |||
149 | struct preboard_status_data { | ||
150 | preboard_client_t client; | ||
151 | preboard_status_cb_t cbfunc; | ||
152 | void *user_data; | ||
153 | }; | ||
154 | |||
155 | static 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 | |||
188 | static 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 | |||
212 | preboard_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 | |||
235 | preboard_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 | |||
30 | struct 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 | */ |
38 | static property_list_service_error_t idevice_to_property_list_service_error(idevice_error_t err) | 40 | static 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 | /** | 61 | property_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 | */ | ||
66 | property_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 | */ | ||
95 | property_list_service_error_t property_list_service_client_free(property_list_service_client_t client) | 81 | property_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 | */ |
119 | static property_list_service_error_t internal_plist_send(property_list_service_client_t client, plist_t plist, int binary) | 109 | static 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 | */ | ||
176 | property_list_service_error_t property_list_service_send_xml_plist(property_list_service_client_t client, plist_t plist) | 155 | property_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 | */ | ||
192 | property_list_service_error_t property_list_service_send_binary_plist(property_list_service_client_t client, plist_t plist) | 160 | property_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 | */ | ||
287 | property_list_service_error_t property_list_service_receive_plist_with_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout) | 265 | property_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 | */ | ||
311 | property_list_service_error_t property_list_service_receive_plist(property_list_service_client_t client, plist_t *plist) | 270 | property_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 | */ | ||
327 | property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client) | 275 | property_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 | */ | ||
344 | property_list_service_error_t property_list_service_disable_ssl(property_list_service_client_t client) | 282 | property_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 | ||
289 | property_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 | ||
35 | struct property_list_service_client_private { | 29 | struct property_list_service_client_private { |
36 | idevice_connection_t connection; | 30 | service_client_t parent; |
37 | }; | 31 | }; |
38 | 32 | ||
39 | typedef struct property_list_service_client_private *property_list_service_client_t; | ||
40 | |||
41 | typedef int16_t property_list_service_error_t; | ||
42 | |||
43 | /* creation and destruction */ | ||
44 | property_list_service_error_t property_list_service_client_new(idevice_t device, uint16_t port, property_list_service_client_t *client); | ||
45 | property_list_service_error_t property_list_service_client_free(property_list_service_client_t client); | ||
46 | |||
47 | /* sending */ | ||
48 | property_list_service_error_t property_list_service_send_xml_plist(property_list_service_client_t client, plist_t plist); | ||
49 | property_list_service_error_t property_list_service_send_binary_plist(property_list_service_client_t client, plist_t plist); | ||
50 | |||
51 | /* receiving */ | ||
52 | property_list_service_error_t property_list_service_receive_plist_with_timeout(property_list_service_client_t client, plist_t *plist, unsigned int timeout); | ||
53 | property_list_service_error_t property_list_service_receive_plist(property_list_service_client_t client, plist_t *plist); | ||
54 | |||
55 | /* misc */ | ||
56 | property_list_service_error_t property_list_service_enable_ssl(property_list_service_client_t client); | ||
57 | property_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 | */ |
48 | static int restored_check_result(plist_t dict) | 49 | static 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 | /** | 95 | static 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 | |||
102 | restored_error_t restored_client_free(restored_client_t client) | 114 | restored_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 | */ | ||
139 | void restored_client_set_label(restored_client_t client, const char *label) | 142 | void 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 | */ | ||
158 | restored_error_t restored_receive(restored_client_t client, plist_t *plist) | 152 | restored_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 | */ | ||
189 | restored_error_t restored_send(restored_client_t client, plist_t plist) | 160 | restored_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 | */ | ||
214 | restored_error_t restored_query_type(restored_client_t client, char **type, uint64_t *version) | 168 | restored_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 | /** | 227 | restored_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 | */ | ||
279 | restored_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 | |||
269 | restored_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 | */ | ||
315 | restored_error_t restored_client_new(idevice_t device, restored_client_t *client, const char *label) | 294 | restored_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 | */ | ||
358 | restored_error_t restored_goodbye(restored_client_t client) | 338 | restored_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 | /** | 370 | restored_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 | */ | ||
398 | restored_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 | */ | ||
427 | restored_error_t restored_reboot(restored_client_t client) | 394 | restored_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 | ||
30 | struct restored_client_private { | 31 | struct 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 | */ | ||
68 | static 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 | |||
89 | static 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 | |||
103 | static 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 | |||
111 | static 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 | |||
125 | static 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 | |||
273 | static 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 | |||
320 | static 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 | |||
345 | static 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 | |||
466 | leave: | ||
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 | |||
475 | static 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 | |||
531 | reverse_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 | |||
610 | reverse_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 | |||
623 | reverse_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 | |||
644 | reverse_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 | |||
663 | reverse_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 | |||
670 | void 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 | |||
679 | void 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 | |||
688 | void 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 | |||
697 | reverse_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 | |||
703 | reverse_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 | |||
710 | reverse_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 | |||
715 | reverse_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 | |||
757 | reverse_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 | |||
762 | reverse_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 | |||
29 | struct 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 | |||
44 | reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent); | ||
45 | reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received); | ||
46 | reverse_proxy_error_t reverse_proxy_receive_with_timeout(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received, unsigned int timeout); | ||
47 | reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist); | ||
48 | reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist); | ||
49 | reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms); | ||
50 | |||
51 | #endif | ||
diff --git a/src/sbservices.c b/src/sbservices.c index 3596cbd..365e130 100644 --- a/src/sbservices.c +++ b/src/sbservices.c | |||
@@ -8,17 +8,20 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
24 | #include <unistd.h> | 27 | #include <unistd.h> |
@@ -26,28 +29,28 @@ | |||
26 | 29 | ||
27 | #include "sbservices.h" | 30 | #include "sbservices.h" |
28 | #include "property_list_service.h" | 31 | #include "property_list_service.h" |
29 | #include "debug.h" | 32 | #include "common/debug.h" |
30 | 33 | ||
31 | /** | 34 | /** |
32 | * Locks an sbservices client, used for thread safety. | 35 | * Locks an sbservices client, used for thread safety. |
33 | * | 36 | * |
34 | * @param client sbservices client to lock. | 37 | * @param client sbservices client to lock. |
35 | */ | 38 | */ |
36 | static void sbs_lock(sbservices_client_t client) | 39 | static void sbservices_lock(sbservices_client_t client) |
37 | { | 40 | { |
38 | debug_info("SBServices: Locked"); | 41 | debug_info("Locked"); |
39 | g_mutex_lock(client->mutex); | 42 | mutex_lock(&client->mutex); |
40 | } | 43 | } |
41 | 44 | ||
42 | /** | 45 | /** |
43 | * Unlocks an sbservices client, used for thread safety. | 46 | * Unlocks an sbservices client, used for thread safety. |
44 | * | 47 | * |
45 | * @param client sbservices client to unlock | 48 | * @param client sbservices client to unlock |
46 | */ | 49 | */ |
47 | static void sbs_unlock(sbservices_client_t client) | 50 | static void sbservices_unlock(sbservices_client_t client) |
48 | { | 51 | { |
49 | debug_info("SBServices: Unlocked"); | 52 | debug_info("Unlocked"); |
50 | g_mutex_unlock(client->mutex); | 53 | mutex_unlock(&client->mutex); |
51 | } | 54 | } |
52 | 55 | ||
53 | /** | 56 | /** |
@@ -61,64 +64,44 @@ static void sbs_unlock(sbservices_client_t client) | |||
61 | */ | 64 | */ |
62 | static sbservices_error_t sbservices_error(property_list_service_error_t err) | 65 | static sbservices_error_t sbservices_error(property_list_service_error_t err) |
63 | { | 66 | { |
64 | switch (err) { | 67 | switch (err) { |
65 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | 68 | case PROPERTY_LIST_SERVICE_E_SUCCESS: |
66 | return SBSERVICES_E_SUCCESS; | 69 | return SBSERVICES_E_SUCCESS; |
67 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | 70 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: |
68 | return SBSERVICES_E_INVALID_ARG; | 71 | return SBSERVICES_E_INVALID_ARG; |
69 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | 72 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: |
70 | return SBSERVICES_E_PLIST_ERROR; | 73 | return SBSERVICES_E_PLIST_ERROR; |
71 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | 74 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: |
72 | return SBSERVICES_E_CONN_FAILED; | 75 | return SBSERVICES_E_CONN_FAILED; |
73 | default: | 76 | default: |
74 | break; | 77 | break; |
75 | } | 78 | } |
76 | return SBSERVICES_E_UNKNOWN_ERROR; | 79 | return SBSERVICES_E_UNKNOWN_ERROR; |
77 | } | 80 | } |
78 | 81 | ||
79 | /** | 82 | sbservices_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 | */ | ||
90 | sbservices_error_t sbservices_client_new(idevice_t device, uint16_t port, sbservices_client_t *client) | ||
91 | { | 83 | { |
92 | /* makes sure thread environment is available */ | ||
93 | if (!g_thread_supported()) | ||
94 | g_thread_init(NULL); | ||
95 | |||
96 | if (!device) | ||
97 | return SBSERVICES_E_INVALID_ARG; | ||
98 | |||
99 | property_list_service_client_t plistclient = NULL; | 84 | property_list_service_client_t plistclient = NULL; |
100 | sbservices_error_t err = sbservices_error(property_list_service_client_new(device, port, &plistclient)); | 85 | sbservices_error_t err = sbservices_error(property_list_service_client_new(device, service, &plistclient)); |
101 | if (err != SBSERVICES_E_SUCCESS) { | 86 | if (err != SBSERVICES_E_SUCCESS) { |
102 | return err; | 87 | return err; |
103 | } | 88 | } |
104 | 89 | ||
105 | sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private)); | 90 | sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private)); |
106 | client_loc->parent = plistclient; | 91 | client_loc->parent = plistclient; |
107 | client_loc->mutex = g_mutex_new(); | 92 | mutex_init(&client_loc->mutex); |
108 | 93 | ||
109 | *client = client_loc; | 94 | *client = client_loc; |
110 | return SBSERVICES_E_SUCCESS; | 95 | return SBSERVICES_E_SUCCESS; |
111 | } | 96 | } |
112 | 97 | ||
113 | /** | 98 | sbservices_error_t sbservices_client_start_service(idevice_t device, sbservices_client_t * client, const char* label) |
114 | * Disconnects an sbservices client from the device and frees up the | 99 | { |
115 | * sbservices client data. | 100 | sbservices_error_t err = SBSERVICES_E_UNKNOWN_ERROR; |
116 | * | 101 | service_client_factory_start_service(device, SBSERVICES_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(sbservices_client_new), &err); |
117 | * @param client The sbservices client to disconnect and free. | 102 | return err; |
118 | * | 103 | } |
119 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | 104 | |
120 | * client is NULL, or an SBSERVICES_E_* error code otherwise. | ||
121 | */ | ||
122 | sbservices_error_t sbservices_client_free(sbservices_client_t client) | 105 | sbservices_error_t sbservices_client_free(sbservices_client_t client) |
123 | { | 106 | { |
124 | if (!client) | 107 | if (!client) |
@@ -126,28 +109,12 @@ sbservices_error_t sbservices_client_free(sbservices_client_t client) | |||
126 | 109 | ||
127 | sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent)); | 110 | sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent)); |
128 | client->parent = NULL; | 111 | client->parent = NULL; |
129 | if (client->mutex) { | 112 | mutex_destroy(&client->mutex); |
130 | g_mutex_free(client->mutex); | ||
131 | } | ||
132 | free(client); | 113 | free(client); |
133 | 114 | ||
134 | return err; | 115 | return err; |
135 | } | 116 | } |
136 | 117 | ||
137 | /** | ||
138 | * Gets the icon state of the connected device. | ||
139 | * | ||
140 | * @param client The connected sbservices client to use. | ||
141 | * @param state Pointer that will point to a newly allocated plist containing | ||
142 | * the current icon state. It is up to the caller to free the memory. | ||
143 | * @param format_version A string to be passed as formatVersion along with | ||
144 | * the request, or NULL if no formatVersion should be passed. This is only | ||
145 | * supported since iOS 4.0 so for older firmware versions this must be set | ||
146 | * to NULL. | ||
147 | * | ||
148 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
149 | * client or state is invalid, or an SBSERVICES_E_* error code otherwise. | ||
150 | */ | ||
151 | sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version) | 118 | sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version) |
152 | { | 119 | { |
153 | if (!client || !client->parent || !state) | 120 | if (!client || !client->parent || !state) |
@@ -156,12 +123,12 @@ sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t | |||
156 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 123 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
157 | 124 | ||
158 | plist_t dict = plist_new_dict(); | 125 | plist_t dict = plist_new_dict(); |
159 | plist_dict_insert_item(dict, "command", plist_new_string("getIconState")); | 126 | plist_dict_set_item(dict, "command", plist_new_string("getIconState")); |
160 | if (format_version) { | 127 | if (format_version) { |
161 | plist_dict_insert_item(dict, "formatVersion", plist_new_string(format_version)); | 128 | plist_dict_set_item(dict, "formatVersion", plist_new_string(format_version)); |
162 | } | 129 | } |
163 | 130 | ||
164 | sbs_lock(client); | 131 | sbservices_lock(client); |
165 | 132 | ||
166 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 133 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
167 | if (res != SBSERVICES_E_SUCCESS) { | 134 | if (res != SBSERVICES_E_SUCCESS) { |
@@ -184,19 +151,10 @@ leave_unlock: | |||
184 | if (dict) { | 151 | if (dict) { |
185 | plist_free(dict); | 152 | plist_free(dict); |
186 | } | 153 | } |
187 | sbs_unlock(client); | 154 | sbservices_unlock(client); |
188 | return res; | 155 | return res; |
189 | } | 156 | } |
190 | 157 | ||
191 | /** | ||
192 | * Sets the icon state of the connected device. | ||
193 | * | ||
194 | * @param client The connected sbservices client to use. | ||
195 | * @param newstate A plist containing the new iconstate. | ||
196 | * | ||
197 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
198 | * client or newstate is NULL, or an SBSERVICES_E_* error code otherwise. | ||
199 | */ | ||
200 | sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) | 158 | sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) |
201 | { | 159 | { |
202 | if (!client || !client->parent || !newstate) | 160 | if (!client || !client->parent || !newstate) |
@@ -205,39 +163,27 @@ sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t | |||
205 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 163 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
206 | 164 | ||
207 | plist_t dict = plist_new_dict(); | 165 | plist_t dict = plist_new_dict(); |
208 | plist_dict_insert_item(dict, "command", plist_new_string("setIconState")); | 166 | plist_dict_set_item(dict, "command", plist_new_string("setIconState")); |
209 | plist_dict_insert_item(dict, "iconState", plist_copy(newstate)); | 167 | plist_dict_set_item(dict, "iconState", plist_copy(newstate)); |
210 | 168 | ||
211 | sbs_lock(client); | 169 | sbservices_lock(client); |
212 | 170 | ||
213 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 171 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
214 | if (res != SBSERVICES_E_SUCCESS) { | 172 | if (res != SBSERVICES_E_SUCCESS) { |
215 | debug_info("could not send plist, error %d", res); | 173 | debug_info("could not send plist, error %d", res); |
216 | } | 174 | } |
217 | /* NO RESPONSE */ | 175 | |
176 | uint32_t bytes = 0; | ||
177 | service_receive_with_timeout(client->parent->parent, malloc(4), 4, &bytes, 2000); | ||
178 | debug_info("setIconState response: %u", bytes); | ||
218 | 179 | ||
219 | if (dict) { | 180 | if (dict) { |
220 | plist_free(dict); | 181 | plist_free(dict); |
221 | } | 182 | } |
222 | sbs_unlock(client); | 183 | sbservices_unlock(client); |
223 | return res; | 184 | return res; |
224 | } | 185 | } |
225 | 186 | ||
226 | /** | ||
227 | * Get the icon of the specified app as PNG data. | ||
228 | * | ||
229 | * @param client The connected sbservices client to use. | ||
230 | * @param bundleId The bundle identifier of the app to retrieve the icon for. | ||
231 | * @param pngdata Pointer that will point to a newly allocated buffer | ||
232 | * containing the PNG data upon successful return. It is up to the caller | ||
233 | * to free the memory. | ||
234 | * @param pngsize Pointer to a uint64_t that will be set to the size of the | ||
235 | * buffer pngdata points to upon successful return. | ||
236 | * | ||
237 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
238 | * client, bundleId, or pngdata are invalid, or an SBSERVICES_E_* error | ||
239 | * code otherwise. | ||
240 | */ | ||
241 | sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) | 187 | sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) |
242 | { | 188 | { |
243 | if (!client || !client->parent || !bundleId || !pngdata) | 189 | if (!client || !client->parent || !bundleId || !pngdata) |
@@ -246,10 +192,10 @@ sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const | |||
246 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 192 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
247 | 193 | ||
248 | plist_t dict = plist_new_dict(); | 194 | plist_t dict = plist_new_dict(); |
249 | plist_dict_insert_item(dict, "command", plist_new_string("getIconPNGData")); | 195 | plist_dict_set_item(dict, "command", plist_new_string("getIconPNGData")); |
250 | plist_dict_insert_item(dict, "bundleId", plist_new_string(bundleId)); | 196 | plist_dict_set_item(dict, "bundleId", plist_new_string(bundleId)); |
251 | 197 | ||
252 | sbs_lock(client); | 198 | sbservices_lock(client); |
253 | 199 | ||
254 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 200 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
255 | if (res != SBSERVICES_E_SUCCESS) { | 201 | if (res != SBSERVICES_E_SUCCESS) { |
@@ -271,25 +217,48 @@ leave_unlock: | |||
271 | if (dict) { | 217 | if (dict) { |
272 | plist_free(dict); | 218 | plist_free(dict); |
273 | } | 219 | } |
274 | sbs_unlock(client); | 220 | sbservices_unlock(client); |
275 | return res; | 221 | return res; |
222 | } | ||
223 | |||
224 | sbservices_error_t sbservices_get_interface_orientation(sbservices_client_t client, sbservices_interface_orientation_t* interface_orientation) | ||
225 | { | ||
226 | if (!client || !client->parent || !interface_orientation) | ||
227 | return SBSERVICES_E_INVALID_ARG; | ||
228 | |||
229 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | ||
230 | |||
231 | plist_t dict = plist_new_dict(); | ||
232 | plist_dict_set_item(dict, "command", plist_new_string("getInterfaceOrientation")); | ||
233 | |||
234 | sbservices_lock(client); | ||
235 | |||
236 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | ||
237 | if (res != SBSERVICES_E_SUCCESS) { | ||
238 | debug_info("could not send plist, error %d", res); | ||
239 | goto leave_unlock; | ||
240 | } | ||
241 | plist_free(dict); | ||
242 | dict = NULL; | ||
243 | |||
244 | res = sbservices_error(property_list_service_receive_plist(client->parent, &dict)); | ||
245 | if (res == SBSERVICES_E_SUCCESS) { | ||
246 | plist_t node = plist_dict_get_item(dict, "interfaceOrientation"); | ||
247 | if (node) { | ||
248 | uint64_t value = SBSERVICES_INTERFACE_ORIENTATION_UNKNOWN; | ||
249 | plist_get_uint_val(node, &value); | ||
250 | *interface_orientation = (sbservices_interface_orientation_t)value; | ||
251 | } | ||
252 | } | ||
276 | 253 | ||
254 | leave_unlock: | ||
255 | if (dict) { | ||
256 | plist_free(dict); | ||
257 | } | ||
258 | sbservices_unlock(client); | ||
259 | return res; | ||
277 | } | 260 | } |
278 | 261 | ||
279 | /** | ||
280 | * Get the home screen wallpaper as PNG data. | ||
281 | * | ||
282 | * @param client The connected sbservices client to use. | ||
283 | * @param pngdata Pointer that will point to a newly allocated buffer | ||
284 | * containing the PNG data upon successful return. It is up to the caller | ||
285 | * to free the memory. | ||
286 | * @param pngsize Pointer to a uint64_t that will be set to the size of the | ||
287 | * buffer pngdata points to upon successful return. | ||
288 | * | ||
289 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
290 | * client or pngdata are invalid, or an SBSERVICES_E_* error | ||
291 | * code otherwise. | ||
292 | */ | ||
293 | sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize) | 262 | sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize) |
294 | { | 263 | { |
295 | if (!client || !client->parent || !pngdata) | 264 | if (!client || !client->parent || !pngdata) |
@@ -298,9 +267,9 @@ sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_clien | |||
298 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 267 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
299 | 268 | ||
300 | plist_t dict = plist_new_dict(); | 269 | plist_t dict = plist_new_dict(); |
301 | plist_dict_insert_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData")); | 270 | plist_dict_set_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData")); |
302 | 271 | ||
303 | sbs_lock(client); | 272 | sbservices_lock(client); |
304 | 273 | ||
305 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 274 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
306 | if (res != SBSERVICES_E_SUCCESS) { | 275 | if (res != SBSERVICES_E_SUCCESS) { |
@@ -322,6 +291,6 @@ leave_unlock: | |||
322 | if (dict) { | 291 | if (dict) { |
323 | plist_free(dict); | 292 | plist_free(dict); |
324 | } | 293 | } |
325 | sbs_unlock(client); | 294 | sbservices_unlock(client); |
326 | return res; | 295 | return res; |
327 | } | 296 | } |
diff --git a/src/sbservices.h b/src/sbservices.h index 3a4120f..b67281e 100644 --- a/src/sbservices.h +++ b/src/sbservices.h | |||
@@ -8,27 +8,28 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef ISBSERVICES_H | ||
22 | #define ISBSERVICES_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __SBSERVICES_H |
23 | #define __SBSERVICES_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/sbservices.h" | 26 | #include "libimobiledevice/sbservices.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | #include <libimobiledevice-glue/thread.h> | ||
28 | 29 | ||
29 | struct sbservices_client_private { | 30 | struct 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 | /** | 68 | screenshotr_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 | */ | ||
76 | screenshotr_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 | /** | 96 | screenshotr_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 | */ | ||
113 | screenshotr_error_t screenshotr_client_free(screenshotr_client_t client) | 103 | screenshotr_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 | */ | ||
137 | screenshotr_error_t screenshotr_take_screenshot(screenshotr_client_t client, char **imgdata, uint64_t *imgsize) | 113 | screenshotr_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 | */ | ||
40 | static 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 | |||
59 | service_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 | |||
83 | service_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 | |||
122 | service_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 | |||
135 | service_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 | |||
156 | service_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 | |||
177 | service_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 | |||
182 | service_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 | |||
189 | service_error_t service_disable_ssl(service_client_t client) | ||
190 | { | ||
191 | return service_disable_bypass_ssl(client, 0); | ||
192 | } | ||
193 | |||
194 | service_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 | |||
201 | service_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 | |||
28 | struct 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 | |||
33 | struct 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 | */ | ||
49 | static 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 | |||
70 | syslog_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 | |||
98 | syslog_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 | |||
105 | syslog_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 | |||
116 | syslog_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 | |||
121 | syslog_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 | |||
141 | void *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 | |||
178 | syslog_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 | |||
206 | syslog_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 | |||
234 | syslog_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 | |||
30 | struct syslog_relay_client_private { | ||
31 | service_client_t parent; | ||
32 | THREAD_T worker; | ||
33 | }; | ||
34 | |||
35 | void *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 | */ | ||
48 | static 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 | |||
58 | static 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 | */ | ||
69 | static 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 | */ | ||
95 | static 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 | */ | ||
136 | void 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 | */ | ||
173 | int 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 | */ | ||
203 | userpref_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 | */ | ||
257 | userpref_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 | */ | ||
289 | userpref_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 | */ | ||
315 | static 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 | */ | ||
342 | static 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 | */ | ||
445 | static 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 | */ | ||
468 | static 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 | */ | ||
496 | 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) | ||
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 | */ | ||
542 | userpref_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 | */ | ||
568 | 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) | ||
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 | |||
35 | typedef int16_t userpref_error_t; | ||
36 | |||
37 | G_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); | ||
38 | G_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); | ||
39 | G_GNUC_INTERNAL userpref_error_t userpref_get_certs_as_pem(gnutls_datum_t *pem_root_cert, gnutls_datum_t *pem_host_cert); | ||
40 | G_GNUC_INTERNAL userpref_error_t userpref_set_device_public_key(const char *uuid, gnutls_datum_t public_key); | ||
41 | userpref_error_t userpref_remove_device_public_key(const char *uuid); | ||
42 | G_GNUC_INTERNAL int userpref_has_device_public_key(const char *uuid); | ||
43 | userpref_error_t userpref_get_paired_uuids(char ***list, unsigned int *count); | ||
44 | void 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 | */ | ||
42 | static 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 | |||
65 | webinspector_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 | |||
92 | webinspector_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 | |||
99 | webinspector_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 | |||
110 | webinspector_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 | |||
167 | webinspector_error_t webinspector_receive(webinspector_client_t client, plist_t * plist) | ||
168 | { | ||
169 | return webinspector_receive_with_timeout(client, plist, 5000); | ||
170 | } | ||
171 | |||
172 | webinspector_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 | |||
31 | struct webinspector_client_private { | ||
32 | property_list_service_client_t parent; | ||
33 | }; | ||
34 | |||
35 | #endif | ||