diff options
Diffstat (limited to 'src')
65 files changed, 11587 insertions, 4687 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 70dc895..1ee9be8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
@@ -1,24 +1,70 @@ | |||
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 | ostrace.c ostrace.h \ | ||
62 | bt_packet_logger.c bt_packet_logger.h | ||
63 | |||
64 | if WIN32 | ||
65 | libimobiledevice_1_0_la_LDFLAGS += -avoid-version -static-libgcc | ||
66 | libimobiledevice_1_0_la_LIBADD += -lole32 -lws2_32 -lgdi32 | ||
67 | endif | ||
68 | |||
69 | pkgconfigdir = $(libdir)/pkgconfig | ||
70 | pkgconfig_DATA = libimobiledevice-1.0.pc | ||
@@ -1,308 +1,246 @@ | |||
1 | /* | 1 | /* |
2 | * afc.c | 2 | * afc.c |
3 | * Contains functions for the built-in AFC client. | 3 | * Contains functions for the built-in AFC client. |
4 | * | 4 | * |
5 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
6 | * Copyright (c) 2009-2014 Nikias Bassen. All Rights Reserved. | ||
5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 7 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
6 | * | 8 | * |
7 | * This library is free software; you can redistribute it and/or | 9 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 10 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 11 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 12 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 13 | * |
12 | * This library is distributed in the hope that it will be useful, | 14 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 17 | * Lesser General Public License for more details. |
16 | * | 18 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 20 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 22 | */ |
21 | 23 | ||
24 | #ifdef HAVE_CONFIG_H | ||
25 | #include <config.h> | ||
26 | #endif | ||
22 | #include <stdio.h> | 27 | #include <stdio.h> |
23 | #include <stdlib.h> | 28 | #include <stdlib.h> |
24 | #include <unistd.h> | ||
25 | #include <string.h> | 29 | #include <string.h> |
26 | 30 | ||
27 | #include "afc.h" | 31 | #ifndef _MSC_VER |
28 | #include "idevice.h" | 32 | #include <unistd.h> |
29 | #include "debug.h" | 33 | #endif |
30 | 34 | ||
31 | /** The maximum size an AFC data packet can be */ | 35 | #include "idevice.h" |
32 | static const int MAXIMUM_PACKET_SIZE = (2 << 15); | 36 | #include "afc.h" |
37 | #include "common/debug.h" | ||
38 | #include "endianness.h" | ||
33 | 39 | ||
34 | /** | 40 | /** |
35 | * Locks an AFC client, done for thread safety stuff | 41 | * Locks an AFC client, done for thread safety stuff |
36 | * | 42 | * |
37 | * @param client The AFC client connection to lock | 43 | * @param client The AFC client connection to lock |
38 | */ | 44 | */ |
39 | static void afc_lock(afc_client_t client) | 45 | static void afc_lock(afc_client_t client) |
40 | { | 46 | { |
41 | debug_info("Locked"); | 47 | debug_info("Locked"); |
42 | g_mutex_lock(client->mutex); | 48 | mutex_lock(&client->mutex); |
43 | } | 49 | } |
44 | 50 | ||
45 | /** | 51 | /** |
46 | * Unlocks an AFC client, done for thread safety stuff. | 52 | * Unlocks an AFC client, done for thread safety stuff. |
47 | * | 53 | * |
48 | * @param client The AFC | 54 | * @param client The AFC |
49 | */ | 55 | */ |
50 | static void afc_unlock(afc_client_t client) | 56 | static void afc_unlock(afc_client_t client) |
51 | { | 57 | { |
52 | debug_info("Unlocked"); | 58 | debug_info("Unlocked"); |
53 | g_mutex_unlock(client->mutex); | 59 | mutex_unlock(&client->mutex); |
54 | } | 60 | } |
55 | 61 | ||
56 | /** | 62 | /** |
57 | * Makes a connection to the AFC service on the device using the given | 63 | * Makes a connection to the AFC service on the device using the given |
58 | * connection. | 64 | * connection. |
59 | * | 65 | * |
60 | * @param connection An idevice_connection_t that must have been previously | 66 | * @param service_client A connected service client |
61 | * connected using idevice_connect(). Note that this connection will | ||
62 | * not be closed by calling afc_client_free(). | ||
63 | * @param client Pointer that will be set to a newly allocated afc_client_t | 67 | * @param client Pointer that will be set to a newly allocated afc_client_t |
64 | * upon successful return. | 68 | * upon successful return. |
65 | * | 69 | * |
66 | * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if connection is | 70 | * @return AFC_E_SUCCESS on success, AFC_E_INVALID_ARG if connection is |
67 | * invalid, or AFC_E_NO_MEM if there is a memory allocation problem. | 71 | * invalid, or AFC_E_NO_MEM if there is a memory allocation problem. |
68 | */ | 72 | */ |
69 | 73 | ||
70 | afc_error_t afc_client_new_from_connection(idevice_connection_t connection, afc_client_t *client) | 74 | afc_error_t afc_client_new_with_service_client(service_client_t service_client, afc_client_t *client) |
71 | { | 75 | { |
72 | /* makes sure thread environment is available */ | 76 | if (!service_client) |
73 | if (!g_thread_supported()) | ||
74 | g_thread_init(NULL); | ||
75 | |||
76 | if (!connection) | ||
77 | return AFC_E_INVALID_ARG; | 77 | return AFC_E_INVALID_ARG; |
78 | 78 | ||
79 | afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private)); | 79 | afc_client_t client_loc = (afc_client_t) malloc(sizeof(struct afc_client_private)); |
80 | client_loc->connection = connection; | 80 | client_loc->parent = service_client; |
81 | client_loc->own_connection = 0; | 81 | client_loc->free_parent = 0; |
82 | 82 | ||
83 | /* allocate a packet */ | 83 | /* allocate a packet */ |
84 | client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); | 84 | client_loc->packet_extra = 1024; |
85 | client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket) + client_loc->packet_extra); | ||
85 | if (!client_loc->afc_packet) { | 86 | if (!client_loc->afc_packet) { |
86 | free(client_loc); | 87 | free(client_loc); |
87 | return AFC_E_NO_MEM; | 88 | return AFC_E_NO_MEM; |
88 | } | 89 | } |
89 | |||
90 | client_loc->afc_packet->packet_num = 0; | 90 | client_loc->afc_packet->packet_num = 0; |
91 | client_loc->afc_packet->entire_length = 0; | 91 | client_loc->afc_packet->entire_length = 0; |
92 | client_loc->afc_packet->this_length = 0; | 92 | client_loc->afc_packet->this_length = 0; |
93 | memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN); | 93 | memcpy(client_loc->afc_packet->magic, AFC_MAGIC, AFC_MAGIC_LEN); |
94 | client_loc->file_handle = 0; | 94 | mutex_init(&client_loc->mutex); |
95 | client_loc->lock = 0; | ||
96 | client_loc->mutex = g_mutex_new(); | ||
97 | 95 | ||
98 | *client = client_loc; | 96 | *client = client_loc; |
99 | return AFC_E_SUCCESS; | 97 | return AFC_E_SUCCESS; |
100 | } | 98 | } |
101 | 99 | ||
102 | /** | 100 | 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 | { | 101 | { |
120 | /* makes sure thread environment is available */ | 102 | if (!device || !service || service->port == 0) |
121 | if (!g_thread_supported()) | ||
122 | g_thread_init(NULL); | ||
123 | |||
124 | if (!device || port==0) | ||
125 | return AFC_E_INVALID_ARG; | 103 | return AFC_E_INVALID_ARG; |
126 | 104 | ||
127 | /* attempt connection */ | 105 | service_client_t parent = NULL; |
128 | idevice_connection_t connection = NULL; | 106 | if (service_client_new(device, service, &parent) != SERVICE_E_SUCCESS) { |
129 | if (idevice_connect(device, port, &connection) != IDEVICE_E_SUCCESS) { | ||
130 | return AFC_E_MUX_ERROR; | 107 | return AFC_E_MUX_ERROR; |
131 | } | 108 | } |
132 | 109 | ||
133 | afc_error_t err = afc_client_new_from_connection(connection, client); | 110 | afc_error_t err = afc_client_new_with_service_client(parent, client); |
134 | if (err != AFC_E_SUCCESS) { | 111 | if (err != AFC_E_SUCCESS) { |
135 | idevice_disconnect(connection); | 112 | service_client_free(parent); |
136 | } else { | 113 | } else { |
137 | (*client)->own_connection = 1; | 114 | (*client)->free_parent = 1; |
138 | } | 115 | } |
139 | return err; | 116 | return err; |
140 | } | 117 | } |
141 | 118 | ||
142 | /** | 119 | 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 | 120 | { |
144 | * client itself, the connection will be closed. | 121 | int32_t err = AFC_E_UNKNOWN_ERROR; |
145 | * | 122 | service_client_factory_start_service(device, AFC_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(afc_client_new), &err); |
146 | * @param client The client to free. | 123 | return err; |
147 | */ | 124 | } |
125 | |||
148 | afc_error_t afc_client_free(afc_client_t client) | 126 | afc_error_t afc_client_free(afc_client_t client) |
149 | { | 127 | { |
150 | if (!client || !client->afc_packet) | 128 | if (!client || !client->afc_packet) |
151 | return AFC_E_INVALID_ARG; | 129 | return AFC_E_INVALID_ARG; |
152 | 130 | ||
153 | if (client->own_connection && client->connection) { | 131 | if (client->free_parent && client->parent) { |
154 | idevice_disconnect(client->connection); | 132 | service_client_free(client->parent); |
155 | client->connection = NULL; | 133 | client->parent = NULL; |
156 | } | 134 | } |
157 | free(client->afc_packet); | 135 | free(client->afc_packet); |
158 | if (client->mutex) { | 136 | mutex_destroy(&client->mutex); |
159 | g_mutex_free(client->mutex); | ||
160 | } | ||
161 | free(client); | 137 | free(client); |
162 | return AFC_E_SUCCESS; | 138 | return AFC_E_SUCCESS; |
163 | } | 139 | } |
164 | 140 | ||
165 | /** | 141 | /** |
166 | * Dispatches an AFC packet over a client. | 142 | * Dispatches an AFC packet over a client. |
167 | * | 143 | * |
168 | * @param client The client to send data through. | 144 | * @param client The client to send data through. |
169 | * @param data The data to send. | 145 | * @param operation The operation to perform. |
170 | * @param length The length to send. | 146 | * @param data The data to send together with the header. |
171 | * @param bytes_sent The number of bytes actually sent. | 147 | * @param data_length The length of the data to send with the header. |
148 | * @param payload The data to send after the header has been sent. | ||
149 | * @param payload_length The length of data to send after the header. | ||
150 | * @param bytes_sent The total number of bytes actually sent. | ||
172 | * | 151 | * |
173 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | 152 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. |
174 | * | ||
175 | * @warning set client->afc_packet->this_length and | ||
176 | * client->afc_packet->entire_length to 0 before calling this. The | ||
177 | * reason is that if you set them to different values, it indicates | ||
178 | * you want to send the data as two packets. | ||
179 | */ | 153 | */ |
180 | static afc_error_t afc_dispatch_packet(afc_client_t client, const char *data, uint32_t length, uint32_t *bytes_sent) | 154 | 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 | { | 155 | { |
182 | uint32_t offset = 0; | ||
183 | uint32_t sent = 0; | 156 | uint32_t sent = 0; |
184 | 157 | ||
185 | if (!client || !client->connection || !client->afc_packet) | 158 | if (!client || !client->parent || !client->afc_packet) |
186 | return AFC_E_INVALID_ARG; | 159 | return AFC_E_INVALID_ARG; |
187 | 160 | ||
188 | *bytes_sent = 0; | 161 | *bytes_sent = 0; |
189 | 162 | ||
190 | if (!data || !length) | 163 | if (!payload || !payload_length) |
191 | length = 0; | 164 | payload_length = 0; |
192 | 165 | ||
193 | client->afc_packet->packet_num++; | 166 | client->afc_packet->packet_num++; |
194 | if (!client->afc_packet->entire_length) { | 167 | client->afc_packet->operation = operation; |
195 | client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length : sizeof(AFCPacket); | 168 | client->afc_packet->entire_length = sizeof(AFCPacket) + data_length + payload_length; |
196 | client->afc_packet->this_length = client->afc_packet->entire_length; | 169 | client->afc_packet->this_length = sizeof(AFCPacket) + data_length; |
197 | } | 170 | |
198 | if (!client->afc_packet->this_length) { | 171 | debug_info("packet length = %i", client->afc_packet->this_length); |
199 | client->afc_packet->this_length = sizeof(AFCPacket); | 172 | |
200 | } | 173 | /* send AFC packet header and data */ |
201 | /* We want to send two segments; buffer+sizeof(AFCPacket) to this_length | 174 | AFCPacket_to_LE(client->afc_packet); |
202 | is the parameters and everything beyond that is the next packet. | 175 | debug_buffer((char*)client->afc_packet, sizeof(AFCPacket) + data_length); |
203 | (for writing) */ | 176 | sent = 0; |
204 | if (client->afc_packet->this_length != client->afc_packet->entire_length) { | 177 | service_send(client->parent, (void*)client->afc_packet, sizeof(AFCPacket) + data_length, &sent); |
205 | offset = client->afc_packet->this_length - sizeof(AFCPacket); | 178 | AFCPacket_from_LE(client->afc_packet); |
206 | 179 | *bytes_sent += sent; | |
207 | debug_info("Offset: %i", offset); | 180 | if (sent < sizeof(AFCPacket) + data_length) { |
208 | if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) { | ||
209 | debug_info("Length did not resemble what it was supposed to based on packet"); | ||
210 | debug_info("length minus offset: %i", length - offset); | ||
211 | debug_info("rest of packet: %i\n", client->afc_packet->entire_length - client->afc_packet->this_length); | ||
212 | return AFC_E_INTERNAL_ERROR; | ||
213 | } | ||
214 | |||
215 | /* send AFC packet header */ | ||
216 | AFCPacket_to_LE(client->afc_packet); | ||
217 | sent = 0; | ||
218 | idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); | ||
219 | AFCPacket_from_LE(client->afc_packet); | ||
220 | if (sent == 0) { | ||
221 | /* FIXME: should this be handled as success?! */ | ||
222 | return AFC_E_SUCCESS; | ||
223 | } | ||
224 | *bytes_sent += sent; | ||
225 | |||
226 | /* send AFC packet data */ | ||
227 | sent = 0; | ||
228 | idevice_connection_send(client->connection, data, offset, &sent); | ||
229 | if (sent == 0) { | ||
230 | return AFC_E_SUCCESS; | ||
231 | } | ||
232 | *bytes_sent += sent; | ||
233 | |||
234 | debug_info("sent the first now go with the second"); | ||
235 | debug_info("Length: %i", length - offset); | ||
236 | debug_info("Buffer: "); | ||
237 | debug_buffer(data + offset, length - offset); | ||
238 | |||
239 | sent = 0; | ||
240 | idevice_connection_send(client->connection, data + offset, length - offset, &sent); | ||
241 | |||
242 | *bytes_sent = sent; | ||
243 | return AFC_E_SUCCESS; | 181 | return AFC_E_SUCCESS; |
244 | } else { | 182 | } |
245 | debug_info("doin things the old way"); | ||
246 | debug_info("packet length = %i", client->afc_packet->this_length); | ||
247 | |||
248 | debug_buffer((char*)client->afc_packet, sizeof(AFCPacket)); | ||
249 | 183 | ||
250 | /* send AFC packet header */ | 184 | sent = 0; |
251 | AFCPacket_to_LE(client->afc_packet); | 185 | if (payload_length > 0) { |
252 | sent = 0; | 186 | if (payload_length > 256) { |
253 | idevice_connection_send(client->connection, (void*)client->afc_packet, sizeof(AFCPacket), &sent); | 187 | debug_info("packet payload follows (256/%u)", payload_length); |
254 | AFCPacket_from_LE(client->afc_packet); | 188 | debug_buffer(payload, 256); |
255 | if (sent == 0) { | 189 | } else { |
256 | return AFC_E_SUCCESS; | 190 | debug_info("packet payload follows"); |
257 | } | 191 | debug_buffer(payload, payload_length); |
258 | *bytes_sent += sent; | ||
259 | /* send AFC packet data (if there's data to send) */ | ||
260 | if (length > 0) { | ||
261 | debug_info("packet data follows"); | ||
262 | |||
263 | debug_buffer(data, length); | ||
264 | idevice_connection_send(client->connection, data, length, &sent); | ||
265 | *bytes_sent += sent; | ||
266 | } | 192 | } |
193 | service_send(client->parent, payload, payload_length, &sent); | ||
194 | } | ||
195 | *bytes_sent += sent; | ||
196 | if (sent < payload_length) { | ||
267 | return AFC_E_SUCCESS; | 197 | return AFC_E_SUCCESS; |
268 | } | 198 | } |
269 | return AFC_E_INTERNAL_ERROR; | 199 | |
200 | return AFC_E_SUCCESS; | ||
270 | } | 201 | } |
271 | 202 | ||
272 | /** | 203 | /** |
273 | * Receives data through an AFC client and sets a variable to the received data. | 204 | * Receives data through an AFC client and sets a variable to the received data. |
274 | * | 205 | * |
275 | * @param client The client to receive data on. | 206 | * @param client The client to receive data on. |
276 | * @param dump_here The char* to point to the newly-received data. | 207 | * @param bytes The char* to point to the newly-received data. |
277 | * @param bytes_recv How much data was received. | 208 | * @param bytes_recv How much data was received. |
278 | * | 209 | * |
279 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | 210 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. |
280 | */ | 211 | */ |
281 | static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint32_t *bytes_recv) | 212 | static afc_error_t afc_receive_data(afc_client_t client, char **bytes, uint32_t *bytes_recv) |
282 | { | 213 | { |
283 | AFCPacket header; | 214 | AFCPacket header; |
284 | uint32_t entire_len = 0; | 215 | uint32_t entire_len = 0; |
285 | uint32_t this_len = 0; | 216 | uint32_t this_len = 0; |
286 | uint32_t current_count = 0; | 217 | uint32_t current_count = 0; |
287 | uint64_t param1 = -1; | 218 | uint64_t param1 = -1; |
219 | char *buf = NULL; | ||
220 | uint32_t recv_len = 0; | ||
288 | 221 | ||
289 | *bytes_recv = 0; | 222 | if (bytes_recv) { |
223 | *bytes_recv = 0; | ||
224 | } | ||
225 | if (bytes) { | ||
226 | *bytes = NULL; | ||
227 | } | ||
290 | 228 | ||
291 | /* first, read the AFC header */ | 229 | /* first, read the AFC header */ |
292 | idevice_connection_receive(client->connection, (char*)&header, sizeof(AFCPacket), bytes_recv); | 230 | service_receive(client->parent, (char*)&header, sizeof(AFCPacket), &recv_len); |
293 | AFCPacket_from_LE(&header); | 231 | AFCPacket_from_LE(&header); |
294 | if (*bytes_recv == 0) { | 232 | if (recv_len == 0) { |
295 | debug_info("Just didn't get enough."); | 233 | debug_info("Just didn't get enough."); |
296 | *dump_here = NULL; | ||
297 | return AFC_E_MUX_ERROR; | 234 | return AFC_E_MUX_ERROR; |
298 | } else if (*bytes_recv < sizeof(AFCPacket)) { | 235 | } |
236 | |||
237 | if (recv_len < sizeof(AFCPacket)) { | ||
299 | debug_info("Did not even get the AFCPacket header"); | 238 | debug_info("Did not even get the AFCPacket header"); |
300 | *dump_here = NULL; | ||
301 | return AFC_E_MUX_ERROR; | 239 | return AFC_E_MUX_ERROR; |
302 | } | 240 | } |
303 | 241 | ||
304 | /* check if it's a valid AFC header */ | 242 | /* check if it's a valid AFC header */ |
305 | if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN)) { | 243 | if (strncmp(header.magic, AFC_MAGIC, AFC_MAGIC_LEN) != 0) { |
306 | debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!"); | 244 | debug_info("Invalid AFC packet received (magic != " AFC_MAGIC ")!"); |
307 | } | 245 | } |
308 | 246 | ||
@@ -310,25 +248,21 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
310 | if (header.packet_num != client->afc_packet->packet_num) { | 248 | if (header.packet_num != client->afc_packet->packet_num) { |
311 | /* otherwise print a warning but do not abort */ | 249 | /* otherwise print a warning but do not abort */ |
312 | debug_info("ERROR: Unexpected packet number (%lld != %lld) aborting.", header.packet_num, client->afc_packet->packet_num); | 250 | debug_info("ERROR: Unexpected packet number (%lld != %lld) aborting.", header.packet_num, client->afc_packet->packet_num); |
313 | *dump_here = NULL; | ||
314 | return AFC_E_OP_HEADER_INVALID; | 251 | return AFC_E_OP_HEADER_INVALID; |
315 | } | 252 | } |
316 | 253 | ||
317 | /* then, read the attached packet */ | 254 | /* then, read the attached packet */ |
318 | if (header.this_length < sizeof(AFCPacket)) { | 255 | if (header.this_length < sizeof(AFCPacket)) { |
319 | debug_info("Invalid AFCPacket header received!"); | 256 | debug_info("Invalid AFCPacket header received!"); |
320 | *dump_here = NULL; | ||
321 | return AFC_E_OP_HEADER_INVALID; | 257 | return AFC_E_OP_HEADER_INVALID; |
322 | } else if ((header.this_length == header.entire_length) | 258 | } |
323 | && header.entire_length == sizeof(AFCPacket)) { | 259 | if ((header.this_length == header.entire_length) |
260 | && header.entire_length == sizeof(AFCPacket)) { | ||
324 | debug_info("Empty AFCPacket received!"); | 261 | debug_info("Empty AFCPacket received!"); |
325 | *dump_here = NULL; | ||
326 | *bytes_recv = 0; | ||
327 | if (header.operation == AFC_OP_DATA) { | 262 | if (header.operation == AFC_OP_DATA) { |
328 | return AFC_E_SUCCESS; | 263 | return AFC_E_SUCCESS; |
329 | } else { | ||
330 | return AFC_E_IO_ERROR; | ||
331 | } | 264 | } |
265 | return AFC_E_IO_ERROR; | ||
332 | } | 266 | } |
333 | 267 | ||
334 | debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation); | 268 | debug_info("received AFC packet, full len=%lld, this len=%lld, operation=0x%llx", header.entire_length, header.this_length, header.operation); |
@@ -336,22 +270,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
336 | entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); | 270 | entire_len = (uint32_t)header.entire_length - sizeof(AFCPacket); |
337 | this_len = (uint32_t)header.this_length - sizeof(AFCPacket); | 271 | this_len = (uint32_t)header.this_length - sizeof(AFCPacket); |
338 | 272 | ||
339 | /* this is here as a check (perhaps a different upper limit is good?) */ | 273 | buf = (char*)malloc(entire_len); |
340 | if (entire_len > (uint32_t)MAXIMUM_PACKET_SIZE) { | ||
341 | fprintf(stderr, "%s: entire_len is larger than MAXIMUM_PACKET_SIZE, (%d > %d)!", __func__, entire_len, MAXIMUM_PACKET_SIZE); | ||
342 | } | ||
343 | |||
344 | *dump_here = (char*)malloc(entire_len); | ||
345 | if (this_len > 0) { | 274 | if (this_len > 0) { |
346 | idevice_connection_receive(client->connection, *dump_here, this_len, bytes_recv); | 275 | recv_len = 0; |
347 | if (*bytes_recv <= 0) { | 276 | service_receive(client->parent, buf, this_len, &recv_len); |
348 | free(*dump_here); | 277 | if (recv_len <= 0) { |
349 | *dump_here = NULL; | 278 | free(buf); |
350 | debug_info("Did not get packet contents!"); | 279 | debug_info("Did not get packet contents!"); |
351 | return AFC_E_NOT_ENOUGH_DATA; | 280 | return AFC_E_NOT_ENOUGH_DATA; |
352 | } else if (*bytes_recv < this_len) { | 281 | } |
353 | free(*dump_here); | 282 | if (recv_len < this_len) { |
354 | *dump_here = NULL; | 283 | free(buf); |
355 | debug_info("Could not receive this_len=%d bytes", this_len); | 284 | debug_info("Could not receive this_len=%d bytes", this_len); |
356 | return AFC_E_NOT_ENOUGH_DATA; | 285 | return AFC_E_NOT_ENOUGH_DATA; |
357 | } | 286 | } |
@@ -361,12 +290,13 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
361 | 290 | ||
362 | if (entire_len > this_len) { | 291 | if (entire_len > this_len) { |
363 | while (current_count < entire_len) { | 292 | while (current_count < entire_len) { |
364 | idevice_connection_receive(client->connection, (*dump_here)+current_count, entire_len - current_count, bytes_recv); | 293 | recv_len = 0; |
365 | if (*bytes_recv <= 0) { | 294 | service_receive(client->parent, buf+current_count, entire_len - current_count, &recv_len); |
366 | debug_info("Error receiving data (recv returned %d)", *bytes_recv); | 295 | if (recv_len <= 0) { |
296 | debug_info("Error receiving data (recv returned %d)", recv_len); | ||
367 | break; | 297 | break; |
368 | } | 298 | } |
369 | current_count += *bytes_recv; | 299 | current_count += recv_len; |
370 | } | 300 | } |
371 | if (current_count < entire_len) { | 301 | if (current_count < entire_len) { |
372 | debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len); | 302 | debug_info("WARNING: could not receive full packet (read %s, size %d)", current_count, entire_len); |
@@ -374,12 +304,17 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
374 | } | 304 | } |
375 | 305 | ||
376 | if (current_count >= sizeof(uint64_t)) { | 306 | if (current_count >= sizeof(uint64_t)) { |
377 | param1 = GUINT64_FROM_LE(*(uint64_t*)(*dump_here)); | 307 | param1 = le64toh(*(uint64_t*)(buf)); |
378 | } | 308 | } |
379 | 309 | ||
380 | debug_info("packet data size = %i", current_count); | 310 | debug_info("packet data size = %i", current_count); |
381 | debug_info("packet data follows"); | 311 | if (current_count > 256) { |
382 | debug_buffer(*dump_here, current_count); | 312 | debug_info("packet data follows (256/%u)", current_count); |
313 | debug_buffer(buf, 256); | ||
314 | } else { | ||
315 | debug_info("packet data follows"); | ||
316 | debug_buffer(buf, current_count); | ||
317 | } | ||
383 | 318 | ||
384 | /* check operation types */ | 319 | /* check operation types */ |
385 | if (header.operation == AFC_OP_STATUS) { | 320 | if (header.operation == AFC_OP_STATUS) { |
@@ -389,8 +324,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
389 | if (param1 != AFC_E_SUCCESS) { | 324 | if (param1 != AFC_E_SUCCESS) { |
390 | /* error status */ | 325 | /* error status */ |
391 | /* free buffer */ | 326 | /* free buffer */ |
392 | free(*dump_here); | 327 | free(buf); |
393 | *dump_here = NULL; | ||
394 | return (afc_error_t)param1; | 328 | return (afc_error_t)param1; |
395 | } | 329 | } |
396 | } else if (header.operation == AFC_OP_DATA) { | 330 | } else if (header.operation == AFC_OP_DATA) { |
@@ -404,16 +338,22 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
404 | debug_info("got a tell response, position=%lld", param1); | 338 | debug_info("got a tell response, position=%lld", param1); |
405 | } else { | 339 | } else { |
406 | /* unknown operation code received */ | 340 | /* unknown operation code received */ |
407 | free(*dump_here); | 341 | free(buf); |
408 | *dump_here = NULL; | ||
409 | *bytes_recv = 0; | ||
410 | 342 | ||
411 | debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); | 343 | debug_info("WARNING: Unknown operation code received 0x%llx param1=%lld", header.operation, param1); |
344 | #ifndef _WIN32 | ||
412 | fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1); | 345 | fprintf(stderr, "%s: WARNING: Unknown operation code received 0x%llx param1=%lld", __func__, (long long)header.operation, (long long)param1); |
346 | #endif | ||
413 | 347 | ||
414 | return AFC_E_OP_NOT_SUPPORTED; | 348 | return AFC_E_OP_NOT_SUPPORTED; |
415 | } | 349 | } |
416 | 350 | ||
351 | if (bytes) { | ||
352 | *bytes = buf; | ||
353 | } else { | ||
354 | free(buf); | ||
355 | } | ||
356 | |||
417 | *bytes_recv = current_count; | 357 | *bytes_recv = current_count; |
418 | return AFC_E_SUCCESS; | 358 | return AFC_E_SUCCESS; |
419 | } | 359 | } |
@@ -421,7 +361,7 @@ static afc_error_t afc_receive_data(afc_client_t client, char **dump_here, uint3 | |||
421 | /** | 361 | /** |
422 | * Returns counts of null characters within a string. | 362 | * Returns counts of null characters within a string. |
423 | */ | 363 | */ |
424 | static uint32_t count_nullspaces(char *string, uint32_t number) | 364 | static uint32_t count_nullspaces(const char *string, uint32_t number) |
425 | { | 365 | { |
426 | uint32_t i = 0, nulls = 0; | 366 | uint32_t i = 0, nulls = 0; |
427 | 367 | ||
@@ -462,32 +402,86 @@ static char **make_strings_list(char *tokens, uint32_t length) | |||
462 | return list; | 402 | return list; |
463 | } | 403 | } |
464 | 404 | ||
465 | /** | 405 | static plist_t *make_dictionary(char *tokens, size_t length) |
466 | * Gets a directory listing of the directory requested. | 406 | { |
467 | * | 407 | size_t j = 0; |
468 | * @param client The client to get a directory listing from. | 408 | plist_t dict = NULL; |
469 | * @param dir The directory to list. (must be a fully-qualified path) | 409 | |
470 | * @param list A char list of files in that directory, terminated by an empty | 410 | if (!tokens || !length) |
471 | * string or NULL if there was an error. | 411 | return NULL; |
472 | * | 412 | |
473 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | 413 | dict = plist_new_dict(); |
474 | */ | 414 | |
475 | afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***list) | 415 | while (j < length) { |
416 | size_t key_len = strnlen(tokens + j, length - j); | ||
417 | if (j + key_len >= length) { | ||
418 | plist_free(dict); | ||
419 | return NULL; | ||
420 | } | ||
421 | char* key = tokens + j; | ||
422 | j += key_len + 1; | ||
423 | |||
424 | if (j >= length) { | ||
425 | plist_free(dict); | ||
426 | return NULL; | ||
427 | } | ||
428 | |||
429 | size_t val_len = strnlen(tokens + j, length - j); | ||
430 | if (j + val_len >= length) { | ||
431 | plist_free(dict); | ||
432 | return NULL; | ||
433 | } | ||
434 | char* val = tokens + j; | ||
435 | j += val_len + 1; | ||
436 | |||
437 | char* endp = NULL; | ||
438 | unsigned long long u64val = strtoull(val, &endp, 10); | ||
439 | if (endp && *endp == '\0') { | ||
440 | plist_dict_set_item(dict, key, plist_new_uint(u64val)); | ||
441 | } else { | ||
442 | plist_dict_set_item(dict, key, plist_new_string(val)); | ||
443 | } | ||
444 | } | ||
445 | |||
446 | return dict; | ||
447 | } | ||
448 | |||
449 | static int _afc_check_packet_buffer(afc_client_t client, uint32_t data_len) | ||
450 | { | ||
451 | if (data_len > client->packet_extra) { | ||
452 | client->packet_extra = (data_len & ~8) + 8; | ||
453 | AFCPacket* newpkt = (AFCPacket*)realloc(client->afc_packet, sizeof(AFCPacket) + client->packet_extra); | ||
454 | if (!newpkt) { | ||
455 | return -1; | ||
456 | } | ||
457 | client->afc_packet = newpkt; | ||
458 | } | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | #define AFC_PACKET_DATA_PTR ((char*)client->afc_packet + sizeof(AFCPacket)) | ||
463 | |||
464 | afc_error_t afc_read_directory(afc_client_t client, const char *path, char ***directory_information) | ||
476 | { | 465 | { |
477 | uint32_t bytes = 0; | 466 | uint32_t bytes = 0; |
478 | char *data = NULL, **list_loc = NULL; | 467 | char *data = NULL, **list_loc = NULL; |
479 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 468 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
480 | 469 | ||
481 | if (!client || !dir || !list || (list && *list)) | 470 | if (!client || !path || !directory_information || (directory_information && *directory_information)) |
482 | return AFC_E_INVALID_ARG; | 471 | return AFC_E_INVALID_ARG; |
483 | 472 | ||
484 | afc_lock(client); | 473 | afc_lock(client); |
485 | 474 | ||
475 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
476 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
477 | afc_unlock(client); | ||
478 | debug_info("Failed to realloc packet buffer"); | ||
479 | return AFC_E_NO_MEM; | ||
480 | } | ||
481 | |||
486 | /* Send the command */ | 482 | /* Send the command */ |
487 | client->afc_packet->operation = AFC_OP_READ_DIR; | 483 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
488 | client->afc_packet->entire_length = 0; | 484 | ret = afc_dispatch_packet(client, AFC_OP_READ_DIR, data_len, NULL, 0, &bytes); |
489 | client->afc_packet->this_length = 0; | ||
490 | ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes); | ||
491 | if (ret != AFC_E_SUCCESS) { | 485 | if (ret != AFC_E_SUCCESS) { |
492 | afc_unlock(client); | 486 | afc_unlock(client); |
493 | return AFC_E_NOT_ENOUGH_DATA; | 487 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -495,6 +489,8 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis | |||
495 | /* Receive the data */ | 489 | /* Receive the data */ |
496 | ret = afc_receive_data(client, &data, &bytes); | 490 | ret = afc_receive_data(client, &data, &bytes); |
497 | if (ret != AFC_E_SUCCESS) { | 491 | if (ret != AFC_E_SUCCESS) { |
492 | if (data) | ||
493 | free(data); | ||
498 | afc_unlock(client); | 494 | afc_unlock(client); |
499 | return ret; | 495 | return ret; |
500 | } | 496 | } |
@@ -504,37 +500,24 @@ afc_error_t afc_read_directory(afc_client_t client, const char *dir, char ***lis | |||
504 | free(data); | 500 | free(data); |
505 | 501 | ||
506 | afc_unlock(client); | 502 | afc_unlock(client); |
507 | *list = list_loc; | 503 | *directory_information = list_loc; |
508 | 504 | ||
509 | return ret; | 505 | return ret; |
510 | } | 506 | } |
511 | 507 | ||
512 | /** | 508 | 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 | { | 509 | { |
525 | uint32_t bytes = 0; | 510 | uint32_t bytes = 0; |
526 | char *data = NULL, **list = NULL; | 511 | char *data = NULL, **list = NULL; |
527 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 512 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
528 | 513 | ||
529 | if (!client || !infos) | 514 | if (!client || !device_information) |
530 | return AFC_E_INVALID_ARG; | 515 | return AFC_E_INVALID_ARG; |
531 | 516 | ||
532 | afc_lock(client); | 517 | afc_lock(client); |
533 | 518 | ||
534 | /* Send the command */ | 519 | /* Send the command */ |
535 | client->afc_packet->operation = AFC_OP_GET_DEVINFO; | 520 | ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes); |
536 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
537 | ret = afc_dispatch_packet(client, NULL, 0, &bytes); | ||
538 | if (ret != AFC_E_SUCCESS) { | 521 | if (ret != AFC_E_SUCCESS) { |
539 | afc_unlock(client); | 522 | afc_unlock(client); |
540 | return AFC_E_NOT_ENOUGH_DATA; | 523 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -542,6 +525,8 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos) | |||
542 | /* Receive the data */ | 525 | /* Receive the data */ |
543 | ret = afc_receive_data(client, &data, &bytes); | 526 | ret = afc_receive_data(client, &data, &bytes); |
544 | if (ret != AFC_E_SUCCESS) { | 527 | if (ret != AFC_E_SUCCESS) { |
528 | if (data) | ||
529 | free(data); | ||
545 | afc_unlock(client); | 530 | afc_unlock(client); |
546 | return ret; | 531 | return ret; |
547 | } | 532 | } |
@@ -552,22 +537,45 @@ afc_error_t afc_get_device_info(afc_client_t client, char ***infos) | |||
552 | 537 | ||
553 | afc_unlock(client); | 538 | afc_unlock(client); |
554 | 539 | ||
555 | *infos = list; | 540 | *device_information = list; |
541 | |||
542 | return ret; | ||
543 | } | ||
544 | |||
545 | afc_error_t afc_get_device_info_plist(afc_client_t client, plist_t *device_information) | ||
546 | { | ||
547 | uint32_t bytes = 0; | ||
548 | char *data = NULL; | ||
549 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | ||
550 | |||
551 | if (!client || !device_information) | ||
552 | return AFC_E_INVALID_ARG; | ||
553 | |||
554 | afc_lock(client); | ||
555 | |||
556 | /* Send the command */ | ||
557 | ret = afc_dispatch_packet(client, AFC_OP_GET_DEVINFO, 0, NULL, 0, &bytes); | ||
558 | if (ret != AFC_E_SUCCESS) { | ||
559 | afc_unlock(client); | ||
560 | return AFC_E_NOT_ENOUGH_DATA; | ||
561 | } | ||
562 | /* Receive the data */ | ||
563 | ret = afc_receive_data(client, &data, &bytes); | ||
564 | if (ret != AFC_E_SUCCESS) { | ||
565 | if (data) | ||
566 | free(data); | ||
567 | afc_unlock(client); | ||
568 | return ret; | ||
569 | } | ||
570 | /* Parse the data */ | ||
571 | *device_information = make_dictionary(data, bytes); | ||
572 | free(data); | ||
573 | |||
574 | afc_unlock(client); | ||
556 | 575 | ||
557 | return ret; | 576 | return ret; |
558 | } | 577 | } |
559 | 578 | ||
560 | /** | ||
561 | * Get a specific key of the device info list for a client connection. | ||
562 | * Known key values are: Model, FSTotalBytes, FSFreeBytes and FSBlockSize. | ||
563 | * This is a helper function for afc_get_device_info(). | ||
564 | * | ||
565 | * @param client The client to get device info for. | ||
566 | * @param key The key to get the value of. | ||
567 | * @param value The value for the key if successful or NULL otherwise. | ||
568 | * | ||
569 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
570 | */ | ||
571 | afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) | 579 | afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char **value) |
572 | { | 580 | { |
573 | afc_error_t ret = AFC_E_INTERNAL_ERROR; | 581 | afc_error_t ret = AFC_E_INTERNAL_ERROR; |
@@ -587,43 +595,40 @@ afc_error_t afc_get_device_info_key(afc_client_t client, const char *key, char * | |||
587 | break; | 595 | break; |
588 | } | 596 | } |
589 | } | 597 | } |
590 | 598 | for (ptr = kvps; *ptr; ptr++) { | |
591 | g_strfreev(kvps); | 599 | free(*ptr); |
600 | } | ||
601 | free(kvps); | ||
592 | 602 | ||
593 | return ret; | 603 | return ret; |
594 | } | 604 | } |
595 | 605 | ||
596 | /** | ||
597 | * Deletes a file or directory. | ||
598 | * | ||
599 | * @param client The client to use. | ||
600 | * @param path The path to delete. (must be a fully-qualified path) | ||
601 | * | ||
602 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
603 | */ | ||
604 | afc_error_t afc_remove_path(afc_client_t client, const char *path) | 606 | afc_error_t afc_remove_path(afc_client_t client, const char *path) |
605 | { | 607 | { |
606 | char *response = NULL; | ||
607 | uint32_t bytes = 0; | 608 | uint32_t bytes = 0; |
608 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 609 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
609 | 610 | ||
610 | if (!client || !path || !client->afc_packet || !client->connection) | 611 | if (!client || !path || !client->afc_packet || !client->parent) |
611 | return AFC_E_INVALID_ARG; | 612 | return AFC_E_INVALID_ARG; |
612 | 613 | ||
613 | afc_lock(client); | 614 | afc_lock(client); |
614 | 615 | ||
616 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
617 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
618 | afc_unlock(client); | ||
619 | debug_info("Failed to realloc packet buffer"); | ||
620 | return AFC_E_NO_MEM; | ||
621 | } | ||
622 | |||
615 | /* Send command */ | 623 | /* Send command */ |
616 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 624 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
617 | client->afc_packet->operation = AFC_OP_REMOVE_PATH; | 625 | ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH, data_len, NULL, 0, &bytes); |
618 | ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes); | ||
619 | if (ret != AFC_E_SUCCESS) { | 626 | if (ret != AFC_E_SUCCESS) { |
620 | afc_unlock(client); | 627 | afc_unlock(client); |
621 | return AFC_E_NOT_ENOUGH_DATA; | 628 | return AFC_E_NOT_ENOUGH_DATA; |
622 | } | 629 | } |
623 | /* Receive response */ | 630 | /* Receive response */ |
624 | ret = afc_receive_data(client, &response, &bytes); | 631 | ret = afc_receive_data(client, NULL, &bytes); |
625 | if (response) | ||
626 | free(response); | ||
627 | 632 | ||
628 | /* special case; unknown error actually means directory not empty */ | 633 | /* special case; unknown error actually means directory not empty */ |
629 | if (ret == AFC_E_UNKNOWN_ERROR) | 634 | if (ret == AFC_E_UNKNOWN_ERROR) |
@@ -634,61 +639,45 @@ afc_error_t afc_remove_path(afc_client_t client, const char *path) | |||
634 | return ret; | 639 | return ret; |
635 | } | 640 | } |
636 | 641 | ||
637 | /** | ||
638 | * Renames a file or directory on the phone. | ||
639 | * | ||
640 | * @param client The client to have rename. | ||
641 | * @param from The name to rename from. (must be a fully-qualified path) | ||
642 | * @param to The new name. (must also be a fully-qualified path) | ||
643 | * | ||
644 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
645 | */ | ||
646 | afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to) | 642 | afc_error_t afc_rename_path(afc_client_t client, const char *from, const char *to) |
647 | { | 643 | { |
648 | char *response = NULL; | 644 | if (!client || !from || !to || !client->afc_packet || !client->parent) |
649 | char *send = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t))); | 645 | return AFC_E_INVALID_ARG; |
646 | |||
650 | uint32_t bytes = 0; | 647 | uint32_t bytes = 0; |
651 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 648 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
652 | 649 | ||
653 | if (!client || !from || !to || !client->afc_packet || !client->connection) | 650 | size_t from_len = strlen(from); |
654 | return AFC_E_INVALID_ARG; | 651 | size_t to_len = strlen(to); |
655 | 652 | ||
656 | afc_lock(client); | 653 | afc_lock(client); |
657 | 654 | ||
655 | uint32_t data_len = (uint32_t)(from_len+1 + to_len+1); | ||
656 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
657 | afc_unlock(client); | ||
658 | debug_info("Failed to realloc packet buffer"); | ||
659 | return AFC_E_NO_MEM; | ||
660 | } | ||
661 | |||
658 | /* Send command */ | 662 | /* Send command */ |
659 | memcpy(send, from, strlen(from) + 1); | 663 | memcpy(AFC_PACKET_DATA_PTR, from, from_len+1); |
660 | memcpy(send + strlen(from) + 1, to, strlen(to) + 1); | 664 | memcpy(AFC_PACKET_DATA_PTR + from_len+1, to, to_len+1); |
661 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 665 | ret = afc_dispatch_packet(client, AFC_OP_RENAME_PATH, data_len, NULL, 0, &bytes); |
662 | client->afc_packet->operation = AFC_OP_RENAME_PATH; | ||
663 | ret = afc_dispatch_packet(client, send, strlen(to)+1 + strlen(from)+1, &bytes); | ||
664 | free(send); | ||
665 | if (ret != AFC_E_SUCCESS) { | 666 | if (ret != AFC_E_SUCCESS) { |
666 | afc_unlock(client); | 667 | afc_unlock(client); |
667 | return AFC_E_NOT_ENOUGH_DATA; | 668 | return AFC_E_NOT_ENOUGH_DATA; |
668 | } | 669 | } |
669 | /* Receive response */ | 670 | /* Receive response */ |
670 | ret = afc_receive_data(client, &response, &bytes); | 671 | ret = afc_receive_data(client, NULL, &bytes); |
671 | if (response) | ||
672 | free(response); | ||
673 | 672 | ||
674 | afc_unlock(client); | 673 | afc_unlock(client); |
675 | 674 | ||
676 | return ret; | 675 | return ret; |
677 | } | 676 | } |
678 | 677 | ||
679 | /** | 678 | 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 | { | 679 | { |
690 | uint32_t bytes = 0; | 680 | uint32_t bytes = 0; |
691 | char *response = NULL; | ||
692 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 681 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
693 | 682 | ||
694 | if (!client) | 683 | if (!client) |
@@ -696,50 +685,49 @@ afc_error_t afc_make_directory(afc_client_t client, const char *dir) | |||
696 | 685 | ||
697 | afc_lock(client); | 686 | afc_lock(client); |
698 | 687 | ||
688 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
689 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
690 | afc_unlock(client); | ||
691 | debug_info("Failed to realloc packet buffer"); | ||
692 | return AFC_E_NO_MEM; | ||
693 | } | ||
694 | |||
699 | /* Send command */ | 695 | /* Send command */ |
700 | client->afc_packet->operation = AFC_OP_MAKE_DIR; | 696 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
701 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 697 | ret = afc_dispatch_packet(client, AFC_OP_MAKE_DIR, data_len, NULL, 0, &bytes); |
702 | ret = afc_dispatch_packet(client, dir, strlen(dir)+1, &bytes); | ||
703 | if (ret != AFC_E_SUCCESS) { | 698 | if (ret != AFC_E_SUCCESS) { |
704 | afc_unlock(client); | 699 | afc_unlock(client); |
705 | return AFC_E_NOT_ENOUGH_DATA; | 700 | return AFC_E_NOT_ENOUGH_DATA; |
706 | } | 701 | } |
707 | /* Receive response */ | 702 | /* Receive response */ |
708 | ret = afc_receive_data(client, &response, &bytes); | 703 | ret = afc_receive_data(client, NULL, &bytes); |
709 | if (response) | ||
710 | free(response); | ||
711 | 704 | ||
712 | afc_unlock(client); | 705 | afc_unlock(client); |
713 | 706 | ||
714 | return ret; | 707 | return ret; |
715 | } | 708 | } |
716 | 709 | ||
717 | /** | 710 | 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 | { | 711 | { |
730 | char *received = NULL; | 712 | char *received = NULL; |
731 | uint32_t bytes = 0; | 713 | uint32_t bytes = 0; |
732 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 714 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
733 | 715 | ||
734 | if (!client || !path || !infolist) | 716 | if (!client || !path || !file_information) |
735 | return AFC_E_INVALID_ARG; | 717 | return AFC_E_INVALID_ARG; |
736 | 718 | ||
737 | afc_lock(client); | 719 | afc_lock(client); |
738 | 720 | ||
721 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
722 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
723 | afc_unlock(client); | ||
724 | debug_info("Failed to realloc packet buffer"); | ||
725 | return AFC_E_NO_MEM; | ||
726 | } | ||
727 | |||
739 | /* Send command */ | 728 | /* Send command */ |
740 | client->afc_packet->operation = AFC_OP_GET_FILE_INFO; | 729 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
741 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 730 | ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); |
742 | ret = afc_dispatch_packet(client, path, strlen(path)+1, &bytes); | ||
743 | if (ret != AFC_E_SUCCESS) { | 731 | if (ret != AFC_E_SUCCESS) { |
744 | afc_unlock(client); | 732 | afc_unlock(client); |
745 | return AFC_E_NOT_ENOUGH_DATA; | 733 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -748,7 +736,7 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf | |||
748 | /* Receive data */ | 736 | /* Receive data */ |
749 | ret = afc_receive_data(client, &received, &bytes); | 737 | ret = afc_receive_data(client, &received, &bytes); |
750 | if (received) { | 738 | if (received) { |
751 | *infolist = make_strings_list(received, bytes); | 739 | *file_information = make_strings_list(received, bytes); |
752 | free(received); | 740 | free(received); |
753 | } | 741 | } |
754 | 742 | ||
@@ -757,51 +745,77 @@ afc_error_t afc_get_file_info(afc_client_t client, const char *path, char ***inf | |||
757 | return ret; | 745 | return ret; |
758 | } | 746 | } |
759 | 747 | ||
760 | /** | 748 | afc_error_t afc_get_file_info_plist(afc_client_t client, const char *path, plist_t *file_information) |
761 | * Opens a file on the phone. | ||
762 | * | ||
763 | * @param client The client to use to open the file. | ||
764 | * @param filename The file to open. (must be a fully-qualified path) | ||
765 | * @param file_mode The mode to use to open the file. Can be AFC_FILE_READ or | ||
766 | * AFC_FILE_WRITE; the former lets you read and write, | ||
767 | * however, and the second one will *create* the file, | ||
768 | * destroying anything previously there. | ||
769 | * @param handle Pointer to a uint64_t that will hold the handle of the file | ||
770 | * | ||
771 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
772 | */ | ||
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 | { | 749 | { |
777 | uint64_t file_mode_loc = GUINT64_TO_LE(file_mode); | 750 | char *received = NULL; |
778 | uint32_t bytes = 0; | 751 | uint32_t bytes = 0; |
779 | char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1)); | ||
780 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 752 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
781 | 753 | ||
782 | /* set handle to 0 so in case an error occurs, the handle is invalid */ | 754 | if (!client || !path || !file_information) |
783 | *handle = 0; | ||
784 | |||
785 | if (!client || !client->connection || !client->afc_packet) | ||
786 | return AFC_E_INVALID_ARG; | 755 | return AFC_E_INVALID_ARG; |
787 | 756 | ||
788 | afc_lock(client); | 757 | afc_lock(client); |
789 | 758 | ||
759 | uint32_t data_len = (uint32_t)strlen(path)+1; | ||
760 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
761 | afc_unlock(client); | ||
762 | debug_info("Failed to realloc packet buffer"); | ||
763 | return AFC_E_NO_MEM; | ||
764 | } | ||
765 | |||
790 | /* Send command */ | 766 | /* Send command */ |
791 | memcpy(data, &file_mode_loc, 8); | 767 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
792 | memcpy(data + 8, filename, strlen(filename)); | 768 | ret = afc_dispatch_packet(client, AFC_OP_GET_FILE_INFO, data_len, NULL, 0, &bytes); |
793 | data[8 + strlen(filename)] = '\0'; | 769 | if (ret != AFC_E_SUCCESS) { |
794 | client->afc_packet->operation = AFC_OP_FILE_OPEN; | 770 | afc_unlock(client); |
795 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 771 | return AFC_E_NOT_ENOUGH_DATA; |
796 | ret = afc_dispatch_packet(client, data, 8 + strlen(filename) + 1, &bytes); | 772 | } |
797 | free(data); | 773 | |
774 | /* Receive data */ | ||
775 | ret = afc_receive_data(client, &received, &bytes); | ||
776 | if (received) { | ||
777 | *file_information = make_dictionary(received, bytes); | ||
778 | free(received); | ||
779 | } | ||
780 | |||
781 | afc_unlock(client); | ||
782 | |||
783 | return ret; | ||
784 | } | ||
785 | |||
786 | afc_error_t afc_file_open(afc_client_t client, const char *filename, afc_file_mode_t file_mode, uint64_t *handle) | ||
787 | { | ||
788 | if (!client || !client->parent || !client->afc_packet) | ||
789 | return AFC_E_INVALID_ARG; | ||
790 | |||
791 | //uint64_t file_mode_loc = htole64(file_mode); | ||
792 | uint32_t bytes = 0; | ||
793 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | ||
794 | |||
795 | /* set handle to 0 so in case an error occurs, the handle is invalid */ | ||
796 | *handle = 0; | ||
797 | |||
798 | afc_lock(client); | ||
799 | |||
800 | uint32_t data_len = (uint32_t)(strlen(filename)+1 + 8); | ||
801 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
802 | afc_unlock(client); | ||
803 | debug_info("Failed to realloc packet buffer"); | ||
804 | return AFC_E_NO_MEM; | ||
805 | } | ||
798 | 806 | ||
807 | /* Send command */ | ||
808 | //memcpy(AFC_PACKET_DATA_PTR, &file_mode_loc, 8); | ||
809 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(file_mode); | ||
810 | memcpy(AFC_PACKET_DATA_PTR + 8, filename, data_len-8); | ||
811 | ret = afc_dispatch_packet(client, AFC_OP_FILE_OPEN, data_len, NULL, 0, &bytes); | ||
799 | if (ret != AFC_E_SUCCESS) { | 812 | if (ret != AFC_E_SUCCESS) { |
800 | debug_info("Didn't receive a response to the command"); | 813 | debug_info("Didn't receive a response to the command"); |
801 | afc_unlock(client); | 814 | afc_unlock(client); |
802 | return AFC_E_NOT_ENOUGH_DATA; | 815 | return AFC_E_NOT_ENOUGH_DATA; |
803 | } | 816 | } |
804 | /* Receive the data */ | 817 | /* Receive the data */ |
818 | char* data = NULL; | ||
805 | ret = afc_receive_data(client, &data, &bytes); | 819 | ret = afc_receive_data(client, &data, &bytes); |
806 | if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { | 820 | if ((ret == AFC_E_SUCCESS) && (bytes > 0) && data) { |
807 | afc_unlock(client); | 821 | afc_unlock(client); |
@@ -811,6 +825,8 @@ afc_file_open(afc_client_t client, const char *filename, | |||
811 | free(data); | 825 | free(data); |
812 | return ret; | 826 | return ret; |
813 | } | 827 | } |
828 | /* in case memory was allocated but no data received or an error occurred */ | ||
829 | free(data); | ||
814 | 830 | ||
815 | debug_info("Didn't get any further data"); | 831 | debug_info("Didn't get any further data"); |
816 | 832 | ||
@@ -819,156 +835,81 @@ afc_file_open(afc_client_t client, const char *filename, | |||
819 | return ret; | 835 | return ret; |
820 | } | 836 | } |
821 | 837 | ||
822 | /** | 838 | 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 | { | 839 | { |
836 | char *input = NULL; | 840 | char *input = NULL; |
837 | uint32_t current_count = 0, bytes_loc = 0; | 841 | uint32_t current_count = 0, bytes_loc = 0; |
838 | const uint32_t MAXIMUM_READ_SIZE = 1 << 16; | 842 | struct readinfo { |
843 | uint64_t handle; | ||
844 | uint64_t size; | ||
845 | }; | ||
839 | afc_error_t ret = AFC_E_SUCCESS; | 846 | afc_error_t ret = AFC_E_SUCCESS; |
840 | 847 | ||
841 | if (!client || !client->afc_packet || !client->connection || handle == 0) | 848 | if (!client || !client->afc_packet || !client->parent || handle == 0) |
842 | return AFC_E_INVALID_ARG; | 849 | return AFC_E_INVALID_ARG; |
843 | debug_info("called for length %i", length); | 850 | debug_info("called for length %i", length); |
844 | 851 | ||
852 | //uint32_t data_len = 8 + 8; | ||
853 | |||
845 | afc_lock(client); | 854 | afc_lock(client); |
846 | 855 | ||
847 | /* Looping here to get around the maximum amount of data that | 856 | /* Send the read command */ |
848 | afc_receive_data can handle */ | 857 | struct readinfo* readinfo = (struct readinfo*)(AFC_PACKET_DATA_PTR); |
849 | while (current_count < length) { | 858 | readinfo->handle = handle; |
850 | debug_info("current count is %i but length is %i", current_count, length); | 859 | readinfo->size = htole64(length); |
851 | 860 | ret = afc_dispatch_packet(client, AFC_OP_FILE_READ, sizeof(struct readinfo), NULL, 0, &bytes_loc); | |
852 | /* Send the read command */ | 861 | if (ret != AFC_E_SUCCESS) { |
853 | AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); | 862 | afc_unlock(client); |
854 | packet->filehandle = handle; | 863 | return AFC_E_NOT_ENOUGH_DATA; |
855 | packet->size = GUINT64_TO_LE(((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE); | 864 | } |
856 | client->afc_packet->operation = AFC_OP_READ; | 865 | /* Receive the data */ |
857 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 866 | ret = afc_receive_data(client, &input, &bytes_loc); |
858 | ret = afc_dispatch_packet(client, (char *) packet, sizeof(AFCFilePacket), &bytes_loc); | 867 | debug_info("afc_receive_data returned error: %d", ret); |
859 | free(packet); | 868 | debug_info("bytes returned: %i", bytes_loc); |
860 | 869 | if (ret != AFC_E_SUCCESS) { | |
861 | if (ret != AFC_E_SUCCESS) { | 870 | afc_unlock(client); |
862 | afc_unlock(client); | 871 | return ret; |
863 | return AFC_E_NOT_ENOUGH_DATA; | 872 | } |
864 | } | 873 | if (bytes_loc == 0) { |
865 | /* Receive the data */ | 874 | if (input) |
866 | ret = afc_receive_data(client, &input, &bytes_loc); | 875 | free(input); |
867 | debug_info("afc_receive_data returned error: %d", ret); | 876 | afc_unlock(client); |
868 | debug_info("bytes returned: %i", bytes_loc); | 877 | *bytes_read = current_count; |
869 | if (ret != AFC_E_SUCCESS) { | 878 | /* FIXME: check that's actually a success */ |
870 | afc_unlock(client); | 879 | return ret; |
871 | return ret; | 880 | } |
872 | } else if (bytes_loc == 0) { | 881 | if (input) { |
873 | if (input) | 882 | debug_info("%d", bytes_loc); |
874 | free(input); | 883 | memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); |
875 | afc_unlock(client); | 884 | free(input); |
876 | *bytes_read = current_count; | 885 | input = NULL; |
877 | /* FIXME: check that's actually a success */ | 886 | current_count += (bytes_loc > length) ? length : bytes_loc; |
878 | return ret; | ||
879 | } else { | ||
880 | if (input) { | ||
881 | debug_info("%d", bytes_loc); | ||
882 | memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); | ||
883 | free(input); | ||
884 | input = NULL; | ||
885 | current_count += (bytes_loc > length) ? length : bytes_loc; | ||
886 | } | ||
887 | } | ||
888 | } | 887 | } |
889 | debug_info("returning current_count as %i", current_count); | ||
890 | 888 | ||
891 | afc_unlock(client); | 889 | afc_unlock(client); |
892 | *bytes_read = current_count; | 890 | *bytes_read = current_count; |
893 | return ret; | 891 | return ret; |
894 | } | 892 | } |
895 | 893 | ||
896 | /** | 894 | 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 | { | 895 | { |
910 | char *acknowledgement = NULL; | 896 | uint32_t current_count = 0; |
911 | const uint32_t MAXIMUM_WRITE_SIZE = 1 << 15; | ||
912 | uint32_t current_count = 0, i = 0; | ||
913 | uint32_t segments = (length / MAXIMUM_WRITE_SIZE); | ||
914 | uint32_t bytes_loc = 0; | 897 | uint32_t bytes_loc = 0; |
915 | char *out_buffer = NULL; | ||
916 | afc_error_t ret = AFC_E_SUCCESS; | 898 | afc_error_t ret = AFC_E_SUCCESS; |
917 | 899 | ||
918 | if (!client || !client->afc_packet || !client->connection || !bytes_written || (handle == 0)) | 900 | if (!client || !client->afc_packet || !client->parent || !bytes_written || (handle == 0)) |
919 | return AFC_E_INVALID_ARG; | 901 | return AFC_E_INVALID_ARG; |
920 | 902 | ||
903 | uint32_t data_len = 8; | ||
904 | |||
921 | afc_lock(client); | 905 | afc_lock(client); |
922 | 906 | ||
923 | debug_info("Write length: %i", length); | 907 | debug_info("Write length: %i", length); |
924 | 908 | ||
925 | /* Divide the file into segments. */ | 909 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; |
926 | for (i = 0; i < segments; i++) { | 910 | ret = afc_dispatch_packet(client, AFC_OP_FILE_WRITE, data_len, data, length, &bytes_loc); |
927 | /* Send the segment */ | ||
928 | client->afc_packet->this_length = sizeof(AFCPacket) + 8; | ||
929 | client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE; | ||
930 | client->afc_packet->operation = AFC_OP_WRITE; | ||
931 | out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); | ||
932 | memcpy(out_buffer, (char *)&handle, sizeof(uint64_t)); | ||
933 | memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE); | ||
934 | ret = afc_dispatch_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8, &bytes_loc); | ||
935 | if (ret != AFC_E_SUCCESS) { | ||
936 | afc_unlock(client); | ||
937 | return AFC_E_NOT_ENOUGH_DATA; | ||
938 | } | ||
939 | free(out_buffer); | ||
940 | out_buffer = NULL; | ||
941 | |||
942 | current_count += bytes_loc; | ||
943 | ret = afc_receive_data(client, &acknowledgement, &bytes_loc); | ||
944 | if (ret != AFC_E_SUCCESS) { | ||
945 | afc_unlock(client); | ||
946 | return ret; | ||
947 | } else { | ||
948 | free(acknowledgement); | ||
949 | } | ||
950 | } | ||
951 | 911 | ||
952 | /* By this point, we should be at the end. i.e. the last segment that didn't | 912 | current_count += bytes_loc - (sizeof(AFCPacket) + 8); |
953 | get sent in the for loop. This length is fine because it's always | ||
954 | sizeof(AFCPacket) + 8, but to be sure we do it again */ | ||
955 | if (current_count == length) { | ||
956 | afc_unlock(client); | ||
957 | *bytes_written = current_count; | ||
958 | return ret; | ||
959 | } | ||
960 | |||
961 | client->afc_packet->this_length = sizeof(AFCPacket) + 8; | ||
962 | client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count); | ||
963 | client->afc_packet->operation = AFC_OP_WRITE; | ||
964 | out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); | ||
965 | memcpy(out_buffer, (char *) &handle, sizeof(uint64_t)); | ||
966 | memcpy(out_buffer + 8, data + current_count, (length - current_count)); | ||
967 | ret = afc_dispatch_packet(client, out_buffer, (length - current_count) + 8, &bytes_loc); | ||
968 | free(out_buffer); | ||
969 | out_buffer = NULL; | ||
970 | |||
971 | current_count += bytes_loc; | ||
972 | 913 | ||
973 | if (ret != AFC_E_SUCCESS) { | 914 | if (ret != AFC_E_SUCCESS) { |
974 | afc_unlock(client); | 915 | afc_unlock(client); |
@@ -976,43 +917,32 @@ afc_file_write(afc_client_t client, uint64_t handle, const char *data, uint32_t | |||
976 | return AFC_E_SUCCESS; | 917 | return AFC_E_SUCCESS; |
977 | } | 918 | } |
978 | 919 | ||
979 | ret = afc_receive_data(client, &acknowledgement, &bytes_loc); | 920 | ret = afc_receive_data(client, NULL, &bytes_loc); |
980 | afc_unlock(client); | 921 | afc_unlock(client); |
981 | if (ret != AFC_E_SUCCESS) { | 922 | if (ret != AFC_E_SUCCESS) { |
982 | debug_info("uh oh?"); | 923 | debug_info("Failed to receive reply (%d)", ret); |
983 | } else { | ||
984 | free(acknowledgement); | ||
985 | } | 924 | } |
986 | *bytes_written = current_count; | 925 | *bytes_written = current_count; |
987 | return ret; | 926 | return ret; |
988 | } | 927 | } |
989 | 928 | ||
990 | /** | ||
991 | * Closes a file on the phone. | ||
992 | * | ||
993 | * @param client The client to close the file with. | ||
994 | * @param handle File handle of a previously opened file. | ||
995 | */ | ||
996 | afc_error_t afc_file_close(afc_client_t client, uint64_t handle) | 929 | afc_error_t afc_file_close(afc_client_t client, uint64_t handle) |
997 | { | 930 | { |
998 | char *buffer = malloc(sizeof(char) * 8); | ||
999 | uint32_t bytes = 0; | 931 | uint32_t bytes = 0; |
1000 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 932 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1001 | 933 | ||
1002 | if (!client || (handle == 0)) | 934 | if (!client || (handle == 0)) |
1003 | return AFC_E_INVALID_ARG; | 935 | return AFC_E_INVALID_ARG; |
1004 | 936 | ||
937 | uint32_t data_len = 8; | ||
938 | |||
1005 | afc_lock(client); | 939 | afc_lock(client); |
1006 | 940 | ||
1007 | debug_info("File handle %i", handle); | 941 | debug_info("File handle %i", handle); |
1008 | 942 | ||
1009 | /* Send command */ | 943 | /* Send command */ |
1010 | memcpy(buffer, &handle, sizeof(uint64_t)); | 944 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; |
1011 | client->afc_packet->operation = AFC_OP_FILE_CLOSE; | 945 | ret = afc_dispatch_packet(client, AFC_OP_FILE_CLOSE, data_len, NULL, 0, &bytes); |
1012 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
1013 | ret = afc_dispatch_packet(client, buffer, 8, &bytes); | ||
1014 | free(buffer); | ||
1015 | buffer = NULL; | ||
1016 | 946 | ||
1017 | if (ret != AFC_E_SUCCESS) { | 947 | if (ret != AFC_E_SUCCESS) { |
1018 | afc_unlock(client); | 948 | afc_unlock(client); |
@@ -1020,32 +950,20 @@ afc_error_t afc_file_close(afc_client_t client, uint64_t handle) | |||
1020 | } | 950 | } |
1021 | 951 | ||
1022 | /* Receive the response */ | 952 | /* Receive the response */ |
1023 | ret = afc_receive_data(client, &buffer, &bytes); | 953 | ret = afc_receive_data(client, NULL, &bytes); |
1024 | if (buffer) | ||
1025 | free(buffer); | ||
1026 | 954 | ||
1027 | afc_unlock(client); | 955 | afc_unlock(client); |
1028 | 956 | ||
1029 | return ret; | 957 | return ret; |
1030 | } | 958 | } |
1031 | 959 | ||
1032 | /** | ||
1033 | * Locks or unlocks a file on the phone. | ||
1034 | * | ||
1035 | * makes use of flock on the device, see | ||
1036 | * http://developer.apple.com/documentation/Darwin/Reference/ManPages/man2/flock.2.html | ||
1037 | * | ||
1038 | * @param client The client to lock the file with. | ||
1039 | * @param handle File handle of a previously opened file. | ||
1040 | * @param operation the lock or unlock operation to perform, this is one of | ||
1041 | * AFC_LOCK_SH (shared lock), AFC_LOCK_EX (exclusive lock), | ||
1042 | * or AFC_LOCK_UN (unlock). | ||
1043 | */ | ||
1044 | afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) | 960 | afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t operation) |
1045 | { | 961 | { |
1046 | char *buffer = malloc(16); | ||
1047 | uint32_t bytes = 0; | 962 | uint32_t bytes = 0; |
1048 | uint64_t op = GUINT64_TO_LE(operation); | 963 | struct lockinfo { |
964 | uint64_t handle; | ||
965 | uint64_t op; | ||
966 | }; | ||
1049 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 967 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1050 | 968 | ||
1051 | if (!client || (handle == 0)) | 969 | if (!client || (handle == 0)) |
@@ -1056,47 +974,31 @@ afc_error_t afc_file_lock(afc_client_t client, uint64_t handle, afc_lock_op_t op | |||
1056 | debug_info("file handle %i", handle); | 974 | debug_info("file handle %i", handle); |
1057 | 975 | ||
1058 | /* Send command */ | 976 | /* Send command */ |
1059 | memcpy(buffer, &handle, sizeof(uint64_t)); | 977 | struct lockinfo* lockinfo = (struct lockinfo*)(AFC_PACKET_DATA_PTR); |
1060 | memcpy(buffer + 8, &op, 8); | 978 | lockinfo->handle = handle; |
1061 | 979 | lockinfo->op = htole64(operation); | |
1062 | client->afc_packet->operation = AFC_OP_FILE_LOCK; | 980 | ret = afc_dispatch_packet(client, AFC_OP_FILE_LOCK, sizeof(struct lockinfo), NULL, 0, &bytes); |
1063 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
1064 | ret = afc_dispatch_packet(client, buffer, 16, &bytes); | ||
1065 | free(buffer); | ||
1066 | buffer = NULL; | ||
1067 | |||
1068 | if (ret != AFC_E_SUCCESS) { | 981 | if (ret != AFC_E_SUCCESS) { |
1069 | afc_unlock(client); | 982 | afc_unlock(client); |
1070 | debug_info("could not send lock command"); | 983 | debug_info("could not send lock command"); |
1071 | return AFC_E_UNKNOWN_ERROR; | 984 | return AFC_E_UNKNOWN_ERROR; |
1072 | } | 985 | } |
1073 | /* Receive the response */ | 986 | /* Receive the response */ |
1074 | ret = afc_receive_data(client, &buffer, &bytes); | 987 | ret = afc_receive_data(client, NULL, &bytes); |
1075 | if (buffer) { | 988 | |
1076 | debug_buffer(buffer, bytes); | ||
1077 | free(buffer); | ||
1078 | } | ||
1079 | afc_unlock(client); | 989 | afc_unlock(client); |
1080 | 990 | ||
1081 | return ret; | 991 | return ret; |
1082 | } | 992 | } |
1083 | 993 | ||
1084 | /** | ||
1085 | * Seeks to a given position of a pre-opened file on the phone. | ||
1086 | * | ||
1087 | * @param client The client to use to seek to the position. | ||
1088 | * @param handle File handle of a previously opened. | ||
1089 | * @param offset Seek offset. | ||
1090 | * @param whence Seeking direction, one of SEEK_SET, SEEK_CUR, or SEEK_END. | ||
1091 | * | ||
1092 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1093 | */ | ||
1094 | afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) | 994 | afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, int whence) |
1095 | { | 995 | { |
1096 | char *buffer = (char *) malloc(sizeof(char) * 24); | ||
1097 | int64_t offset_loc = (int64_t)GUINT64_TO_LE(offset); | ||
1098 | uint64_t whence_loc = GUINT64_TO_LE(whence); | ||
1099 | uint32_t bytes = 0; | 996 | uint32_t bytes = 0; |
997 | struct seekinfo { | ||
998 | uint64_t handle; | ||
999 | uint64_t whence; | ||
1000 | int64_t offset; | ||
1001 | }; | ||
1100 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1002 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1101 | 1003 | ||
1102 | if (!client || (handle == 0)) | 1004 | if (!client || (handle == 0)) |
@@ -1105,57 +1007,40 @@ afc_error_t afc_file_seek(afc_client_t client, uint64_t handle, int64_t offset, | |||
1105 | afc_lock(client); | 1007 | afc_lock(client); |
1106 | 1008 | ||
1107 | /* Send the command */ | 1009 | /* Send the command */ |
1108 | memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ | 1010 | struct seekinfo* seekinfo = (struct seekinfo*)(AFC_PACKET_DATA_PTR); |
1109 | memcpy(buffer + 8, &whence_loc, sizeof(uint64_t)); /* fromwhere */ | 1011 | seekinfo->handle = handle; |
1110 | memcpy(buffer + 16, &offset_loc, sizeof(uint64_t)); /* offset */ | 1012 | seekinfo->whence = htole64(whence); |
1111 | client->afc_packet->operation = AFC_OP_FILE_SEEK; | 1013 | seekinfo->offset = (int64_t)htole64(offset); |
1112 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 1014 | ret = afc_dispatch_packet(client, AFC_OP_FILE_SEEK, sizeof(struct seekinfo), NULL, 0, &bytes); |
1113 | ret = afc_dispatch_packet(client, buffer, 24, &bytes); | ||
1114 | free(buffer); | ||
1115 | buffer = NULL; | ||
1116 | 1015 | ||
1117 | if (ret != AFC_E_SUCCESS) { | 1016 | if (ret != AFC_E_SUCCESS) { |
1118 | afc_unlock(client); | 1017 | afc_unlock(client); |
1119 | return AFC_E_NOT_ENOUGH_DATA; | 1018 | return AFC_E_NOT_ENOUGH_DATA; |
1120 | } | 1019 | } |
1121 | /* Receive response */ | 1020 | /* Receive response */ |
1122 | ret = afc_receive_data(client, &buffer, &bytes); | 1021 | ret = afc_receive_data(client, NULL, &bytes); |
1123 | if (buffer) | ||
1124 | free(buffer); | ||
1125 | 1022 | ||
1126 | afc_unlock(client); | 1023 | afc_unlock(client); |
1127 | 1024 | ||
1128 | return ret; | 1025 | return ret; |
1129 | } | 1026 | } |
1130 | 1027 | ||
1131 | /** | ||
1132 | * Returns current position in a pre-opened file on the phone. | ||
1133 | * | ||
1134 | * @param client The client to use. | ||
1135 | * @param handle File handle of a previously opened file. | ||
1136 | * @param position Position in bytes of indicator | ||
1137 | * | ||
1138 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1139 | */ | ||
1140 | afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position) | 1028 | afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *position) |
1141 | { | 1029 | { |
1142 | char *buffer = (char *) malloc(sizeof(char) * 8); | 1030 | char *buffer = NULL; |
1143 | uint32_t bytes = 0; | 1031 | uint32_t bytes = 0; |
1144 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1032 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1145 | 1033 | ||
1146 | if (!client || (handle == 0)) | 1034 | if (!client || (handle == 0)) |
1147 | return AFC_E_INVALID_ARG; | 1035 | return AFC_E_INVALID_ARG; |
1148 | 1036 | ||
1037 | uint32_t data_len = 8; | ||
1038 | |||
1149 | afc_lock(client); | 1039 | afc_lock(client); |
1150 | 1040 | ||
1151 | /* Send the command */ | 1041 | /* Send the command */ |
1152 | memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ | 1042 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = handle; |
1153 | client->afc_packet->operation = AFC_OP_FILE_TELL; | 1043 | ret = afc_dispatch_packet(client, AFC_OP_FILE_TELL, data_len, NULL, 0, &bytes); |
1154 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | ||
1155 | ret = afc_dispatch_packet(client, buffer, 8, &bytes); | ||
1156 | free(buffer); | ||
1157 | buffer = NULL; | ||
1158 | |||
1159 | if (ret != AFC_E_SUCCESS) { | 1044 | if (ret != AFC_E_SUCCESS) { |
1160 | afc_unlock(client); | 1045 | afc_unlock(client); |
1161 | return AFC_E_NOT_ENOUGH_DATA; | 1046 | return AFC_E_NOT_ENOUGH_DATA; |
@@ -1166,33 +1051,22 @@ afc_error_t afc_file_tell(afc_client_t client, uint64_t handle, uint64_t *positi | |||
1166 | if (bytes > 0 && buffer) { | 1051 | if (bytes > 0 && buffer) { |
1167 | /* Get the position */ | 1052 | /* Get the position */ |
1168 | memcpy(position, buffer, sizeof(uint64_t)); | 1053 | memcpy(position, buffer, sizeof(uint64_t)); |
1169 | *position = GUINT64_FROM_LE(*position); | 1054 | *position = le64toh(*position); |
1170 | } | 1055 | } |
1171 | if (buffer) | 1056 | free(buffer); |
1172 | free(buffer); | ||
1173 | 1057 | ||
1174 | afc_unlock(client); | 1058 | afc_unlock(client); |
1175 | 1059 | ||
1176 | return ret; | 1060 | return ret; |
1177 | } | 1061 | } |
1178 | 1062 | ||
1179 | /** | ||
1180 | * Sets the size of a file on the phone. | ||
1181 | * | ||
1182 | * @param client The client to use to set the file size. | ||
1183 | * @param handle File handle of a previously opened file. | ||
1184 | * @param newsize The size to set the file to. | ||
1185 | * | ||
1186 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1187 | * | ||
1188 | * @note This function is more akin to ftruncate than truncate, and truncate | ||
1189 | * calls would have to open the file before calling this, sadly. | ||
1190 | */ | ||
1191 | afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize) | 1063 | afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t newsize) |
1192 | { | 1064 | { |
1193 | char *buffer = (char *) malloc(sizeof(char) * 16); | ||
1194 | uint32_t bytes = 0; | 1065 | uint32_t bytes = 0; |
1195 | uint64_t newsize_loc = GUINT64_TO_LE(newsize); | 1066 | struct truncinfo { |
1067 | uint64_t handle; | ||
1068 | uint64_t newsize; | ||
1069 | }; | ||
1196 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1070 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1197 | 1071 | ||
1198 | if (!client || (handle == 0)) | 1072 | if (!client || (handle == 0)) |
@@ -1201,160 +1075,240 @@ afc_error_t afc_file_truncate(afc_client_t client, uint64_t handle, uint64_t new | |||
1201 | afc_lock(client); | 1075 | afc_lock(client); |
1202 | 1076 | ||
1203 | /* Send command */ | 1077 | /* Send command */ |
1204 | memcpy(buffer, &handle, sizeof(uint64_t)); /* handle */ | 1078 | struct truncinfo* truncinfo = (struct truncinfo*)(AFC_PACKET_DATA_PTR); |
1205 | memcpy(buffer + 8, &newsize_loc, sizeof(uint64_t)); /* newsize */ | 1079 | truncinfo->handle = handle; |
1206 | client->afc_packet->operation = AFC_OP_FILE_SET_SIZE; | 1080 | truncinfo->newsize = htole64(newsize); |
1207 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | 1081 | ret = afc_dispatch_packet(client, AFC_OP_FILE_SET_SIZE, sizeof(struct truncinfo), NULL, 0, &bytes); |
1208 | ret = afc_dispatch_packet(client, buffer, 16, &bytes); | ||
1209 | free(buffer); | ||
1210 | buffer = NULL; | ||
1211 | 1082 | ||
1212 | if (ret != AFC_E_SUCCESS) { | 1083 | if (ret != AFC_E_SUCCESS) { |
1213 | afc_unlock(client); | 1084 | afc_unlock(client); |
1214 | return AFC_E_NOT_ENOUGH_DATA; | 1085 | return AFC_E_NOT_ENOUGH_DATA; |
1215 | } | 1086 | } |
1216 | /* Receive response */ | 1087 | /* Receive response */ |
1217 | ret = afc_receive_data(client, &buffer, &bytes); | 1088 | ret = afc_receive_data(client, NULL, &bytes); |
1218 | if (buffer) | ||
1219 | free(buffer); | ||
1220 | 1089 | ||
1221 | afc_unlock(client); | 1090 | afc_unlock(client); |
1222 | 1091 | ||
1223 | return ret; | 1092 | return ret; |
1224 | } | 1093 | } |
1225 | 1094 | ||
1226 | /** | ||
1227 | * Sets the size of a file on the phone without prior opening it. | ||
1228 | * | ||
1229 | * @param client The client to use to set the file size. | ||
1230 | * @param path The path of the file to be truncated. | ||
1231 | * @param newsize The size to set the file to. | ||
1232 | * | ||
1233 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1234 | */ | ||
1235 | afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize) | 1095 | afc_error_t afc_truncate(afc_client_t client, const char *path, uint64_t newsize) |
1236 | { | 1096 | { |
1237 | char *response = NULL; | 1097 | if (!client || !path || !client->afc_packet || !client->parent) |
1238 | char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); | 1098 | return AFC_E_INVALID_ARG; |
1099 | |||
1239 | uint32_t bytes = 0; | 1100 | uint32_t bytes = 0; |
1240 | uint64_t size_requested = GUINT64_TO_LE(newsize); | ||
1241 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1101 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1242 | 1102 | ||
1243 | if (!client || !path || !client->afc_packet || !client->connection) | ||
1244 | return AFC_E_INVALID_ARG; | ||
1245 | |||
1246 | afc_lock(client); | 1103 | afc_lock(client); |
1247 | 1104 | ||
1105 | uint32_t data_len = 8 + (uint32_t)(strlen(path)+1); | ||
1106 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
1107 | afc_unlock(client); | ||
1108 | debug_info("Failed to realloc packet buffer"); | ||
1109 | return AFC_E_NO_MEM; | ||
1110 | } | ||
1111 | |||
1248 | /* Send command */ | 1112 | /* Send command */ |
1249 | memcpy(send, &size_requested, 8); | 1113 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(newsize); |
1250 | memcpy(send + 8, path, strlen(path) + 1); | 1114 | memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8); |
1251 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 1115 | ret = afc_dispatch_packet(client, AFC_OP_TRUNCATE, data_len, NULL, 0, &bytes); |
1252 | client->afc_packet->operation = AFC_OP_TRUNCATE; | ||
1253 | ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes); | ||
1254 | free(send); | ||
1255 | if (ret != AFC_E_SUCCESS) { | 1116 | if (ret != AFC_E_SUCCESS) { |
1256 | afc_unlock(client); | 1117 | afc_unlock(client); |
1257 | return AFC_E_NOT_ENOUGH_DATA; | 1118 | return AFC_E_NOT_ENOUGH_DATA; |
1258 | } | 1119 | } |
1259 | /* Receive response */ | 1120 | /* Receive response */ |
1260 | ret = afc_receive_data(client, &response, &bytes); | 1121 | ret = afc_receive_data(client, NULL, &bytes); |
1261 | if (response) | ||
1262 | free(response); | ||
1263 | 1122 | ||
1264 | afc_unlock(client); | 1123 | afc_unlock(client); |
1265 | 1124 | ||
1266 | return ret; | 1125 | return ret; |
1267 | } | 1126 | } |
1268 | 1127 | ||
1269 | /** | ||
1270 | * Creates a hard link or symbolic link on the device. | ||
1271 | * | ||
1272 | * @param client The client to use for making a link | ||
1273 | * @param linktype 1 = hard link, 2 = symlink | ||
1274 | * @param target The file to be linked. | ||
1275 | * @param linkname The name of link. | ||
1276 | * | ||
1277 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1278 | */ | ||
1279 | afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) | 1128 | afc_error_t afc_make_link(afc_client_t client, afc_link_type_t linktype, const char *target, const char *linkname) |
1280 | { | 1129 | { |
1281 | char *response = NULL; | 1130 | if (!client || !target || !linkname || !client->afc_packet || !client->parent) |
1282 | char *send = (char *) malloc(sizeof(char) * (strlen(target)+1 + strlen(linkname)+1 + 8)); | 1131 | return AFC_E_INVALID_ARG; |
1132 | |||
1283 | uint32_t bytes = 0; | 1133 | uint32_t bytes = 0; |
1284 | uint64_t type = GUINT64_TO_LE(linktype); | ||
1285 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1134 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1286 | 1135 | ||
1287 | if (!client || !target || !linkname || !client->afc_packet || !client->connection) | 1136 | size_t target_len = strlen(target); |
1288 | return AFC_E_INVALID_ARG; | 1137 | size_t link_len = strlen(linkname); |
1289 | 1138 | ||
1290 | afc_lock(client); | 1139 | afc_lock(client); |
1291 | 1140 | ||
1292 | debug_info("link type: %lld", type); | 1141 | uint32_t data_len = 8 + target_len + 1 + link_len + 1; |
1293 | debug_info("target: %s, length:%d", target, strlen(target)); | 1142 | if (_afc_check_packet_buffer(client, data_len) < 0) { |
1294 | debug_info("linkname: %s, length:%d", linkname, strlen(linkname)); | 1143 | afc_unlock(client); |
1144 | debug_info("Failed to realloc packet buffer"); | ||
1145 | return AFC_E_NO_MEM; | ||
1146 | } | ||
1147 | |||
1148 | debug_info("link type: %lld", htole64(linktype)); | ||
1149 | debug_info("target: %s, length:%d", target, target_len); | ||
1150 | debug_info("linkname: %s, length:%d", linkname, link_len); | ||
1295 | 1151 | ||
1296 | /* Send command */ | 1152 | /* Send command */ |
1297 | memcpy(send, &type, 8); | 1153 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(linktype); |
1298 | memcpy(send + 8, target, strlen(target) + 1); | 1154 | memcpy(AFC_PACKET_DATA_PTR + 8, target, target_len + 1); |
1299 | memcpy(send + 8 + strlen(target) + 1, linkname, strlen(linkname) + 1); | 1155 | memcpy(AFC_PACKET_DATA_PTR + 8 + target_len + 1, linkname, link_len + 1); |
1300 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | 1156 | ret = afc_dispatch_packet(client, AFC_OP_MAKE_LINK, data_len, NULL, 0, &bytes); |
1301 | client->afc_packet->operation = AFC_OP_MAKE_LINK; | ||
1302 | ret = afc_dispatch_packet(client, send, 8 + strlen(linkname) + 1 + strlen(target) + 1, &bytes); | ||
1303 | free(send); | ||
1304 | if (ret != AFC_E_SUCCESS) { | 1157 | if (ret != AFC_E_SUCCESS) { |
1305 | afc_unlock(client); | 1158 | afc_unlock(client); |
1306 | return AFC_E_NOT_ENOUGH_DATA; | 1159 | return AFC_E_NOT_ENOUGH_DATA; |
1307 | } | 1160 | } |
1308 | /* Receive response */ | 1161 | /* Receive response */ |
1309 | ret = afc_receive_data(client, &response, &bytes); | 1162 | ret = afc_receive_data(client, NULL, &bytes); |
1310 | if (response) | ||
1311 | free(response); | ||
1312 | 1163 | ||
1313 | afc_unlock(client); | 1164 | afc_unlock(client); |
1314 | 1165 | ||
1315 | return ret; | 1166 | return ret; |
1316 | } | 1167 | } |
1317 | 1168 | ||
1318 | /** | ||
1319 | * Sets the modification time of a file on the phone. | ||
1320 | * | ||
1321 | * @param client The client to use to set the file size. | ||
1322 | * @param path Path of the file for which the modification time should be set. | ||
1323 | * @param mtime The modification time to set in nanoseconds since epoch. | ||
1324 | * | ||
1325 | * @return AFC_E_SUCCESS on success or an AFC_E_* error value. | ||
1326 | */ | ||
1327 | afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime) | 1169 | afc_error_t afc_set_file_time(afc_client_t client, const char *path, uint64_t mtime) |
1328 | { | 1170 | { |
1329 | char *response = NULL; | 1171 | if (!client || !path || !client->afc_packet || !client->parent) |
1330 | char *send = (char *) malloc(sizeof(char) * (strlen(path) + 1 + 8)); | 1172 | return AFC_E_INVALID_ARG; |
1173 | |||
1331 | uint32_t bytes = 0; | 1174 | uint32_t bytes = 0; |
1332 | uint64_t mtime_loc = GUINT64_TO_LE(mtime); | ||
1333 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | 1175 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; |
1334 | 1176 | ||
1335 | if (!client || !path || !client->afc_packet || !client->connection) | 1177 | afc_lock(client); |
1178 | |||
1179 | uint32_t data_len = 8 + strlen(path) + 1; | ||
1180 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
1181 | afc_unlock(client); | ||
1182 | debug_info("Failed to realloc packet buffer"); | ||
1183 | return AFC_E_NO_MEM; | ||
1184 | } | ||
1185 | |||
1186 | /* Send command */ | ||
1187 | *(uint64_t*)(AFC_PACKET_DATA_PTR) = htole64(mtime); | ||
1188 | memcpy(AFC_PACKET_DATA_PTR + 8, path, data_len-8); | ||
1189 | ret = afc_dispatch_packet(client, AFC_OP_SET_FILE_MOD_TIME, data_len, NULL, 0, &bytes); | ||
1190 | if (ret != AFC_E_SUCCESS) { | ||
1191 | afc_unlock(client); | ||
1192 | return AFC_E_NOT_ENOUGH_DATA; | ||
1193 | } | ||
1194 | /* Receive response */ | ||
1195 | ret = afc_receive_data(client, NULL, &bytes); | ||
1196 | |||
1197 | afc_unlock(client); | ||
1198 | |||
1199 | return ret; | ||
1200 | } | ||
1201 | |||
1202 | afc_error_t afc_remove_path_and_contents(afc_client_t client, const char *path) | ||
1203 | { | ||
1204 | uint32_t bytes = 0; | ||
1205 | afc_error_t ret = AFC_E_UNKNOWN_ERROR; | ||
1206 | |||
1207 | if (!client || !path || !client->afc_packet || !client->parent) | ||
1336 | return AFC_E_INVALID_ARG; | 1208 | return AFC_E_INVALID_ARG; |
1337 | 1209 | ||
1338 | afc_lock(client); | 1210 | afc_lock(client); |
1339 | 1211 | ||
1212 | uint32_t data_len = strlen(path) + 1; | ||
1213 | if (_afc_check_packet_buffer(client, data_len) < 0) { | ||
1214 | afc_unlock(client); | ||
1215 | debug_info("Failed to realloc packet buffer"); | ||
1216 | return AFC_E_NO_MEM; | ||
1217 | } | ||
1218 | |||
1340 | /* Send command */ | 1219 | /* Send command */ |
1341 | memcpy(send, &mtime_loc, 8); | 1220 | memcpy(AFC_PACKET_DATA_PTR, path, data_len); |
1342 | memcpy(send + 8, path, strlen(path) + 1); | 1221 | ret = afc_dispatch_packet(client, AFC_OP_REMOVE_PATH_AND_CONTENTS, data_len, NULL, 0, &bytes); |
1343 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
1344 | client->afc_packet->operation = AFC_OP_SET_FILE_TIME; | ||
1345 | ret = afc_dispatch_packet(client, send, 8 + strlen(path) + 1, &bytes); | ||
1346 | free(send); | ||
1347 | if (ret != AFC_E_SUCCESS) { | 1222 | if (ret != AFC_E_SUCCESS) { |
1348 | afc_unlock(client); | 1223 | afc_unlock(client); |
1349 | return AFC_E_NOT_ENOUGH_DATA; | 1224 | return AFC_E_NOT_ENOUGH_DATA; |
1350 | } | 1225 | } |
1351 | /* Receive response */ | 1226 | /* Receive response */ |
1352 | ret = afc_receive_data(client, &response, &bytes); | 1227 | ret = afc_receive_data(client, NULL, &bytes); |
1353 | if (response) | ||
1354 | free(response); | ||
1355 | 1228 | ||
1356 | afc_unlock(client); | 1229 | afc_unlock(client); |
1357 | 1230 | ||
1358 | return ret; | 1231 | return ret; |
1359 | } | 1232 | } |
1360 | 1233 | ||
1234 | afc_error_t afc_dictionary_free(char **dictionary) | ||
1235 | { | ||
1236 | int i = 0; | ||
1237 | |||
1238 | if (!dictionary) | ||
1239 | return AFC_E_INVALID_ARG; | ||
1240 | |||
1241 | for (i = 0; dictionary[i]; i++) { | ||
1242 | free(dictionary[i]); | ||
1243 | } | ||
1244 | free(dictionary); | ||
1245 | |||
1246 | return AFC_E_SUCCESS; | ||
1247 | } | ||
1248 | |||
1249 | const char* afc_strerror(afc_error_t err) | ||
1250 | { | ||
1251 | switch (err) { | ||
1252 | case AFC_E_SUCCESS: | ||
1253 | return "Success"; | ||
1254 | case AFC_E_UNKNOWN_ERROR: | ||
1255 | return "Unknown Error"; | ||
1256 | case AFC_E_OP_HEADER_INVALID: | ||
1257 | return "Operation header invalid"; | ||
1258 | case AFC_E_NO_RESOURCES: | ||
1259 | return "No resources"; | ||
1260 | case AFC_E_READ_ERROR: | ||
1261 | return "Read error"; | ||
1262 | case AFC_E_WRITE_ERROR: | ||
1263 | return "Write error"; | ||
1264 | case AFC_E_UNKNOWN_PACKET_TYPE: | ||
1265 | return "Unknown packet type"; | ||
1266 | case AFC_E_INVALID_ARG: | ||
1267 | return "Invalid argument"; | ||
1268 | case AFC_E_OBJECT_NOT_FOUND: | ||
1269 | return "Not found"; | ||
1270 | case AFC_E_OBJECT_IS_DIR: | ||
1271 | return "Object is a directory"; | ||
1272 | case AFC_E_PERM_DENIED: | ||
1273 | return "Permission denied"; | ||
1274 | case AFC_E_SERVICE_NOT_CONNECTED: | ||
1275 | return "Service not connected"; | ||
1276 | case AFC_E_OP_TIMEOUT: | ||
1277 | return "Timeout"; | ||
1278 | case AFC_E_TOO_MUCH_DATA: | ||
1279 | return "Too much data"; | ||
1280 | case AFC_E_END_OF_DATA: | ||
1281 | return "End of data"; | ||
1282 | case AFC_E_OP_NOT_SUPPORTED: | ||
1283 | return "Operation not supported"; | ||
1284 | case AFC_E_OBJECT_EXISTS: | ||
1285 | return "Object exists"; | ||
1286 | case AFC_E_OBJECT_BUSY: | ||
1287 | return "Object busy"; | ||
1288 | case AFC_E_NO_SPACE_LEFT: | ||
1289 | return "No space left on device"; | ||
1290 | case AFC_E_OP_WOULD_BLOCK: | ||
1291 | return "Operation would block"; | ||
1292 | case AFC_E_IO_ERROR: | ||
1293 | return "I/O error"; | ||
1294 | case AFC_E_OP_INTERRUPTED: | ||
1295 | return "Operation interrupted"; | ||
1296 | case AFC_E_OP_IN_PROGRESS: | ||
1297 | return "Operation on progress"; | ||
1298 | case AFC_E_INTERNAL_ERROR: | ||
1299 | return "Internal error"; | ||
1300 | case AFC_E_MUX_ERROR: | ||
1301 | return "MUX error"; | ||
1302 | case AFC_E_NO_MEM: | ||
1303 | return "Out of memory"; | ||
1304 | case AFC_E_NOT_ENOUGH_DATA: | ||
1305 | return "Not enough data"; | ||
1306 | case AFC_E_DIR_NOT_EMPTY: | ||
1307 | return "Directory not empty"; | ||
1308 | case AFC_E_FORCE_SIGNED_TYPE: | ||
1309 | return "Force signed type"; | ||
1310 | default: | ||
1311 | break; | ||
1312 | } | ||
1313 | return "Unknown Error"; | ||
1314 | } | ||
@@ -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..06068c6 100644 --- a/src/house_arrest.c +++ b/src/house_arrest.c | |||
@@ -8,26 +8,33 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | |||
28 | #ifndef _MSC_VER | ||
24 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #endif | ||
31 | |||
25 | #include <plist/plist.h> | 32 | #include <plist/plist.h> |
26 | 33 | ||
27 | #include "house_arrest.h" | 34 | #include "house_arrest.h" |
28 | #include "property_list_service.h" | 35 | #include "property_list_service.h" |
29 | #include "afc.h" | 36 | #include "afc.h" |
30 | #include "debug.h" | 37 | #include "common/debug.h" |
31 | 38 | ||
32 | /** | 39 | /** |
33 | * Convert a property_list_service_error_t value to a house_arrest_error_t | 40 | * Convert a property_list_service_error_t value to a house_arrest_error_t |
@@ -40,39 +47,25 @@ | |||
40 | */ | 47 | */ |
41 | static house_arrest_error_t house_arrest_error(property_list_service_error_t err) | 48 | static house_arrest_error_t house_arrest_error(property_list_service_error_t err) |
42 | { | 49 | { |
43 | switch (err) { | 50 | switch (err) { |
44 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | 51 | case PROPERTY_LIST_SERVICE_E_SUCCESS: |
45 | return HOUSE_ARREST_E_SUCCESS; | 52 | return HOUSE_ARREST_E_SUCCESS; |
46 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | 53 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: |
47 | return HOUSE_ARREST_E_INVALID_ARG; | 54 | return HOUSE_ARREST_E_INVALID_ARG; |
48 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | 55 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: |
49 | return HOUSE_ARREST_E_PLIST_ERROR; | 56 | return HOUSE_ARREST_E_PLIST_ERROR; |
50 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | 57 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: |
51 | return HOUSE_ARREST_E_CONN_FAILED; | 58 | return HOUSE_ARREST_E_CONN_FAILED; |
52 | default: | 59 | default: |
53 | break; | 60 | break; |
54 | } | 61 | } |
55 | return HOUSE_ARREST_E_UNKNOWN_ERROR; | 62 | return HOUSE_ARREST_E_UNKNOWN_ERROR; |
56 | } | 63 | } |
57 | 64 | ||
58 | /** | 65 | 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 | { | 66 | { |
71 | if (!device) | ||
72 | return HOUSE_ARREST_E_INVALID_ARG; | ||
73 | |||
74 | property_list_service_client_t plistclient = NULL; | 67 | property_list_service_client_t plistclient = NULL; |
75 | house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, port, &plistclient)); | 68 | house_arrest_error_t err = house_arrest_error(property_list_service_client_new(device, service, &plistclient)); |
76 | if (err != HOUSE_ARREST_E_SUCCESS) { | 69 | if (err != HOUSE_ARREST_E_SUCCESS) { |
77 | return err; | 70 | return err; |
78 | } | 71 | } |
@@ -85,27 +78,20 @@ house_arrest_error_t house_arrest_client_new(idevice_t device, uint16_t port, ho | |||
85 | return HOUSE_ARREST_E_SUCCESS; | 78 | return HOUSE_ARREST_E_SUCCESS; |
86 | } | 79 | } |
87 | 80 | ||
88 | /** | 81 | 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 | 82 | { |
90 | * house_arrest client data. | 83 | house_arrest_error_t err = HOUSE_ARREST_E_UNKNOWN_ERROR; |
91 | * | 84 | service_client_factory_start_service(device, HOUSE_ARREST_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(house_arrest_client_new), &err); |
92 | * @note After using afc_client_new_from_house_arrest_client(), make sure | 85 | return err; |
93 | * you call afc_client_free() before calling this function to ensure | 86 | } |
94 | * a proper cleanup. Do not call this function if you still need to | 87 | |
95 | * perform AFC operations since it will close the connection. | ||
96 | * | ||
97 | * @param client The house_arrest client to disconnect and free. | ||
98 | * | ||
99 | * @return HOUSE_ARREST_E_SUCCESS on success, HOUSE_ARREST_E_INVALID_ARG when | ||
100 | * client is NULL, or an HOUSE_ARREST_E_* error code otherwise. | ||
101 | */ | ||
102 | house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) | 88 | house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) |
103 | { | 89 | { |
104 | if (!client) | 90 | if (!client) |
105 | return HOUSE_ARREST_E_INVALID_ARG; | 91 | return HOUSE_ARREST_E_INVALID_ARG; |
106 | 92 | ||
107 | house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS; | 93 | house_arrest_error_t err = HOUSE_ARREST_E_SUCCESS; |
108 | if (client->parent && client->parent->connection) { | 94 | if (client->parent && client->parent->parent->connection) { |
109 | house_arrest_error(property_list_service_client_free(client->parent)); | 95 | house_arrest_error(property_list_service_client_free(client->parent)); |
110 | } | 96 | } |
111 | client->parent = NULL; | 97 | client->parent = NULL; |
@@ -114,70 +100,34 @@ house_arrest_error_t house_arrest_client_free(house_arrest_client_t client) | |||
114 | return err; | 100 | return err; |
115 | } | 101 | } |
116 | 102 | ||
117 | /** | ||
118 | * Sends a generic request to the connected house_arrest service. | ||
119 | * | ||
120 | * @param client The house_arrest client to use. | ||
121 | * @param dict The request to send as a plist of type PLIST_DICT. | ||
122 | * | ||
123 | * @note If this function returns HOUSE_ARREST_E_SUCCESS it does not mean | ||
124 | * that the request was successful. To check for success or failure you | ||
125 | * need to call house_arrest_get_result(). | ||
126 | * @see house_arrest_get_result | ||
127 | * | ||
128 | * @return HOUSE_ARREST_E_SUCCESS if the request was successfully sent, | ||
129 | * HOUSE_ARREST_E_INVALID_ARG if client or dict is invalid, | ||
130 | * HOUSE_ARREST_E_PLIST_ERROR if dict is not a plist of type PLIST_DICT, | ||
131 | * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode, | ||
132 | * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured. | ||
133 | */ | ||
134 | house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict) | 103 | house_arrest_error_t house_arrest_send_request(house_arrest_client_t client, plist_t dict) |
135 | { | 104 | { |
136 | if (!client || !client->parent || !dict) | 105 | if (!client || !client->parent || !dict) |
137 | return HOUSE_ARREST_E_INVALID_ARG; | 106 | return HOUSE_ARREST_E_INVALID_ARG; |
138 | if (plist_get_node_type(dict) != PLIST_DICT) | 107 | if (plist_get_node_type(dict) != PLIST_DICT) |
139 | return HOUSE_ARREST_E_PLIST_ERROR; | 108 | return HOUSE_ARREST_E_PLIST_ERROR; |
140 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) | 109 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) |
141 | return HOUSE_ARREST_E_INVALID_MODE; | 110 | return HOUSE_ARREST_E_INVALID_MODE; |
142 | 111 | ||
143 | house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict)); | 112 | house_arrest_error_t res = house_arrest_error(property_list_service_send_xml_plist(client->parent, dict)); |
144 | if (res != HOUSE_ARREST_E_SUCCESS) { | 113 | if (res != HOUSE_ARREST_E_SUCCESS) { |
145 | debug_info("could not send plist, error %d", res); | 114 | debug_info("could not send plist, error %d", res); |
146 | } | 115 | } |
147 | return res; | 116 | return res; |
148 | } | 117 | } |
149 | 118 | ||
150 | /** | ||
151 | * Send a command to the connected house_arrest service. | ||
152 | * Calls house_arrest_send_request() internally. | ||
153 | * | ||
154 | * @param client The house_arrest client to use. | ||
155 | * @param command The command to send. Currently, only VendContainer and | ||
156 | * VendDocuments are known. | ||
157 | * @param appid The application identifier to pass along with the . | ||
158 | * | ||
159 | * @note If this function returns HOUSE_ARREST_E_SUCCESS it does not mean | ||
160 | * that the command was successful. To check for success or failure you | ||
161 | * need to call house_arrest_get_result(). | ||
162 | * @see house_arrest_get_result | ||
163 | * | ||
164 | * @return HOUSE_ARREST_E_SUCCESS if the command was successfully sent, | ||
165 | * HOUSE_ARREST_E_INVALID_ARG if client, command, or appid is invalid, | ||
166 | * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode, | ||
167 | * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured. | ||
168 | */ | ||
169 | house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid) | 119 | house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, const char *command, const char *appid) |
170 | { | 120 | { |
171 | if (!client || !client->parent || !command || !appid) | 121 | if (!client || !client->parent || !command || !appid) |
172 | return HOUSE_ARREST_E_INVALID_ARG; | 122 | return HOUSE_ARREST_E_INVALID_ARG; |
173 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) | 123 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) |
174 | return HOUSE_ARREST_E_INVALID_MODE; | 124 | return HOUSE_ARREST_E_INVALID_MODE; |
175 | 125 | ||
176 | house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR; | 126 | house_arrest_error_t res = HOUSE_ARREST_E_UNKNOWN_ERROR; |
177 | 127 | ||
178 | plist_t dict = plist_new_dict(); | 128 | plist_t dict = plist_new_dict(); |
179 | plist_dict_insert_item(dict, "Command", plist_new_string(command)); | 129 | plist_dict_set_item(dict, "Command", plist_new_string(command)); |
180 | plist_dict_insert_item(dict, "Identifier", plist_new_string(appid)); | 130 | plist_dict_set_item(dict, "Identifier", plist_new_string(appid)); |
181 | 131 | ||
182 | res = house_arrest_send_request(client, dict); | 132 | res = house_arrest_send_request(client, dict); |
183 | 133 | ||
@@ -186,63 +136,30 @@ house_arrest_error_t house_arrest_send_command(house_arrest_client_t client, con | |||
186 | return res; | 136 | return res; |
187 | } | 137 | } |
188 | 138 | ||
189 | /** | ||
190 | * Retrieves the result of a previously sent house_arrest_request_* request. | ||
191 | * | ||
192 | * @param client The house_arrest client to use | ||
193 | * @param dict Pointer that will be set to a plist containing the result to | ||
194 | * the last performed operation. It holds a key 'Status' with the value | ||
195 | * 'Complete' on success or a key 'Error' with an error description as | ||
196 | * value. The caller is responsible for freeing the returned plist. | ||
197 | * | ||
198 | * @return HOUSE_ARREST_E_SUCCESS if a result plist was retrieved, | ||
199 | * HOUSE_ARREST_E_INVALID_ARG if client is invalid, | ||
200 | * HOUSE_ARREST_E_INVALID_MODE if the client is not in the correct mode, | ||
201 | * or HOUSE_ARREST_E_CONN_FAILED if a connection error occured. | ||
202 | */ | ||
203 | house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict) | 139 | house_arrest_error_t house_arrest_get_result(house_arrest_client_t client, plist_t *dict) |
204 | { | 140 | { |
205 | if (!client || !client->parent) | 141 | if (!client || !client->parent) |
206 | return HOUSE_ARREST_E_INVALID_ARG; | 142 | return HOUSE_ARREST_E_INVALID_ARG; |
207 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) | 143 | if (client->mode != HOUSE_ARREST_CLIENT_MODE_NORMAL) |
208 | return HOUSE_ARREST_E_INVALID_MODE; | 144 | return HOUSE_ARREST_E_INVALID_MODE; |
209 | 145 | ||
210 | house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict)); | 146 | house_arrest_error_t res = house_arrest_error(property_list_service_receive_plist(client->parent, dict)); |
211 | if (res != HOUSE_ARREST_E_SUCCESS) { | 147 | if (res != HOUSE_ARREST_E_SUCCESS) { |
212 | debug_info("could not get result, error %d", res); | 148 | debug_info("could not get result, error %d", res); |
213 | if (*dict) { | 149 | if (*dict) { |
214 | plist_free(*dict); | 150 | plist_free(*dict); |
215 | *dict = NULL; | 151 | *dict = NULL; |
216 | } | 152 | } |
217 | } | 153 | } |
218 | return res; | 154 | return res; |
219 | } | 155 | } |
220 | 156 | ||
221 | /** | ||
222 | * Creates an AFC client using the given house_arrest client's connection | ||
223 | * allowing file access to a specific application directory requested by | ||
224 | * functions like house_arrest_request_vendor_documents(). | ||
225 | * | ||
226 | * @param client The house_arrest client to use. | ||
227 | * @param afc_client Pointer that will be set to a newly allocated afc_client_t | ||
228 | * upon successful return. | ||
229 | * | ||
230 | * @note After calling this function the house_arrest client will go in an | ||
231 | * AFC mode that will only allow calling house_arrest_client_free(). | ||
232 | * Only call house_arrest_client_free() if all AFC operations have | ||
233 | * completed since it will close the connection. | ||
234 | * | ||
235 | * @return AFC_E_SUCCESS if the afc client was successfully created, | ||
236 | * AFC_E_INVALID_ARG if client is invalid or was already used to create | ||
237 | * an afc client, or an AFC_E_* error code returned by | ||
238 | * afc_client_new_from_connection(). | ||
239 | */ | ||
240 | afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client) | 157 | afc_error_t afc_client_new_from_house_arrest_client(house_arrest_client_t client, afc_client_t *afc_client) |
241 | { | 158 | { |
242 | if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { | 159 | if (!client || !client->parent || (client->mode == HOUSE_ARREST_CLIENT_MODE_AFC)) { |
243 | return AFC_E_INVALID_ARG; | 160 | return AFC_E_INVALID_ARG; |
244 | } | 161 | } |
245 | afc_error_t err = afc_client_new_from_connection(client->parent->connection, afc_client); | 162 | afc_error_t err = afc_client_new_with_service_client(client->parent->parent, afc_client); |
246 | if (err == AFC_E_SUCCESS) { | 163 | if (err == AFC_E_SUCCESS) { |
247 | client->mode = HOUSE_ARREST_CLIENT_MODE_AFC; | 164 | client->mode = HOUSE_ARREST_CLIENT_MODE_AFC; |
248 | } | 165 | } |
diff --git a/src/house_arrest.h b/src/house_arrest.h index 6d13a88..5612a29 100644 --- a/src/house_arrest.h +++ b/src/house_arrest.h | |||
@@ -8,21 +8,21 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef IHOUSE_ARREST_H | ||
22 | #define IHOUSE_ARREST_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __HOUSE_ARREST_H |
23 | #define __HOUSE_ARREST_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/house_arrest.h" | 26 | #include "libimobiledevice/house_arrest.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | 28 | ||
diff --git a/src/idevice.c b/src/idevice.c index 5a9d49b..0af27fd 100644 --- a/src/idevice.c +++ b/src/idevice.c | |||
@@ -1,98 +1,377 @@ | |||
1 | /* | 1 | /* |
2 | * idevice.c | 2 | * idevice.c |
3 | * Device discovery and communication interface. | 3 | * Device discovery and communication interface. |
4 | * | 4 | * |
5 | * Copyright (c) 2009-2021 Nikias Bassen. All Rights Reserved. | ||
6 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 7 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
6 | * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. | ||
7 | * | 8 | * |
8 | * This library is free software; you can redistribute it and/or | 9 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public | 10 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either | 11 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. | 12 | * version 2.1 of the License, or (at your option) any later version. |
12 | * | 13 | * |
13 | * This library is distributed in the hope that it will be useful, | 14 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. | 17 | * Lesser General Public License for more details. |
17 | * | 18 | * |
18 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, write to the Free Software | 20 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ | 22 | */ |
22 | 23 | ||
24 | #ifdef HAVE_CONFIG_H | ||
25 | #include <config.h> | ||
26 | #endif | ||
27 | |||
23 | #include <stdlib.h> | 28 | #include <stdlib.h> |
24 | #include <string.h> | 29 | #include <string.h> |
25 | #include <errno.h> | 30 | #include <errno.h> |
31 | #include <time.h> | ||
32 | |||
33 | #ifdef _WIN32 | ||
34 | #include <winsock2.h> | ||
35 | #include <ws2tcpip.h> | ||
36 | #include <windows.h> | ||
37 | #else | ||
38 | #include <sys/socket.h> | ||
39 | #include <netinet/in.h> | ||
40 | #endif | ||
26 | 41 | ||
27 | #include <usbmuxd.h> | 42 | #include <usbmuxd.h> |
43 | |||
44 | #if defined(HAVE_OPENSSL) | ||
45 | #include <openssl/err.h> | ||
46 | #include <openssl/rsa.h> | ||
47 | #include <openssl/ssl.h> | ||
48 | #elif defined(HAVE_GNUTLS) | ||
28 | #include <gnutls/gnutls.h> | 49 | #include <gnutls/gnutls.h> |
50 | #elif defined(HAVE_MBEDTLS) | ||
51 | #include <mbedtls/rsa.h> | ||
52 | #include <mbedtls/ssl.h> | ||
53 | #include <mbedtls/entropy.h> | ||
54 | #include <mbedtls/ctr_drbg.h> | ||
55 | #include <mbedtls/debug.h> | ||
56 | #else | ||
57 | #error No supported TLS/SSL library enabled | ||
58 | #endif | ||
59 | |||
60 | #include <libimobiledevice-glue/socket.h> | ||
61 | #include <libimobiledevice-glue/thread.h> | ||
62 | |||
29 | #include "idevice.h" | 63 | #include "idevice.h" |
30 | #include "userpref.h" | 64 | #include "lockdown.h" |
31 | #include "debug.h" | 65 | #include "common/userpref.h" |
66 | #include "common/debug.h" | ||
67 | |||
68 | #ifndef ECONNREFUSED | ||
69 | #define ECONNREFUSED 107 | ||
70 | #endif | ||
71 | #ifndef ETIMEDOUT | ||
72 | #define ETIMEDOUT 138 | ||
73 | #endif | ||
74 | |||
75 | |||
76 | #ifdef HAVE_OPENSSL | ||
77 | |||
78 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || \ | ||
79 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20020000L)) | ||
80 | #define TLS_method TLSv1_method | ||
81 | #endif | ||
82 | |||
83 | #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) | ||
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 | // Reference: https://stackoverflow.com/a/2390626/1806760 | ||
128 | // Initializer/finalizer sample for MSVC and GCC/Clang. | ||
129 | // 2010-2016 Joe Lowe. Released into the public domain. | ||
130 | |||
131 | #ifdef __cplusplus | ||
132 | #define INITIALIZER(f) \ | ||
133 | static void f(void); \ | ||
134 | struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ | ||
135 | static void f(void) | ||
136 | #elif defined(_MSC_VER) | ||
137 | #pragma section(".CRT$XCU",read) | ||
138 | #define INITIALIZER2_(f,p) \ | ||
139 | static void f(void); \ | ||
140 | __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ | ||
141 | __pragma(comment(linker,"/include:" p #f "_")) \ | ||
142 | static void f(void) | ||
143 | #ifdef _WIN64 | ||
144 | #define INITIALIZER(f) INITIALIZER2_(f,"") | ||
145 | #else | ||
146 | #define INITIALIZER(f) INITIALIZER2_(f,"_") | ||
147 | #endif | ||
148 | #else | ||
149 | #define INITIALIZER(f) \ | ||
150 | static void f(void) __attribute__((__constructor__)); \ | ||
151 | static void f(void) | ||
152 | #endif | ||
153 | |||
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 | INITIALIZER(internal_idevice_init) | ||
185 | { | ||
186 | #if defined(HAVE_OPENSSL) | ||
187 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
188 | int i; | ||
189 | SSL_library_init(); | ||
190 | |||
191 | mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); | ||
192 | if (!mutex_buf) | ||
193 | return; | ||
194 | for (i = 0; i < CRYPTO_num_locks(); i++) | ||
195 | mutex_init(&mutex_buf[i]); | ||
196 | |||
197 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
198 | CRYPTO_set_id_callback(id_function); | ||
199 | #else | ||
200 | CRYPTO_THREADID_set_callback(id_function); | ||
201 | #endif | ||
202 | CRYPTO_set_locking_callback(locking_function); | ||
203 | #endif | ||
204 | #elif defined(HAVE_GNUTLS) | ||
205 | gnutls_global_init(); | ||
206 | #elif defined(HAVE_MBEDTLS) | ||
207 | // NO-OP | ||
208 | #endif | ||
209 | atexit(internal_idevice_deinit); | ||
210 | } | ||
211 | |||
212 | const char* libimobiledevice_version() | ||
213 | { | ||
214 | #ifndef PACKAGE_VERSION | ||
215 | #error PACKAGE_VERSION is not defined! | ||
216 | #endif | ||
217 | return PACKAGE_VERSION; | ||
218 | } | ||
219 | |||
220 | struct idevice_subscription_context { | ||
221 | idevice_event_cb_t callback; | ||
222 | void *user_data; | ||
223 | usbmuxd_subscription_context_t ctx; | ||
224 | }; | ||
32 | 225 | ||
33 | static idevice_event_cb_t event_cb = NULL; | 226 | static idevice_subscription_context_t event_ctx = NULL; |
34 | 227 | ||
35 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) | 228 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) |
36 | { | 229 | { |
230 | idevice_subscription_context_t context = (idevice_subscription_context_t)user_data; | ||
37 | idevice_event_t ev; | 231 | idevice_event_t ev; |
38 | 232 | ||
39 | ev.event = event->event; | 233 | ev.event = event->event; |
40 | ev.uuid = event->device.uuid; | 234 | ev.udid = event->device.udid; |
41 | ev.conn_type = CONNECTION_USBMUXD; | 235 | ev.conn_type = 0; |
236 | if (event->device.conn_type == CONNECTION_TYPE_USB) { | ||
237 | ev.conn_type = CONNECTION_USBMUXD; | ||
238 | } else if (event->device.conn_type == CONNECTION_TYPE_NETWORK) { | ||
239 | ev.conn_type = CONNECTION_NETWORK; | ||
240 | } else { | ||
241 | debug_info("Unknown connection type %d", event->device.conn_type); | ||
242 | } | ||
42 | 243 | ||
43 | if (event_cb) { | 244 | if (context->callback) { |
44 | event_cb(&ev, user_data); | 245 | context->callback(&ev, context->user_data); |
45 | } | 246 | } |
46 | } | 247 | } |
47 | 248 | ||
48 | /** | 249 | 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 | { | 250 | { |
60 | event_cb = callback; | 251 | if (!context || !callback) { |
61 | int res = usbmuxd_subscribe(usbmux_event_cb, user_data); | 252 | return IDEVICE_E_INVALID_ARG; |
62 | if (res != 0) { | 253 | } |
63 | event_cb = NULL; | 254 | *context = malloc(sizeof(struct idevice_subscription_context)); |
64 | debug_info("Error %d when subscribing usbmux event callback!", res); | 255 | if (!*context) { |
256 | debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__); | ||
257 | return IDEVICE_E_UNKNOWN_ERROR; | ||
258 | } | ||
259 | (*context)->callback = callback; | ||
260 | (*context)->user_data = user_data; | ||
261 | int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context); | ||
262 | if (res != 0) { | ||
263 | free(*context); | ||
264 | *context = NULL; | ||
265 | debug_info("ERROR: usbmuxd_subscribe() returned %d!", res); | ||
65 | return IDEVICE_E_UNKNOWN_ERROR; | 266 | return IDEVICE_E_UNKNOWN_ERROR; |
66 | } | 267 | } |
67 | return IDEVICE_E_SUCCESS; | 268 | return IDEVICE_E_SUCCESS; |
68 | } | 269 | } |
69 | 270 | ||
70 | /** | 271 | 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 | { | 272 | { |
78 | event_cb = NULL; | 273 | if (!context) { |
79 | int res = usbmuxd_unsubscribe(); | 274 | return IDEVICE_E_INVALID_ARG; |
275 | } | ||
276 | int res = usbmuxd_events_unsubscribe(context->ctx); | ||
80 | if (res != 0) { | 277 | if (res != 0) { |
81 | debug_info("Error %d when unsubscribing usbmux event callback!", res); | 278 | debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res); |
82 | return IDEVICE_E_UNKNOWN_ERROR; | 279 | return IDEVICE_E_UNKNOWN_ERROR; |
83 | } | 280 | } |
281 | if (context == event_ctx) { | ||
282 | event_ctx = NULL; | ||
283 | } | ||
284 | free(context); | ||
285 | return IDEVICE_E_SUCCESS; | ||
286 | } | ||
287 | |||
288 | idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) | ||
289 | { | ||
290 | if (event_ctx) { | ||
291 | idevice_events_unsubscribe(event_ctx); | ||
292 | } | ||
293 | return idevice_events_subscribe(&event_ctx, callback, user_data); | ||
294 | } | ||
295 | |||
296 | idevice_error_t idevice_event_unsubscribe(void) | ||
297 | { | ||
298 | if (!event_ctx) { | ||
299 | return IDEVICE_E_SUCCESS; | ||
300 | } | ||
301 | event_ctx->callback = NULL; | ||
302 | return idevice_events_unsubscribe(event_ctx); | ||
303 | } | ||
304 | |||
305 | idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count) | ||
306 | { | ||
307 | usbmuxd_device_info_t *dev_list; | ||
308 | |||
309 | *devices = NULL; | ||
310 | *count = 0; | ||
311 | |||
312 | if (usbmuxd_get_device_list(&dev_list) < 0) { | ||
313 | debug_info("ERROR: usbmuxd is not running!", __func__); | ||
314 | return IDEVICE_E_NO_DEVICE; | ||
315 | } | ||
316 | |||
317 | idevice_info_t *newlist = NULL; | ||
318 | int i, newcount = 0; | ||
319 | |||
320 | for (i = 0; dev_list[i].handle > 0; i++) { | ||
321 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
322 | newlist[newcount] = malloc(sizeof(struct idevice_info)); | ||
323 | newlist[newcount]->udid = strdup(dev_list[i].udid); | ||
324 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { | ||
325 | newlist[newcount]->conn_type = CONNECTION_USBMUXD; | ||
326 | newlist[newcount]->conn_data = NULL; | ||
327 | } else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) { | ||
328 | newlist[newcount]->conn_type = CONNECTION_NETWORK; | ||
329 | struct sockaddr* saddr = (struct sockaddr*)(dev_list[i].conn_data); | ||
330 | size_t addrlen = 0; | ||
331 | switch (saddr->sa_family) { | ||
332 | case AF_INET: | ||
333 | addrlen = sizeof(struct sockaddr_in); | ||
334 | break; | ||
335 | #ifdef AF_INET6 | ||
336 | case AF_INET6: | ||
337 | addrlen = sizeof(struct sockaddr_in6); | ||
338 | break; | ||
339 | #endif | ||
340 | default: | ||
341 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
342 | continue; | ||
343 | } | ||
344 | newlist[newcount]->conn_data = malloc(addrlen); | ||
345 | memcpy(newlist[newcount]->conn_data, dev_list[i].conn_data, addrlen); | ||
346 | } | ||
347 | newcount++; | ||
348 | *devices = newlist; | ||
349 | } | ||
350 | usbmuxd_device_list_free(&dev_list); | ||
351 | |||
352 | *count = newcount; | ||
353 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
354 | newlist[newcount] = NULL; | ||
355 | *devices = newlist; | ||
356 | |||
357 | return IDEVICE_E_SUCCESS; | ||
358 | } | ||
359 | |||
360 | idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices) | ||
361 | { | ||
362 | if (devices) { | ||
363 | int i = 0; | ||
364 | while (devices[i]) { | ||
365 | free(devices[i]->udid); | ||
366 | free(devices[i]->conn_data); | ||
367 | free(devices[i]); | ||
368 | i++; | ||
369 | } | ||
370 | free(devices); | ||
371 | } | ||
84 | return IDEVICE_E_SUCCESS; | 372 | return IDEVICE_E_SUCCESS; |
85 | } | 373 | } |
86 | 374 | ||
87 | /** | ||
88 | * Get a list of currently available devices. | ||
89 | * | ||
90 | * @param devices List of uuids of devices that are currently available. | ||
91 | * This list is terminated by a NULL pointer. | ||
92 | * @param count Number of devices found. | ||
93 | * | ||
94 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
95 | */ | ||
96 | idevice_error_t idevice_get_device_list(char ***devices, int *count) | 375 | idevice_error_t idevice_get_device_list(char ***devices, int *count) |
97 | { | 376 | { |
98 | usbmuxd_device_info_t *dev_list; | 377 | usbmuxd_device_info_t *dev_list; |
@@ -101,7 +380,7 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
101 | *count = 0; | 380 | *count = 0; |
102 | 381 | ||
103 | if (usbmuxd_get_device_list(&dev_list) < 0) { | 382 | if (usbmuxd_get_device_list(&dev_list) < 0) { |
104 | debug_info("ERROR: usbmuxd is not running!\n", __func__); | 383 | debug_info("ERROR: usbmuxd is not running!", __func__); |
105 | return IDEVICE_E_NO_DEVICE; | 384 | return IDEVICE_E_NO_DEVICE; |
106 | } | 385 | } |
107 | 386 | ||
@@ -109,9 +388,11 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
109 | int i, newcount = 0; | 388 | int i, newcount = 0; |
110 | 389 | ||
111 | for (i = 0; dev_list[i].handle > 0; i++) { | 390 | for (i = 0; dev_list[i].handle > 0; i++) { |
112 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | 391 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { |
113 | newlist[newcount++] = strdup(dev_list[i].uuid); | 392 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); |
114 | *devices = newlist; | 393 | newlist[newcount++] = strdup(dev_list[i].udid); |
394 | *devices = newlist; | ||
395 | } | ||
115 | } | 396 | } |
116 | usbmuxd_device_list_free(&dev_list); | 397 | usbmuxd_device_list_free(&dev_list); |
117 | 398 | ||
@@ -123,62 +404,101 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
123 | return IDEVICE_E_SUCCESS; | 404 | return IDEVICE_E_SUCCESS; |
124 | } | 405 | } |
125 | 406 | ||
126 | /** | ||
127 | * Free a list of device uuids. | ||
128 | * | ||
129 | * @param devices List of uuids to free. | ||
130 | * | ||
131 | * @return Always returnes IDEVICE_E_SUCCESS. | ||
132 | */ | ||
133 | idevice_error_t idevice_device_list_free(char **devices) | 407 | idevice_error_t idevice_device_list_free(char **devices) |
134 | { | 408 | { |
135 | if (devices) { | 409 | if (devices) { |
136 | int i = 0; | 410 | int i = 0; |
137 | while (devices[i++]) { | 411 | while (devices[i]) { |
138 | free(devices[i]); | 412 | free(devices[i]); |
413 | i++; | ||
139 | } | 414 | } |
140 | free(devices); | 415 | free(devices); |
141 | } | 416 | } |
142 | return IDEVICE_E_SUCCESS; | 417 | return IDEVICE_E_SUCCESS; |
143 | } | 418 | } |
144 | 419 | ||
145 | /** | 420 | void idevice_set_debug_level(int level) |
146 | * Creates an idevice_t structure for the device specified by uuid, | 421 | { |
147 | * if the device is available. | 422 | internal_set_debug_level(level); |
148 | * | 423 | } |
149 | * @note The resulting idevice_t structure has to be freed with | 424 | |
150 | * idevice_free() if it is no longer used. | 425 | static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev) |
151 | * | 426 | { |
152 | * @param device Upon calling this function, a pointer to a location of type | 427 | if (!muxdev) |
153 | * idevice_t. On successful return, this location will be populated. | 428 | return NULL; |
154 | * @param uuid The UUID to match. | 429 | |
155 | * | 430 | idevice_t device = (idevice_t)malloc(sizeof(struct idevice_private)); |
156 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | 431 | if (!device) |
157 | */ | 432 | return NULL; |
158 | idevice_error_t idevice_new(idevice_t * device, const char *uuid) | 433 | |
434 | device->udid = strdup(muxdev->udid); | ||
435 | device->mux_id = muxdev->handle; | ||
436 | device->version = 0; | ||
437 | device->device_class = 0; | ||
438 | switch (muxdev->conn_type) { | ||
439 | case CONNECTION_TYPE_USB: | ||
440 | device->conn_type = CONNECTION_USBMUXD; | ||
441 | device->conn_data = NULL; | ||
442 | break; | ||
443 | case CONNECTION_TYPE_NETWORK: | ||
444 | device->conn_type = CONNECTION_NETWORK; | ||
445 | struct sockaddr* saddr = (struct sockaddr*)(muxdev->conn_data); | ||
446 | size_t addrlen = 0; | ||
447 | switch (saddr->sa_family) { | ||
448 | case AF_INET: | ||
449 | addrlen = sizeof(struct sockaddr_in); | ||
450 | break; | ||
451 | #ifdef AF_INET6 | ||
452 | case AF_INET6: | ||
453 | addrlen = sizeof(struct sockaddr_in6); | ||
454 | break; | ||
455 | #endif | ||
456 | default: | ||
457 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
458 | free(device->udid); | ||
459 | free(device); | ||
460 | return NULL; | ||
461 | } | ||
462 | device->conn_data = malloc(addrlen); | ||
463 | memcpy(device->conn_data, muxdev->conn_data, addrlen); | ||
464 | break; | ||
465 | default: | ||
466 | device->conn_type = 0; | ||
467 | device->conn_data = NULL; | ||
468 | break; | ||
469 | } | ||
470 | return device; | ||
471 | } | ||
472 | |||
473 | idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options) | ||
159 | { | 474 | { |
160 | usbmuxd_device_info_t muxdev; | 475 | usbmuxd_device_info_t muxdev; |
161 | int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); | 476 | int usbmux_options = 0; |
477 | if (options & IDEVICE_LOOKUP_USBMUX) { | ||
478 | usbmux_options |= DEVICE_LOOKUP_USBMUX; | ||
479 | } | ||
480 | if (options & IDEVICE_LOOKUP_NETWORK) { | ||
481 | usbmux_options |= DEVICE_LOOKUP_NETWORK; | ||
482 | } | ||
483 | if (options & IDEVICE_LOOKUP_PREFER_NETWORK) { | ||
484 | usbmux_options |= DEVICE_LOOKUP_PREFER_NETWORK; | ||
485 | } | ||
486 | int res = usbmuxd_get_device(udid, &muxdev, usbmux_options); | ||
162 | if (res > 0) { | 487 | if (res > 0) { |
163 | idevice_t phone = (idevice_t) malloc(sizeof(struct idevice_private)); | 488 | *device = idevice_from_mux_device(&muxdev); |
164 | phone->uuid = strdup(muxdev.uuid); | 489 | if (!*device) { |
165 | phone->conn_type = CONNECTION_USBMUXD; | 490 | return IDEVICE_E_UNKNOWN_ERROR; |
166 | phone->conn_data = (void*)(long)muxdev.handle; | 491 | } |
167 | *device = phone; | ||
168 | return IDEVICE_E_SUCCESS; | 492 | return IDEVICE_E_SUCCESS; |
169 | } | 493 | } |
170 | /* other connection types could follow here */ | ||
171 | |||
172 | return IDEVICE_E_NO_DEVICE; | 494 | return IDEVICE_E_NO_DEVICE; |
173 | } | 495 | } |
174 | 496 | ||
175 | /** | 497 | idevice_error_t idevice_new(idevice_t * device, const char *udid) |
176 | * Cleans up an idevice structure, then frees the structure itself. | 498 | { |
177 | * This is a library-level function; deals directly with the device to tear | 499 | return idevice_new_with_options(device, udid, 0); |
178 | * down relations, but otherwise is mostly internal. | 500 | } |
179 | * | 501 | |
180 | * @param device idevice_t to free. | ||
181 | */ | ||
182 | idevice_error_t idevice_free(idevice_t device) | 502 | idevice_error_t idevice_free(idevice_t device) |
183 | { | 503 | { |
184 | if (!device) | 504 | if (!device) |
@@ -187,11 +507,8 @@ idevice_error_t idevice_free(idevice_t device) | |||
187 | 507 | ||
188 | ret = IDEVICE_E_SUCCESS; | 508 | ret = IDEVICE_E_SUCCESS; |
189 | 509 | ||
190 | free(device->uuid); | 510 | free(device->udid); |
191 | 511 | ||
192 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
193 | device->conn_data = 0; | ||
194 | } | ||
195 | if (device->conn_data) { | 512 | if (device->conn_data) { |
196 | free(device->conn_data); | 513 | free(device->conn_data); |
197 | } | 514 | } |
@@ -199,16 +516,6 @@ idevice_error_t idevice_free(idevice_t device) | |||
199 | return ret; | 516 | return ret; |
200 | } | 517 | } |
201 | 518 | ||
202 | /** | ||
203 | * Set up a connection to the given device. | ||
204 | * | ||
205 | * @param device The device to connect to. | ||
206 | * @param port The destination port to connect to. | ||
207 | * @param connection Pointer to an idevice_connection_t that will be filled | ||
208 | * with the necessary data of the connection. | ||
209 | * | ||
210 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
211 | */ | ||
212 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) | 519 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) |
213 | { | 520 | { |
214 | if (!device) { | 521 | if (!device) { |
@@ -216,31 +523,80 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect | |||
216 | } | 523 | } |
217 | 524 | ||
218 | if (device->conn_type == CONNECTION_USBMUXD) { | 525 | if (device->conn_type == CONNECTION_USBMUXD) { |
219 | int sfd = usbmuxd_connect((uint32_t)(long)device->conn_data, port); | 526 | int sfd = usbmuxd_connect(device->mux_id, port); |
220 | if (sfd < 0) { | 527 | if (sfd < 0) { |
221 | debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); | 528 | debug_info("ERROR: Connecting to usbmux device failed: %d (%s)", sfd, strerror(-sfd)); |
529 | switch (-sfd) { | ||
530 | case ECONNREFUSED: | ||
531 | return IDEVICE_E_CONNREFUSED; | ||
532 | case ENODEV: | ||
533 | return IDEVICE_E_NO_DEVICE; | ||
534 | default: | ||
535 | break; | ||
536 | } | ||
222 | return IDEVICE_E_UNKNOWN_ERROR; | 537 | return IDEVICE_E_UNKNOWN_ERROR; |
223 | } | 538 | } |
224 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | 539 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); |
225 | new_connection->type = CONNECTION_USBMUXD; | 540 | new_connection->type = CONNECTION_USBMUXD; |
226 | new_connection->data = (void*)(long)sfd; | 541 | new_connection->data = (void*)(uintptr_t)sfd; |
227 | new_connection->ssl_data = NULL; | 542 | new_connection->ssl_data = NULL; |
543 | new_connection->device = device; | ||
544 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
545 | new_connection->status = IDEVICE_E_SUCCESS; | ||
228 | *connection = new_connection; | 546 | *connection = new_connection; |
229 | return IDEVICE_E_SUCCESS; | 547 | return IDEVICE_E_SUCCESS; |
230 | } else { | 548 | } |
231 | debug_info("Unknown connection type %d", device->conn_type); | 549 | if (device->conn_type == CONNECTION_NETWORK) { |
550 | struct sockaddr* saddr = (struct sockaddr*)(device->conn_data); | ||
551 | switch (saddr->sa_family) { | ||
552 | case AF_INET: | ||
553 | #ifdef AF_INET6 | ||
554 | case AF_INET6: | ||
555 | #endif | ||
556 | break; | ||
557 | default: | ||
558 | debug_info("Unsupported address family 0x%02x", saddr->sa_family); | ||
559 | return IDEVICE_E_UNKNOWN_ERROR; | ||
560 | } | ||
561 | |||
562 | char addrtxt[48]; | ||
563 | addrtxt[0] = '\0'; | ||
564 | |||
565 | if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) { | ||
566 | debug_info("Failed to convert network address: %d (%s)", errno, strerror(errno)); | ||
567 | } | ||
568 | |||
569 | debug_info("Connecting to %s port %d...", addrtxt, port); | ||
570 | |||
571 | int sfd = socket_connect_addr(saddr, port); | ||
572 | if (sfd < 0) { | ||
573 | int result = errno; | ||
574 | debug_info("ERROR: Connecting to network device failed: %d (%s)", result, strerror(result)); | ||
575 | switch (result) { | ||
576 | case ECONNREFUSED: | ||
577 | return IDEVICE_E_CONNREFUSED; | ||
578 | default: | ||
579 | break; | ||
580 | } | ||
581 | return IDEVICE_E_NO_DEVICE; | ||
582 | } | ||
583 | |||
584 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | ||
585 | new_connection->type = CONNECTION_NETWORK; | ||
586 | new_connection->data = (void*)(uintptr_t)sfd; | ||
587 | new_connection->ssl_data = NULL; | ||
588 | new_connection->device = device; | ||
589 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
590 | |||
591 | *connection = new_connection; | ||
592 | |||
593 | return IDEVICE_E_SUCCESS; | ||
232 | } | 594 | } |
233 | 595 | ||
596 | debug_info("Unknown connection type %d", device->conn_type); | ||
234 | return IDEVICE_E_UNKNOWN_ERROR; | 597 | return IDEVICE_E_UNKNOWN_ERROR; |
235 | } | 598 | } |
236 | 599 | ||
237 | /** | ||
238 | * Disconnect from the device and clean up the connection structure. | ||
239 | * | ||
240 | * @param connection The connection to close. | ||
241 | * | ||
242 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
243 | */ | ||
244 | idevice_error_t idevice_disconnect(idevice_connection_t connection) | 600 | idevice_error_t idevice_disconnect(idevice_connection_t connection) |
245 | { | 601 | { |
246 | if (!connection) { | 602 | if (!connection) { |
@@ -252,12 +608,20 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection) | |||
252 | } | 608 | } |
253 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; | 609 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; |
254 | if (connection->type == CONNECTION_USBMUXD) { | 610 | if (connection->type == CONNECTION_USBMUXD) { |
255 | usbmuxd_disconnect((int)(long)connection->data); | 611 | usbmuxd_disconnect((int)(uintptr_t)connection->data); |
612 | connection->data = NULL; | ||
613 | result = IDEVICE_E_SUCCESS; | ||
614 | } else if (connection->type == CONNECTION_NETWORK) { | ||
615 | socket_close((int)(uintptr_t)connection->data); | ||
616 | connection->data = NULL; | ||
256 | result = IDEVICE_E_SUCCESS; | 617 | result = IDEVICE_E_SUCCESS; |
257 | } else { | 618 | } else { |
258 | debug_info("Unknown connection type %d", connection->type); | 619 | debug_info("Unknown connection type %d", connection->type); |
259 | } | 620 | } |
621 | |||
260 | free(connection); | 622 | free(connection); |
623 | connection = NULL; | ||
624 | |||
261 | return result; | 625 | return result; |
262 | } | 626 | } |
263 | 627 | ||
@@ -271,46 +635,111 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection, | |||
271 | } | 635 | } |
272 | 636 | ||
273 | if (connection->type == CONNECTION_USBMUXD) { | 637 | if (connection->type == CONNECTION_USBMUXD) { |
274 | int res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); | 638 | int res; |
639 | do { | ||
640 | res = usbmuxd_send((int)(uintptr_t)connection->data, data, len, sent_bytes); | ||
641 | } while (res == -EAGAIN); | ||
275 | if (res < 0) { | 642 | if (res < 0) { |
276 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); | 643 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); |
277 | return IDEVICE_E_UNKNOWN_ERROR; | 644 | return IDEVICE_E_UNKNOWN_ERROR; |
278 | } | 645 | } |
279 | return IDEVICE_E_SUCCESS; | 646 | return IDEVICE_E_SUCCESS; |
280 | } else { | ||
281 | debug_info("Unknown connection type %d", connection->type); | ||
282 | } | 647 | } |
648 | if (connection->type == CONNECTION_NETWORK) { | ||
649 | int s = socket_send((int)(uintptr_t)connection->data, (void*)data, len); | ||
650 | if (s < 0) { | ||
651 | *sent_bytes = 0; | ||
652 | return IDEVICE_E_UNKNOWN_ERROR; | ||
653 | } | ||
654 | *sent_bytes = s; | ||
655 | return IDEVICE_E_SUCCESS; | ||
656 | } | ||
657 | |||
658 | debug_info("Unknown connection type %d", connection->type); | ||
283 | return IDEVICE_E_UNKNOWN_ERROR; | 659 | return IDEVICE_E_UNKNOWN_ERROR; |
284 | 660 | ||
285 | } | 661 | } |
286 | 662 | ||
287 | /** | ||
288 | * Send data to a device via the given connection. | ||
289 | * | ||
290 | * @param connection The connection to send data over. | ||
291 | * @param data Buffer with data to send. | ||
292 | * @param len Size of the buffer to send. | ||
293 | * @param sent_bytes Pointer to an uint32_t that will be filled | ||
294 | * with the number of bytes actually sent. | ||
295 | * | ||
296 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
297 | */ | ||
298 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) | 663 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) |
299 | { | 664 | { |
300 | if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { | 665 | if (!connection || !data |
666 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
667 | || (connection->ssl_data && !connection->ssl_data->session) | ||
668 | #endif | ||
669 | ) { | ||
301 | return IDEVICE_E_INVALID_ARG; | 670 | return IDEVICE_E_INVALID_ARG; |
302 | } | 671 | } |
303 | 672 | ||
304 | if (connection->ssl_data) { | 673 | if (connection->ssl_data) { |
305 | ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); | 674 | connection->status = IDEVICE_E_SUCCESS; |
306 | if ((uint32_t)sent == (uint32_t)len) { | 675 | uint32_t sent = 0; |
307 | *sent_bytes = sent; | 676 | while (sent < len) { |
308 | return IDEVICE_E_SUCCESS; | 677 | #if defined(HAVE_OPENSSL) |
678 | int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent)); | ||
679 | if (s <= 0) { | ||
680 | int sslerr = SSL_get_error(connection->ssl_data->session, s); | ||
681 | if (sslerr == SSL_ERROR_WANT_WRITE) { | ||
682 | continue; | ||
683 | } | ||
684 | break; | ||
685 | } | ||
686 | #elif defined(HAVE_GNUTLS) | ||
687 | ssize_t s = gnutls_record_send(connection->ssl_data->session, (void*)(data+sent), (size_t)(len-sent)); | ||
688 | #elif defined(HAVE_MBEDTLS) | ||
689 | int s = mbedtls_ssl_write(&connection->ssl_data->ctx, (const unsigned char*)(data+sent), (size_t)(len-sent)); | ||
690 | #endif | ||
691 | if (s < 0) { | ||
692 | break; | ||
693 | } | ||
694 | sent += s; | ||
695 | } | ||
696 | debug_info("SSL_write %d, sent %d", len, sent); | ||
697 | if (sent < len) { | ||
698 | *sent_bytes = 0; | ||
699 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; | ||
700 | } | ||
701 | *sent_bytes = sent; | ||
702 | return IDEVICE_E_SUCCESS; | ||
703 | } | ||
704 | uint32_t sent = 0; | ||
705 | while (sent < len) { | ||
706 | uint32_t bytes = 0; | ||
707 | int s = internal_connection_send(connection, data+sent, len-sent, &bytes); | ||
708 | if (s < 0) { | ||
709 | break; | ||
710 | } | ||
711 | sent += bytes; | ||
712 | } | ||
713 | debug_info("internal_connection_send %d, sent %d", len, sent); | ||
714 | if (sent < len) { | ||
715 | *sent_bytes = sent; | ||
716 | if (sent == 0) { | ||
717 | return IDEVICE_E_UNKNOWN_ERROR; | ||
718 | } | ||
719 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
720 | } | ||
721 | *sent_bytes = sent; | ||
722 | return IDEVICE_E_SUCCESS; | ||
723 | } | ||
724 | |||
725 | static inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received) | ||
726 | { | ||
727 | if (conn_error < 0) { | ||
728 | switch (conn_error) { | ||
729 | case -EAGAIN: | ||
730 | if (len) { | ||
731 | debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error)); | ||
732 | } else { | ||
733 | debug_info("ERROR: received partial data (%s)", strerror(-conn_error)); | ||
734 | } | ||
735 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
736 | case -ETIMEDOUT: | ||
737 | return IDEVICE_E_TIMEOUT; | ||
738 | default: | ||
739 | return IDEVICE_E_UNKNOWN_ERROR; | ||
309 | } | 740 | } |
310 | *sent_bytes = 0; | ||
311 | return IDEVICE_E_SSL_ERROR; | ||
312 | } | 741 | } |
313 | return internal_connection_send(connection, data, len, sent_bytes); | 742 | return IDEVICE_E_SUCCESS; |
314 | } | 743 | } |
315 | 744 | ||
316 | /** | 745 | /** |
@@ -324,47 +753,92 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t | |||
324 | } | 753 | } |
325 | 754 | ||
326 | if (connection->type == CONNECTION_USBMUXD) { | 755 | if (connection->type == CONNECTION_USBMUXD) { |
327 | int res = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); | 756 | int conn_error = usbmuxd_recv_timeout((int)(uintptr_t)connection->data, data, len, recv_bytes, timeout); |
328 | if (res < 0) { | 757 | idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes); |
329 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res)); | 758 | if (error == IDEVICE_E_UNKNOWN_ERROR) { |
330 | return IDEVICE_E_UNKNOWN_ERROR; | 759 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error)); |
331 | } | 760 | } |
332 | return IDEVICE_E_SUCCESS; | 761 | return error; |
333 | } else { | 762 | } |
334 | debug_info("Unknown connection type %d", connection->type); | 763 | if (connection->type == CONNECTION_NETWORK) { |
764 | int res = socket_receive_timeout((int)(uintptr_t)connection->data, data, len, 0, timeout); | ||
765 | idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0); | ||
766 | if (error == IDEVICE_E_SUCCESS) { | ||
767 | *recv_bytes = (uint32_t)res; | ||
768 | } else if (error == IDEVICE_E_UNKNOWN_ERROR) { | ||
769 | debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res)); | ||
770 | } | ||
771 | return error; | ||
335 | } | 772 | } |
773 | |||
774 | debug_info("Unknown connection type %d", connection->type); | ||
336 | return IDEVICE_E_UNKNOWN_ERROR; | 775 | return IDEVICE_E_UNKNOWN_ERROR; |
337 | } | 776 | } |
338 | 777 | ||
339 | /** | ||
340 | * Receive data from a device via the given connection. | ||
341 | * This function will return after the given timeout even if no data has been | ||
342 | * received. | ||
343 | * | ||
344 | * @param connection The connection to receive data from. | ||
345 | * @param data Buffer that will be filled with the received data. | ||
346 | * This buffer has to be large enough to hold len bytes. | ||
347 | * @param len Buffer size or number of bytes to receive. | ||
348 | * @param recv_bytes Number of bytes actually received. | ||
349 | * @param timeout Timeout in milliseconds after which this function should | ||
350 | * return even if no data has been received. | ||
351 | * | ||
352 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
353 | */ | ||
354 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) | 778 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) |
355 | { | 779 | { |
356 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 780 | if (!connection |
781 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
782 | || (connection->ssl_data && !connection->ssl_data->session) | ||
783 | #endif | ||
784 | || len == 0 | ||
785 | ) { | ||
357 | return IDEVICE_E_INVALID_ARG; | 786 | return IDEVICE_E_INVALID_ARG; |
358 | } | 787 | } |
359 | 788 | ||
360 | if (connection->ssl_data) { | 789 | if (connection->ssl_data) { |
361 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 790 | uint32_t received = 0; |
362 | if (received > 0) { | 791 | |
792 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
793 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
794 | } | ||
795 | |||
796 | // this should be reset after the SSL_read call on all codepaths, as | ||
797 | // the supplied timeout should only apply to the current read. | ||
798 | connection->ssl_recv_timeout = timeout; | ||
799 | connection->status = IDEVICE_E_SUCCESS; | ||
800 | while (received < len) { | ||
801 | #if defined(HAVE_OPENSSL) | ||
802 | int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received); | ||
803 | if (r > 0) { | ||
804 | received += r; | ||
805 | } else { | ||
806 | int sslerr = SSL_get_error(connection->ssl_data->session, r); | ||
807 | if (sslerr == SSL_ERROR_WANT_READ) { | ||
808 | continue; | ||
809 | } else if (sslerr == SSL_ERROR_ZERO_RETURN) { | ||
810 | if (connection->status == IDEVICE_E_TIMEOUT) { | ||
811 | SSL_set_shutdown(connection->ssl_data->session, 0); | ||
812 | } | ||
813 | } | ||
814 | break; | ||
815 | } | ||
816 | #elif defined(HAVE_GNUTLS) | ||
817 | ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received); | ||
818 | if (r > 0) { | ||
819 | received += r; | ||
820 | } else { | ||
821 | break; | ||
822 | } | ||
823 | #elif defined(HAVE_MBEDTLS) | ||
824 | int r = mbedtls_ssl_read(&connection->ssl_data->ctx, (void*)(data+received), (size_t)len-received); | ||
825 | if (r > 0) { | ||
826 | received += r; | ||
827 | } else { | ||
828 | break; | ||
829 | } | ||
830 | #endif | ||
831 | } | ||
832 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
833 | |||
834 | debug_info("SSL_read %d, received %d", len, received); | ||
835 | if (received < len) { | ||
363 | *recv_bytes = received; | 836 | *recv_bytes = received; |
364 | return IDEVICE_E_SUCCESS; | 837 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; |
365 | } | 838 | } |
366 | *recv_bytes = 0; | 839 | |
367 | return IDEVICE_E_SSL_ERROR; | 840 | *recv_bytes = received; |
841 | return IDEVICE_E_SUCCESS; | ||
368 | } | 842 | } |
369 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); | 843 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); |
370 | } | 844 | } |
@@ -379,40 +853,50 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti | |||
379 | } | 853 | } |
380 | 854 | ||
381 | if (connection->type == CONNECTION_USBMUXD) { | 855 | if (connection->type == CONNECTION_USBMUXD) { |
382 | int res = usbmuxd_recv((int)(long)connection->data, data, len, recv_bytes); | 856 | int res = usbmuxd_recv((int)(uintptr_t)connection->data, data, len, recv_bytes); |
383 | if (res < 0) { | 857 | if (res < 0) { |
384 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); | 858 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); |
385 | return IDEVICE_E_UNKNOWN_ERROR; | 859 | return IDEVICE_E_UNKNOWN_ERROR; |
386 | } | 860 | } |
387 | |||
388 | return IDEVICE_E_SUCCESS; | 861 | return IDEVICE_E_SUCCESS; |
389 | } else { | ||
390 | debug_info("Unknown connection type %d", connection->type); | ||
391 | } | 862 | } |
863 | if (connection->type == CONNECTION_NETWORK) { | ||
864 | int res = socket_receive((int)(uintptr_t)connection->data, data, len); | ||
865 | if (res < 0) { | ||
866 | debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res)); | ||
867 | return IDEVICE_E_UNKNOWN_ERROR; | ||
868 | } | ||
869 | *recv_bytes = (uint32_t)res; | ||
870 | return IDEVICE_E_SUCCESS; | ||
871 | } | ||
872 | |||
873 | debug_info("Unknown connection type %d", connection->type); | ||
392 | return IDEVICE_E_UNKNOWN_ERROR; | 874 | return IDEVICE_E_UNKNOWN_ERROR; |
393 | } | 875 | } |
394 | 876 | ||
395 | /** | ||
396 | * Receive data from a device via the given connection. | ||
397 | * This function is like idevice_connection_receive_timeout, but with a | ||
398 | * predefined reasonable timeout. | ||
399 | * | ||
400 | * @param connection The connection to receive data from. | ||
401 | * @param data Buffer that will be filled with the received data. | ||
402 | * This buffer has to be large enough to hold len bytes. | ||
403 | * @param len Buffer size or number of bytes to receive. | ||
404 | * @param recv_bytes Number of bytes actually received. | ||
405 | * | ||
406 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
407 | */ | ||
408 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) | 877 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) |
409 | { | 878 | { |
410 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 879 | if (!connection |
880 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
881 | || (connection->ssl_data && !connection->ssl_data->session) | ||
882 | #endif | ||
883 | ) { | ||
411 | return IDEVICE_E_INVALID_ARG; | 884 | return IDEVICE_E_INVALID_ARG; |
412 | } | 885 | } |
413 | 886 | ||
414 | if (connection->ssl_data) { | 887 | if (connection->ssl_data) { |
888 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
889 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
890 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
891 | } | ||
892 | #if defined(HAVE_OPENSSL) | ||
893 | int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len); | ||
894 | debug_info("SSL_read %d, received %d", len, received); | ||
895 | #elif defined(HAVE_GNUTLS) | ||
415 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 896 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); |
897 | #elif defined(HAVE_MBEDTLS) | ||
898 | int received = mbedtls_ssl_read(&connection->ssl_data->ctx, (unsigned char*)data, (size_t)len); | ||
899 | #endif | ||
416 | if (received > 0) { | 900 | if (received > 0) { |
417 | *recv_bytes = received; | 901 | *recv_bytes = received; |
418 | return IDEVICE_E_SUCCESS; | 902 | return IDEVICE_E_SUCCESS; |
@@ -423,89 +907,119 @@ idevice_error_t idevice_connection_receive(idevice_connection_t connection, char | |||
423 | return internal_connection_receive(connection, data, len, recv_bytes); | 907 | return internal_connection_receive(connection, data, len, recv_bytes); |
424 | } | 908 | } |
425 | 909 | ||
426 | /** | 910 | 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 | { | 911 | { |
431 | if (!device) | 912 | if (!connection || !fd) { |
432 | return IDEVICE_E_INVALID_ARG; | 913 | return IDEVICE_E_INVALID_ARG; |
914 | } | ||
433 | 915 | ||
434 | if (device->conn_type == CONNECTION_USBMUXD) { | 916 | if (connection->type == CONNECTION_USBMUXD) { |
435 | *handle = (uint32_t)(long)device->conn_data; | 917 | *fd = (int)(uintptr_t)connection->data; |
436 | return IDEVICE_E_SUCCESS; | 918 | return IDEVICE_E_SUCCESS; |
437 | } else { | ||
438 | debug_info("Unknown connection type %d", device->conn_type); | ||
439 | } | 919 | } |
920 | if (connection->type == CONNECTION_NETWORK) { | ||
921 | *fd = (int)(uintptr_t)connection->data; | ||
922 | return IDEVICE_E_SUCCESS; | ||
923 | } | ||
924 | |||
925 | debug_info("Unknown connection type %d", connection->type); | ||
440 | return IDEVICE_E_UNKNOWN_ERROR; | 926 | return IDEVICE_E_UNKNOWN_ERROR; |
441 | } | 927 | } |
442 | 928 | ||
443 | /** | 929 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) |
444 | * Gets the unique id for the device. | 930 | { |
445 | */ | 931 | if (!device || !handle) |
446 | idevice_error_t idevice_get_uuid(idevice_t device, char **uuid) | 932 | return IDEVICE_E_INVALID_ARG; |
933 | |||
934 | *handle = device->mux_id; | ||
935 | return IDEVICE_E_SUCCESS; | ||
936 | } | ||
937 | |||
938 | idevice_error_t idevice_get_udid(idevice_t device, char **udid) | ||
447 | { | 939 | { |
448 | if (!device || !uuid) | 940 | if (!device || !udid) |
449 | return IDEVICE_E_INVALID_ARG; | 941 | return IDEVICE_E_INVALID_ARG; |
450 | 942 | ||
451 | *uuid = strdup(device->uuid); | 943 | if (device->udid) { |
944 | *udid = strdup(device->udid); | ||
945 | } | ||
452 | return IDEVICE_E_SUCCESS; | 946 | return IDEVICE_E_SUCCESS; |
453 | } | 947 | } |
454 | 948 | ||
949 | unsigned int idevice_get_device_version(idevice_t device) | ||
950 | { | ||
951 | if (!device) { | ||
952 | return 0; | ||
953 | } | ||
954 | if (!device->version) { | ||
955 | lockdownd_client_t lockdown = NULL; | ||
956 | lockdownd_client_new(device, &lockdown, NULL); | ||
957 | // we don't handle any errors here. We should have the product version cached now. | ||
958 | lockdownd_client_free(lockdown); | ||
959 | } | ||
960 | return device->version; | ||
961 | } | ||
962 | |||
963 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
964 | typedef ssize_t ssl_cb_ret_type_t; | ||
965 | #elif defined(HAVE_MBEDTLS) | ||
966 | typedef int ssl_cb_ret_type_t; | ||
967 | #endif | ||
968 | |||
455 | /** | 969 | /** |
456 | * Internally used gnutls callback function for receiving encrypted data. | 970 | * Internally used SSL callback function for receiving encrypted data. |
457 | */ | 971 | */ |
458 | static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 972 | static ssl_cb_ret_type_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length) |
459 | { | 973 | { |
460 | int bytes = 0, pos_start_fill = 0; | 974 | uint32_t bytes = 0; |
461 | size_t tbytes = 0; | 975 | uint32_t pos = 0; |
462 | int this_len = length; | ||
463 | idevice_error_t res; | 976 | idevice_error_t res; |
464 | idevice_connection_t connection = (idevice_connection_t)transport; | 977 | unsigned int timeout = connection->ssl_recv_timeout; |
465 | char *recv_buffer; | ||
466 | 978 | ||
467 | debug_info("pre-read client wants %zi bytes", length); | 979 | debug_info("pre-read length = %zi bytes", length); |
468 | |||
469 | recv_buffer = (char *) malloc(sizeof(char) * this_len); | ||
470 | 980 | ||
471 | /* repeat until we have the full data or an error occurs */ | 981 | /* repeat until we have the full data or an error occurs */ |
472 | do { | 982 | do { |
473 | if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { | 983 | bytes = 0; |
474 | debug_info("ERROR: idevice_connection_receive returned %d", res); | 984 | if (timeout == (unsigned int)-1) { |
475 | return res; | 985 | res = internal_connection_receive(connection, buffer + pos, (uint32_t)length - pos, &bytes); |
986 | } else { | ||
987 | res = internal_connection_receive_timeout(connection, buffer + pos, (uint32_t)length - pos, &bytes, (unsigned int)timeout); | ||
988 | } | ||
989 | if (res != IDEVICE_E_SUCCESS) { | ||
990 | if (res != IDEVICE_E_TIMEOUT) { | ||
991 | debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res); | ||
992 | } | ||
993 | connection->status = res; | ||
994 | return -1; | ||
476 | } | 995 | } |
477 | debug_info("post-read we got %i bytes", bytes); | 996 | debug_info("read %i bytes", bytes); |
478 | 997 | ||
479 | /* increase read count */ | 998 | /* increase read count */ |
480 | tbytes += bytes; | 999 | pos += bytes; |
481 | 1000 | if (pos < (uint32_t)length) { | |
482 | /* fill the buffer with what we got right now */ | 1001 | debug_info("re-read trying to read missing %i bytes", (uint32_t)length - pos); |
483 | memcpy(buffer + pos_start_fill, recv_buffer, bytes); | ||
484 | pos_start_fill += bytes; | ||
485 | |||
486 | if (tbytes >= length) { | ||
487 | break; | ||
488 | } | 1002 | } |
1003 | } while (pos < (uint32_t)length); | ||
489 | 1004 | ||
490 | this_len = length - tbytes; | 1005 | debug_info("post-read received %i bytes", pos); |
491 | debug_info("re-read trying to read missing %i bytes", this_len); | ||
492 | } while (tbytes < length); | ||
493 | 1006 | ||
494 | if (recv_buffer) { | 1007 | return pos; |
495 | free(recv_buffer); | ||
496 | } | ||
497 | return tbytes; | ||
498 | } | 1008 | } |
499 | 1009 | ||
500 | /** | 1010 | /** |
501 | * Internally used gnutls callback function for sending encrypted data. | 1011 | * Internally used SSL callback function for sending encrypted data. |
502 | */ | 1012 | */ |
503 | static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 1013 | static ssl_cb_ret_type_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length) |
504 | { | 1014 | { |
505 | uint32_t bytes = 0; | 1015 | uint32_t bytes = 0; |
506 | idevice_connection_t connection = (idevice_connection_t)transport; | 1016 | idevice_error_t res; |
507 | debug_info("pre-send length = %zi", length); | 1017 | debug_info("pre-send length = %zi bytes", length); |
508 | internal_connection_send(connection, buffer, length, &bytes); | 1018 | if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) { |
1019 | debug_info("ERROR: internal_connection_send returned %d", res); | ||
1020 | connection->status = res; | ||
1021 | return -1; | ||
1022 | } | ||
509 | debug_info("post-send sent %i bytes", bytes); | 1023 | debug_info("post-send sent %i bytes", bytes); |
510 | return bytes; | 1024 | return bytes; |
511 | } | 1025 | } |
@@ -518,6 +1032,14 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
518 | if (!ssl_data) | 1032 | if (!ssl_data) |
519 | return; | 1033 | return; |
520 | 1034 | ||
1035 | #if defined(HAVE_OPENSSL) | ||
1036 | if (ssl_data->session) { | ||
1037 | SSL_free(ssl_data->session); | ||
1038 | } | ||
1039 | if (ssl_data->ctx) { | ||
1040 | SSL_CTX_free(ssl_data->ctx); | ||
1041 | } | ||
1042 | #elif defined(HAVE_GNUTLS) | ||
521 | if (ssl_data->session) { | 1043 | if (ssl_data->session) { |
522 | gnutls_deinit(ssl_data->session); | 1044 | gnutls_deinit(ssl_data->session); |
523 | } | 1045 | } |
@@ -536,20 +1058,121 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
536 | if (ssl_data->host_privkey) { | 1058 | if (ssl_data->host_privkey) { |
537 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); | 1059 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); |
538 | } | 1060 | } |
1061 | #elif defined(HAVE_MBEDTLS) | ||
1062 | mbedtls_pk_free(&ssl_data->root_privkey); | ||
1063 | mbedtls_x509_crt_free(&ssl_data->certificate); | ||
1064 | mbedtls_entropy_free(&ssl_data->entropy); | ||
1065 | mbedtls_ctr_drbg_free(&ssl_data->ctr_drbg); | ||
1066 | mbedtls_ssl_config_free(&ssl_data->config); | ||
1067 | mbedtls_ssl_free(&ssl_data->ctx); | ||
1068 | #endif | ||
1069 | } | ||
1070 | |||
1071 | #ifdef HAVE_OPENSSL | ||
1072 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1073 | 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) | ||
1074 | #else | ||
1075 | static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue) | ||
1076 | #endif | ||
1077 | { | ||
1078 | ssize_t bytes = 0; | ||
1079 | idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b); | ||
1080 | #if OPENSSL_VERSION_NUMBER < 0x30000000L | ||
1081 | size_t len = (size_t)argi; | ||
1082 | #endif | ||
1083 | switch (oper) { | ||
1084 | case (BIO_CB_READ|BIO_CB_RETURN): | ||
1085 | if (argp) { | ||
1086 | bytes = internal_ssl_read(conn, (char *)argp, len); | ||
1087 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1088 | *processed = (size_t)(bytes < 0) ? 0 : bytes; | ||
1089 | #endif | ||
1090 | return (long)bytes; | ||
1091 | } | ||
1092 | return 0; | ||
1093 | case (BIO_CB_PUTS|BIO_CB_RETURN): | ||
1094 | len = strlen(argp); | ||
1095 | // fallthrough | ||
1096 | case (BIO_CB_WRITE|BIO_CB_RETURN): | ||
1097 | bytes = internal_ssl_write(conn, argp, len); | ||
1098 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1099 | *processed = (size_t)(bytes < 0) ? 0 : bytes; | ||
1100 | #endif | ||
1101 | return (long)bytes; | ||
1102 | default: | ||
1103 | return retvalue; | ||
1104 | } | ||
1105 | } | ||
1106 | |||
1107 | static BIO *ssl_idevice_bio_new(idevice_connection_t conn) | ||
1108 | { | ||
1109 | BIO *b = BIO_new(BIO_s_null()); | ||
1110 | if (!b) return NULL; | ||
1111 | BIO_set_callback_arg(b, (char *)conn); | ||
1112 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1113 | BIO_set_callback_ex(b, ssl_idevice_bio_callback); | ||
1114 | #else | ||
1115 | BIO_set_callback(b, ssl_idevice_bio_callback); | ||
1116 | #endif | ||
1117 | return b; | ||
1118 | } | ||
1119 | |||
1120 | static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx) | ||
1121 | { | ||
1122 | return 1; | ||
1123 | } | ||
1124 | |||
1125 | #ifndef STRIP_DEBUG_CODE | ||
1126 | static const char *ssl_error_to_string(int e) | ||
1127 | { | ||
1128 | switch(e) { | ||
1129 | case SSL_ERROR_NONE: | ||
1130 | return "SSL_ERROR_NONE"; | ||
1131 | case SSL_ERROR_SSL: | ||
1132 | return ERR_error_string(ERR_get_error(), NULL); | ||
1133 | case SSL_ERROR_WANT_READ: | ||
1134 | return "SSL_ERROR_WANT_READ"; | ||
1135 | case SSL_ERROR_WANT_WRITE: | ||
1136 | return "SSL_ERROR_WANT_WRITE"; | ||
1137 | case SSL_ERROR_WANT_X509_LOOKUP: | ||
1138 | return "SSL_ERROR_WANT_X509_LOOKUP"; | ||
1139 | case SSL_ERROR_SYSCALL: | ||
1140 | return "SSL_ERROR_SYSCALL"; | ||
1141 | case SSL_ERROR_ZERO_RETURN: | ||
1142 | return "SSL_ERROR_ZERO_RETURN"; | ||
1143 | case SSL_ERROR_WANT_CONNECT: | ||
1144 | return "SSL_ERROR_WANT_CONNECT"; | ||
1145 | case SSL_ERROR_WANT_ACCEPT: | ||
1146 | return "SSL_ERROR_WANT_ACCEPT"; | ||
1147 | default: | ||
1148 | return "UNKOWN_ERROR_VALUE"; | ||
1149 | } | ||
539 | } | 1150 | } |
1151 | #endif | ||
1152 | #endif | ||
540 | 1153 | ||
1154 | #if defined(HAVE_GNUTLS) | ||
541 | /** | 1155 | /** |
542 | * Internally used gnutls callback function that gets called during handshake. | 1156 | * Internally used gnutls callback function that gets called during handshake. |
543 | */ | 1157 | */ |
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) | 1158 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
1159 | 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) | ||
1160 | #else | ||
1161 | 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) | ||
1162 | #endif | ||
545 | { | 1163 | { |
546 | int res = -1; | 1164 | int res = -1; |
547 | gnutls_certificate_type_t type = gnutls_certificate_type_get (session); | 1165 | gnutls_certificate_type_t type = gnutls_certificate_type_get(session); |
548 | if (type == GNUTLS_CRT_X509) { | 1166 | if (type == GNUTLS_CRT_X509) { |
549 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr (session); | 1167 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr(session); |
550 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { | 1168 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { |
551 | debug_info("Passing certificate"); | 1169 | debug_info("Passing certificate"); |
1170 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 | ||
1171 | st->cert_type = type; | ||
1172 | st->key_type = GNUTLS_PRIVKEY_X509; | ||
1173 | #else | ||
552 | st->type = type; | 1174 | st->type = type; |
1175 | #endif | ||
553 | st->ncerts = 1; | 1176 | st->ncerts = 1; |
554 | st->cert.x509 = &ssl_data->host_cert; | 1177 | st->cert.x509 = &ssl_data->host_cert; |
555 | st->key.x509 = ssl_data->host_privkey; | 1178 | st->key.x509 = ssl_data->host_privkey; |
@@ -559,46 +1182,204 @@ static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_ | |||
559 | } | 1182 | } |
560 | return res; | 1183 | return res; |
561 | } | 1184 | } |
1185 | #elif defined(HAVE_MBEDTLS) | ||
1186 | static void _mbedtls_log_cb(void* ctx, int level, const char* filename, int line, const char* message) | ||
1187 | { | ||
1188 | fprintf(stderr, "[mbedtls][%d] %s:%d => %s", level, filename, line, message); | ||
1189 | } | ||
1190 | |||
1191 | static int cert_verify_cb(void* ctx, mbedtls_x509_crt* cert, int depth, uint32_t *flags) | ||
1192 | { | ||
1193 | *flags = 0; | ||
1194 | return 0; | ||
1195 | } | ||
1196 | |||
1197 | static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len) | ||
1198 | { | ||
1199 | memset(buf, 4, len); | ||
1200 | return 0; | ||
1201 | } | ||
1202 | #endif | ||
562 | 1203 | ||
563 | /** | ||
564 | * Enables SSL for the given connection. | ||
565 | * | ||
566 | * @param connection The connection to enable SSL for. | ||
567 | * | ||
568 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
569 | * is NULL or connection->ssl_data is non-NULL, or IDEVICE_E_SSL_ERROR when | ||
570 | * SSL initialization, setup, or handshake fails. | ||
571 | */ | ||
572 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | 1204 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) |
573 | { | 1205 | { |
574 | if (!connection || connection->ssl_data) | 1206 | if (!connection || connection->ssl_data) |
575 | return IDEVICE_E_INVALID_ARG; | 1207 | return IDEVICE_E_INVALID_ARG; |
576 | 1208 | ||
577 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; | 1209 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; |
578 | uint32_t return_me = 0; | 1210 | plist_t pair_record = NULL; |
1211 | |||
1212 | userpref_error_t uerr = userpref_read_pair_record(connection->device->udid, &pair_record); | ||
1213 | if (uerr != USERPREF_E_SUCCESS) { | ||
1214 | debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s (%d)", connection->device->udid, uerr); | ||
1215 | return ret; | ||
1216 | } | ||
1217 | |||
1218 | #if defined(HAVE_OPENSSL) | ||
1219 | key_data_t root_cert = { NULL, 0 }; | ||
1220 | key_data_t root_privkey = { NULL, 0 }; | ||
1221 | |||
1222 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
1223 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
1224 | |||
1225 | if (pair_record) | ||
1226 | plist_free(pair_record); | ||
1227 | |||
1228 | BIO *ssl_bio = ssl_idevice_bio_new(connection); | ||
1229 | if (!ssl_bio) { | ||
1230 | debug_info("ERROR: Could not create SSL bio."); | ||
1231 | return ret; | ||
1232 | } | ||
1233 | |||
1234 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); | ||
1235 | if (ssl_ctx == NULL) { | ||
1236 | debug_info("ERROR: Could not create SSL context."); | ||
1237 | BIO_free(ssl_bio); | ||
1238 | return ret; | ||
1239 | } | ||
1240 | |||
1241 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || \ | ||
1242 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3060000fL)) | ||
1243 | SSL_CTX_set_security_level(ssl_ctx, 0); | ||
1244 | #endif | ||
1245 | |||
1246 | #if OPENSSL_VERSION_NUMBER < 0x10100002L || \ | ||
1247 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL)) | ||
1248 | /* force use of TLSv1 for older devices */ | ||
1249 | if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) { | ||
1250 | #ifdef SSL_OP_NO_TLSv1_1 | ||
1251 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); | ||
1252 | #endif | ||
1253 | #ifdef SSL_OP_NO_TLSv1_2 | ||
1254 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); | ||
1255 | #endif | ||
1256 | #ifdef SSL_OP_NO_TLSv1_3 | ||
1257 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); | ||
1258 | #endif | ||
1259 | } | ||
1260 | #else | ||
1261 | SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); | ||
1262 | if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) { | ||
1263 | SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION); | ||
1264 | if (connection->device->version == 0) { | ||
1265 | /* | ||
1266 | iOS 1 doesn't understand TLS1_VERSION, it can only speak SSL3_VERSION. | ||
1267 | However, modern OpenSSL is usually compiled without SSLv3 support. | ||
1268 | So if we set min_proto_version to SSL3_VERSION on an OpenSSL instance which doesn't support it, | ||
1269 | it will just ignore min_proto_version altogether and fall back to an even higher version. | ||
1270 | To avoid accidentally breaking iOS 2.0+, we set min version to 0 instead. | ||
1271 | Here is what documentation says: | ||
1272 | Setting the minimum or maximum version to 0, | ||
1273 | will enable protocol versions down to the lowest version, | ||
1274 | or up to the highest version supported by the library, respectively. | ||
1275 | */ | ||
1276 | SSL_CTX_set_min_proto_version(ssl_ctx, 0); | ||
1277 | } | ||
1278 | } | ||
1279 | #endif | ||
1280 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1281 | #if defined(SSL_OP_IGNORE_UNEXPECTED_EOF) | ||
1282 | /* | ||
1283 | * For OpenSSL 3 and later, mark close_notify alerts as optional. | ||
1284 | * For prior versions of OpenSSL we check for SSL_ERROR_SYSCALL when | ||
1285 | * reading instead (this error changes to SSL_ERROR_SSL in OpenSSL 3). | ||
1286 | */ | ||
1287 | SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); | ||
1288 | #endif | ||
1289 | #if defined(SSL_OP_LEGACY_SERVER_CONNECT) | ||
1290 | /* | ||
1291 | * Without setting SSL_OP_LEGACY_SERVER_CONNECT, OpenSSL 3 fails with | ||
1292 | * error "unsafe legacy renegotiation disabled" when talking to iOS 5 | ||
1293 | */ | ||
1294 | SSL_CTX_set_options(ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT); | ||
1295 | #endif | ||
1296 | #endif | ||
1297 | |||
1298 | BIO* membp; | ||
1299 | X509* rootCert = NULL; | ||
1300 | membp = BIO_new_mem_buf(root_cert.data, root_cert.size); | ||
1301 | PEM_read_bio_X509(membp, &rootCert, NULL, NULL); | ||
1302 | BIO_free(membp); | ||
1303 | if (SSL_CTX_use_certificate(ssl_ctx, rootCert) != 1) { | ||
1304 | debug_info("WARNING: Could not load RootCertificate"); | ||
1305 | } | ||
1306 | X509_free(rootCert); | ||
1307 | free(root_cert.data); | ||
1308 | |||
1309 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
1310 | EVP_PKEY* rootPrivKey = NULL; | ||
1311 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
1312 | PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
1313 | BIO_free(membp); | ||
1314 | if (SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
1315 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
1316 | } | ||
1317 | EVP_PKEY_free(rootPrivKey); | ||
1318 | #else | ||
1319 | RSA* rootPrivKey = NULL; | ||
1320 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
1321 | PEM_read_bio_RSAPrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
1322 | BIO_free(membp); | ||
1323 | if (SSL_CTX_use_RSAPrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
1324 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
1325 | } | ||
1326 | RSA_free(rootPrivKey); | ||
1327 | #endif | ||
1328 | free(root_privkey.data); | ||
1329 | |||
1330 | SSL *ssl = SSL_new(ssl_ctx); | ||
1331 | if (!ssl) { | ||
1332 | debug_info("ERROR: Could not create SSL object"); | ||
1333 | BIO_free(ssl_bio); | ||
1334 | SSL_CTX_free(ssl_ctx); | ||
1335 | return ret; | ||
1336 | } | ||
1337 | SSL_set_connect_state(ssl); | ||
1338 | SSL_set_verify(ssl, 0, ssl_verify_callback); | ||
1339 | SSL_set_bio(ssl, ssl_bio, ssl_bio); | ||
579 | 1340 | ||
1341 | debug_info("Performing SSL handshake"); | ||
1342 | int ssl_error = 0; | ||
1343 | do { | ||
1344 | ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl)); | ||
1345 | if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) { | ||
1346 | break; | ||
1347 | } | ||
1348 | #ifdef _WIN32 | ||
1349 | Sleep(100); | ||
1350 | #else | ||
1351 | struct timespec ts = { 0, 100000000 }; | ||
1352 | nanosleep(&ts, NULL); | ||
1353 | #endif | ||
1354 | } while (1); | ||
1355 | if (ssl_error != 0) { | ||
1356 | debug_info("ERROR during SSL handshake: %s", ssl_error_to_string(ssl_error)); | ||
1357 | SSL_free(ssl); | ||
1358 | SSL_CTX_free(ssl_ctx); | ||
1359 | } else { | ||
1360 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
1361 | ssl_data_loc->session = ssl; | ||
1362 | ssl_data_loc->ctx = ssl_ctx; | ||
1363 | connection->ssl_data = ssl_data_loc; | ||
1364 | ret = IDEVICE_E_SUCCESS; | ||
1365 | debug_info("SSL mode enabled, %s, cipher: %s", SSL_get_version(ssl), SSL_get_cipher(ssl)); | ||
1366 | } | ||
1367 | /* required for proper multi-thread clean up to prevent leaks */ | ||
1368 | openssl_remove_thread_state(); | ||
1369 | #elif defined(HAVE_GNUTLS) | ||
580 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | 1370 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); |
581 | 1371 | ||
582 | /* Set up GnuTLS... */ | 1372 | /* Set up GnuTLS... */ |
583 | debug_info("enabling SSL mode"); | 1373 | debug_info("enabling SSL mode"); |
584 | errno = 0; | 1374 | errno = 0; |
585 | gnutls_global_init(); | ||
586 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); | 1375 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); |
587 | gnutls_certificate_client_set_retrieve_function (ssl_data_loc->certificate, internal_cert_callback); | 1376 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
1377 | gnutls_certificate_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
1378 | #else | ||
1379 | gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
1380 | #endif | ||
588 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); | 1381 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); |
589 | { | 1382 | gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-TLS1.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL); |
590 | int protocol_priority[16] = { GNUTLS_SSL3, 0 }; | ||
591 | int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 }; | ||
592 | int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; | ||
593 | int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; | ||
594 | int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; | ||
595 | |||
596 | gnutls_cipher_set_priority(ssl_data_loc->session, cipher_priority); | ||
597 | gnutls_compression_set_priority(ssl_data_loc->session, comp_priority); | ||
598 | gnutls_kx_set_priority(ssl_data_loc->session, kx_priority); | ||
599 | gnutls_protocol_set_priority(ssl_data_loc->session, protocol_priority); | ||
600 | gnutls_mac_set_priority(ssl_data_loc->session, mac_priority); | ||
601 | } | ||
602 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); | 1383 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); |
603 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); | 1384 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); |
604 | 1385 | ||
@@ -607,10 +1388,13 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
607 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); | 1388 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); |
608 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); | 1389 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); |
609 | 1390 | ||
610 | userpref_error_t uerr = userpref_get_keys_and_certs(ssl_data_loc->root_privkey, ssl_data_loc->root_cert, ssl_data_loc->host_privkey, ssl_data_loc->host_cert); | 1391 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, ssl_data_loc->root_cert); |
611 | if (uerr != USERPREF_E_SUCCESS) { | 1392 | pair_record_import_crt_with_name(pair_record, USERPREF_HOST_CERTIFICATE_KEY, ssl_data_loc->host_cert); |
612 | debug_info("Error %d when loading keys and certificates! %d", uerr); | 1393 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, ssl_data_loc->root_privkey); |
613 | } | 1394 | pair_record_import_key_with_name(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, ssl_data_loc->host_privkey); |
1395 | |||
1396 | if (pair_record) | ||
1397 | plist_free(pair_record); | ||
614 | 1398 | ||
615 | debug_info("GnuTLS step 1..."); | 1399 | debug_info("GnuTLS step 1..."); |
616 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); | 1400 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); |
@@ -619,46 +1403,146 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
619 | debug_info("GnuTLS step 3..."); | 1403 | debug_info("GnuTLS step 3..."); |
620 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); | 1404 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); |
621 | debug_info("GnuTLS step 4 -- now handshaking..."); | 1405 | debug_info("GnuTLS step 4 -- now handshaking..."); |
622 | if (errno) | 1406 | if (errno) { |
623 | debug_info("WARN: errno says %s before handshake!", strerror(errno)); | 1407 | debug_info("WARNING: errno says %s before handshake!", strerror(errno)); |
624 | return_me = gnutls_handshake(ssl_data_loc->session); | 1408 | } |
1409 | |||
1410 | int return_me = 0; | ||
1411 | do { | ||
1412 | return_me = gnutls_handshake(ssl_data_loc->session); | ||
1413 | } while(return_me == GNUTLS_E_AGAIN || return_me == GNUTLS_E_INTERRUPTED); | ||
1414 | |||
625 | debug_info("GnuTLS handshake done..."); | 1415 | debug_info("GnuTLS handshake done..."); |
626 | 1416 | ||
627 | if (return_me != GNUTLS_E_SUCCESS) { | 1417 | if (return_me != GNUTLS_E_SUCCESS) { |
628 | internal_ssl_cleanup(ssl_data_loc); | 1418 | internal_ssl_cleanup(ssl_data_loc); |
629 | free(ssl_data_loc); | 1419 | free(ssl_data_loc); |
630 | debug_info("GnuTLS reported something wrong."); | 1420 | debug_info("GnuTLS reported something wrong: %s", gnutls_strerror(return_me)); |
631 | gnutls_perror(return_me); | ||
632 | debug_info("oh.. errno says %s", strerror(errno)); | 1421 | debug_info("oh.. errno says %s", strerror(errno)); |
633 | } else { | 1422 | } else { |
634 | connection->ssl_data = ssl_data_loc; | 1423 | connection->ssl_data = ssl_data_loc; |
635 | ret = IDEVICE_E_SUCCESS; | 1424 | ret = IDEVICE_E_SUCCESS; |
636 | debug_info("SSL mode enabled"); | 1425 | debug_info("SSL mode enabled"); |
637 | } | 1426 | } |
1427 | #elif defined(HAVE_MBEDTLS) | ||
1428 | key_data_t root_cert = { NULL, 0 }; | ||
1429 | key_data_t root_privkey = { NULL, 0 }; | ||
1430 | |||
1431 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
1432 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
1433 | |||
1434 | plist_free(pair_record); | ||
1435 | |||
1436 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
1437 | |||
1438 | mbedtls_ssl_init(&ssl_data_loc->ctx); | ||
1439 | mbedtls_ssl_config_init(&ssl_data_loc->config); | ||
1440 | mbedtls_entropy_init(&ssl_data_loc->entropy); | ||
1441 | mbedtls_ctr_drbg_init(&ssl_data_loc->ctr_drbg); | ||
1442 | |||
1443 | int r = mbedtls_ctr_drbg_seed(&ssl_data_loc->ctr_drbg, mbedtls_entropy_func, &ssl_data_loc->entropy, NULL, 0); | ||
1444 | if (r != 0) { | ||
1445 | debug_info("ERROR: [mbedtls] mbedtls_ctr_drbg_seed failed: %d", r); | ||
1446 | return ret; | ||
1447 | } | ||
1448 | |||
1449 | if (mbedtls_ssl_config_defaults(&ssl_data_loc->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { | ||
1450 | debug_info("ERROR: [mbedtls] Failed to set config defaults"); | ||
1451 | return ret; | ||
1452 | } | ||
1453 | |||
1454 | mbedtls_ssl_conf_rng(&ssl_data_loc->config, mbedtls_ctr_drbg_random, &ssl_data_loc->ctr_drbg); | ||
1455 | |||
1456 | mbedtls_ssl_conf_dbg(&ssl_data_loc->config, _mbedtls_log_cb, NULL); | ||
1457 | |||
1458 | mbedtls_ssl_conf_verify(&ssl_data_loc->config, cert_verify_cb, NULL); | ||
1459 | |||
1460 | mbedtls_ssl_setup(&ssl_data_loc->ctx, &ssl_data_loc->config); | ||
1461 | |||
1462 | mbedtls_ssl_set_bio(&ssl_data_loc->ctx, connection, (mbedtls_ssl_send_t*)&internal_ssl_write, (mbedtls_ssl_recv_t*)&internal_ssl_read, NULL); | ||
1463 | |||
1464 | mbedtls_x509_crt_init(&ssl_data_loc->certificate); | ||
1465 | |||
1466 | int crterr = mbedtls_x509_crt_parse(&ssl_data_loc->certificate, root_cert.data, root_cert.size); | ||
1467 | if (crterr < 0) { | ||
1468 | debug_info("ERROR: [mbedtls] parsing root cert failed: %d", crterr); | ||
1469 | return ret; | ||
1470 | } | ||
1471 | |||
1472 | mbedtls_ssl_conf_ca_chain(&ssl_data_loc->config, &ssl_data_loc->certificate, NULL); | ||
1473 | |||
1474 | mbedtls_pk_init(&ssl_data_loc->root_privkey); | ||
1475 | |||
1476 | #if MBEDTLS_VERSION_NUMBER >= 0x03000000 | ||
1477 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0, &_mbedtls_f_rng, NULL); | ||
1478 | #else | ||
1479 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0); | ||
1480 | #endif | ||
1481 | if (pkerr < 0) { | ||
1482 | debug_info("ERROR: [mbedtls] parsing private key failed: %d (size=%d)", pkerr, root_privkey.size); | ||
1483 | return ret; | ||
1484 | } | ||
1485 | |||
1486 | mbedtls_ssl_conf_own_cert(&ssl_data_loc->config, &ssl_data_loc->certificate, &ssl_data_loc->root_privkey); | ||
1487 | |||
1488 | int return_me = 0; | ||
1489 | do { | ||
1490 | return_me = mbedtls_ssl_handshake(&ssl_data_loc->ctx); | ||
1491 | } while (return_me == MBEDTLS_ERR_SSL_WANT_READ || return_me == MBEDTLS_ERR_SSL_WANT_WRITE); | ||
1492 | |||
1493 | if (return_me != 0) { | ||
1494 | debug_info("ERROR during SSL handshake: %d", return_me); | ||
1495 | internal_ssl_cleanup(ssl_data_loc); | ||
1496 | free(ssl_data_loc); | ||
1497 | } else { | ||
1498 | connection->ssl_data = ssl_data_loc; | ||
1499 | ret = IDEVICE_E_SUCCESS; | ||
1500 | debug_info("SSL mode enabled, %s, cipher: %s", mbedtls_ssl_get_version(&ssl_data_loc->ctx), mbedtls_ssl_get_ciphersuite(&ssl_data_loc->ctx)); | ||
1501 | debug_info("SSL mode enabled"); | ||
1502 | } | ||
1503 | #endif | ||
638 | return ret; | 1504 | return ret; |
639 | } | 1505 | } |
640 | 1506 | ||
641 | /** | ||
642 | * Disable SSL for the given connection. | ||
643 | * | ||
644 | * @param connection The connection to disable SSL for. | ||
645 | * | ||
646 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
647 | * is NULL. This function also returns IDEVICE_E_SUCCESS when SSL is not | ||
648 | * enabled and does no further error checking on cleanup. | ||
649 | */ | ||
650 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | 1507 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) |
651 | { | 1508 | { |
1509 | return idevice_connection_disable_bypass_ssl(connection, 0); | ||
1510 | } | ||
1511 | |||
1512 | idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass) | ||
1513 | { | ||
652 | if (!connection) | 1514 | if (!connection) |
653 | return IDEVICE_E_INVALID_ARG; | 1515 | return IDEVICE_E_INVALID_ARG; |
654 | if (!connection->ssl_data) { | 1516 | if (!connection->ssl_data) { |
655 | /* ignore if ssl is not enabled */ | 1517 | /* ignore if ssl is not enabled */ |
656 | return IDEVICE_E_SUCCESS; | 1518 | return IDEVICE_E_SUCCESS; |
657 | } | 1519 | } |
658 | 1520 | ||
659 | if (connection->ssl_data->session) { | 1521 | // some services require plain text communication after SSL handshake |
660 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | 1522 | // sending out SSL_shutdown will cause bytes |
1523 | if (!sslBypass) { | ||
1524 | #if defined(HAVE_OPENSSL) | ||
1525 | if (connection->ssl_data->session) { | ||
1526 | /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */ | ||
1527 | if (SSL_shutdown(connection->ssl_data->session) == 0) { | ||
1528 | /* Only try bidirectional shutdown if we know it can complete */ | ||
1529 | int ssl_error; | ||
1530 | if ((ssl_error = SSL_get_error(connection->ssl_data->session, 0)) == SSL_ERROR_NONE) { | ||
1531 | SSL_shutdown(connection->ssl_data->session); | ||
1532 | } else { | ||
1533 | debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error); | ||
1534 | } | ||
1535 | } | ||
1536 | } | ||
1537 | #elif defined(HAVE_GNUTLS) | ||
1538 | if (connection->ssl_data->session) { | ||
1539 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | ||
1540 | } | ||
1541 | #elif defined(HAVE_MBEDTLS) | ||
1542 | mbedtls_ssl_close_notify(&connection->ssl_data->ctx); | ||
1543 | #endif | ||
661 | } | 1544 | } |
1545 | |||
662 | internal_ssl_cleanup(connection->ssl_data); | 1546 | internal_ssl_cleanup(connection->ssl_data); |
663 | free(connection->ssl_data); | 1547 | free(connection->ssl_data); |
664 | connection->ssl_data = NULL; | 1548 | connection->ssl_data = NULL; |
@@ -668,3 +1552,27 @@ idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | |||
668 | return IDEVICE_E_SUCCESS; | 1552 | return IDEVICE_E_SUCCESS; |
669 | } | 1553 | } |
670 | 1554 | ||
1555 | const char* idevice_strerror(idevice_error_t err) | ||
1556 | { | ||
1557 | switch (err) { | ||
1558 | case IDEVICE_E_SUCCESS: | ||
1559 | return "Success"; | ||
1560 | case IDEVICE_E_INVALID_ARG: | ||
1561 | return "Invalid argument"; | ||
1562 | case IDEVICE_E_UNKNOWN_ERROR: | ||
1563 | return "Unknown Error"; | ||
1564 | case IDEVICE_E_NO_DEVICE: | ||
1565 | return "No device"; | ||
1566 | case IDEVICE_E_NOT_ENOUGH_DATA: | ||
1567 | return "Not enough data"; | ||
1568 | case IDEVICE_E_CONNREFUSED: | ||
1569 | return "Connection refused"; | ||
1570 | case IDEVICE_E_SSL_ERROR: | ||
1571 | return "SSL error"; | ||
1572 | case IDEVICE_E_TIMEOUT: | ||
1573 | return "Timeout"; | ||
1574 | default: | ||
1575 | break; | ||
1576 | } | ||
1577 | return "Unknown Error"; | ||
1578 | } | ||
diff --git a/src/idevice.h b/src/idevice.h index 231b3ab..e05338e 100644 --- a/src/idevice.h +++ b/src/idevice.h | |||
@@ -8,51 +8,95 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef IDEVICE_H | ||
22 | #define IDEVICE_H | ||
23 | 21 | ||
22 | #ifndef __DEVICE_H | ||
23 | #define __DEVICE_H | ||
24 | |||
25 | #ifdef HAVE_CONFIG_H | ||
26 | #include <config.h> | ||
27 | #endif | ||
28 | |||
29 | #if defined(HAVE_OPENSSL) | ||
30 | #include <openssl/ssl.h> | ||
31 | #elif defined(HAVE_GNUTLS) | ||
24 | #include <gnutls/gnutls.h> | 32 | #include <gnutls/gnutls.h> |
25 | #include <gnutls/x509.h> | 33 | #include <gnutls/x509.h> |
34 | #elif defined(HAVE_MBEDTLS) | ||
35 | #include <mbedtls/ssl.h> | ||
36 | #include <mbedtls/entropy.h> | ||
37 | #include <mbedtls/ctr_drbg.h> | ||
38 | #endif | ||
26 | 39 | ||
40 | #ifdef LIBIMOBILEDEVICE_STATIC | ||
41 | #define LIBIMOBILEDEVICE_API | ||
42 | #elif defined(_WIN32) | ||
43 | #define LIBIMOBILEDEVICE_API __declspec( dllexport ) | ||
44 | #else | ||
45 | #if __GNUC__ >= 4 | ||
46 | #define LIBIMOBILEDEVICE_API __attribute__((visibility("default"))) | ||
47 | #else | ||
48 | #define LIBIMOBILEDEVICE_API | ||
49 | #endif | ||
50 | #endif | ||
51 | |||
52 | #include "common/userpref.h" | ||
27 | #include "libimobiledevice/libimobiledevice.h" | 53 | #include "libimobiledevice/libimobiledevice.h" |
28 | 54 | ||
29 | enum connection_type { | 55 | #define DEVICE_CLASS_IPHONE 1 |
30 | CONNECTION_USBMUXD = 1 | 56 | #define DEVICE_CLASS_IPAD 2 |
31 | }; | 57 | #define DEVICE_CLASS_IPOD 3 |
58 | #define DEVICE_CLASS_APPLETV 4 | ||
59 | #define DEVICE_CLASS_WATCH 5 | ||
60 | #define DEVICE_CLASS_UNKNOWN 255 | ||
32 | 61 | ||
33 | struct ssl_data_private { | 62 | struct ssl_data_private { |
63 | #if defined(HAVE_OPENSSL) | ||
64 | SSL *session; | ||
65 | SSL_CTX *ctx; | ||
66 | #elif defined(HAVE_GNUTLS) | ||
34 | gnutls_certificate_credentials_t certificate; | 67 | gnutls_certificate_credentials_t certificate; |
35 | gnutls_session_t session; | 68 | gnutls_session_t session; |
36 | gnutls_x509_privkey_t root_privkey; | 69 | gnutls_x509_privkey_t root_privkey; |
37 | gnutls_x509_crt_t root_cert; | 70 | gnutls_x509_crt_t root_cert; |
38 | gnutls_x509_privkey_t host_privkey; | 71 | gnutls_x509_privkey_t host_privkey; |
39 | gnutls_x509_crt_t host_cert; | 72 | gnutls_x509_crt_t host_cert; |
73 | #elif defined(HAVE_MBEDTLS) | ||
74 | mbedtls_ssl_context ctx; | ||
75 | mbedtls_ssl_config config; | ||
76 | mbedtls_entropy_context entropy; | ||
77 | mbedtls_ctr_drbg_context ctr_drbg; | ||
78 | mbedtls_x509_crt certificate; | ||
79 | mbedtls_pk_context root_privkey; | ||
80 | #endif | ||
40 | }; | 81 | }; |
41 | typedef struct ssl_data_private *ssl_data_t; | 82 | typedef struct ssl_data_private *ssl_data_t; |
42 | 83 | ||
43 | struct idevice_connection_private { | 84 | struct idevice_connection_private { |
44 | enum connection_type type; | 85 | idevice_t device; |
86 | enum idevice_connection_type type; | ||
45 | void *data; | 87 | void *data; |
46 | ssl_data_t ssl_data; | 88 | ssl_data_t ssl_data; |
89 | unsigned int ssl_recv_timeout; | ||
90 | idevice_error_t status; | ||
47 | }; | 91 | }; |
48 | 92 | ||
49 | struct idevice_private { | 93 | struct idevice_private { |
50 | char *uuid; | 94 | char *udid; |
51 | enum connection_type conn_type; | 95 | uint32_t mux_id; |
96 | enum idevice_connection_type conn_type; | ||
52 | void *conn_data; | 97 | void *conn_data; |
98 | int version; | ||
99 | int device_class; | ||
53 | }; | 100 | }; |
54 | 101 | ||
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 | 102 | #endif |
diff --git a/src/installation_proxy.c b/src/installation_proxy.c index 4a76dd2..bb6ef01 100644 --- a/src/installation_proxy.c +++ b/src/installation_proxy.c | |||
@@ -2,63 +2,214 @@ | |||
2 | * installation_proxy.c | 2 | * installation_proxy.c |
3 | * com.apple.mobile.installation_proxy service implementation. | 3 | * com.apple.mobile.installation_proxy service implementation. |
4 | * | 4 | * |
5 | * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved. |
6 | * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved. | ||
6 | * | 7 | * |
7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 9 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 10 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 11 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 12 | * |
12 | * This library is distributed in the hope that it will be useful, | 13 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 16 | * Lesser General Public License for more details. |
16 | * | 17 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 18 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 19 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 21 | */ |
21 | 22 | ||
23 | #ifdef HAVE_CONFIG_H | ||
24 | #include <config.h> | ||
25 | #endif | ||
22 | #include <string.h> | 26 | #include <string.h> |
23 | #include <stdlib.h> | 27 | #include <stdlib.h> |
28 | #include <inttypes.h> | ||
29 | |||
30 | #ifndef _MSC_VER | ||
24 | #include <unistd.h> | 31 | #include <unistd.h> |
32 | #endif | ||
33 | |||
25 | #include <plist/plist.h> | 34 | #include <plist/plist.h> |
26 | 35 | ||
27 | #include "installation_proxy.h" | 36 | #include "installation_proxy.h" |
28 | #include "property_list_service.h" | 37 | #include "property_list_service.h" |
29 | #include "debug.h" | 38 | #include "common/debug.h" |
39 | |||
40 | typedef enum { | ||
41 | INSTPROXY_COMMAND_TYPE_ASYNC, | ||
42 | INSTPROXY_COMMAND_TYPE_SYNC | ||
43 | } instproxy_command_type_t; | ||
30 | 44 | ||
31 | struct instproxy_status_data { | 45 | struct instproxy_status_data { |
32 | instproxy_client_t client; | 46 | instproxy_client_t client; |
47 | plist_t command; | ||
33 | instproxy_status_cb_t cbfunc; | 48 | instproxy_status_cb_t cbfunc; |
34 | char *operation; | ||
35 | void *user_data; | 49 | void *user_data; |
36 | }; | 50 | }; |
37 | 51 | ||
38 | /** | 52 | /** |
53 | * Converts an error string identifier to a instproxy_error_t value. | ||
54 | * Used internally to get correct error codes from a response. | ||
55 | * | ||
56 | * @param name The error name to convert. | ||
57 | * @param error_detail Pointer to store error detail text if available. The | ||
58 | * caller is reponsible for freeing the allocated buffer after use. If NULL | ||
59 | * is passed no error detail will be returned. | ||
60 | * | ||
61 | * @return A matching instproxy_error_t error code or | ||
62 | * INSTPROXY_E_UNKNOWN_ERROR otherwise. | ||
63 | */ | ||
64 | static instproxy_error_t instproxy_strtoerr(const char* name) | ||
65 | { | ||
66 | instproxy_error_t err = INSTPROXY_E_UNKNOWN_ERROR; | ||
67 | |||
68 | if (strcmp(name, "AlreadyArchived") == 0) { | ||
69 | err = INSTPROXY_E_ALREADY_ARCHIVED; | ||
70 | } else if (strcmp(name, "APIInternalError") == 0) { | ||
71 | err = INSTPROXY_E_API_INTERNAL_ERROR; | ||
72 | } else if (strcmp(name, "ApplicationAlreadyInstalled") == 0) { | ||
73 | err = INSTPROXY_E_APPLICATION_ALREADY_INSTALLED; | ||
74 | } else if (strcmp(name, "ApplicationMoveFailed") == 0) { | ||
75 | err = INSTPROXY_E_APPLICATION_MOVE_FAILED; | ||
76 | } else if (strcmp(name, "ApplicationSINFCaptureFailed") == 0) { | ||
77 | err = INSTPROXY_E_APPLICATION_SINF_CAPTURE_FAILED; | ||
78 | } else if (strcmp(name, "ApplicationSandboxFailed") == 0) { | ||
79 | err = INSTPROXY_E_APPLICATION_SANDBOX_FAILED; | ||
80 | } else if (strcmp(name, "ApplicationVerificationFailed") == 0) { | ||
81 | err = INSTPROXY_E_APPLICATION_VERIFICATION_FAILED; | ||
82 | } else if (strcmp(name, "ArchiveDestructionFailed") == 0) { | ||
83 | err = INSTPROXY_E_ARCHIVE_DESTRUCTION_FAILED; | ||
84 | } else if (strcmp(name, "BundleVerificationFailed") == 0) { | ||
85 | err = INSTPROXY_E_BUNDLE_VERIFICATION_FAILED; | ||
86 | } else if (strcmp(name, "CarrierBundleCopyFailed") == 0) { | ||
87 | err = INSTPROXY_E_CARRIER_BUNDLE_COPY_FAILED; | ||
88 | } else if (strcmp(name, "CarrierBundleDirectoryCreationFailed") == 0) { | ||
89 | err = INSTPROXY_E_CARRIER_BUNDLE_DIRECTORY_CREATION_FAILED; | ||
90 | } else if (strcmp(name, "CarrierBundleMissingSupportedSIMs") == 0) { | ||
91 | err = INSTPROXY_E_CARRIER_BUNDLE_MISSING_SUPPORTED_SIMS; | ||
92 | } else if (strcmp(name, "CommCenterNotificationFailed") == 0) { | ||
93 | err = INSTPROXY_E_COMM_CENTER_NOTIFICATION_FAILED; | ||
94 | } else if (strcmp(name, "ContainerCreationFailed") == 0) { | ||
95 | err = INSTPROXY_E_CONTAINER_CREATION_FAILED; | ||
96 | } else if (strcmp(name, "ContainerP0wnFailed") == 0) { | ||
97 | err = INSTPROXY_E_CONTAINER_P0WN_FAILED; | ||
98 | } else if (strcmp(name, "ContainerRemovalFailed") == 0) { | ||
99 | err = INSTPROXY_E_CONTAINER_REMOVAL_FAILED; | ||
100 | } else if (strcmp(name, "EmbeddedProfileInstallFailed") == 0) { | ||
101 | err = INSTPROXY_E_EMBEDDED_PROFILE_INSTALL_FAILED; | ||
102 | } else if (strcmp(name, "ExecutableTwiddleFailed") == 0) { | ||
103 | err = INSTPROXY_E_EXECUTABLE_TWIDDLE_FAILED; | ||
104 | } else if (strcmp(name, "ExistenceCheckFailed") == 0) { | ||
105 | err = INSTPROXY_E_EXISTENCE_CHECK_FAILED; | ||
106 | } else if (strcmp(name, "InstallMapUpdateFailed") == 0) { | ||
107 | err = INSTPROXY_E_INSTALL_MAP_UPDATE_FAILED; | ||
108 | } else if (strcmp(name, "ManifestCaptureFailed") == 0) { | ||
109 | err = INSTPROXY_E_MANIFEST_CAPTURE_FAILED; | ||
110 | } else if (strcmp(name, "MapGenerationFailed") == 0) { | ||
111 | err = INSTPROXY_E_MAP_GENERATION_FAILED; | ||
112 | } else if (strcmp(name, "MissingBundleExecutable") == 0) { | ||
113 | err = INSTPROXY_E_MISSING_BUNDLE_EXECUTABLE; | ||
114 | } else if (strcmp(name, "MissingBundleIdentifier") == 0) { | ||
115 | err = INSTPROXY_E_MISSING_BUNDLE_IDENTIFIER; | ||
116 | } else if (strcmp(name, "MissingBundlePath") == 0) { | ||
117 | err = INSTPROXY_E_MISSING_BUNDLE_PATH; | ||
118 | } else if (strcmp(name, "MissingContainer") == 0) { | ||
119 | err = INSTPROXY_E_MISSING_CONTAINER; | ||
120 | } else if (strcmp(name, "NotificationFailed") == 0) { | ||
121 | err = INSTPROXY_E_NOTIFICATION_FAILED; | ||
122 | } else if (strcmp(name, "PackageExtractionFailed") == 0) { | ||
123 | err = INSTPROXY_E_PACKAGE_EXTRACTION_FAILED; | ||
124 | } else if (strcmp(name, "PackageInspectionFailed") == 0) { | ||
125 | err = INSTPROXY_E_PACKAGE_INSPECTION_FAILED; | ||
126 | } else if (strcmp(name, "PackageMoveFailed") == 0) { | ||
127 | err = INSTPROXY_E_PACKAGE_MOVE_FAILED; | ||
128 | } else if (strcmp(name, "PathConversionFailed") == 0) { | ||
129 | err = INSTPROXY_E_PATH_CONVERSION_FAILED; | ||
130 | } else if (strcmp(name, "RestoreContainerFailed") == 0) { | ||
131 | err = INSTPROXY_E_RESTORE_CONTAINER_FAILED; | ||
132 | } else if (strcmp(name, "SeatbeltProfileRemovalFailed") == 0) { | ||
133 | err = INSTPROXY_E_SEATBELT_PROFILE_REMOVAL_FAILED; | ||
134 | } else if (strcmp(name, "StageCreationFailed") == 0) { | ||
135 | err = INSTPROXY_E_STAGE_CREATION_FAILED; | ||
136 | } else if (strcmp(name, "SymlinkFailed") == 0) { | ||
137 | err = INSTPROXY_E_SYMLINK_FAILED; | ||
138 | } else if (strcmp(name, "UnknownCommand") == 0) { | ||
139 | err = INSTPROXY_E_UNKNOWN_COMMAND; | ||
140 | } else if (strcmp(name, "iTunesArtworkCaptureFailed") == 0) { | ||
141 | err = INSTPROXY_E_ITUNES_ARTWORK_CAPTURE_FAILED; | ||
142 | } else if (strcmp(name, "iTunesMetadataCaptureFailed") == 0) { | ||
143 | err = INSTPROXY_E_ITUNES_METADATA_CAPTURE_FAILED; | ||
144 | } else if (strcmp(name, "DeviceOSVersionTooLow") == 0) { | ||
145 | err = INSTPROXY_E_DEVICE_OS_VERSION_TOO_LOW; | ||
146 | } else if (strcmp(name, "DeviceFamilyNotSupported") == 0) { | ||
147 | err = INSTPROXY_E_DEVICE_FAMILY_NOT_SUPPORTED; | ||
148 | } else if (strcmp(name, "PackagePatchFailed") == 0) { | ||
149 | err = INSTPROXY_E_PACKAGE_PATCH_FAILED; | ||
150 | } else if (strcmp(name, "IncorrectArchitecture") == 0) { | ||
151 | err = INSTPROXY_E_INCORRECT_ARCHITECTURE; | ||
152 | } else if (strcmp(name, "PluginCopyFailed") == 0) { | ||
153 | err = INSTPROXY_E_PLUGIN_COPY_FAILED; | ||
154 | } else if (strcmp(name, "BreadcrumbFailed") == 0) { | ||
155 | err = INSTPROXY_E_BREADCRUMB_FAILED; | ||
156 | } else if (strcmp(name, "BreadcrumbUnlockFailed") == 0) { | ||
157 | err = INSTPROXY_E_BREADCRUMB_UNLOCK_FAILED; | ||
158 | } else if (strcmp(name, "GeoJSONCaptureFailed") == 0) { | ||
159 | err = INSTPROXY_E_GEOJSON_CAPTURE_FAILED; | ||
160 | } else if (strcmp(name, "NewsstandArtworkCaptureFailed") == 0) { | ||
161 | err = INSTPROXY_E_NEWSSTAND_ARTWORK_CAPTURE_FAILED; | ||
162 | } else if (strcmp(name, "MissingCommand") == 0) { | ||
163 | err = INSTPROXY_E_MISSING_COMMAND; | ||
164 | } else if (strcmp(name, "NotEntitled") == 0) { | ||
165 | err = INSTPROXY_E_NOT_ENTITLED; | ||
166 | } else if (strcmp(name, "MissingPackagePath") == 0) { | ||
167 | err = INSTPROXY_E_MISSING_PACKAGE_PATH; | ||
168 | } else if (strcmp(name, "MissingContainerPath") == 0) { | ||
169 | err = INSTPROXY_E_MISSING_CONTAINER_PATH; | ||
170 | } else if (strcmp(name, "MissingApplicationIdentifier") == 0) { | ||
171 | err = INSTPROXY_E_MISSING_APPLICATION_IDENTIFIER; | ||
172 | } else if (strcmp(name, "MissingAttributeValue") == 0) { | ||
173 | err = INSTPROXY_E_MISSING_ATTRIBUTE_VALUE; | ||
174 | } else if (strcmp(name, "LookupFailed") == 0) { | ||
175 | err = INSTPROXY_E_LOOKUP_FAILED; | ||
176 | } else if (strcmp(name, "DictCreationFailed") == 0) { | ||
177 | err = INSTPROXY_E_DICT_CREATION_FAILED; | ||
178 | } else if (strcmp(name, "InstallProhibited") == 0) { | ||
179 | err = INSTPROXY_E_INSTALL_PROHIBITED; | ||
180 | } else if (strcmp(name, "UninstallProhibited") == 0) { | ||
181 | err = INSTPROXY_E_UNINSTALL_PROHIBITED; | ||
182 | } else if (strcmp(name, "MissingBundleVersion") == 0) { | ||
183 | err = INSTPROXY_E_MISSING_BUNDLE_VERSION; | ||
184 | } | ||
185 | |||
186 | return err; | ||
187 | } | ||
188 | |||
189 | /** | ||
39 | * Locks an installation_proxy client, used for thread safety. | 190 | * Locks an installation_proxy client, used for thread safety. |
40 | * | 191 | * |
41 | * @param client The installation_proxy client to lock | 192 | * @param client The installation_proxy client to lock |
42 | */ | 193 | */ |
43 | static void instproxy_lock(instproxy_client_t client) | 194 | static void instproxy_lock(instproxy_client_t client) |
44 | { | 195 | { |
45 | debug_info("InstallationProxy: Locked"); | 196 | debug_info("Locked"); |
46 | g_mutex_lock(client->mutex); | 197 | mutex_lock(&client->mutex); |
47 | } | 198 | } |
48 | 199 | ||
49 | /** | 200 | /** |
50 | * Unlocks an installation_proxy client, used for thread safety. | 201 | * Unlocks an installation_proxy client, used for thread safety. |
51 | * | 202 | * |
52 | * @param client The installation_proxy client to lock | 203 | * @param client The installation_proxy client to lock |
53 | */ | 204 | */ |
54 | static void instproxy_unlock(instproxy_client_t client) | 205 | static void instproxy_unlock(instproxy_client_t client) |
55 | { | 206 | { |
56 | debug_info("InstallationProxy: Unlocked"); | 207 | debug_info("Unlocked"); |
57 | g_mutex_unlock(client->mutex); | 208 | mutex_unlock(&client->mutex); |
58 | } | 209 | } |
59 | 210 | ||
60 | /** | 211 | /** |
61 | * Convert a property_list_service_error_t value to an instproxy_error_t value. | 212 | * Converts a property_list_service_error_t value to an instproxy_error_t value. |
62 | * Used internally to get correct error codes. | 213 | * Used internally to get correct error codes. |
63 | * | 214 | * |
64 | * @param err A property_list_service_error_t error code | 215 | * @param err A property_list_service_error_t error code |
@@ -77,186 +228,80 @@ static instproxy_error_t instproxy_error(property_list_service_error_t err) | |||
77 | return INSTPROXY_E_PLIST_ERROR; | 228 | return INSTPROXY_E_PLIST_ERROR; |
78 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | 229 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: |
79 | return INSTPROXY_E_CONN_FAILED; | 230 | return INSTPROXY_E_CONN_FAILED; |
231 | case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: | ||
232 | return INSTPROXY_E_RECEIVE_TIMEOUT; | ||
80 | default: | 233 | default: |
81 | break; | 234 | break; |
82 | } | 235 | } |
83 | return INSTPROXY_E_UNKNOWN_ERROR; | 236 | return INSTPROXY_E_UNKNOWN_ERROR; |
84 | } | 237 | } |
85 | 238 | ||
86 | /** | 239 | 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 | { | 240 | { |
99 | /* makes sure thread environment is available */ | ||
100 | if (!g_thread_supported()) | ||
101 | g_thread_init(NULL); | ||
102 | |||
103 | if (!device) | ||
104 | return INSTPROXY_E_INVALID_ARG; | ||
105 | |||
106 | property_list_service_client_t plistclient = NULL; | 241 | property_list_service_client_t plistclient = NULL; |
107 | if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 242 | instproxy_error_t err = instproxy_error(property_list_service_client_new(device, service, &plistclient)); |
108 | return INSTPROXY_E_CONN_FAILED; | 243 | if (err != INSTPROXY_E_SUCCESS) { |
244 | return err; | ||
109 | } | 245 | } |
110 | 246 | ||
111 | instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); | 247 | instproxy_client_t client_loc = (instproxy_client_t) malloc(sizeof(struct instproxy_client_private)); |
112 | client_loc->parent = plistclient; | 248 | client_loc->parent = plistclient; |
113 | client_loc->mutex = g_mutex_new(); | 249 | mutex_init(&client_loc->mutex); |
114 | client_loc->status_updater = NULL; | 250 | client_loc->receive_status_thread = THREAD_T_NULL; |
115 | 251 | ||
116 | *client = client_loc; | 252 | *client = client_loc; |
117 | return INSTPROXY_E_SUCCESS; | 253 | return INSTPROXY_E_SUCCESS; |
118 | } | 254 | } |
119 | 255 | ||
120 | /** | 256 | 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 | 257 | { |
122 | * installation_proxy client data. | 258 | int32_t err = INSTPROXY_E_UNKNOWN_ERROR; |
123 | * | 259 | service_client_factory_start_service(device, INSTPROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(instproxy_client_new), &err); |
124 | * @param client The installation_proxy client to disconnect and free. | 260 | return err; |
125 | * | 261 | } |
126 | * @return INSTPROXY_E_SUCCESS on success | 262 | |
127 | * or INSTPROXY_E_INVALID_ARG if client is NULL. | ||
128 | */ | ||
129 | instproxy_error_t instproxy_client_free(instproxy_client_t client) | 263 | instproxy_error_t instproxy_client_free(instproxy_client_t client) |
130 | { | 264 | { |
131 | if (!client) | 265 | if (!client) |
132 | return INSTPROXY_E_INVALID_ARG; | 266 | return INSTPROXY_E_INVALID_ARG; |
133 | 267 | ||
134 | property_list_service_client_free(client->parent); | 268 | property_list_service_client_t parent = client->parent; |
135 | client->parent = NULL; | 269 | client->parent = NULL; |
136 | if (client->status_updater) { | 270 | if (client->receive_status_thread) { |
137 | debug_info("joining status_updater"); | 271 | debug_info("joining receive_status_thread"); |
138 | g_thread_join(client->status_updater); | 272 | thread_join(client->receive_status_thread); |
139 | } | 273 | thread_free(client->receive_status_thread); |
140 | if (client->mutex) { | 274 | client->receive_status_thread = THREAD_T_NULL; |
141 | g_mutex_free(client->mutex); | ||
142 | } | 275 | } |
276 | property_list_service_client_free(parent); | ||
277 | mutex_destroy(&client->mutex); | ||
143 | free(client); | 278 | free(client); |
144 | 279 | ||
145 | return INSTPROXY_E_SUCCESS; | 280 | return INSTPROXY_E_SUCCESS; |
146 | } | 281 | } |
147 | 282 | ||
148 | /** | 283 | /** |
149 | * Send a command with specified options to the device. | 284 | * Sends a command to the device. |
150 | * Only used internally. | 285 | * Only used internally. |
151 | * | 286 | * |
152 | * @param client The connected installation_proxy client. | 287 | * @param client The connected installation_proxy client. |
153 | * @param command The command to execute. Required. | 288 | * @param command The command to execute. Required. |
154 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | ||
155 | * @param appid The ApplicationIdentifier to add or NULL if not required. | ||
156 | * @param package_path The installation package path or NULL if not required. | ||
157 | * | 289 | * |
158 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | 290 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if |
159 | * an error occured. | 291 | * an error occurred. |
160 | */ | 292 | */ |
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) | 293 | static instproxy_error_t instproxy_send_command(instproxy_client_t client, plist_t command) |
162 | { | ||
163 | if (!client || !command || (client_options && (plist_get_node_type(client_options) != PLIST_DICT))) | ||
164 | return INSTPROXY_E_INVALID_ARG; | ||
165 | |||
166 | plist_t dict = plist_new_dict(); | ||
167 | if (appid) { | ||
168 | plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid)); | ||
169 | } | ||
170 | if (client_options && (plist_dict_get_size(client_options) > 0)) { | ||
171 | plist_dict_insert_item(dict, "ClientOptions", plist_copy(client_options)); | ||
172 | } | ||
173 | plist_dict_insert_item(dict, "Command", plist_new_string(command)); | ||
174 | if (package_path) { | ||
175 | plist_dict_insert_item(dict, "PackagePath", plist_new_string(package_path)); | ||
176 | } | ||
177 | |||
178 | instproxy_error_t err = instproxy_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
179 | plist_free(dict); | ||
180 | return err; | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * List installed applications. This function runs synchronously. | ||
185 | * | ||
186 | * @param client The connected installation_proxy client | ||
187 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | ||
188 | * Valid client options include: | ||
189 | * "ApplicationType" -> "User" | ||
190 | * "ApplicationType" -> "System" | ||
191 | * @param result Pointer that will be set to a plist that will hold an array | ||
192 | * of PLIST_DICT holding information about the applications found. | ||
193 | * | ||
194 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | ||
195 | * an error occured. | ||
196 | */ | ||
197 | instproxy_error_t instproxy_browse(instproxy_client_t client, plist_t client_options, plist_t *result) | ||
198 | { | 294 | { |
199 | if (!client || !client->parent || !result) | 295 | if (!client || !command) |
200 | return INSTPROXY_E_INVALID_ARG; | 296 | return INSTPROXY_E_INVALID_ARG; |
201 | 297 | ||
202 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | 298 | instproxy_error_t res = instproxy_error(property_list_service_send_xml_plist(client->parent, command)); |
203 | 299 | ||
204 | instproxy_lock(client); | ||
205 | res = instproxy_send_command(client, "Browse", client_options, NULL, NULL); | ||
206 | if (res != INSTPROXY_E_SUCCESS) { | 300 | if (res != INSTPROXY_E_SUCCESS) { |
207 | debug_info("could not send plist"); | 301 | debug_info("could not send command plist, error %d", res); |
208 | goto leave_unlock; | 302 | return res; |
209 | } | ||
210 | |||
211 | int browsing = 0; | ||
212 | plist_t apps_array = plist_new_array(); | ||
213 | plist_t dict = NULL; | ||
214 | |||
215 | do { | ||
216 | browsing = 0; | ||
217 | dict = NULL; | ||
218 | res = instproxy_error(property_list_service_receive_plist(client->parent, &dict)); | ||
219 | if (res != INSTPROXY_E_SUCCESS) { | ||
220 | break; | ||
221 | } | ||
222 | if (dict) { | ||
223 | uint64_t i; | ||
224 | uint64_t current_amount = 0; | ||
225 | char *status = NULL; | ||
226 | plist_t camount = plist_dict_get_item(dict, "CurrentAmount"); | ||
227 | plist_t pstatus = plist_dict_get_item(dict, "Status"); | ||
228 | if (camount) { | ||
229 | plist_get_uint_val(camount, ¤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 | } | 303 | } |
257 | 304 | ||
258 | leave_unlock: | ||
259 | instproxy_unlock(client); | ||
260 | return res; | 305 | return res; |
261 | } | 306 | } |
262 | 307 | ||
@@ -269,78 +314,99 @@ leave_unlock: | |||
269 | * | 314 | * |
270 | * @param client The connected installation proxy client | 315 | * @param client The connected installation proxy client |
271 | * @param status_cb Pointer to a callback function or NULL | 316 | * @param status_cb Pointer to a callback function or NULL |
272 | * @param operation Operation name. Will be passed to the callback function | 317 | * @param command Operation specificiation in plist. Will be passed to the |
273 | * in async mode or shown in debug messages in sync mode. | 318 | * status_cb callback. |
274 | * @param user_data Callback data passed to status_cb. | 319 | * @param user_data Callback data passed to status_cb. |
275 | */ | 320 | */ |
276 | static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, instproxy_status_cb_t status_cb, const char *operation, void *user_data) | 321 | 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 | { | 322 | { |
278 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | 323 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
279 | int ok = 1; | 324 | int complete = 0; |
280 | plist_t dict = NULL; | 325 | plist_t node = NULL; |
326 | char* command_name = NULL; | ||
327 | char* status_name = NULL; | ||
328 | char* error_name = NULL; | ||
329 | char* error_description = NULL; | ||
330 | uint64_t error_code = 0; | ||
331 | #ifndef STRIP_DEBUG_CODE | ||
332 | int percent_complete = 0; | ||
333 | #endif | ||
334 | |||
335 | instproxy_command_get_name(command, &command_name); | ||
281 | 336 | ||
282 | do { | 337 | do { |
338 | /* receive status response */ | ||
283 | instproxy_lock(client); | 339 | instproxy_lock(client); |
284 | res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &dict, 30000)); | 340 | res = instproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000)); |
285 | instproxy_unlock(client); | 341 | instproxy_unlock(client); |
286 | if (res != INSTPROXY_E_SUCCESS) { | 342 | |
343 | /* break out if we have a communication problem */ | ||
344 | if (res != INSTPROXY_E_SUCCESS && res != INSTPROXY_E_RECEIVE_TIMEOUT) { | ||
287 | debug_info("could not receive plist, error %d", res); | 345 | debug_info("could not receive plist, error %d", res); |
288 | break; | 346 | break; |
289 | } | 347 | } |
290 | if (dict) { | 348 | |
291 | /* invoke callback function */ | 349 | /* parse status response */ |
292 | if (status_cb) { | 350 | if (node) { |
293 | status_cb(operation, dict, user_data); | 351 | /* check status for possible error to allow reporting it and aborting it gracefully */ |
352 | res = instproxy_status_get_error(node, &error_name, &error_description, &error_code); | ||
353 | if (res != INSTPROXY_E_SUCCESS) { | ||
354 | debug_info("command: %s, error %d, code 0x%08"PRIx64", name: %s, description: \"%s\"", command_name, res, error_code, error_name, error_description ? error_description: "N/A"); | ||
355 | complete = 1; | ||
294 | } | 356 | } |
295 | /* check for 'Error', so we can abort cleanly */ | 357 | |
296 | plist_t err = plist_dict_get_item(dict, "Error"); | 358 | if (error_name) { |
297 | if (err) { | 359 | free(error_name); |
360 | error_name = NULL; | ||
361 | } | ||
362 | |||
363 | if (error_description) { | ||
364 | free(error_description); | ||
365 | error_description = NULL; | ||
366 | } | ||
367 | |||
368 | /* check status from response */ | ||
369 | instproxy_status_get_name(node, &status_name); | ||
370 | if (!status_name) { | ||
371 | debug_info("ignoring message without Status key:"); | ||
372 | debug_plist(node); | ||
373 | } else { | ||
374 | if (!strcmp(status_name, "Complete")) { | ||
375 | complete = 1; | ||
376 | } else { | ||
377 | res = INSTPROXY_E_OP_IN_PROGRESS; | ||
378 | } | ||
298 | #ifndef STRIP_DEBUG_CODE | 379 | #ifndef STRIP_DEBUG_CODE |
299 | char *err_msg = NULL; | 380 | percent_complete = -1; |
300 | plist_get_string_val(err, &err_msg); | 381 | instproxy_status_get_percent_complete(node, &percent_complete); |
301 | if (err_msg) { | 382 | if (percent_complete >= 0) { |
302 | debug_info("(%s): ERROR: %s", operation, err_msg); | 383 | debug_info("command: %s, status: %s, percent (%d%%)", command_name, status_name, percent_complete); |
303 | free(err_msg); | 384 | } else { |
385 | debug_info("command: %s, status: %s", command_name, status_name); | ||
304 | } | 386 | } |
305 | #endif | 387 | #endif |
306 | ok = 0; | 388 | free(status_name); |
307 | res = INSTPROXY_E_OP_FAILED; | 389 | status_name = NULL; |
308 | } | 390 | } |
309 | /* get 'Status' */ | 391 | |
310 | plist_t status = plist_dict_get_item(dict, "Status"); | 392 | /* invoke status callback function */ |
311 | if (status) { | 393 | if (status_cb) { |
312 | char *status_msg = NULL; | 394 | status_cb(command, node, user_data); |
313 | plist_get_string_val(status, &status_msg); | ||
314 | if (status_msg) { | ||
315 | if (!strcmp(status_msg, "Complete")) { | ||
316 | ok = 0; | ||
317 | res = INSTPROXY_E_SUCCESS; | ||
318 | } | ||
319 | #ifndef STRIP_DEBUG_CODE | ||
320 | plist_t npercent = plist_dict_get_item(dict, "PercentComplete"); | ||
321 | if (npercent) { | ||
322 | uint64_t val = 0; | ||
323 | int percent; | ||
324 | plist_get_uint_val(npercent, &val); | ||
325 | percent = val; | ||
326 | debug_info("(%s): %s (%d%%)", operation, status_msg, percent); | ||
327 | } else { | ||
328 | debug_info("(%s): %s", operation, status_msg); | ||
329 | } | ||
330 | #endif | ||
331 | free(status_msg); | ||
332 | } | ||
333 | } | 395 | } |
334 | plist_free(dict); | 396 | |
335 | dict = NULL; | 397 | plist_free(node); |
398 | node = NULL; | ||
336 | } | 399 | } |
337 | } while (ok && client->parent); | 400 | } while (!complete && client->parent); |
401 | |||
402 | if (command_name) | ||
403 | free(command_name); | ||
338 | 404 | ||
339 | return res; | 405 | return res; |
340 | } | 406 | } |
341 | 407 | ||
342 | /** | 408 | /** |
343 | * Internally used status updater thread function that will call the specified | 409 | * Internally used "receive status" thread function that will call the specified |
344 | * callback function when status update messages (or error messages) are | 410 | * callback function when status update messages (or error messages) are |
345 | * received. | 411 | * received. |
346 | * | 412 | * |
@@ -349,20 +415,27 @@ static instproxy_error_t instproxy_perform_operation(instproxy_client_t client, | |||
349 | * | 415 | * |
350 | * @return Always NULL. | 416 | * @return Always NULL. |
351 | */ | 417 | */ |
352 | static gpointer instproxy_status_updater(gpointer arg) | 418 | static void* instproxy_receive_status_loop_thread(void* arg) |
353 | { | 419 | { |
354 | struct instproxy_status_data *data = (struct instproxy_status_data*)arg; | 420 | struct instproxy_status_data *data = (struct instproxy_status_data*)arg; |
355 | 421 | ||
356 | /* run until the operation is complete or an error occurs */ | 422 | /* run until the command is complete or an error occurs */ |
357 | (void)instproxy_perform_operation(data->client, data->cbfunc, data->operation, data->user_data); | 423 | (void)instproxy_receive_status_loop(data->client, data->command, data->cbfunc, data->user_data); |
358 | 424 | ||
359 | /* cleanup */ | 425 | /* cleanup */ |
360 | instproxy_lock(data->client); | 426 | instproxy_lock(data->client); |
427 | |||
361 | debug_info("done, cleaning up."); | 428 | debug_info("done, cleaning up."); |
362 | if (data->operation) { | 429 | |
363 | free(data->operation); | 430 | if (data->command) { |
431 | plist_free(data->command); | ||
364 | } | 432 | } |
365 | data->client->status_updater = NULL; | 433 | |
434 | if (data->client->receive_status_thread) { | ||
435 | thread_free(data->client->receive_status_thread); | ||
436 | data->client->receive_status_thread = THREAD_T_NULL; | ||
437 | } | ||
438 | |||
366 | instproxy_unlock(data->client); | 439 | instproxy_unlock(data->client); |
367 | free(data); | 440 | free(data); |
368 | 441 | ||
@@ -370,379 +443,493 @@ static gpointer instproxy_status_updater(gpointer arg) | |||
370 | } | 443 | } |
371 | 444 | ||
372 | /** | 445 | /** |
373 | * Internally used helper function that creates a status updater thread which | 446 | * Internally used helper function that creates a "receive status" thread which |
374 | * will call the passed callback function when status updates occur. | 447 | * will call the passed callback function when a status is received. |
375 | * If status_cb is NULL no thread will be created, but the operation will | 448 | * |
376 | * run synchronously until it completes or an error occurs. | 449 | * If async is 0 no thread will be created and the command will run |
450 | * synchronously until it completes or an error occurs. | ||
377 | * | 451 | * |
378 | * @param client The connected installation proxy client | 452 | * @param client The connected installation proxy client |
379 | * @param status_cb Pointer to a callback function or NULL | 453 | * @param command Operation name. Will be passed to the callback function |
380 | * @param operation Operation name. Will be passed to the callback function | ||
381 | * in async mode or shown in debug messages in sync mode. | 454 | * in async mode or shown in debug messages in sync mode. |
455 | * @param async A boolean indicating if receive loop should be run | ||
456 | * asynchronously or block. | ||
457 | * @param status_cb Pointer to a callback function or NULL. | ||
382 | * @param user_data Callback data passed to status_cb. | 458 | * @param user_data Callback data passed to status_cb. |
383 | * | 459 | * |
384 | * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or | 460 | * @return INSTPROXY_E_SUCCESS when the thread was created (async mode), or |
385 | * when the operation completed successfully (sync). | 461 | * when the command completed successfully (sync). |
386 | * An INSTPROXY_E_* error value is returned if an error occured. | 462 | * An INSTPROXY_E_* error value is returned if an error occurred. |
387 | */ | 463 | */ |
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) | 464 | 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 | { | 465 | { |
466 | if (!client || !client->parent || !command) { | ||
467 | return INSTPROXY_E_INVALID_ARG; | ||
468 | } | ||
469 | |||
470 | if (client->receive_status_thread) { | ||
471 | return INSTPROXY_E_OP_IN_PROGRESS; | ||
472 | } | ||
473 | |||
390 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | 474 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
391 | if (status_cb) { | 475 | if (async == INSTPROXY_COMMAND_TYPE_ASYNC) { |
392 | /* async mode */ | 476 | /* async mode */ |
393 | struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); | 477 | struct instproxy_status_data *data = (struct instproxy_status_data*)malloc(sizeof(struct instproxy_status_data)); |
394 | if (data) { | 478 | if (data) { |
395 | data->client = client; | 479 | data->client = client; |
480 | data->command = plist_copy(command); | ||
396 | data->cbfunc = status_cb; | 481 | data->cbfunc = status_cb; |
397 | data->operation = strdup(operation); | ||
398 | data->user_data = user_data; | 482 | data->user_data = user_data; |
399 | 483 | ||
400 | client->status_updater = g_thread_create(instproxy_status_updater, data, TRUE, NULL); | 484 | if (thread_new(&client->receive_status_thread, instproxy_receive_status_loop_thread, data) == 0) { |
401 | if (client->status_updater) { | ||
402 | res = INSTPROXY_E_SUCCESS; | 485 | res = INSTPROXY_E_SUCCESS; |
403 | } | 486 | } |
404 | } | 487 | } |
405 | } else { | 488 | } else { |
406 | /* sync mode */ | 489 | /* sync mode as a fallback */ |
407 | res = instproxy_perform_operation(client, NULL, operation, NULL); | 490 | res = instproxy_receive_status_loop(client, command, status_cb, user_data); |
408 | } | 491 | } |
492 | |||
409 | return res; | 493 | return res; |
410 | } | 494 | } |
411 | 495 | ||
412 | |||
413 | /** | 496 | /** |
414 | * Internal function used by instproxy_install and instproxy_upgrade. | 497 | * Internal core function to send a command and process the response. |
415 | * | 498 | * |
416 | * @param client The connected installation_proxy client | 499 | * @param client The connected installation_proxy client |
417 | * @param pkg_path Path of the installation package (inside the AFC jail) | 500 | * @param command The command specification dictionary. |
418 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | 501 | * @param async A boolean indicating whether the receive loop should be run |
419 | * @param status_cb Callback function for progress and status information. If | 502 | * asynchronously or block until completing the command. |
420 | * NULL is passed, this function will run synchronously. | 503 | * @param status_cb Callback function to call if a command status is received. |
421 | * @param command The command to execute. | ||
422 | * @param user_data Callback data passed to status_cb. | 504 | * @param user_data Callback data passed to status_cb. |
423 | * | 505 | * |
424 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | 506 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if |
425 | * an error occured. | 507 | * an error occurred. |
426 | */ | 508 | */ |
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) | 509 | 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 | { | 510 | { |
429 | if (!client || !client->parent || !pkg_path) { | 511 | if (!client || !client->parent || !command) { |
430 | return INSTPROXY_E_INVALID_ARG; | 512 | return INSTPROXY_E_INVALID_ARG; |
431 | } | 513 | } |
432 | if (client->status_updater) { | 514 | |
515 | if (client->receive_status_thread) { | ||
433 | return INSTPROXY_E_OP_IN_PROGRESS; | 516 | return INSTPROXY_E_OP_IN_PROGRESS; |
434 | } | 517 | } |
435 | 518 | ||
519 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
520 | |||
521 | /* send command */ | ||
436 | instproxy_lock(client); | 522 | instproxy_lock(client); |
437 | instproxy_error_t res = instproxy_send_command(client, command, client_options, NULL, pkg_path); | 523 | res = instproxy_send_command(client, command); |
438 | instproxy_unlock(client); | 524 | instproxy_unlock(client); |
439 | 525 | ||
440 | if (res != INSTPROXY_E_SUCCESS) { | 526 | /* loop until status or error is received */ |
441 | debug_info("could not send plist, error %d", res); | 527 | res = instproxy_receive_status_loop_with_callback(client, command, async, status_cb, user_data); |
442 | return res; | ||
443 | } | ||
444 | 528 | ||
445 | return instproxy_create_status_updater(client, status_cb, command, user_data); | 529 | return res; |
446 | } | 530 | } |
447 | 531 | ||
448 | /** | 532 | 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 | { | 533 | { |
474 | return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Install", user_data); | 534 | if (!client || !client->parent || !status_cb) |
535 | return INSTPROXY_E_INVALID_ARG; | ||
536 | |||
537 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
538 | |||
539 | plist_t command = plist_new_dict(); | ||
540 | plist_dict_set_item(command, "Command", plist_new_string("Browse")); | ||
541 | if (client_options) | ||
542 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
543 | |||
544 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, (void*)user_data); | ||
545 | |||
546 | plist_free(command); | ||
547 | |||
548 | return res; | ||
475 | } | 549 | } |
476 | 550 | ||
477 | /** | 551 | 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 | { | 552 | { |
505 | return instproxy_install_or_upgrade(client, pkg_path, client_options, status_cb, "Upgrade", user_data); | 553 | plist_t *result_array = (plist_t*)user_data; |
554 | uint64_t current_amount = 0; | ||
555 | plist_t current_list = NULL; | ||
556 | uint64_t i; | ||
557 | |||
558 | instproxy_status_get_current_list(status, NULL, NULL, ¤t_amount, ¤t_list); | ||
559 | |||
560 | debug_info("current_amount: %d", current_amount); | ||
561 | |||
562 | if (current_amount > 0) { | ||
563 | for (i = 0; current_list && (i < current_amount); i++) { | ||
564 | plist_t item = plist_array_get_item(current_list, i); | ||
565 | plist_array_append_item(*result_array, plist_copy(item)); | ||
566 | } | ||
567 | } | ||
568 | |||
569 | if (current_list) | ||
570 | plist_free(current_list); | ||
506 | } | 571 | } |
507 | 572 | ||
508 | /** | 573 | 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 | { | 574 | { |
529 | if (!client || !client->parent || !appid) { | 575 | if (!client || !client->parent || !result) |
530 | return INSTPROXY_E_INVALID_ARG; | 576 | return INSTPROXY_E_INVALID_ARG; |
531 | } | ||
532 | |||
533 | if (client->status_updater) { | ||
534 | return INSTPROXY_E_OP_IN_PROGRESS; | ||
535 | } | ||
536 | 577 | ||
537 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | 578 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
538 | plist_t dict = plist_new_dict(); | ||
539 | plist_dict_insert_item(dict, "ApplicationIdentifier", plist_new_string(appid)); | ||
540 | plist_dict_insert_item(dict, "Command", plist_new_string("Uninstall")); | ||
541 | 579 | ||
542 | instproxy_lock(client); | 580 | plist_t result_array = plist_new_array(); |
543 | res = instproxy_send_command(client, "Uninstall", client_options, appid, NULL); | ||
544 | instproxy_unlock(client); | ||
545 | 581 | ||
546 | plist_free(dict); | 582 | plist_t command = plist_new_dict(); |
583 | plist_dict_set_item(command, "Command", plist_new_string("Browse")); | ||
584 | if (client_options) | ||
585 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
547 | 586 | ||
548 | if (res != INSTPROXY_E_SUCCESS) { | 587 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_append_current_list_to_result_cb, (void*)&result_array); |
549 | debug_info("could not send plist, error %d", res); | 588 | |
550 | return res; | 589 | if (res == INSTPROXY_E_SUCCESS) { |
590 | *result = result_array; | ||
591 | } else { | ||
592 | plist_free(result_array); | ||
551 | } | 593 | } |
552 | 594 | ||
553 | return instproxy_create_status_updater(client, status_cb, "Uninstall", user_data); | 595 | plist_free(command); |
596 | |||
597 | return res; | ||
554 | } | 598 | } |
555 | 599 | ||
556 | /** | 600 | static void instproxy_copy_lookup_result_cb(plist_t command, plist_t status, void *user_data) |
557 | * List archived applications. This function runs synchronously. | ||
558 | * | ||
559 | * @see instproxy_archive | ||
560 | * | ||
561 | * @param client The connected installation_proxy client | ||
562 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | ||
563 | * Currently there are no known client options, so pass NULL here. | ||
564 | * @param result Pointer that will be set to a plist containing a PLIST_DICT | ||
565 | * holding information about the archived applications found. | ||
566 | * | ||
567 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | ||
568 | * an error occured. | ||
569 | */ | ||
570 | instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) | ||
571 | { | 601 | { |
602 | plist_t* result = (plist_t*)user_data; | ||
603 | |||
604 | plist_t node = plist_dict_get_item(status, "LookupResult"); | ||
605 | if (node) { | ||
606 | *result = plist_copy(node); | ||
607 | } | ||
608 | } | ||
609 | |||
610 | instproxy_error_t instproxy_lookup(instproxy_client_t client, const char** appids, plist_t client_options, plist_t *result) | ||
611 | { | ||
612 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
613 | int i = 0; | ||
614 | plist_t lookup_result = NULL; | ||
615 | plist_t command = NULL; | ||
616 | plist_t appid_array = NULL; | ||
617 | plist_t node = NULL; | ||
618 | |||
572 | if (!client || !client->parent || !result) | 619 | if (!client || !client->parent || !result) |
573 | return INSTPROXY_E_INVALID_ARG; | 620 | return INSTPROXY_E_INVALID_ARG; |
574 | 621 | ||
575 | instproxy_lock(client); | 622 | command = plist_new_dict(); |
576 | instproxy_error_t res = instproxy_send_command(client, "LookupArchives", client_options, NULL, NULL); | 623 | plist_dict_set_item(command, "Command", plist_new_string("Lookup")); |
624 | if (client_options) { | ||
625 | node = plist_copy(client_options); | ||
626 | } else if (appids) { | ||
627 | node = plist_new_dict(); | ||
628 | } | ||
577 | 629 | ||
578 | if (res != INSTPROXY_E_SUCCESS) { | 630 | /* add bundle identifiers to client options */ |
579 | debug_info("could not send plist, error %d", res); | 631 | if (appids) { |
580 | goto leave_unlock; | 632 | appid_array = plist_new_array(); |
633 | while (appids[i]) { | ||
634 | plist_array_append_item(appid_array, plist_new_string(appids[i])); | ||
635 | i++; | ||
636 | } | ||
637 | plist_dict_set_item(node, "BundleIDs", appid_array); | ||
581 | } | 638 | } |
582 | 639 | ||
583 | res = instproxy_error(property_list_service_receive_plist(client->parent, result)); | 640 | if (node) { |
584 | if (res != INSTPROXY_E_SUCCESS) { | 641 | plist_dict_set_item(command, "ClientOptions", node); |
585 | debug_info("could not receive plist, error %d", res); | ||
586 | goto leave_unlock; | ||
587 | } | 642 | } |
588 | 643 | ||
589 | res = INSTPROXY_E_SUCCESS; | 644 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result); |
645 | |||
646 | if (res == INSTPROXY_E_SUCCESS) { | ||
647 | *result = lookup_result; | ||
648 | } else { | ||
649 | plist_free(lookup_result); | ||
650 | } | ||
651 | |||
652 | plist_free(command); | ||
653 | |||
654 | return res; | ||
655 | } | ||
656 | |||
657 | 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) | ||
658 | { | ||
659 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
660 | |||
661 | plist_t command = plist_new_dict(); | ||
662 | plist_dict_set_item(command, "Command", plist_new_string("Install")); | ||
663 | if (client_options) | ||
664 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
665 | plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path)); | ||
666 | |||
667 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
668 | |||
669 | plist_free(command); | ||
670 | |||
671 | return res; | ||
672 | } | ||
673 | |||
674 | 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) | ||
675 | { | ||
676 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
677 | |||
678 | plist_t command = plist_new_dict(); | ||
679 | plist_dict_set_item(command, "Command", plist_new_string("Upgrade")); | ||
680 | if (client_options) | ||
681 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
682 | plist_dict_set_item(command, "PackagePath", plist_new_string(pkg_path)); | ||
683 | |||
684 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
685 | |||
686 | plist_free(command); | ||
687 | |||
688 | return res; | ||
689 | } | ||
690 | |||
691 | 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) | ||
692 | { | ||
693 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
694 | |||
695 | plist_t command = plist_new_dict(); | ||
696 | plist_dict_set_item(command, "Command", plist_new_string("Uninstall")); | ||
697 | if (client_options) | ||
698 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
699 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
700 | |||
701 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
702 | |||
703 | plist_free(command); | ||
704 | |||
705 | return res; | ||
706 | } | ||
707 | |||
708 | instproxy_error_t instproxy_lookup_archives(instproxy_client_t client, plist_t client_options, plist_t *result) | ||
709 | { | ||
710 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
711 | |||
712 | plist_t command = plist_new_dict(); | ||
713 | plist_dict_set_item(command, "Command", plist_new_string("LookupArchives")); | ||
714 | if (client_options) | ||
715 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
716 | |||
717 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)result); | ||
718 | |||
719 | plist_free(command); | ||
590 | 720 | ||
591 | leave_unlock: | ||
592 | instproxy_unlock(client); | ||
593 | return res; | 721 | return res; |
594 | } | 722 | } |
595 | 723 | ||
596 | /** | ||
597 | * Archive an application on the device. | ||
598 | * This function tells the device to make an archive of the specified | ||
599 | * application. This results in the device creating a ZIP archive in the | ||
600 | * 'ApplicationArchives' directory and uninstalling the application. | ||
601 | * | ||
602 | * @param client The connected installation proxy client | ||
603 | * @param appid ApplicationIdentifier of the app to archive. | ||
604 | * @param client_options The client options to use, as PLIST_DICT, or NULL. | ||
605 | * Valid options include: | ||
606 | * "SkipUninstall" -> Boolean | ||
607 | * "ArchiveType" -> "ApplicationOnly" | ||
608 | * @param status_cb Callback function for progress and status information. If | ||
609 | * NULL is passed, this function will run synchronously. | ||
610 | * @param user_data Callback data passed to status_cb. | ||
611 | * | ||
612 | * @return INSTPROXY_E_SUCCESS on success or an INSTPROXY_E_* error value if | ||
613 | * an error occured. | ||
614 | * | ||
615 | * @note If a callback function is given (async mode), this function returns | ||
616 | * INSTPROXY_E_SUCCESS immediately if the status updater thread has been | ||
617 | * created successfully; any error occuring during the operation has to be | ||
618 | * handled inside the specified callback function. | ||
619 | */ | ||
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) | 724 | 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 | { | 725 | { |
622 | if (!client || !client->parent || !appid) | 726 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
727 | |||
728 | plist_t command = plist_new_dict(); | ||
729 | plist_dict_set_item(command, "Command", plist_new_string("Archive")); | ||
730 | if (client_options) | ||
731 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
732 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
733 | |||
734 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
735 | |||
736 | plist_free(command); | ||
737 | |||
738 | return res; | ||
739 | } | ||
740 | |||
741 | 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) | ||
742 | { | ||
743 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
744 | |||
745 | plist_t command = plist_new_dict(); | ||
746 | plist_dict_set_item(command, "Command", plist_new_string("Restore")); | ||
747 | if (client_options) | ||
748 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
749 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
750 | |||
751 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
752 | |||
753 | plist_free(command); | ||
754 | |||
755 | return res; | ||
756 | } | ||
757 | |||
758 | 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) | ||
759 | { | ||
760 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
761 | |||
762 | plist_t command = plist_new_dict(); | ||
763 | plist_dict_set_item(command, "Command", plist_new_string("RemoveArchive")); | ||
764 | if (client_options) | ||
765 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
766 | plist_dict_set_item(command, "ApplicationIdentifier", plist_new_string(appid)); | ||
767 | |||
768 | res = instproxy_perform_command(client, command, status_cb == NULL ? INSTPROXY_COMMAND_TYPE_SYNC : INSTPROXY_COMMAND_TYPE_ASYNC, status_cb, user_data); | ||
769 | |||
770 | plist_free(command); | ||
771 | |||
772 | return res; | ||
773 | } | ||
774 | |||
775 | instproxy_error_t instproxy_check_capabilities_match(instproxy_client_t client, const char** capabilities, plist_t client_options, plist_t *result) | ||
776 | { | ||
777 | if (!client || !capabilities || !result) | ||
623 | return INSTPROXY_E_INVALID_ARG; | 778 | return INSTPROXY_E_INVALID_ARG; |
624 | 779 | ||
625 | if (client->status_updater) { | 780 | plist_t lookup_result = NULL; |
626 | return INSTPROXY_E_OP_IN_PROGRESS; | 781 | |
782 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; | ||
783 | |||
784 | plist_t command = plist_new_dict(); | ||
785 | plist_dict_set_item(command, "Command", plist_new_string("CheckCapabilitiesMatch")); | ||
786 | if (client_options) | ||
787 | plist_dict_set_item(command, "ClientOptions", plist_copy(client_options)); | ||
788 | |||
789 | if (capabilities) { | ||
790 | int i = 0; | ||
791 | plist_t capabilities_array = plist_new_array(); | ||
792 | while (capabilities[i]) { | ||
793 | plist_array_append_item(capabilities_array, plist_new_string(capabilities[i])); | ||
794 | i++; | ||
795 | } | ||
796 | plist_dict_set_item(command, "Capabilities", capabilities_array); | ||
627 | } | 797 | } |
628 | 798 | ||
629 | instproxy_lock(client); | 799 | res = instproxy_perform_command(client, command, INSTPROXY_COMMAND_TYPE_SYNC, instproxy_copy_lookup_result_cb, (void*)&lookup_result); |
630 | instproxy_error_t res = instproxy_send_command(client, "Archive", client_options, appid, NULL); | ||
631 | instproxy_unlock(client); | ||
632 | 800 | ||
633 | if (res != INSTPROXY_E_SUCCESS) { | 801 | if (res == INSTPROXY_E_SUCCESS) { |
634 | debug_info("could not send plist, error %d", res); | 802 | *result = lookup_result; |
635 | return res; | 803 | } else { |
804 | plist_free(lookup_result); | ||
636 | } | 805 | } |
637 | return instproxy_create_status_updater(client, status_cb, "Archive", user_data); | 806 | |
807 | plist_free(command); | ||
808 | |||
809 | return res; | ||
638 | } | 810 | } |
639 | 811 | ||
640 | /** | 812 | 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 | { | 813 | { |
663 | if (!client || !client->parent || !appid) | 814 | instproxy_error_t res = INSTPROXY_E_UNKNOWN_ERROR; |
815 | |||
816 | if (!status || !name) | ||
664 | return INSTPROXY_E_INVALID_ARG; | 817 | return INSTPROXY_E_INVALID_ARG; |
665 | 818 | ||
666 | if (client->status_updater) { | 819 | plist_t node = plist_dict_get_item(status, "Error"); |
667 | return INSTPROXY_E_OP_IN_PROGRESS; | 820 | if (node) { |
821 | plist_get_string_val(node, name); | ||
822 | } else { | ||
823 | /* no error here */ | ||
824 | res = INSTPROXY_E_SUCCESS; | ||
668 | } | 825 | } |
669 | 826 | ||
670 | instproxy_lock(client); | 827 | if (code != NULL) { |
671 | instproxy_error_t res = instproxy_send_command(client, "Restore", client_options, appid, NULL); | 828 | *code = 0; |
672 | instproxy_unlock(client); | 829 | node = plist_dict_get_item(status, "ErrorDetail"); |
830 | if (node) { | ||
831 | plist_get_uint_val(node, code); | ||
832 | *code &= 0xffffffff; | ||
833 | } | ||
834 | } | ||
673 | 835 | ||
674 | if (res != INSTPROXY_E_SUCCESS) { | 836 | if (description != NULL) { |
675 | debug_info("could not send plist, error %d", res); | 837 | node = plist_dict_get_item(status, "ErrorDescription"); |
676 | return res; | 838 | if (node) { |
839 | plist_get_string_val(node, description); | ||
840 | } | ||
841 | } | ||
842 | |||
843 | if (*name) { | ||
844 | res = instproxy_strtoerr(*name); | ||
677 | } | 845 | } |
678 | return instproxy_create_status_updater(client, status_cb, "Restore", user_data); | 846 | |
847 | return res; | ||
679 | } | 848 | } |
680 | 849 | ||
681 | /** | 850 | 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 | { | 851 | { |
704 | if (!client || !client->parent || !appid) | 852 | if (name) { |
705 | return INSTPROXY_E_INVALID_ARG; | 853 | plist_t node = plist_dict_get_item(status, "Status"); |
854 | if (node) { | ||
855 | plist_get_string_val(node, name); | ||
856 | } else { | ||
857 | *name = NULL; | ||
858 | } | ||
859 | } | ||
860 | } | ||
706 | 861 | ||
707 | if (client->status_updater) { | 862 | void instproxy_status_get_percent_complete(plist_t status, int *percent) |
708 | return INSTPROXY_E_OP_IN_PROGRESS; | 863 | { |
864 | uint64_t val = 0; | ||
865 | if (percent) { | ||
866 | plist_t node = plist_dict_get_item(status, "PercentComplete"); | ||
867 | if (node) { | ||
868 | plist_get_uint_val(node, &val); | ||
869 | *percent = val; | ||
870 | } | ||
709 | } | 871 | } |
872 | } | ||
710 | 873 | ||
711 | instproxy_lock(client); | 874 | 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); | 875 | { |
713 | instproxy_unlock(client); | 876 | plist_t node = NULL; |
877 | |||
878 | if (status && plist_get_node_type(status) == PLIST_DICT) { | ||
879 | /* command specific logic: parse browsed list */ | ||
880 | if (list != NULL) { | ||
881 | node = plist_dict_get_item(status, "CurrentList"); | ||
882 | if (node) { | ||
883 | *current_amount = plist_array_get_size(node); | ||
884 | *list = plist_copy(node); | ||
885 | } | ||
886 | } | ||
714 | 887 | ||
715 | if (res != INSTPROXY_E_SUCCESS) { | 888 | if (total != NULL) { |
716 | debug_info("could not send plist, error %d", res); | 889 | node = plist_dict_get_item(status, "Total"); |
717 | return res; | 890 | if (node) { |
891 | plist_get_uint_val(node, total); | ||
892 | } | ||
893 | } | ||
894 | |||
895 | if (current_amount != NULL) { | ||
896 | node = plist_dict_get_item(status, "CurrentAmount"); | ||
897 | if (node) { | ||
898 | plist_get_uint_val(node, current_amount); | ||
899 | } | ||
900 | } | ||
901 | |||
902 | if (current_index != NULL) { | ||
903 | node = plist_dict_get_item(status, "CurrentIndex"); | ||
904 | if (node) { | ||
905 | plist_get_uint_val(node, current_index); | ||
906 | } | ||
907 | } | ||
718 | } | 908 | } |
719 | return instproxy_create_status_updater(client, status_cb, "RemoveArchive", user_data); | ||
720 | } | 909 | } |
721 | 910 | ||
722 | /** | 911 | void instproxy_command_get_name(plist_t command, char** name) |
723 | * Create a new client_options plist. | 912 | { |
724 | * | 913 | if (name) { |
725 | * @return A new plist_t of type PLIST_DICT. | 914 | plist_t node = plist_dict_get_item(command, "Command"); |
726 | */ | 915 | if (node) { |
727 | plist_t instproxy_client_options_new() | 916 | plist_get_string_val(node, name); |
917 | } else { | ||
918 | *name = NULL; | ||
919 | } | ||
920 | } | ||
921 | } | ||
922 | |||
923 | plist_t instproxy_client_options_new(void) | ||
728 | { | 924 | { |
729 | return plist_new_dict(); | 925 | return plist_new_dict(); |
730 | } | 926 | } |
731 | 927 | ||
732 | /** | ||
733 | * Add one or more new key:value pairs to the given client_options. | ||
734 | * | ||
735 | * @param client_options The client options to modify. | ||
736 | * @param ... KEY, VALUE, [KEY, VALUE], NULL | ||
737 | * | ||
738 | * @note The keys and values passed are expected to be strings, except for | ||
739 | * "ApplicationSINF" and "iTunesMetadata" expecting a plist node of type | ||
740 | * PLIST_DATA as value, or "SkipUninstall" needing int as value. | ||
741 | */ | ||
742 | void instproxy_client_options_add(plist_t client_options, ...) | 928 | void instproxy_client_options_add(plist_t client_options, ...) |
743 | { | 929 | { |
744 | if (!client_options) | 930 | if (!client_options) |
745 | return; | 931 | return; |
932 | |||
746 | va_list args; | 933 | va_list args; |
747 | va_start(args, client_options); | 934 | va_start(args, client_options); |
748 | char *arg = va_arg(args, char*); | 935 | char *arg = va_arg(args, char*); |
@@ -750,21 +937,21 @@ void instproxy_client_options_add(plist_t client_options, ...) | |||
750 | char *key = strdup(arg); | 937 | char *key = strdup(arg); |
751 | if (!strcmp(key, "SkipUninstall")) { | 938 | if (!strcmp(key, "SkipUninstall")) { |
752 | int intval = va_arg(args, int); | 939 | int intval = va_arg(args, int); |
753 | plist_dict_insert_item(client_options, key, plist_new_bool(intval)); | 940 | plist_dict_set_item(client_options, key, plist_new_bool(intval)); |
754 | } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata")) { | 941 | } else if (!strcmp(key, "ApplicationSINF") || !strcmp(key, "iTunesMetadata") || !strcmp(key, "ReturnAttributes") || !strcmp(key, "BundleIDs")) { |
755 | plist_t plistval = va_arg(args, plist_t); | 942 | plist_t plistval = va_arg(args, plist_t); |
756 | if (!plistval) { | 943 | if (!plistval) { |
757 | free(key); | 944 | free(key); |
758 | break; | 945 | break; |
759 | } | 946 | } |
760 | plist_dict_insert_item(client_options, key, plist_copy(plistval)); | 947 | plist_dict_set_item(client_options, key, plist_copy(plistval)); |
761 | } else { | 948 | } else { |
762 | char *strval = va_arg(args, char*); | 949 | char *strval = va_arg(args, char*); |
763 | if (!strval) { | 950 | if (!strval) { |
764 | free(key); | 951 | free(key); |
765 | break; | 952 | break; |
766 | } | 953 | } |
767 | plist_dict_insert_item(client_options, key, plist_new_string(strval)); | 954 | plist_dict_set_item(client_options, key, plist_new_string(strval)); |
768 | } | 955 | } |
769 | free(key); | 956 | free(key); |
770 | arg = va_arg(args, char*); | 957 | arg = va_arg(args, char*); |
@@ -772,15 +959,106 @@ void instproxy_client_options_add(plist_t client_options, ...) | |||
772 | va_end(args); | 959 | va_end(args); |
773 | } | 960 | } |
774 | 961 | ||
775 | /** | 962 | void instproxy_client_options_set_return_attributes(plist_t client_options, ...) |
776 | * Free client_options plist. | 963 | { |
777 | * | 964 | if (!client_options) |
778 | * @param client_options The client options plist to free. Does nothing if NULL | 965 | return; |
779 | * is passed. | 966 | |
780 | */ | 967 | plist_t return_attributes = plist_new_array(); |
968 | |||
969 | va_list args; | ||
970 | va_start(args, client_options); | ||
971 | char *arg = va_arg(args, char*); | ||
972 | while (arg) { | ||
973 | char *attribute = strdup(arg); | ||
974 | plist_array_append_item(return_attributes, plist_new_string(attribute)); | ||
975 | free(attribute); | ||
976 | arg = va_arg(args, char*); | ||
977 | } | ||
978 | va_end(args); | ||
979 | |||
980 | plist_dict_set_item(client_options, "ReturnAttributes", return_attributes); | ||
981 | } | ||
982 | |||
781 | void instproxy_client_options_free(plist_t client_options) | 983 | void instproxy_client_options_free(plist_t client_options) |
782 | { | 984 | { |
783 | if (client_options) { | 985 | if (client_options) { |
784 | plist_free(client_options); | 986 | plist_free(client_options); |
785 | } | 987 | } |
786 | } | 988 | } |
989 | |||
990 | instproxy_error_t instproxy_client_get_path_for_bundle_identifier(instproxy_client_t client, const char* bundle_id, char** path) | ||
991 | { | ||
992 | if (!client || !client->parent || !bundle_id) | ||
993 | return INSTPROXY_E_INVALID_ARG; | ||
994 | |||
995 | plist_t apps = NULL; | ||
996 | |||
997 | // create client options for any application types | ||
998 | plist_t client_opts = instproxy_client_options_new(); | ||
999 | instproxy_client_options_add(client_opts, "ApplicationType", "Any", NULL); | ||
1000 | |||
1001 | // only return attributes we need | ||
1002 | instproxy_client_options_set_return_attributes(client_opts, "CFBundleIdentifier", "CFBundleExecutable", "Path", NULL); | ||
1003 | |||
1004 | // only query for specific appid | ||
1005 | const char* appids[] = {bundle_id, NULL}; | ||
1006 | |||
1007 | // query device for list of apps | ||
1008 | instproxy_error_t ierr = instproxy_lookup(client, appids, client_opts, &apps); | ||
1009 | |||
1010 | instproxy_client_options_free(client_opts); | ||
1011 | |||
1012 | if (ierr != INSTPROXY_E_SUCCESS) { | ||
1013 | return ierr; | ||
1014 | } | ||
1015 | |||
1016 | plist_t app_found = plist_access_path(apps, 1, bundle_id); | ||
1017 | if (!app_found) { | ||
1018 | if (apps) | ||
1019 | plist_free(apps); | ||
1020 | *path = NULL; | ||
1021 | return INSTPROXY_E_OP_FAILED; | ||
1022 | } | ||
1023 | |||
1024 | char* path_str = NULL; | ||
1025 | plist_t path_p = plist_dict_get_item(app_found, "Path"); | ||
1026 | if (path_p) { | ||
1027 | plist_get_string_val(path_p, &path_str); | ||
1028 | } | ||
1029 | |||
1030 | char* exec_str = NULL; | ||
1031 | plist_t exec_p = plist_dict_get_item(app_found, "CFBundleExecutable"); | ||
1032 | if (exec_p) { | ||
1033 | plist_get_string_val(exec_p, &exec_str); | ||
1034 | } | ||
1035 | |||
1036 | if (!path_str) { | ||
1037 | debug_info("app path not found"); | ||
1038 | return INSTPROXY_E_OP_FAILED; | ||
1039 | } | ||
1040 | |||
1041 | if (!exec_str) { | ||
1042 | debug_info("bundle executable not found"); | ||
1043 | return INSTPROXY_E_OP_FAILED; | ||
1044 | } | ||
1045 | |||
1046 | plist_free(apps); | ||
1047 | |||
1048 | char* ret = (char*)malloc(strlen(path_str) + 1 + strlen(exec_str) + 1); | ||
1049 | strcpy(ret, path_str); | ||
1050 | strcat(ret, "/"); | ||
1051 | strcat(ret, exec_str); | ||
1052 | |||
1053 | *path = ret; | ||
1054 | |||
1055 | if (path_str) { | ||
1056 | free(path_str); | ||
1057 | } | ||
1058 | |||
1059 | if (exec_str) { | ||
1060 | free(exec_str); | ||
1061 | } | ||
1062 | |||
1063 | return INSTPROXY_E_SUCCESS; | ||
1064 | } | ||
diff --git a/src/installation_proxy.h b/src/installation_proxy.h index b497d62..5bdbb71 100644 --- a/src/installation_proxy.h +++ b/src/installation_proxy.h | |||
@@ -2,34 +2,36 @@ | |||
2 | * installation_proxy.h | 2 | * installation_proxy.h |
3 | * com.apple.mobile.installation_proxy service header file. | 3 | * com.apple.mobile.installation_proxy service header file. |
4 | * | 4 | * |
5 | * Copyright (c) 2009 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved. |
6 | * Copyright (c) 2010-2013 Nikias Bassen, All Rights Reserved. | ||
6 | * | 7 | * |
7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 9 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 10 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 11 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 12 | * |
12 | * This library is distributed in the hope that it will be useful, | 13 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 16 | * Lesser General Public License for more details. |
16 | * | 17 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 18 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 19 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 21 | */ |
21 | #ifndef IINSTALLATION_PROXY_H | ||
22 | #define IINSTALLATION_PROXY_H | ||
23 | 22 | ||
24 | #include <glib.h> | 23 | #ifndef __INSTALLATION_PROXY_H |
24 | #define __INSTALLATION_PROXY_H | ||
25 | 25 | ||
26 | #include "idevice.h" | ||
26 | #include "libimobiledevice/installation_proxy.h" | 27 | #include "libimobiledevice/installation_proxy.h" |
27 | #include "property_list_service.h" | 28 | #include "property_list_service.h" |
29 | #include <libimobiledevice-glue/thread.h> | ||
28 | 30 | ||
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..c457cb2 --- /dev/null +++ b/src/lockdown-cu.c | |||
@@ -0,0 +1,1197 @@ | |||
1 | /* | ||
2 | * lockdown-cu.c | ||
3 | * com.apple.mobile.lockdownd service CU additions | ||
4 | * | ||
5 | * Copyright (c) 2021 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | |||
26 | #include <string.h> | ||
27 | #include <stdlib.h> | ||
28 | #define _GNU_SOURCE 1 | ||
29 | #define __USE_GNU 1 | ||
30 | #include <stdio.h> | ||
31 | #include <ctype.h> | ||
32 | |||
33 | #ifndef _MSC_VER | ||
34 | #include <unistd.h> | ||
35 | #endif | ||
36 | |||
37 | #include <plist/plist.h> | ||
38 | |||
39 | #include "idevice.h" | ||
40 | #include "lockdown.h" | ||
41 | #include "common/debug.h" | ||
42 | |||
43 | #ifdef HAVE_WIRELESS_PAIRING | ||
44 | |||
45 | #include <libimobiledevice-glue/utils.h> | ||
46 | #include <libimobiledevice-glue/socket.h> | ||
47 | #include <libimobiledevice-glue/opack.h> | ||
48 | #include <libimobiledevice-glue/tlv.h> | ||
49 | |||
50 | #if defined(HAVE_OPENSSL) | ||
51 | #include <openssl/hmac.h> | ||
52 | #include <openssl/evp.h> | ||
53 | #include <openssl/rand.h> | ||
54 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) | ||
55 | #include <openssl/chacha.h> | ||
56 | #include <openssl/poly1305.h> | ||
57 | #endif | ||
58 | #elif defined(HAVE_GCRYPT) | ||
59 | #include <gcrypt.h> | ||
60 | #elif defined(HAVE_MBEDTLS) | ||
61 | #include <mbedtls/md.h> | ||
62 | #include <mbedtls/chachapoly.h> | ||
63 | #endif | ||
64 | |||
65 | #ifdef __APPLE__ | ||
66 | #include <sys/sysctl.h> | ||
67 | #include <SystemConfiguration/SystemConfiguration.h> | ||
68 | #include <CoreFoundation/CoreFoundation.h> | ||
69 | #include <TargetConditionals.h> | ||
70 | #endif | ||
71 | |||
72 | #include "property_list_service.h" | ||
73 | #include "common/userpref.h" | ||
74 | |||
75 | #include "endianness.h" | ||
76 | |||
77 | #include "srp.h" | ||
78 | #include "ed25519.h" | ||
79 | |||
80 | /* {{{ SRP6a parameters */ | ||
81 | static const unsigned char kSRPModulus3072[384] = { | ||
82 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, | ||
83 | 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, | ||
84 | 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, | ||
85 | 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, | ||
86 | 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, | ||
87 | 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, | ||
88 | 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, | ||
89 | 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, | ||
90 | 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, | ||
91 | 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, | ||
92 | 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, | ||
93 | 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, | ||
94 | 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, | ||
95 | 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, | ||
96 | 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, | ||
97 | 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, | ||
98 | 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, | ||
99 | 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, | ||
100 | 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, | ||
101 | 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, | ||
102 | 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, | ||
103 | 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, | ||
104 | 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, | ||
105 | 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
106 | }; | ||
107 | |||
108 | static const unsigned char kSRPGenerator5 = 5; | ||
109 | /* }}} */ | ||
110 | |||
111 | /* {{{ HKDF */ | ||
112 | #if defined(HAVE_OPENSSL) | ||
113 | #define MD_ALGO_SHA512 EVP_sha512() | ||
114 | typedef const EVP_MD* MD_ALGO_TYPE_T; | ||
115 | #define MD_ALGO_DIGEST_SIZE EVP_MD_size | ||
116 | #define MD_MAX_DIGEST_SIZE EVP_MAX_MD_SIZE | ||
117 | |||
118 | #elif defined(HAVE_GCRYPT) | ||
119 | #define MD_ALGO_SHA512 GCRY_MD_SHA512 | ||
120 | typedef int MD_ALGO_TYPE_T; | ||
121 | #define MD_ALGO_DIGEST_SIZE gcry_md_get_algo_dlen | ||
122 | #define MD_MAX_DIGEST_SIZE 64 | ||
123 | |||
124 | 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) | ||
125 | { | ||
126 | gcry_md_hd_t hd; | ||
127 | if (gcry_md_open(&hd, md, GCRY_MD_FLAG_HMAC)) { | ||
128 | debug_info("gcry_md_open() failed"); | ||
129 | return; | ||
130 | } | ||
131 | if (gcry_md_setkey(hd, key, key_len)) { | ||
132 | gcry_md_close (hd); | ||
133 | debug_info("gcry_md_setkey() failed"); | ||
134 | return; | ||
135 | } | ||
136 | gcry_md_write(hd, data, data_len); | ||
137 | |||
138 | unsigned char* digest = gcry_md_read(hd, md); | ||
139 | if (!digest) { | ||
140 | gcry_md_close(hd); | ||
141 | debug_info("gcry_md_read() failed"); | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | *out_len = gcry_md_get_algo_dlen(md); | ||
146 | memcpy(out, digest, *out_len); | ||
147 | gcry_md_close(hd); | ||
148 | } | ||
149 | #elif defined(HAVE_MBEDTLS) | ||
150 | #define MD_ALGO_SHA512 MBEDTLS_MD_SHA512 | ||
151 | typedef mbedtls_md_type_t MD_ALGO_TYPE_T; | ||
152 | #define MD_ALGO_DIGEST_SIZE(x) mbedtls_md_get_size(mbedtls_md_info_from_type(x)) | ||
153 | #define MD_MAX_DIGEST_SIZE MBEDTLS_MD_MAX_SIZE | ||
154 | |||
155 | 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) | ||
156 | { | ||
157 | mbedtls_md_context_t mdctx; | ||
158 | mbedtls_md_init(&mdctx); | ||
159 | int mr = mbedtls_md_setup(&mdctx, mbedtls_md_info_from_type(md), 1); | ||
160 | if (mr != 0) { | ||
161 | debug_info("mbedtls_md_setup() failed: %d", mr); | ||
162 | return; | ||
163 | } | ||
164 | |||
165 | mr = mbedtls_md_hmac_starts(&mdctx, key, key_len); | ||
166 | if (mr != 0) { | ||
167 | mbedtls_md_free(&mdctx); | ||
168 | debug_info("mbedtls_md_hmac_starts() failed: %d", mr); | ||
169 | return; | ||
170 | } | ||
171 | |||
172 | mbedtls_md_hmac_update(&mdctx, data, data_len); | ||
173 | |||
174 | mr = mbedtls_md_hmac_finish(&mdctx, out); | ||
175 | if (mr == 0) { | ||
176 | *out_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md)); | ||
177 | } else { | ||
178 | debug_info("mbedtls_md_hmac_finish() failed: %d", mr); | ||
179 | } | ||
180 | mbedtls_md_free(&mdctx); | ||
181 | } | ||
182 | #endif | ||
183 | |||
184 | 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) | ||
185 | { | ||
186 | unsigned char empty_salt[MD_MAX_DIGEST_SIZE]; | ||
187 | if (!md || !out || !out_len || !*out_len) return; | ||
188 | if (salt_len == 0) { | ||
189 | salt_len = MD_ALGO_DIGEST_SIZE(md); | ||
190 | salt = (unsigned char*)empty_salt; | ||
191 | } | ||
192 | HMAC(md, salt, salt_len, input_key_material, input_key_material_len, out, out_len); | ||
193 | } | ||
194 | |||
195 | 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) | ||
196 | { | ||
197 | if (!md || !out || !out_len || !*out_len) return; | ||
198 | unsigned int md_size = MD_ALGO_DIGEST_SIZE(md); | ||
199 | if (*out_len > 255 * md_size) { | ||
200 | *out_len = 0; | ||
201 | return; | ||
202 | } | ||
203 | int blocks_needed = (*out_len) / md_size; | ||
204 | if (((*out_len) % md_size) != 0) blocks_needed++; | ||
205 | unsigned int okm_len = 0; | ||
206 | unsigned char okm_block[MD_MAX_DIGEST_SIZE]; | ||
207 | unsigned int okm_block_len = 0; | ||
208 | int i; | ||
209 | for (i = 0; i < blocks_needed; i++) { | ||
210 | unsigned int output_block_len = okm_block_len + info_len + 1; | ||
211 | unsigned char* output_block = malloc(output_block_len); | ||
212 | if (okm_block_len > 0) { | ||
213 | memcpy(output_block, okm_block, okm_block_len); | ||
214 | } | ||
215 | memcpy(output_block + okm_block_len, info, info_len); | ||
216 | output_block[okm_block_len + info_len] = (uint8_t)(i+1); | ||
217 | |||
218 | HMAC(md, prk, prk_len, output_block, output_block_len, okm_block, &okm_block_len); | ||
219 | if (okm_len < *out_len) { | ||
220 | memcpy(out + okm_len, okm_block, (okm_len + okm_block_len > *out_len) ? *out_len - okm_len : okm_block_len); | ||
221 | } | ||
222 | okm_len += okm_block_len; | ||
223 | free(output_block); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | 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) | ||
228 | { | ||
229 | if (!md || !initial_key_material || !out || !out_len || !*out_len) return; | ||
230 | |||
231 | unsigned char prk[MD_MAX_DIGEST_SIZE]; | ||
232 | unsigned int prk_len = MD_ALGO_DIGEST_SIZE(md); | ||
233 | |||
234 | hkdf_md_extract(md, salt, salt_len, initial_key_material, initial_key_material_size, prk, &prk_len); | ||
235 | if (prk_len > 0) { | ||
236 | hkdf_md_expand(md, prk, prk_len, info, info_len, out, out_len); | ||
237 | } else { | ||
238 | *out_len = 0; | ||
239 | } | ||
240 | } | ||
241 | /* }}} */ | ||
242 | |||
243 | /* {{{ chacha20 poly1305 encryption/decryption */ | ||
244 | #if defined(HAVE_OPENSSL) && defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) | ||
245 | /* {{{ From: OpenBSD's e_chacha20poly1305.c */ | ||
246 | /* | ||
247 | * Copyright (c) 2015 Reyk Floter <reyk@openbsd.org> | ||
248 | * Copyright (c) 2014, Google Inc. | ||
249 | * | ||
250 | * Permission to use, copy, modify, and/or distribute this software for any | ||
251 | * purpose with or without fee is hereby granted, provided that the above | ||
252 | * copyright notice and this permission notice appear in all copies. | ||
253 | * | ||
254 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
255 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
256 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||
257 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
258 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
259 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
260 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
261 | */ | ||
262 | static void | ||
263 | poly1305_update_with_length(poly1305_state *poly1305, | ||
264 | const unsigned char *data, size_t data_len) | ||
265 | { | ||
266 | size_t j = data_len; | ||
267 | unsigned char length_bytes[8]; | ||
268 | unsigned i; | ||
269 | |||
270 | for (i = 0; i < sizeof(length_bytes); i++) { | ||
271 | length_bytes[i] = j; | ||
272 | j >>= 8; | ||
273 | } | ||
274 | |||
275 | if (data != NULL) | ||
276 | CRYPTO_poly1305_update(poly1305, data, data_len); | ||
277 | CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes)); | ||
278 | } | ||
279 | |||
280 | static void | ||
281 | poly1305_update_with_pad16(poly1305_state *poly1305, | ||
282 | const unsigned char *data, size_t data_len) | ||
283 | { | ||
284 | static const unsigned char zero_pad16[16]; | ||
285 | size_t pad_len; | ||
286 | |||
287 | CRYPTO_poly1305_update(poly1305, data, data_len); | ||
288 | |||
289 | /* pad16() is defined in RFC 7539 2.8.1. */ | ||
290 | if ((pad_len = data_len % 16) == 0) | ||
291 | return; | ||
292 | |||
293 | CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len); | ||
294 | } | ||
295 | /* }}} */ | ||
296 | #endif | ||
297 | |||
298 | 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) | ||
299 | { | ||
300 | #if defined(HAVE_OPENSSL) | ||
301 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) | ||
302 | #if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) | ||
303 | const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); | ||
304 | EVP_AEAD_CTX ctx; | ||
305 | EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); | ||
306 | EVP_AEAD_CTX_seal(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); | ||
307 | #else | ||
308 | unsigned char poly1305_key[32]; | ||
309 | poly1305_state poly1305; | ||
310 | uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; | ||
311 | const unsigned char* iv = nonce + 4; | ||
312 | |||
313 | memset(poly1305_key, 0, sizeof(poly1305_key)); | ||
314 | CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); | ||
315 | |||
316 | CRYPTO_poly1305_init(&poly1305, poly1305_key); | ||
317 | poly1305_update_with_pad16(&poly1305, ad, ad_len); | ||
318 | CRYPTO_chacha_20(out, in, in_len, key, iv, ctr + 1); | ||
319 | poly1305_update_with_pad16(&poly1305, out, in_len); | ||
320 | poly1305_update_with_length(&poly1305, NULL, ad_len); | ||
321 | poly1305_update_with_length(&poly1305, NULL, in_len); | ||
322 | |||
323 | CRYPTO_poly1305_finish(&poly1305, out + in_len); | ||
324 | |||
325 | *out_len = in_len + 16; | ||
326 | #endif | ||
327 | #elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) | ||
328 | int outl = 0; | ||
329 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
330 | EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); | ||
331 | EVP_EncryptUpdate(ctx, out, &outl, in, in_len); | ||
332 | *out_len = outl; | ||
333 | outl = 0; | ||
334 | EVP_EncryptFinal_ex(ctx, out + *out_len, &outl); | ||
335 | *out_len += outl; | ||
336 | EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, out + *out_len); | ||
337 | EVP_CIPHER_CTX_free(ctx); | ||
338 | *out_len += 16; | ||
339 | #else | ||
340 | #error Please use a newer version of OpenSSL (>= 1.1.0) | ||
341 | #endif | ||
342 | #elif defined(HAVE_GCRYPT) | ||
343 | #if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) | ||
344 | gcry_cipher_hd_t hd; | ||
345 | if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { | ||
346 | debug_info("gcry_cipher_open() failed"); | ||
347 | return; | ||
348 | } | ||
349 | gcry_cipher_setkey(hd, key, 32); | ||
350 | gcry_cipher_setiv(hd, nonce, 12); | ||
351 | gcry_cipher_authenticate(hd, ad, ad_len); | ||
352 | *out_len = in_len + 16; | ||
353 | if (gcry_cipher_encrypt(hd, out, *out_len, in, in_len)) { | ||
354 | *out_len = 0; | ||
355 | } | ||
356 | gcry_cipher_gettag(hd, out+in_len, 16); | ||
357 | gcry_cipher_close(hd); | ||
358 | #else | ||
359 | #error Please use a newer version of libgcrypt (>= 1.7.0) | ||
360 | #endif | ||
361 | #elif defined (HAVE_MBEDTLS) | ||
362 | mbedtls_chachapoly_context ctx; | ||
363 | mbedtls_chachapoly_init(&ctx); | ||
364 | mbedtls_chachapoly_setkey(&ctx, key); | ||
365 | if (mbedtls_chachapoly_encrypt_and_tag(&ctx, in_len, nonce, ad, ad_len, in, out, out+in_len) != 0) { | ||
366 | *out_len = 0; | ||
367 | } | ||
368 | mbedtls_chachapoly_free(&ctx); | ||
369 | #else | ||
370 | #error chacha20_poly1305_encrypt_96 is not implemented | ||
371 | #endif | ||
372 | } | ||
373 | |||
374 | 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) | ||
375 | { | ||
376 | unsigned char _nonce[12]; | ||
377 | *(uint32_t*)(&_nonce[0]) = 0; | ||
378 | memcpy(&_nonce[4], nonce, 8); | ||
379 | chacha20_poly1305_encrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); | ||
380 | } | ||
381 | |||
382 | 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) | ||
383 | { | ||
384 | #if defined(HAVE_OPENSSL) | ||
385 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) | ||
386 | #if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) | ||
387 | const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); | ||
388 | EVP_AEAD_CTX ctx; | ||
389 | EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); | ||
390 | EVP_AEAD_CTX_open(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); | ||
391 | #else | ||
392 | unsigned char mac[16]; | ||
393 | unsigned char poly1305_key[32]; | ||
394 | poly1305_state poly1305; | ||
395 | size_t plaintext_len = in_len - 16; | ||
396 | uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; | ||
397 | const unsigned char *iv = nonce + 4; | ||
398 | |||
399 | memset(poly1305_key, 0, sizeof(poly1305_key)); | ||
400 | CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); | ||
401 | |||
402 | CRYPTO_poly1305_init(&poly1305, poly1305_key); | ||
403 | poly1305_update_with_pad16(&poly1305, ad, ad_len); | ||
404 | poly1305_update_with_pad16(&poly1305, in, plaintext_len); | ||
405 | poly1305_update_with_length(&poly1305, NULL, ad_len); | ||
406 | poly1305_update_with_length(&poly1305, NULL, plaintext_len); | ||
407 | |||
408 | CRYPTO_poly1305_finish(&poly1305, mac); | ||
409 | |||
410 | if (memcmp(mac, in + plaintext_len, 16) != 0) { | ||
411 | *out_len = 0; | ||
412 | return; | ||
413 | } | ||
414 | |||
415 | CRYPTO_chacha_20(out, in, plaintext_len, key, iv, ctr + 1); | ||
416 | *out_len = plaintext_len; | ||
417 | #endif | ||
418 | #elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) | ||
419 | int outl = 0; | ||
420 | size_t plaintext_len = in_len - 16; | ||
421 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
422 | EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); | ||
423 | EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, in + plaintext_len); | ||
424 | EVP_DecryptUpdate(ctx, out, &outl, in, plaintext_len); | ||
425 | *out_len = outl; | ||
426 | outl = 0; | ||
427 | if (EVP_DecryptFinal_ex(ctx, out + *out_len, &outl) == 1) { | ||
428 | *out_len += outl; | ||
429 | } else { | ||
430 | *out_len = 0; | ||
431 | } | ||
432 | EVP_CIPHER_CTX_free(ctx); | ||
433 | #else | ||
434 | #error Please use a newer version of OpenSSL (>= 1.1.0) | ||
435 | #endif | ||
436 | #elif defined(HAVE_GCRYPT) | ||
437 | #if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) | ||
438 | gcry_cipher_hd_t hd; | ||
439 | if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { | ||
440 | debug_info("gcry_cipher_open() failed"); | ||
441 | return; | ||
442 | } | ||
443 | gcry_cipher_setkey(hd, key, 32); | ||
444 | gcry_cipher_setiv(hd, nonce, 12); | ||
445 | gcry_cipher_authenticate(hd, ad, ad_len); | ||
446 | unsigned int plaintext_len = in_len - 16; | ||
447 | gcry_cipher_decrypt(hd, out, *out_len, in, plaintext_len); | ||
448 | if (gcry_cipher_checktag(hd, in + plaintext_len, 16) == 0) { | ||
449 | *out_len = plaintext_len; | ||
450 | } else { | ||
451 | *out_len = 0; | ||
452 | } | ||
453 | gcry_cipher_close(hd); | ||
454 | #else | ||
455 | #error Please use a newer version of libgcrypt (>= 1.7.0) | ||
456 | #endif | ||
457 | #elif defined(HAVE_MBEDTLS) | ||
458 | mbedtls_chachapoly_context ctx; | ||
459 | mbedtls_chachapoly_init(&ctx); | ||
460 | mbedtls_chachapoly_setkey(&ctx, key); | ||
461 | unsigned int plaintext_len = in_len - 16; | ||
462 | if (mbedtls_chachapoly_auth_decrypt(&ctx, plaintext_len, nonce, ad, ad_len, in + plaintext_len, in, out) == 0) { | ||
463 | *out_len = plaintext_len; | ||
464 | } else { | ||
465 | *out_len = 0; | ||
466 | } | ||
467 | mbedtls_chachapoly_free(&ctx); | ||
468 | #else | ||
469 | #error chacha20_poly1305_decrypt_96 is not implemented | ||
470 | #endif | ||
471 | } | ||
472 | |||
473 | 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) | ||
474 | { | ||
475 | unsigned char _nonce[12]; | ||
476 | *(uint32_t*)(&_nonce[0]) = 0; | ||
477 | memcpy(&_nonce[4], nonce, 8); | ||
478 | chacha20_poly1305_decrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); | ||
479 | } | ||
480 | /* }}} */ | ||
481 | |||
482 | #define PAIRING_ERROR(x) \ | ||
483 | debug_info(x); \ | ||
484 | if (pairing_callback) { \ | ||
485 | pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, (char*)x, NULL); \ | ||
486 | } | ||
487 | |||
488 | #define PAIRING_ERROR_FMT(...) \ | ||
489 | sprintf(tmp, __VA_ARGS__); \ | ||
490 | debug_info(tmp); \ | ||
491 | if (pairing_callback) { \ | ||
492 | pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, tmp, NULL); \ | ||
493 | } | ||
494 | |||
495 | #endif /* HAVE_WIRELESS_PAIRING */ | ||
496 | |||
497 | 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) | ||
498 | { | ||
499 | #ifdef HAVE_WIRELESS_PAIRING | ||
500 | if (!client || !pairing_callback || (host_info && plist_get_node_type(host_info) != PLIST_DICT) || (acl && plist_get_node_type(acl) != PLIST_DICT)) | ||
501 | return LOCKDOWN_E_INVALID_ARG; | ||
502 | |||
503 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
504 | |||
505 | if (client->device && client->device->version == 0) { | ||
506 | plist_t p_version = NULL; | ||
507 | if (lockdownd_get_value(client, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { | ||
508 | int vers[3] = {0, 0, 0}; | ||
509 | char *s_version = NULL; | ||
510 | plist_get_string_val(p_version, &s_version); | ||
511 | if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { | ||
512 | client->device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]); | ||
513 | } | ||
514 | free(s_version); | ||
515 | } | ||
516 | plist_free(p_version); | ||
517 | } | ||
518 | |||
519 | char* pairing_uuid = NULL; | ||
520 | if (host_info) { | ||
521 | plist_t accountid = plist_dict_get_item(host_info, "accountID"); | ||
522 | if (accountid && plist_get_node_type(accountid) == PLIST_STRING) { | ||
523 | plist_get_string_val(accountid, &pairing_uuid); | ||
524 | } | ||
525 | } | ||
526 | if (!pairing_uuid) { | ||
527 | userpref_read_system_buid(&pairing_uuid); | ||
528 | } | ||
529 | if (!pairing_uuid) { | ||
530 | pairing_uuid = generate_uuid(); | ||
531 | } | ||
532 | unsigned int pairing_uuid_len = strlen(pairing_uuid); | ||
533 | |||
534 | SRP_initialize_library(); | ||
535 | |||
536 | SRP* srp = SRP_new(SRP6a_sha512_client_method()); | ||
537 | if (!srp) { | ||
538 | PAIRING_ERROR("Failed to initialize SRP") | ||
539 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
540 | } | ||
541 | |||
542 | char tmp[256]; | ||
543 | plist_t dict = NULL; | ||
544 | uint8_t current_state = 0; | ||
545 | uint8_t final_state = 6; | ||
546 | |||
547 | unsigned char* salt = NULL; | ||
548 | unsigned int salt_size = 0; | ||
549 | unsigned char* pubkey = NULL; | ||
550 | unsigned int pubkey_size = 0; | ||
551 | |||
552 | unsigned char setup_encryption_key[32]; | ||
553 | |||
554 | cstr *thekey = NULL; | ||
555 | |||
556 | do { | ||
557 | current_state++; | ||
558 | |||
559 | dict = plist_new_dict(); | ||
560 | plist_dict_set_item(dict, "Request", plist_new_string("CUPairingCreate")); | ||
561 | if (current_state == 1) { | ||
562 | plist_dict_set_item(dict, "Flags", plist_new_uint(1)); | ||
563 | } else { | ||
564 | plist_dict_set_item(dict, "Flags", plist_new_uint(0)); | ||
565 | } | ||
566 | |||
567 | tlv_buf_t tlv = tlv_buf_new(); | ||
568 | |||
569 | if (current_state == 1) { | ||
570 | /* send method */ | ||
571 | tlv_buf_append(tlv, 0x00, 1, (void*)"\x00"); // 0x00 (Method), 1 bytes, 00 | ||
572 | } else if (current_state == 3) { | ||
573 | /* generate public key */ | ||
574 | cstr* own_pub = NULL; | ||
575 | SRP_gen_pub(srp, &own_pub); | ||
576 | |||
577 | if (!own_pub) { | ||
578 | PAIRING_ERROR("[SRP] Failed to generate public key") | ||
579 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
580 | break; | ||
581 | } | ||
582 | |||
583 | /* compute key from remote's public key */ | ||
584 | if (SRP_compute_key(srp, &thekey, pubkey, pubkey_size) != 0) { | ||
585 | cstr_free(own_pub); | ||
586 | PAIRING_ERROR("[SRP] Failed to compute key") | ||
587 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
588 | break; | ||
589 | } | ||
590 | |||
591 | /* compute response */ | ||
592 | cstr *response = NULL; | ||
593 | SRP_respond(srp, &response); | ||
594 | |||
595 | /* send our public key + response */ | ||
596 | tlv_buf_append(tlv, 0x03, own_pub->length, own_pub->data); | ||
597 | tlv_buf_append(tlv, 0x04, response->length, response->data); | ||
598 | cstr_free(response); | ||
599 | cstr_free(own_pub); | ||
600 | } else if (current_state == 5) { | ||
601 | /* send encrypted info */ | ||
602 | |||
603 | static const char PAIR_SETUP_ENCRYPT_SALT[] = "Pair-Setup-Encrypt-Salt"; | ||
604 | static const char PAIR_SETUP_ENCRYPT_INFO[] = "Pair-Setup-Encrypt-Info"; | ||
605 | static const char PAIR_SETUP_CONTROLLER_SIGN_SALT[] = "Pair-Setup-Controller-Sign-Salt"; | ||
606 | static const char PAIR_SETUP_CONTROLLER_SIGN_INFO[] = "Pair-Setup-Controller-Sign-Info"; | ||
607 | |||
608 | // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Encrypt-Salt + Pair-Setup-Encrypt-Info | ||
609 | // result used as key for chacha20-poly1305 | ||
610 | unsigned int setup_encryption_key_len = sizeof(setup_encryption_key); | ||
611 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_ENCRYPT_SALT, sizeof(PAIR_SETUP_ENCRYPT_SALT)-1, (unsigned char*)PAIR_SETUP_ENCRYPT_INFO, sizeof(PAIR_SETUP_ENCRYPT_INFO)-1, (unsigned char*)thekey->data, thekey->length, setup_encryption_key, &setup_encryption_key_len); | ||
612 | |||
613 | unsigned char ed25519_pubkey[32]; | ||
614 | unsigned char ed25519_privkey[64]; | ||
615 | unsigned char ed25519seed[32]; | ||
616 | ed25519_create_seed(ed25519seed); | ||
617 | |||
618 | ed25519_create_keypair(ed25519_pubkey, ed25519_privkey, ed25519seed); | ||
619 | |||
620 | unsigned int signbuf_len = pairing_uuid_len + 64; | ||
621 | unsigned char* signbuf = malloc(signbuf_len); | ||
622 | unsigned int hkdf_len = 32; | ||
623 | // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Controller-Sign-Salt + Pair-Setup-Controller-Sign-Info | ||
624 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_SALT, sizeof(PAIR_SETUP_CONTROLLER_SIGN_SALT)-1, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_INFO, sizeof(PAIR_SETUP_CONTROLLER_SIGN_INFO)-1, (unsigned char*)thekey->data, thekey->length, signbuf, &hkdf_len); | ||
625 | |||
626 | memcpy(signbuf + 32, pairing_uuid, pairing_uuid_len); | ||
627 | memcpy(signbuf + 32 + pairing_uuid_len, ed25519_pubkey, 32); | ||
628 | |||
629 | unsigned char ed_sig[64]; | ||
630 | ed25519_sign(ed_sig, signbuf, 0x64, ed25519_pubkey, ed25519_privkey); | ||
631 | |||
632 | tlv_buf_t tlvbuf = tlv_buf_new(); | ||
633 | tlv_buf_append(tlvbuf, 0x01, pairing_uuid_len, (void*)pairing_uuid); | ||
634 | tlv_buf_append(tlvbuf, 0x03, sizeof(ed25519_pubkey), ed25519_pubkey); | ||
635 | tlv_buf_append(tlvbuf, 0x0a, sizeof(ed_sig), ed_sig); | ||
636 | |||
637 | /* ACL */ | ||
638 | unsigned char* odata = NULL; | ||
639 | unsigned int olen = 0; | ||
640 | if (acl) { | ||
641 | opack_encode_from_plist(acl, &odata, &olen); | ||
642 | } else { | ||
643 | /* defaut ACL */ | ||
644 | plist_t acl_plist = plist_new_dict(); | ||
645 | plist_dict_set_item(acl_plist, "com.apple.ScreenCapture", plist_new_bool(1)); | ||
646 | plist_dict_set_item(acl_plist, "com.apple.developer", plist_new_bool(1)); | ||
647 | opack_encode_from_plist(acl_plist, &odata, &olen); | ||
648 | plist_free(acl_plist); | ||
649 | } | ||
650 | tlv_buf_append(tlvbuf, 0x12, olen, odata); | ||
651 | free(odata); | ||
652 | |||
653 | /* HOST INFORMATION */ | ||
654 | char hostname[256]; | ||
655 | #if defined(__APPLE__) && !defined(TARGET_OS_IPHONE) | ||
656 | CFStringRef cname = SCDynamicStoreCopyComputerName(NULL, NULL); | ||
657 | CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8); | ||
658 | CFRelease(cname); | ||
659 | #else | ||
660 | #ifdef _WIN32 | ||
661 | DWORD hostname_len = sizeof(hostname); | ||
662 | GetComputerName(hostname, &hostname_len); | ||
663 | #else | ||
664 | gethostname(hostname, sizeof(hostname)); | ||
665 | #endif | ||
666 | #endif | ||
667 | |||
668 | char modelname[256]; | ||
669 | modelname[0] = '\0'; | ||
670 | #ifdef __APPLE__ | ||
671 | size_t len = sizeof(modelname); | ||
672 | sysctlbyname("hw.model", &modelname, &len, NULL, 0); | ||
673 | #endif | ||
674 | if (strlen(modelname) == 0) { | ||
675 | strcpy(modelname, "HackbookPro13,37"); | ||
676 | } | ||
677 | |||
678 | unsigned char primary_mac_addr[6] = { 0, 0, 0, 0, 0, 0 }; | ||
679 | if (get_primary_mac_address(primary_mac_addr) != 0) { | ||
680 | debug_info("Failed to get primary mac address"); | ||
681 | } | ||
682 | debug_info("Primary mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", primary_mac_addr[0], primary_mac_addr[1], primary_mac_addr[2], primary_mac_addr[3], primary_mac_addr[4], primary_mac_addr[5]); | ||
683 | |||
684 | // "OPACK" encoded device info | ||
685 | plist_t info_plist = plist_new_dict(); | ||
686 | //plist_dict_set_item(info_plist, "altIRK", plist_new_data((char*)altIRK, 16)); | ||
687 | plist_dict_set_item(info_plist, "accountID", plist_new_string(pairing_uuid)); | ||
688 | plist_dict_set_item(info_plist, "model", plist_new_string(modelname)); | ||
689 | plist_dict_set_item(info_plist, "name", plist_new_string(hostname)); | ||
690 | plist_dict_set_item(info_plist, "mac", plist_new_data((char*)primary_mac_addr, 6)); | ||
691 | if (host_info) { | ||
692 | plist_dict_merge(&info_plist, host_info); | ||
693 | } | ||
694 | opack_encode_from_plist(info_plist, &odata, &olen); | ||
695 | plist_free(info_plist); | ||
696 | tlv_buf_append(tlvbuf, 0x11, olen, odata); | ||
697 | free(odata); | ||
698 | |||
699 | size_t encrypted_len = tlvbuf->length + 16; | ||
700 | unsigned char* encrypted_buf = (unsigned char*)malloc(encrypted_len); | ||
701 | |||
702 | chacha20_poly1305_encrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg05", NULL, 0, tlvbuf->data, tlvbuf->length, encrypted_buf, &encrypted_len); | ||
703 | |||
704 | tlv_buf_free(tlvbuf); | ||
705 | |||
706 | tlv_buf_append(tlv, 0x05, encrypted_len, encrypted_buf); | ||
707 | free(encrypted_buf); | ||
708 | } else { | ||
709 | tlv_buf_free(tlv); | ||
710 | PAIRING_ERROR("[SRP] Invalid state"); | ||
711 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
712 | break; | ||
713 | } | ||
714 | tlv_buf_append(tlv, 0x06, 1, ¤t_state); | ||
715 | plist_dict_set_item(dict, "Payload", plist_new_data((char*)tlv->data, tlv->length)); | ||
716 | tlv_buf_free(tlv); | ||
717 | |||
718 | plist_dict_set_item(dict, "Label", plist_new_string(client->label)); | ||
719 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); | ||
720 | |||
721 | ret = lockdownd_send(client, dict); | ||
722 | plist_free(dict); | ||
723 | dict = NULL; | ||
724 | |||
725 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
726 | break; | ||
727 | } | ||
728 | |||
729 | current_state++; | ||
730 | |||
731 | ret = lockdownd_receive(client, &dict); | ||
732 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
733 | break; | ||
734 | } | ||
735 | ret = lockdown_check_result(dict, "CUPairingCreate"); | ||
736 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
737 | break; | ||
738 | } | ||
739 | |||
740 | plist_t extresp = plist_dict_get_item(dict, "ExtendedResponse"); | ||
741 | if (!extresp) { | ||
742 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
743 | break; | ||
744 | } | ||
745 | plist_t blob = plist_dict_get_item(extresp, "Payload"); | ||
746 | if (!blob) { | ||
747 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
748 | break; | ||
749 | } | ||
750 | uint64_t data_len = 0; | ||
751 | const char* data = plist_get_data_ptr(blob, &data_len); | ||
752 | |||
753 | uint8_t state = 0; | ||
754 | if (!tlv_data_get_uint8(data, data_len, 0x06, &state)) { | ||
755 | PAIRING_ERROR("[SRP] ERROR: Could not find state in response"); | ||
756 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
757 | break; | ||
758 | } | ||
759 | if (state != current_state) { | ||
760 | PAIRING_ERROR_FMT("[SRP] ERROR: Unexpected state %d, expected %d", state, current_state); | ||
761 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
762 | break; | ||
763 | } | ||
764 | |||
765 | unsigned int errval = 0; | ||
766 | uint64_t u64val = 0; | ||
767 | tlv_data_get_uint(data, data_len, 0x07, &u64val); | ||
768 | debug_buffer(data, data_len); | ||
769 | errval = (unsigned int)u64val; | ||
770 | if (errval > 0) { | ||
771 | if (errval == 3) { | ||
772 | u64val = 0; | ||
773 | tlv_data_get_uint(data, data_len, 0x08, &u64val); | ||
774 | if (u64val > 0) { | ||
775 | uint32_t retry_delay = (uint32_t)u64val; | ||
776 | PAIRING_ERROR_FMT("[SRP] Pairing is blocked for another %u seconds", retry_delay) | ||
777 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
778 | break; | ||
779 | } | ||
780 | } else if (errval == 2 && state == 4) { | ||
781 | PAIRING_ERROR_FMT("[SRP] Invalid PIN") | ||
782 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
783 | break; | ||
784 | } else { | ||
785 | PAIRING_ERROR_FMT("[SRP] Received error %u in state %d.", errval, state); | ||
786 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
787 | break; | ||
788 | } | ||
789 | } | ||
790 | |||
791 | if (state == 2) { | ||
792 | /* receive salt and public key */ | ||
793 | if (!tlv_data_copy_data(data, data_len, 0x02, (void**)&salt, &salt_size)) { | ||
794 | PAIRING_ERROR("[SRP] ERROR: Could not find salt in response"); | ||
795 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
796 | break; | ||
797 | } | ||
798 | if (!tlv_data_copy_data(data, data_len, 0x03, (void**)&pubkey, &pubkey_size)) { | ||
799 | PAIRING_ERROR("[SRP] ERROR: Could not find public key in response"); | ||
800 | |||
801 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
802 | break; | ||
803 | } | ||
804 | |||
805 | const char PAIR_SETUP[] = "Pair-Setup"; | ||
806 | if (SRP_set_user_raw(srp, (const unsigned char*)PAIR_SETUP, sizeof(PAIR_SETUP)-1) != 0) { | ||
807 | PAIRING_ERROR("[SRP] Failed to set SRP user"); | ||
808 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
809 | break; | ||
810 | } | ||
811 | |||
812 | /* kSRPParameters_3072_SHA512 */ | ||
813 | if (SRP_set_params(srp, kSRPModulus3072, sizeof(kSRPModulus3072), &kSRPGenerator5, 1, salt, salt_size) != 0) { | ||
814 | PAIRING_ERROR("[SRP] Failed to set SRP parameters"); | ||
815 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
816 | break; | ||
817 | |||
818 | } | ||
819 | |||
820 | if (pairing_callback) { | ||
821 | char pin[64]; | ||
822 | unsigned int pin_len = sizeof(pin); | ||
823 | pairing_callback(LOCKDOWN_CU_PAIRING_PIN_REQUESTED, cb_user_data, pin, &pin_len); | ||
824 | |||
825 | SRP_set_auth_password_raw(srp, (const unsigned char*)pin, pin_len); | ||
826 | } | ||
827 | } else if (state == 4) { | ||
828 | /* receive proof */ | ||
829 | unsigned char* proof = NULL; | ||
830 | unsigned int proof_len = 0; | ||
831 | |||
832 | if (!tlv_data_copy_data(data, data_len, 0x04, (void**)&proof, &proof_len)) { | ||
833 | PAIRING_ERROR("[SRP] ERROR: Could not find proof data in response"); | ||
834 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
835 | break; | ||
836 | } | ||
837 | |||
838 | /* verify */ | ||
839 | int vrfy_result = SRP_verify(srp, proof, proof_len); | ||
840 | free(proof); | ||
841 | |||
842 | if (vrfy_result == 0) { | ||
843 | debug_info("[SRP] PIN verified successfully"); | ||
844 | } else { | ||
845 | PAIRING_ERROR("[SRP] PIN verification failure"); | ||
846 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
847 | break; | ||
848 | } | ||
849 | |||
850 | } else if (state == 6) { | ||
851 | int srp_pair_success = 0; | ||
852 | plist_t node = plist_dict_get_item(extresp, "doSRPPair"); | ||
853 | if (node) { | ||
854 | const char* strv = plist_get_string_ptr(node, NULL); | ||
855 | if (strcmp(strv, "succeed") == 0) { | ||
856 | srp_pair_success = 1; | ||
857 | } | ||
858 | } | ||
859 | if (!srp_pair_success) { | ||
860 | PAIRING_ERROR("SRP Pairing failed"); | ||
861 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
862 | break; | ||
863 | } | ||
864 | |||
865 | /* receive encrypted info */ | ||
866 | unsigned char* encrypted_buf = NULL; | ||
867 | unsigned int enc_len = 0; | ||
868 | if (!tlv_data_copy_data(data, data_len, 0x05, (void**)&encrypted_buf, &enc_len)) { | ||
869 | PAIRING_ERROR("[SRP] ERROR: Could not find encrypted data in response"); | ||
870 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
871 | break; | ||
872 | } | ||
873 | size_t plain_len = enc_len-16; | ||
874 | unsigned char* plain_buf = malloc(plain_len); | ||
875 | chacha20_poly1305_decrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg06", NULL, 0, encrypted_buf, enc_len, plain_buf, &plain_len); | ||
876 | free(encrypted_buf); | ||
877 | |||
878 | unsigned char* dev_info = NULL; | ||
879 | unsigned int dev_info_len = 0; | ||
880 | int res = tlv_data_copy_data(plain_buf, plain_len, 0x11, (void**)&dev_info, &dev_info_len); | ||
881 | free(plain_buf); | ||
882 | if (!res) { | ||
883 | PAIRING_ERROR("[SRP] ERROR: Failed to locate device info in response"); | ||
884 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
885 | break; | ||
886 | } | ||
887 | plist_t device_info = NULL; | ||
888 | opack_decode_to_plist(dev_info, dev_info_len, &device_info); | ||
889 | free(dev_info); | ||
890 | |||
891 | if (!device_info) { | ||
892 | PAIRING_ERROR("[SRP] ERROR: Failed to parse device info"); | ||
893 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
894 | break; | ||
895 | } | ||
896 | |||
897 | if (pairing_callback) { | ||
898 | pairing_callback(LOCKDOWN_CU_PAIRING_DEVICE_INFO, cb_user_data, device_info, NULL); | ||
899 | } | ||
900 | plist_free(device_info); | ||
901 | } else { | ||
902 | PAIRING_ERROR("[SRP] ERROR: Invalid state"); | ||
903 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
904 | break; | ||
905 | } | ||
906 | plist_free(dict); | ||
907 | dict = NULL; | ||
908 | |||
909 | } while (current_state != final_state); | ||
910 | |||
911 | plist_free(dict); | ||
912 | |||
913 | free(salt); | ||
914 | free(pubkey); | ||
915 | |||
916 | SRP_free(srp); | ||
917 | srp = NULL; | ||
918 | |||
919 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
920 | if (thekey) { | ||
921 | cstr_free(thekey); | ||
922 | } | ||
923 | return ret; | ||
924 | } | ||
925 | |||
926 | free(client->cu_key); | ||
927 | client->cu_key = malloc(thekey->length); | ||
928 | memcpy(client->cu_key, thekey->data, thekey->length); | ||
929 | client->cu_key_len = thekey->length; | ||
930 | cstr_free(thekey); | ||
931 | |||
932 | return LOCKDOWN_E_SUCCESS; | ||
933 | #else | ||
934 | debug_info("not supported"); | ||
935 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
936 | #endif | ||
937 | } | ||
938 | |||
939 | lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t client, const char* request, plist_t request_payload, plist_t* reply) | ||
940 | { | ||
941 | #ifdef HAVE_WIRELESS_PAIRING | ||
942 | if (!client || !request) | ||
943 | return LOCKDOWN_E_INVALID_ARG; | ||
944 | |||
945 | if (!client->cu_key) | ||
946 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
947 | |||
948 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
949 | |||
950 | /* derive keys */ | ||
951 | unsigned char cu_write_key[32]; | ||
952 | unsigned int cu_write_key_len = sizeof(cu_write_key); | ||
953 | static const char WRITE_KEY_SALT_MDLD[] = "WriteKeySaltMDLD"; | ||
954 | static const char WRITE_KEY_INFO_MDLD[] = "WriteKeyInfoMDLD"; | ||
955 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)WRITE_KEY_SALT_MDLD, sizeof(WRITE_KEY_SALT_MDLD)-1, (unsigned char*)WRITE_KEY_INFO_MDLD, sizeof(WRITE_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_write_key, &cu_write_key_len); | ||
956 | |||
957 | unsigned char cu_read_key[32]; | ||
958 | unsigned int cu_read_key_len = sizeof(cu_write_key); | ||
959 | static const char READ_KEY_SALT_MDLD[] = "ReadKeySaltMDLD"; | ||
960 | static const char READ_KEY_INFO_MDLD[] = "ReadKeyInfoMDLD"; | ||
961 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)READ_KEY_SALT_MDLD, sizeof(READ_KEY_SALT_MDLD)-1, (unsigned char*)READ_KEY_INFO_MDLD, sizeof(READ_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_read_key, &cu_read_key_len); | ||
962 | |||
963 | // Starting with iOS/tvOS 11.2 and WatchOS 4.2, this nonce is random and sent along with the request. Before, the request doesn't have a nonce and it uses hardcoded nonce "sendone01234". | ||
964 | unsigned char cu_nonce[] = "sendone01234"; // guaranteed to be random by fair dice troll | ||
965 | if (client->device->version >= IDEVICE_DEVICE_VERSION(11,2,0)) { | ||
966 | #if defined(HAVE_OPENSSL) | ||
967 | RAND_bytes(cu_nonce, sizeof(cu_nonce)-1); | ||
968 | #elif defined(HAVE_GCRYPT) | ||
969 | gcry_create_nonce(cu_nonce, sizeof(cu_nonce)-1); | ||
970 | #endif | ||
971 | } | ||
972 | |||
973 | debug_plist(request_payload); | ||
974 | |||
975 | /* convert request payload to binary */ | ||
976 | uint32_t bin_len = 0; | ||
977 | char* bin = NULL; | ||
978 | plist_to_bin(request_payload, &bin, &bin_len); | ||
979 | |||
980 | /* encrypt request */ | ||
981 | size_t encrypted_len = bin_len + 16; | ||
982 | unsigned char* encrypted_buf = malloc(encrypted_len); | ||
983 | chacha20_poly1305_encrypt_96(cu_write_key, cu_nonce, NULL, 0, (unsigned char*)bin, bin_len, encrypted_buf, &encrypted_len); | ||
984 | free(bin); | ||
985 | bin = NULL; | ||
986 | |||
987 | plist_t dict = plist_new_dict(); | ||
988 | plist_dict_set_item(dict,"Request", plist_new_string(request)); | ||
989 | plist_dict_set_item(dict, "Payload", plist_new_data((char*)encrypted_buf, encrypted_len)); | ||
990 | free(encrypted_buf); | ||
991 | plist_dict_set_item(dict, "Nonce", plist_new_data((char*)cu_nonce, sizeof(cu_nonce))); | ||
992 | plist_dict_set_item(dict, "Label", plist_new_string(client->label)); | ||
993 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); | ||
994 | |||
995 | /* send to device */ | ||
996 | ret = lockdownd_send(client, dict); | ||
997 | plist_free(dict); | ||
998 | dict = NULL; | ||
999 | |||
1000 | if (ret != LOCKDOWN_E_SUCCESS) | ||
1001 | return ret; | ||
1002 | |||
1003 | /* Now get device's answer */ | ||
1004 | ret = lockdownd_receive(client, &dict); | ||
1005 | if (ret != LOCKDOWN_E_SUCCESS) | ||
1006 | return ret; | ||
1007 | |||
1008 | ret = lockdown_check_result(dict, request); | ||
1009 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1010 | plist_free(dict); | ||
1011 | return ret; | ||
1012 | } | ||
1013 | |||
1014 | /* get payload */ | ||
1015 | plist_t blob = plist_dict_get_item(dict, "Payload"); | ||
1016 | if (!blob) { | ||
1017 | plist_free(dict); | ||
1018 | return LOCKDOWN_E_DICT_ERROR; | ||
1019 | } | ||
1020 | |||
1021 | uint64_t dl = 0; | ||
1022 | const char* dt = plist_get_data_ptr(blob, &dl); | ||
1023 | |||
1024 | /* see if we have a nonce */ | ||
1025 | blob = plist_dict_get_item(dict, "Nonce"); | ||
1026 | const unsigned char* rnonce = (unsigned char*)"receiveone01"; | ||
1027 | if (blob) { | ||
1028 | uint64_t rl = 0; | ||
1029 | rnonce = (const unsigned char*)plist_get_data_ptr(blob, &rl); | ||
1030 | } | ||
1031 | |||
1032 | /* decrypt payload */ | ||
1033 | size_t decrypted_len = dl-16; | ||
1034 | unsigned char* decrypted = malloc(decrypted_len); | ||
1035 | chacha20_poly1305_decrypt_96(cu_read_key, (unsigned char*)rnonce, NULL, 0, (unsigned char*)dt, dl, decrypted, &decrypted_len); | ||
1036 | plist_free(dict); | ||
1037 | dict = NULL; | ||
1038 | |||
1039 | plist_from_memory((const char*)decrypted, decrypted_len, &dict, NULL); | ||
1040 | if (!dict) { | ||
1041 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
1042 | debug_info("Failed to parse PLIST from decrypted payload:"); | ||
1043 | debug_buffer((const char*)decrypted, decrypted_len); | ||
1044 | free(decrypted); | ||
1045 | return ret; | ||
1046 | } | ||
1047 | free(decrypted); | ||
1048 | |||
1049 | debug_plist(dict); | ||
1050 | |||
1051 | if (reply) { | ||
1052 | *reply = dict; | ||
1053 | } else { | ||
1054 | plist_free(dict); | ||
1055 | } | ||
1056 | |||
1057 | return LOCKDOWN_E_SUCCESS; | ||
1058 | #else | ||
1059 | debug_info("not supported"); | ||
1060 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1061 | #endif | ||
1062 | } | ||
1063 | |||
1064 | lockdownd_error_t lockdownd_get_value_cu(lockdownd_client_t client, const char* domain, const char* key, plist_t* value) | ||
1065 | { | ||
1066 | #ifdef HAVE_WIRELESS_PAIRING | ||
1067 | if (!client) | ||
1068 | return LOCKDOWN_E_INVALID_ARG; | ||
1069 | |||
1070 | if (!client->cu_key) | ||
1071 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1072 | |||
1073 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
1074 | |||
1075 | plist_t request = plist_new_dict(); | ||
1076 | if (domain) { | ||
1077 | plist_dict_set_item(request, "Domain", plist_new_string(domain)); | ||
1078 | } | ||
1079 | if (key) { | ||
1080 | plist_dict_set_item(request, "Key", plist_new_string(key)); | ||
1081 | } | ||
1082 | |||
1083 | plist_t reply = NULL; | ||
1084 | ret = lockdownd_cu_send_request_and_get_reply(client, "GetValueCU", request, &reply); | ||
1085 | plist_free(request); | ||
1086 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1087 | return ret; | ||
1088 | } | ||
1089 | |||
1090 | plist_t value_node = plist_dict_get_item(reply, "Value"); | ||
1091 | if (value_node) { | ||
1092 | debug_info("has a value"); | ||
1093 | *value = plist_copy(value_node); | ||
1094 | } | ||
1095 | plist_free(reply); | ||
1096 | |||
1097 | return ret; | ||
1098 | #else | ||
1099 | debug_info("not supported"); | ||
1100 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1101 | #endif | ||
1102 | } | ||
1103 | |||
1104 | lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client) | ||
1105 | { | ||
1106 | #ifdef HAVE_WIRELESS_PAIRING | ||
1107 | if (!client) | ||
1108 | return LOCKDOWN_E_INVALID_ARG; | ||
1109 | |||
1110 | if (!client->cu_key) | ||
1111 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1112 | |||
1113 | lockdownd_error_t ret; | ||
1114 | |||
1115 | plist_t wifi_mac = NULL; | ||
1116 | ret = lockdownd_get_value_cu(client, NULL, "WiFiAddress", &wifi_mac); | ||
1117 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1118 | return ret; | ||
1119 | } | ||
1120 | |||
1121 | plist_t pubkey = NULL; | ||
1122 | ret = lockdownd_get_value_cu(client, NULL, "DevicePublicKey", &pubkey); | ||
1123 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1124 | plist_free(wifi_mac); | ||
1125 | return ret; | ||
1126 | } | ||
1127 | |||
1128 | key_data_t public_key = { NULL, 0 }; | ||
1129 | uint64_t data_len = 0; | ||
1130 | plist_get_data_val(pubkey, (char**)&public_key.data, &data_len); | ||
1131 | public_key.size = (unsigned int)data_len; | ||
1132 | plist_free(pubkey); | ||
1133 | |||
1134 | plist_t pair_record_plist = plist_new_dict(); | ||
1135 | pair_record_generate_keys_and_certs(pair_record_plist, public_key, client->device->version); | ||
1136 | |||
1137 | char* host_id = NULL; | ||
1138 | char* system_buid = NULL; | ||
1139 | |||
1140 | /* set SystemBUID */ | ||
1141 | userpref_read_system_buid(&system_buid); | ||
1142 | if (system_buid) { | ||
1143 | plist_dict_set_item(pair_record_plist, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); | ||
1144 | free(system_buid); | ||
1145 | } | ||
1146 | |||
1147 | /* set HostID */ | ||
1148 | host_id = generate_uuid(); | ||
1149 | pair_record_set_host_id(pair_record_plist, host_id); | ||
1150 | free(host_id); | ||
1151 | |||
1152 | plist_t request_pair_record = plist_copy(pair_record_plist); | ||
1153 | /* remove stuff that is private */ | ||
1154 | plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); | ||
1155 | plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); | ||
1156 | |||
1157 | plist_t request = plist_new_dict(); | ||
1158 | plist_dict_set_item(request, "PairRecord", request_pair_record); | ||
1159 | plist_t pairing_opts = plist_new_dict(); | ||
1160 | plist_dict_set_item(pairing_opts, "ExtendedPairingErrors", plist_new_bool(1)); | ||
1161 | plist_dict_set_item(request, "PairingOptions", pairing_opts); | ||
1162 | |||
1163 | plist_t reply = NULL; | ||
1164 | ret = lockdownd_cu_send_request_and_get_reply(client, "PairCU", request, &reply); | ||
1165 | plist_free(request); | ||
1166 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1167 | plist_free(wifi_mac); | ||
1168 | return ret; | ||
1169 | } | ||
1170 | |||
1171 | char *s_udid = NULL; | ||
1172 | plist_t p_udid = plist_dict_get_item(reply, "UDID"); | ||
1173 | if (p_udid) { | ||
1174 | plist_get_string_val(p_udid, &s_udid); | ||
1175 | } | ||
1176 | plist_t ebag = plist_dict_get_item(reply, "EscrowBag"); | ||
1177 | if (ebag) { | ||
1178 | plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(ebag)); | ||
1179 | } | ||
1180 | plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, wifi_mac); | ||
1181 | plist_free(reply); | ||
1182 | |||
1183 | if (userpref_save_pair_record(s_udid, 0, pair_record_plist) != 0) { | ||
1184 | printf("Failed to save pair record for UDID %s\n", s_udid); | ||
1185 | } | ||
1186 | free(s_udid); | ||
1187 | s_udid = NULL; | ||
1188 | plist_free(pair_record_plist); | ||
1189 | |||
1190 | ret = LOCKDOWN_E_SUCCESS; | ||
1191 | |||
1192 | return ret; | ||
1193 | #else | ||
1194 | debug_info("not supported"); | ||
1195 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1196 | #endif | ||
1197 | } | ||
diff --git a/src/lockdown.c b/src/lockdown.c index 935f24e..32389c9 100644 --- a/src/lockdown.c +++ b/src/lockdown.c | |||
@@ -2,6 +2,8 @@ | |||
2 | * lockdown.c | 2 | * lockdown.c |
3 | * com.apple.mobile.lockdownd service implementation. | 3 | * com.apple.mobile.lockdownd service implementation. |
4 | * | 4 | * |
5 | * Copyright (c) 2009-2015 Martin Szulecki All Rights Reserved. | ||
6 | * Copyright (c) 2014-2015 Nikias Bassen All Rights Reserved. | ||
5 | * Copyright (c) 2010 Bryan Forbes All Rights Reserved. | 7 | * Copyright (c) 2010 Bryan Forbes All Rights Reserved. |
6 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 8 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
7 | * | 9 | * |
@@ -20,36 +22,129 @@ | |||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ | 23 | */ |
22 | 24 | ||
25 | #ifdef HAVE_CONFIG_H | ||
26 | #include <config.h> | ||
27 | #endif | ||
28 | |||
23 | #include <string.h> | 29 | #include <string.h> |
24 | #include <stdlib.h> | 30 | #include <stdlib.h> |
25 | #define _GNU_SOURCE 1 | 31 | #define _GNU_SOURCE 1 |
26 | #define __USE_GNU 1 | 32 | #define __USE_GNU 1 |
27 | #include <stdio.h> | 33 | #include <stdio.h> |
28 | #include <ctype.h> | 34 | #include <ctype.h> |
29 | #include <glib.h> | 35 | |
30 | #include <libtasn1.h> | 36 | #ifndef _MSC_VER |
31 | #include <gnutls/x509.h> | 37 | #include <unistd.h> |
38 | #endif | ||
39 | |||
32 | #include <plist/plist.h> | 40 | #include <plist/plist.h> |
41 | #include <libimobiledevice-glue/utils.h> | ||
33 | 42 | ||
34 | #include "property_list_service.h" | 43 | #include "property_list_service.h" |
35 | #include "lockdown.h" | 44 | #include "lockdown.h" |
36 | #include "idevice.h" | 45 | #include "idevice.h" |
37 | #include "debug.h" | 46 | #include "common/debug.h" |
38 | #include "userpref.h" | 47 | #include "common/userpref.h" |
39 | 48 | #include "asprintf.h" | |
40 | #define RESULT_SUCCESS 0 | 49 | |
41 | #define RESULT_FAILURE 1 | 50 | #ifdef _WIN32 |
42 | 51 | #include <windows.h> | |
43 | const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { | 52 | #define sleep(x) Sleep(x*1000) |
44 | {"PKCS1", 536872976, 0}, | 53 | #endif |
45 | {0, 1073741836, 0}, | 54 | |
46 | {"RSAPublicKey", 536870917, 0}, | 55 | struct st_lockdownd_error_str_map { |
47 | {"modulus", 1073741827, 0}, | 56 | const char *lockdown_errstr; |
48 | {"publicExponent", 3, 0}, | 57 | const char *errstr; |
49 | {0, 0, 0} | 58 | lockdownd_error_t errcode; |
59 | }; | ||
60 | |||
61 | static struct st_lockdownd_error_str_map lockdownd_error_str_map[] = { | ||
62 | { "InvalidResponse", "Invalid response", LOCKDOWN_E_INVALID_RESPONSE }, | ||
63 | { "MissingKey", "Missing key", LOCKDOWN_E_MISSING_KEY }, | ||
64 | { "MissingValue", "Missing value", LOCKDOWN_E_MISSING_VALUE }, | ||
65 | { "GetProhibited", "Get value prohibited", LOCKDOWN_E_GET_PROHIBITED }, | ||
66 | { "SetProhibited", "Set value prohibited", LOCKDOWN_E_SET_PROHIBITED }, | ||
67 | { "RemoveProhibited", "Remove value prohibited", LOCKDOWN_E_REMOVE_PROHIBITED }, | ||
68 | { "ImmutableValue", "Immutable value", LOCKDOWN_E_IMMUTABLE_VALUE }, | ||
69 | { "PasswordProtected", "Password protected", LOCKDOWN_E_PASSWORD_PROTECTED }, | ||
70 | { "UserDeniedPairing", "User denied pairing", LOCKDOWN_E_USER_DENIED_PAIRING }, | ||
71 | { "PairingDialogResponsePending", "Pairing dialog response pending", LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING }, | ||
72 | { "MissingHostID", "Missing HostID", LOCKDOWN_E_MISSING_HOST_ID }, | ||
73 | { "InvalidHostID", "Invalid HostID", LOCKDOWN_E_INVALID_HOST_ID }, | ||
74 | { "SessionActive", "Session active", LOCKDOWN_E_SESSION_ACTIVE }, | ||
75 | { "SessionInactive", "Session inactive", LOCKDOWN_E_SESSION_INACTIVE }, | ||
76 | { "MissingSessionID", "Missing session ID", LOCKDOWN_E_MISSING_SESSION_ID }, | ||
77 | { "InvalidSessionID", "Invalid session ID", LOCKDOWN_E_INVALID_SESSION_ID }, | ||
78 | { "MissingService", "Missing service", LOCKDOWN_E_MISSING_SERVICE }, | ||
79 | { "InvalidService", "Invalid service", LOCKDOWN_E_INVALID_SERVICE }, | ||
80 | { "ServiceLimit", "Service limit reached", LOCKDOWN_E_SERVICE_LIMIT }, | ||
81 | { "MissingPairRecord", "Missing pair record", LOCKDOWN_E_MISSING_PAIR_RECORD }, | ||
82 | { "SavePairRecordFailed", "Saving pair record failed", LOCKDOWN_E_SAVE_PAIR_RECORD_FAILED }, | ||
83 | { "InvalidPairRecord", "Invalid pair record", LOCKDOWN_E_INVALID_PAIR_RECORD }, | ||
84 | { "InvalidActivationRecord", "Invalid activation record", LOCKDOWN_E_INVALID_ACTIVATION_RECORD }, | ||
85 | { "MissingActivationRecord", "Missing activation record", LOCKDOWN_E_MISSING_ACTIVATION_RECORD }, | ||
86 | { "ServiceProhibited", "Service prohibited", LOCKDOWN_E_SERVICE_PROHIBITED }, | ||
87 | { "EscrowLocked", "Escrow lockded", LOCKDOWN_E_ESCROW_LOCKED }, | ||
88 | { "PairingProhibitedOverThisConnection", "Pairing prohibited over this connection", LOCKDOWN_E_PAIRING_PROHIBITED_OVER_THIS_CONNECTION }, | ||
89 | { "FMiPProtected", "Find My iPhone/iPod/iPad protected", LOCKDOWN_E_FMIP_PROTECTED }, | ||
90 | { "MCProtected", "MC protected" , LOCKDOWN_E_MC_PROTECTED }, | ||
91 | { "MCChallengeRequired", "MC challenge required", LOCKDOWN_E_MC_CHALLENGE_REQUIRED }, | ||
92 | { NULL, NULL, 0 } | ||
50 | }; | 93 | }; |
51 | 94 | ||
52 | /** | 95 | /** |
96 | * Convert an error string identifier to a lockdownd_error_t value. | ||
97 | * Used internally to get correct error codes from a response. | ||
98 | * | ||
99 | * @param name The error name to convert. | ||
100 | * | ||
101 | * @return A matching lockdownd_error_t error code, | ||
102 | * LOCKDOWN_E_UNKNOWN_ERROR otherwise. | ||
103 | */ | ||
104 | static lockdownd_error_t lockdownd_strtoerr(const char* name) | ||
105 | { | ||
106 | lockdownd_error_t err = LOCKDOWN_E_UNKNOWN_ERROR; | ||
107 | int i = 0; | ||
108 | while (lockdownd_error_str_map[i].lockdown_errstr) { | ||
109 | if (strcmp(lockdownd_error_str_map[i].lockdown_errstr, name) == 0) { | ||
110 | return lockdownd_error_str_map[i].errcode; | ||
111 | } | ||
112 | i++; | ||
113 | } | ||
114 | return err; | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * Convert a property_list_service_error_t value to a lockdownd_error_t | ||
119 | * value. Used internally to get correct error codes. | ||
120 | * | ||
121 | * @param err A property_list_service_error_t error code | ||
122 | * | ||
123 | * @return A matching lockdownd_error_t error code, | ||
124 | * LOCKDOWND_E_UNKNOWN_ERROR otherwise. | ||
125 | */ | ||
126 | static lockdownd_error_t lockdownd_error(property_list_service_error_t err) | ||
127 | { | ||
128 | switch (err) { | ||
129 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | ||
130 | return LOCKDOWN_E_SUCCESS; | ||
131 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | ||
132 | return LOCKDOWN_E_INVALID_ARG; | ||
133 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | ||
134 | return LOCKDOWN_E_PLIST_ERROR; | ||
135 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | ||
136 | return LOCKDOWN_E_MUX_ERROR; | ||
137 | case PROPERTY_LIST_SERVICE_E_SSL_ERROR: | ||
138 | return LOCKDOWN_E_SSL_ERROR; | ||
139 | case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT: | ||
140 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
141 | default: | ||
142 | break; | ||
143 | } | ||
144 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
145 | } | ||
146 | |||
147 | /** | ||
53 | * Internally used function for checking the result from lockdown's answer | 148 | * Internally used function for checking the result from lockdown's answer |
54 | * plist to a previously sent request. | 149 | * plist to a previously sent request. |
55 | * | 150 | * |
@@ -57,58 +152,66 @@ const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { | |||
57 | * @param query_match Name of the request to match or NULL if no match is | 152 | * @param query_match Name of the request to match or NULL if no match is |
58 | * required. | 153 | * required. |
59 | * | 154 | * |
60 | * @return RESULT_SUCCESS when the result is 'Success', | 155 | * @return LOCKDOWN_E_SUCCESS when the result is 'Success', |
61 | * RESULT_FAILURE when the result is 'Failure', | 156 | * LOCKDOWN_E_UNKNOWN_ERROR when the result is 'Failure', |
62 | * or a negative value if an error occured during evaluation. | 157 | * or a specific error code if derieved from the result. |
63 | */ | 158 | */ |
64 | static int lockdown_check_result(plist_t dict, const char *query_match) | 159 | lockdownd_error_t lockdown_check_result(plist_t dict, const char *query_match) |
65 | { | 160 | { |
66 | int ret = -1; | 161 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
67 | 162 | ||
68 | plist_t query_node = plist_dict_get_item(dict, "Request"); | 163 | plist_t query_node = plist_dict_get_item(dict, "Request"); |
69 | if (!query_node) { | 164 | if (!query_node) { |
70 | return ret; | 165 | return ret; |
71 | } | 166 | } |
167 | |||
72 | if (plist_get_node_type(query_node) != PLIST_STRING) { | 168 | if (plist_get_node_type(query_node) != PLIST_STRING) { |
73 | return ret; | 169 | return ret; |
74 | } else { | ||
75 | char *query_value = NULL; | ||
76 | plist_get_string_val(query_node, &query_value); | ||
77 | if (!query_value) { | ||
78 | return ret; | ||
79 | } | ||
80 | if (query_match && (strcmp(query_value, query_match) != 0)) { | ||
81 | free(query_value); | ||
82 | return ret; | ||
83 | } | ||
84 | free(query_value); | ||
85 | } | 170 | } |
86 | 171 | ||
87 | plist_t result_node = plist_dict_get_item(dict, "Result"); | 172 | const char *query_value = plist_get_string_ptr(query_node, NULL); |
88 | if (!result_node) { | 173 | if (!query_value) { |
89 | return ret; | 174 | return ret; |
90 | } | 175 | } |
91 | 176 | ||
92 | plist_type result_type = plist_get_node_type(result_node); | 177 | if (query_match && (strcmp(query_value, query_match) != 0)) { |
93 | 178 | return ret; | |
94 | if (result_type == PLIST_STRING) { | 179 | } |
95 | |||
96 | char *result_value = NULL; | ||
97 | 180 | ||
98 | plist_get_string_val(result_node, &result_value); | 181 | /* Check for 'Error' in reply */ |
182 | plist_t err_node = plist_dict_get_item(dict, "Error"); | ||
183 | if (err_node) { | ||
184 | if (plist_get_node_type(err_node) == PLIST_STRING) { | ||
185 | const char *err_value = plist_get_string_ptr(err_node, NULL); | ||
186 | if (err_value) { | ||
187 | debug_info("ERROR: %s", err_value); | ||
188 | ret = lockdownd_strtoerr(err_value); | ||
189 | } else { | ||
190 | debug_info("ERROR: unknown error occurred"); | ||
191 | } | ||
192 | } | ||
193 | return ret; | ||
194 | } | ||
99 | 195 | ||
196 | plist_t result_node = plist_dict_get_item(dict, "Result"); | ||
197 | if (!result_node) { | ||
198 | /* With iOS 5+ 'Result' is not present anymore. | ||
199 | If there is no 'Error', we can just assume success. */ | ||
200 | return LOCKDOWN_E_SUCCESS; | ||
201 | } | ||
202 | if (plist_get_node_type(result_node) == PLIST_STRING) { | ||
203 | const char *result_value = plist_get_string_ptr(result_node, NULL); | ||
100 | if (result_value) { | 204 | if (result_value) { |
101 | if (!strcmp(result_value, "Success")) { | 205 | if (!strcmp(result_value, "Success")) { |
102 | ret = RESULT_SUCCESS; | 206 | ret = LOCKDOWN_E_SUCCESS; |
103 | } else if (!strcmp(result_value, "Failure")) { | 207 | } else if (!strcmp(result_value, "Failure")) { |
104 | ret = RESULT_FAILURE; | 208 | ret = LOCKDOWN_E_UNKNOWN_ERROR; |
105 | } else { | 209 | } else { |
106 | debug_info("ERROR: unknown result value '%s'", result_value); | 210 | debug_info("ERROR: unknown result value '%s'", result_value); |
107 | } | 211 | } |
108 | } | 212 | } |
109 | if (result_value) | ||
110 | free(result_value); | ||
111 | } | 213 | } |
214 | |||
112 | return ret; | 215 | return ret; |
113 | } | 216 | } |
114 | 217 | ||
@@ -123,20 +226,10 @@ static void plist_dict_add_label(plist_t plist, const char *label) | |||
123 | { | 226 | { |
124 | if (plist && label) { | 227 | if (plist && label) { |
125 | if (plist_get_node_type(plist) == PLIST_DICT) | 228 | if (plist_get_node_type(plist) == PLIST_DICT) |
126 | plist_dict_insert_item(plist, "Label", plist_new_string(label)); | 229 | plist_dict_set_item(plist, "Label", plist_new_string(label)); |
127 | } | 230 | } |
128 | } | 231 | } |
129 | 232 | ||
130 | /** | ||
131 | * Closes the lockdownd session by sending the StopSession request. | ||
132 | * | ||
133 | * @see lockdownd_start_session | ||
134 | * | ||
135 | * @param client The lockdown client | ||
136 | * @param session_id The id of a running session | ||
137 | * | ||
138 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
139 | */ | ||
140 | lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) | 233 | lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char *session_id) |
141 | { | 234 | { |
142 | if (!client) | 235 | if (!client) |
@@ -151,8 +244,8 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char * | |||
151 | 244 | ||
152 | plist_t dict = plist_new_dict(); | 245 | plist_t dict = plist_new_dict(); |
153 | plist_dict_add_label(dict, client->label); | 246 | plist_dict_add_label(dict, client->label); |
154 | plist_dict_insert_item(dict,"Request", plist_new_string("StopSession")); | 247 | plist_dict_set_item(dict,"Request", plist_new_string("StopSession")); |
155 | plist_dict_insert_item(dict,"SessionID", plist_new_string(session_id)); | 248 | plist_dict_set_item(dict,"SessionID", plist_new_string(session_id)); |
156 | 249 | ||
157 | debug_info("stopping session %s", session_id); | 250 | debug_info("stopping session %s", session_id); |
158 | 251 | ||
@@ -168,64 +261,74 @@ lockdownd_error_t lockdownd_stop_session(lockdownd_client_t client, const char * | |||
168 | return LOCKDOWN_E_PLIST_ERROR; | 261 | return LOCKDOWN_E_PLIST_ERROR; |
169 | } | 262 | } |
170 | 263 | ||
171 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 264 | ret = lockdown_check_result(dict, "StopSession"); |
172 | if (lockdown_check_result(dict, "StopSession") == RESULT_SUCCESS) { | 265 | if (ret == LOCKDOWN_E_SUCCESS) { |
173 | debug_info("success"); | 266 | debug_info("success"); |
174 | ret = LOCKDOWN_E_SUCCESS; | ||
175 | } | 267 | } |
268 | |||
176 | plist_free(dict); | 269 | plist_free(dict); |
177 | dict = NULL; | 270 | dict = NULL; |
271 | |||
272 | if (client->session_id) { | ||
273 | free(client->session_id); | ||
274 | client->session_id = NULL; | ||
275 | } | ||
276 | |||
178 | if (client->ssl_enabled) { | 277 | if (client->ssl_enabled) { |
179 | property_list_service_disable_ssl(client->parent); | 278 | property_list_service_disable_ssl(client->parent); |
279 | client->ssl_enabled = 0; | ||
180 | } | 280 | } |
281 | |||
181 | return ret; | 282 | return ret; |
182 | } | 283 | } |
183 | 284 | ||
184 | /** | 285 | 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 | { | 286 | { |
194 | if (!client) | 287 | if (!client) |
195 | return LOCKDOWN_E_INVALID_ARG; | 288 | return LOCKDOWN_E_INVALID_ARG; |
196 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
197 | 289 | ||
198 | if (client->session_id) { | 290 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
199 | lockdownd_stop_session(client, client->session_id); | ||
200 | free(client->session_id); | ||
201 | } | ||
202 | 291 | ||
203 | if (client->parent) { | 292 | if (client->parent) { |
204 | lockdownd_goodbye(client); | ||
205 | |||
206 | if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { | 293 | if (property_list_service_client_free(client->parent) == PROPERTY_LIST_SERVICE_E_SUCCESS) { |
207 | ret = LOCKDOWN_E_SUCCESS; | 294 | ret = LOCKDOWN_E_SUCCESS; |
208 | } | 295 | } |
209 | } | 296 | } |
210 | 297 | ||
211 | if (client->uuid) { | 298 | if (client->session_id) { |
212 | free(client->uuid); | 299 | free(client->session_id); |
300 | client->session_id = NULL; | ||
213 | } | 301 | } |
214 | if (client->label) { | 302 | if (client->label) { |
215 | free(client->label); | 303 | free(client->label); |
216 | } | 304 | } |
305 | if (client->cu_key) { | ||
306 | free(client->cu_key); | ||
307 | client->cu_key = NULL; | ||
308 | } | ||
217 | 309 | ||
218 | free(client); | 310 | free(client); |
311 | client = NULL; | ||
312 | |||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | lockdownd_error_t lockdownd_client_free(lockdownd_client_t client) | ||
317 | { | ||
318 | if (!client) | ||
319 | return LOCKDOWN_E_INVALID_ARG; | ||
320 | |||
321 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
322 | |||
323 | if (client->session_id) { | ||
324 | lockdownd_stop_session(client, client->session_id); | ||
325 | } | ||
326 | |||
327 | ret = lockdownd_client_free_simple(client); | ||
328 | |||
219 | return ret; | 329 | return ret; |
220 | } | 330 | } |
221 | 331 | ||
222 | /** | ||
223 | * Sets the label to send for requests to lockdownd. | ||
224 | * | ||
225 | * @param client The lockdown client | ||
226 | * @param label The label to set or NULL to disable sending a label | ||
227 | * | ||
228 | */ | ||
229 | void lockdownd_client_set_label(lockdownd_client_t client, const char *label) | 332 | void lockdownd_client_set_label(lockdownd_client_t client, const char *label) |
230 | { | 333 | { |
231 | if (client) { | 334 | if (client) { |
@@ -236,69 +339,22 @@ void lockdownd_client_set_label(lockdownd_client_t client, const char *label) | |||
236 | } | 339 | } |
237 | } | 340 | } |
238 | 341 | ||
239 | /** | ||
240 | * Receives a plist from lockdownd. | ||
241 | * | ||
242 | * @param client The lockdownd client | ||
243 | * @param plist The plist to store the received data | ||
244 | * | ||
245 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | ||
246 | * plist is NULL | ||
247 | */ | ||
248 | lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) | 342 | lockdownd_error_t lockdownd_receive(lockdownd_client_t client, plist_t *plist) |
249 | { | 343 | { |
250 | if (!client || !plist || (plist && *plist)) | 344 | if (!client || !plist || (plist && *plist)) |
251 | return LOCKDOWN_E_INVALID_ARG; | 345 | return LOCKDOWN_E_INVALID_ARG; |
252 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | ||
253 | property_list_service_error_t err; | ||
254 | |||
255 | err = property_list_service_receive_plist(client->parent, plist); | ||
256 | if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { | ||
257 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
258 | } | ||
259 | 346 | ||
260 | if (!*plist) | 347 | return lockdownd_error(property_list_service_receive_plist(client->parent, plist)); |
261 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
262 | |||
263 | return ret; | ||
264 | } | 348 | } |
265 | 349 | ||
266 | /** | ||
267 | * Sends a plist to lockdownd. | ||
268 | * | ||
269 | * @note This function is low-level and should only be used if you need to send | ||
270 | * a new type of message. | ||
271 | * | ||
272 | * @param client The lockdownd client | ||
273 | * @param plist The plist to send | ||
274 | * | ||
275 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | ||
276 | * plist is NULL | ||
277 | */ | ||
278 | lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) | 350 | lockdownd_error_t lockdownd_send(lockdownd_client_t client, plist_t plist) |
279 | { | 351 | { |
280 | if (!client || !plist) | 352 | if (!client || !plist) |
281 | return LOCKDOWN_E_INVALID_ARG; | 353 | return LOCKDOWN_E_INVALID_ARG; |
282 | 354 | ||
283 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | 355 | return lockdownd_error(property_list_service_send_xml_plist(client->parent, plist)); |
284 | idevice_error_t err; | ||
285 | |||
286 | err = property_list_service_send_xml_plist(client->parent, plist); | ||
287 | if (err != PROPERTY_LIST_SERVICE_E_SUCCESS) { | ||
288 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
289 | } | ||
290 | return ret; | ||
291 | } | 356 | } |
292 | 357 | ||
293 | /** | ||
294 | * Query the type of the service daemon. Depending on whether the device is | ||
295 | * queried in normal mode or restore mode, different types will be returned. | ||
296 | * | ||
297 | * @param client The lockdownd client | ||
298 | * @param type The type returned by the service daemon. Pass NULL to ignore. | ||
299 | * | ||
300 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
301 | */ | ||
302 | lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | 358 | lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) |
303 | { | 359 | { |
304 | if (!client) | 360 | if (!client) |
@@ -308,7 +364,7 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | |||
308 | 364 | ||
309 | plist_t dict = plist_new_dict(); | 365 | plist_t dict = plist_new_dict(); |
310 | plist_dict_add_label(dict, client->label); | 366 | plist_dict_add_label(dict, client->label); |
311 | plist_dict_insert_item(dict,"Request", plist_new_string("QueryType")); | 367 | plist_dict_set_item(dict,"Request", plist_new_string("QueryType")); |
312 | 368 | ||
313 | debug_info("called"); | 369 | debug_info("called"); |
314 | ret = lockdownd_send(client, dict); | 370 | ret = lockdownd_send(client, dict); |
@@ -322,14 +378,21 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | |||
322 | return ret; | 378 | return ret; |
323 | 379 | ||
324 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 380 | ret = LOCKDOWN_E_UNKNOWN_ERROR; |
325 | if (lockdown_check_result(dict, "QueryType") == RESULT_SUCCESS) { | 381 | plist_t type_node = plist_dict_get_item(dict, "Type"); |
382 | if (type_node && (plist_get_node_type(type_node) == PLIST_STRING)) { | ||
383 | char* typestr = NULL; | ||
384 | plist_get_string_val(type_node, &typestr); | ||
385 | debug_info("success with type %s", typestr); | ||
326 | /* return the type if requested */ | 386 | /* return the type if requested */ |
327 | if (type != NULL) { | 387 | if (type != NULL) { |
328 | plist_t type_node = plist_dict_get_item(dict, "Type"); | 388 | *type = typestr; |
329 | plist_get_string_val(type_node, type); | 389 | } else { |
390 | free(typestr); | ||
330 | } | 391 | } |
331 | debug_info("success with type %s", *type); | ||
332 | ret = LOCKDOWN_E_SUCCESS; | 392 | ret = LOCKDOWN_E_SUCCESS; |
393 | } else { | ||
394 | debug_info("hmm. QueryType response does not contain a type?!"); | ||
395 | debug_plist(dict); | ||
333 | } | 396 | } |
334 | plist_free(dict); | 397 | plist_free(dict); |
335 | dict = NULL; | 398 | dict = NULL; |
@@ -337,16 +400,6 @@ lockdownd_error_t lockdownd_query_type(lockdownd_client_t client, char **type) | |||
337 | return ret; | 400 | return ret; |
338 | } | 401 | } |
339 | 402 | ||
340 | /** | ||
341 | * Retrieves a preferences plist using an optional domain and/or key name. | ||
342 | * | ||
343 | * @param client An initialized lockdownd client. | ||
344 | * @param domain The domain to query on or NULL for global domain | ||
345 | * @param key The key name to request or NULL to query for all keys | ||
346 | * @param value A plist node representing the result value node | ||
347 | * | ||
348 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
349 | */ | ||
350 | lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) | 403 | lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *domain, const char *key, plist_t *value) |
351 | { | 404 | { |
352 | if (!client) | 405 | if (!client) |
@@ -359,12 +412,12 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom | |||
359 | dict = plist_new_dict(); | 412 | dict = plist_new_dict(); |
360 | plist_dict_add_label(dict, client->label); | 413 | plist_dict_add_label(dict, client->label); |
361 | if (domain) { | 414 | if (domain) { |
362 | plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); | 415 | plist_dict_set_item(dict,"Domain", plist_new_string(domain)); |
363 | } | 416 | } |
364 | if (key) { | 417 | if (key) { |
365 | plist_dict_insert_item(dict,"Key", plist_new_string(key)); | 418 | plist_dict_set_item(dict,"Key", plist_new_string(key)); |
366 | } | 419 | } |
367 | plist_dict_insert_item(dict,"Request", plist_new_string("GetValue")); | 420 | plist_dict_set_item(dict,"Request", plist_new_string("GetValue")); |
368 | 421 | ||
369 | /* send to device */ | 422 | /* send to device */ |
370 | ret = lockdownd_send(client, dict); | 423 | ret = lockdownd_send(client, dict); |
@@ -380,10 +433,11 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom | |||
380 | if (ret != LOCKDOWN_E_SUCCESS) | 433 | if (ret != LOCKDOWN_E_SUCCESS) |
381 | return ret; | 434 | return ret; |
382 | 435 | ||
383 | if (lockdown_check_result(dict, "GetValue") == RESULT_SUCCESS) { | 436 | ret = lockdown_check_result(dict, "GetValue"); |
437 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
384 | debug_info("success"); | 438 | debug_info("success"); |
385 | ret = LOCKDOWN_E_SUCCESS; | ||
386 | } | 439 | } |
440 | |||
387 | if (ret != LOCKDOWN_E_SUCCESS) { | 441 | if (ret != LOCKDOWN_E_SUCCESS) { |
388 | plist_free(dict); | 442 | plist_free(dict); |
389 | return ret; | 443 | return ret; |
@@ -400,17 +454,6 @@ lockdownd_error_t lockdownd_get_value(lockdownd_client_t client, const char *dom | |||
400 | return ret; | 454 | return ret; |
401 | } | 455 | } |
402 | 456 | ||
403 | /** | ||
404 | * Sets a preferences value using a plist and optional by domain and/or key name. | ||
405 | * | ||
406 | * @param client an initialized lockdownd client. | ||
407 | * @param domain the domain to query on or NULL for global domain | ||
408 | * @param key the key name to set the value or NULL to set a value dict plist | ||
409 | * @param value a plist node of any node type representing the value to set | ||
410 | * | ||
411 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | ||
412 | * value is NULL | ||
413 | */ | ||
414 | lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) | 457 | lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *domain, const char *key, plist_t value) |
415 | { | 458 | { |
416 | if (!client || !value) | 459 | if (!client || !value) |
@@ -423,13 +466,13 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom | |||
423 | dict = plist_new_dict(); | 466 | dict = plist_new_dict(); |
424 | plist_dict_add_label(dict, client->label); | 467 | plist_dict_add_label(dict, client->label); |
425 | if (domain) { | 468 | if (domain) { |
426 | plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); | 469 | plist_dict_set_item(dict,"Domain", plist_new_string(domain)); |
427 | } | 470 | } |
428 | if (key) { | 471 | if (key) { |
429 | plist_dict_insert_item(dict,"Key", plist_new_string(key)); | 472 | plist_dict_set_item(dict,"Key", plist_new_string(key)); |
430 | } | 473 | } |
431 | plist_dict_insert_item(dict,"Request", plist_new_string("SetValue")); | 474 | plist_dict_set_item(dict,"Request", plist_new_string("SetValue")); |
432 | plist_dict_insert_item(dict,"Value", value); | 475 | plist_dict_set_item(dict,"Value", value); |
433 | 476 | ||
434 | /* send to device */ | 477 | /* send to device */ |
435 | ret = lockdownd_send(client, dict); | 478 | ret = lockdownd_send(client, dict); |
@@ -445,9 +488,9 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom | |||
445 | if (ret != LOCKDOWN_E_SUCCESS) | 488 | if (ret != LOCKDOWN_E_SUCCESS) |
446 | return ret; | 489 | return ret; |
447 | 490 | ||
448 | if (lockdown_check_result(dict, "SetValue") == RESULT_SUCCESS) { | 491 | ret = lockdown_check_result(dict, "SetValue"); |
492 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
449 | debug_info("success"); | 493 | debug_info("success"); |
450 | ret = LOCKDOWN_E_SUCCESS; | ||
451 | } | 494 | } |
452 | 495 | ||
453 | if (ret != LOCKDOWN_E_SUCCESS) { | 496 | if (ret != LOCKDOWN_E_SUCCESS) { |
@@ -459,17 +502,6 @@ lockdownd_error_t lockdownd_set_value(lockdownd_client_t client, const char *dom | |||
459 | return ret; | 502 | return ret; |
460 | } | 503 | } |
461 | 504 | ||
462 | /** | ||
463 | * Removes a preference node by domain and/or key name. | ||
464 | * | ||
465 | * @note: Use with caution as this could remove vital information on the device | ||
466 | * | ||
467 | * @param client An initialized lockdownd client. | ||
468 | * @param domain The domain to query on or NULL for global domain | ||
469 | * @param key The key name to remove or NULL remove all keys for the current domain | ||
470 | * | ||
471 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
472 | */ | ||
473 | lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) | 505 | lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char *domain, const char *key) |
474 | { | 506 | { |
475 | if (!client) | 507 | if (!client) |
@@ -482,12 +514,12 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * | |||
482 | dict = plist_new_dict(); | 514 | dict = plist_new_dict(); |
483 | plist_dict_add_label(dict, client->label); | 515 | plist_dict_add_label(dict, client->label); |
484 | if (domain) { | 516 | if (domain) { |
485 | plist_dict_insert_item(dict,"Domain", plist_new_string(domain)); | 517 | plist_dict_set_item(dict,"Domain", plist_new_string(domain)); |
486 | } | 518 | } |
487 | if (key) { | 519 | if (key) { |
488 | plist_dict_insert_item(dict,"Key", plist_new_string(key)); | 520 | plist_dict_set_item(dict,"Key", plist_new_string(key)); |
489 | } | 521 | } |
490 | plist_dict_insert_item(dict,"Request", plist_new_string("RemoveValue")); | 522 | plist_dict_set_item(dict,"Request", plist_new_string("RemoveValue")); |
491 | 523 | ||
492 | /* send to device */ | 524 | /* send to device */ |
493 | ret = lockdownd_send(client, dict); | 525 | ret = lockdownd_send(client, dict); |
@@ -503,9 +535,9 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * | |||
503 | if (ret != LOCKDOWN_E_SUCCESS) | 535 | if (ret != LOCKDOWN_E_SUCCESS) |
504 | return ret; | 536 | return ret; |
505 | 537 | ||
506 | if (lockdown_check_result(dict, "RemoveValue") == RESULT_SUCCESS) { | 538 | ret = lockdown_check_result(dict, "RemoveValue"); |
539 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
507 | debug_info("success"); | 540 | debug_info("success"); |
508 | ret = LOCKDOWN_E_SUCCESS; | ||
509 | } | 541 | } |
510 | 542 | ||
511 | if (ret != LOCKDOWN_E_SUCCESS) { | 543 | if (ret != LOCKDOWN_E_SUCCESS) { |
@@ -517,16 +549,7 @@ lockdownd_error_t lockdownd_remove_value(lockdownd_client_t client, const char * | |||
517 | return ret; | 549 | return ret; |
518 | } | 550 | } |
519 | 551 | ||
520 | /** | 552 | 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 | { | 553 | { |
531 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 554 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
532 | plist_t value = NULL; | 555 | plist_t value = NULL; |
@@ -535,7 +558,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu | |||
535 | if (ret != LOCKDOWN_E_SUCCESS) { | 558 | if (ret != LOCKDOWN_E_SUCCESS) { |
536 | return ret; | 559 | return ret; |
537 | } | 560 | } |
538 | plist_get_string_val(value, uuid); | 561 | plist_get_string_val(value, udid); |
539 | 562 | ||
540 | plist_free(value); | 563 | plist_free(value); |
541 | value = NULL; | 564 | value = NULL; |
@@ -551,7 +574,7 @@ lockdownd_error_t lockdownd_get_device_uuid(lockdownd_client_t client, char **uu | |||
551 | * | 574 | * |
552 | * @return LOCKDOWN_E_SUCCESS on success | 575 | * @return LOCKDOWN_E_SUCCESS on success |
553 | */ | 576 | */ |
554 | lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnutls_datum_t * public_key) | 577 | static lockdownd_error_t lockdownd_get_device_public_key_as_key_data(lockdownd_client_t client, key_data_t *public_key) |
555 | { | 578 | { |
556 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 579 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
557 | plist_t value = NULL; | 580 | plist_t value = NULL; |
@@ -572,15 +595,6 @@ lockdownd_error_t lockdownd_get_device_public_key(lockdownd_client_t client, gnu | |||
572 | return ret; | 595 | return ret; |
573 | } | 596 | } |
574 | 597 | ||
575 | /** | ||
576 | * Retrieves the name of the device from lockdownd set by the user. | ||
577 | * | ||
578 | * @param client An initialized lockdownd client. | ||
579 | * @param device_name Holds the name of the device. The caller is | ||
580 | * responsible for freeing the memory. | ||
581 | * | ||
582 | * @return LOCKDOWN_E_SUCCESS on success | ||
583 | */ | ||
584 | lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) | 598 | lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **device_name) |
585 | { | 599 | { |
586 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 600 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
@@ -598,29 +612,20 @@ lockdownd_error_t lockdownd_get_device_name(lockdownd_client_t client, char **de | |||
598 | return ret; | 612 | return ret; |
599 | } | 613 | } |
600 | 614 | ||
601 | /** | ||
602 | * Creates a new lockdownd client for the device. | ||
603 | * | ||
604 | * @note This function does not pair with the device or start a session. This | ||
605 | * has to be done manually by the caller after the client is created. | ||
606 | * The device disconnects automatically if the lockdown connection idles | ||
607 | * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon | ||
608 | * as the connection is no longer needed. | ||
609 | * | ||
610 | * @param device The device to create a lockdownd client for | ||
611 | * @param client The pointer to the location of the new lockdownd_client | ||
612 | * @param label The label to use for communication. Usually the program name. | ||
613 | * | ||
614 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
615 | */ | ||
616 | lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) | 615 | lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *client, const char *label) |
617 | { | 616 | { |
618 | if (!client) | 617 | if (!device || !client) |
619 | return LOCKDOWN_E_INVALID_ARG; | 618 | return LOCKDOWN_E_INVALID_ARG; |
620 | 619 | ||
620 | static struct lockdownd_service_descriptor service = { | ||
621 | .port = 0xf27e, | ||
622 | .ssl_enabled = 0 | ||
623 | }; | ||
624 | char *type = NULL; | ||
625 | |||
621 | property_list_service_client_t plistclient = NULL; | 626 | property_list_service_client_t plistclient = NULL; |
622 | if (property_list_service_client_new(device, 0xf27e, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 627 | if (property_list_service_client_new(device, (lockdownd_service_descriptor_t)&service, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { |
623 | debug_info("could not connect to lockdownd (device %s)", device->uuid); | 628 | debug_info("could not connect to lockdownd (device %s)", device->udid); |
624 | return LOCKDOWN_E_MUX_ERROR; | 629 | return LOCKDOWN_E_MUX_ERROR; |
625 | } | 630 | } |
626 | 631 | ||
@@ -628,31 +633,69 @@ lockdownd_error_t lockdownd_client_new(idevice_t device, lockdownd_client_t *cli | |||
628 | client_loc->parent = plistclient; | 633 | client_loc->parent = plistclient; |
629 | client_loc->ssl_enabled = 0; | 634 | client_loc->ssl_enabled = 0; |
630 | client_loc->session_id = NULL; | 635 | client_loc->session_id = NULL; |
631 | client_loc->uuid = NULL; | 636 | client_loc->device = device; |
637 | client_loc->cu_key = NULL; | ||
638 | client_loc->cu_key_len = 0; | ||
639 | |||
640 | if (device->udid) { | ||
641 | debug_info("device udid: %s", device->udid); | ||
642 | } | ||
643 | |||
632 | client_loc->label = label ? strdup(label) : NULL; | 644 | client_loc->label = label ? strdup(label) : NULL; |
633 | 645 | ||
646 | int is_lockdownd = 0; | ||
647 | if (lockdownd_query_type(client_loc, &type) != LOCKDOWN_E_SUCCESS) { | ||
648 | debug_info("QueryType failed in the lockdownd client."); | ||
649 | } else if (!strcmp("com.apple.mobile.lockdown", type)) { | ||
650 | is_lockdownd = 1; | ||
651 | } else { | ||
652 | debug_info("QueryType request returned \"%s\"", type); | ||
653 | } | ||
654 | free(type); | ||
655 | |||
634 | *client = client_loc; | 656 | *client = client_loc; |
635 | 657 | ||
658 | if (is_lockdownd && device->version == 0) { | ||
659 | plist_t p_version = NULL; | ||
660 | if (lockdownd_get_value(client_loc, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { | ||
661 | int vers[3] = {0, 0, 0}; | ||
662 | char *s_version = NULL; | ||
663 | plist_get_string_val(p_version, &s_version); | ||
664 | if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { | ||
665 | device->version = IDEVICE_DEVICE_VERSION(vers[0], vers[1], vers[2]); | ||
666 | } | ||
667 | free(s_version); | ||
668 | } | ||
669 | plist_free(p_version); | ||
670 | } | ||
671 | if (is_lockdownd && device->device_class == 0) { | ||
672 | plist_t p_device_class = NULL; | ||
673 | if (lockdownd_get_value(client_loc, NULL, "DeviceClass", &p_device_class) == LOCKDOWN_E_SUCCESS) { | ||
674 | char* s_device_class = NULL; | ||
675 | plist_get_string_val(p_device_class, &s_device_class); | ||
676 | if (s_device_class != NULL) { | ||
677 | if (!strcmp(s_device_class, "iPhone")) { | ||
678 | device->device_class = DEVICE_CLASS_IPHONE; | ||
679 | } else if (!strcmp(s_device_class, "iPad")) { | ||
680 | device->device_class = DEVICE_CLASS_IPAD; | ||
681 | } else if (!strcmp(s_device_class, "iPod")) { | ||
682 | device->device_class = DEVICE_CLASS_IPOD; | ||
683 | } else if (!strcmp(s_device_class, "Watch")) { | ||
684 | device->device_class = DEVICE_CLASS_WATCH; | ||
685 | } else if (!strcmp(s_device_class, "AppleTV")) { | ||
686 | device->device_class = DEVICE_CLASS_APPLETV; | ||
687 | } else { | ||
688 | device->device_class = DEVICE_CLASS_UNKNOWN; | ||
689 | } | ||
690 | free(s_device_class); | ||
691 | } | ||
692 | } | ||
693 | plist_free(p_device_class); | ||
694 | } | ||
695 | |||
636 | return LOCKDOWN_E_SUCCESS; | 696 | return LOCKDOWN_E_SUCCESS; |
637 | } | 697 | } |
638 | 698 | ||
639 | /** | ||
640 | * Creates a new lockdownd client for the device and starts initial handshake. | ||
641 | * The handshake consists out of query_type, validate_pair, pair and | ||
642 | * start_session calls. It uses the internal pairing record management. | ||
643 | * | ||
644 | * @note The device disconnects automatically if the lockdown connection idles | ||
645 | * for more than 10 seconds. Make sure to call lockdownd_client_free() as soon | ||
646 | * as the connection is no longer needed. | ||
647 | * | ||
648 | * @param device The device to create a lockdownd client for | ||
649 | * @param client The pointer to the location of the new lockdownd_client | ||
650 | * @param label The label to use for communication. Usually the program name. | ||
651 | * Pass NULL to disable sending the label in requests to lockdownd. | ||
652 | * | ||
653 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
654 | * LOCKDOWN_E_INVALID_CONF if configuration data is wrong | ||
655 | */ | ||
656 | lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) | 699 | lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdownd_client_t *client, const char *label) |
657 | { | 700 | { |
658 | if (!client) | 701 | if (!client) |
@@ -660,8 +703,8 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown | |||
660 | 703 | ||
661 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | 704 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; |
662 | lockdownd_client_t client_loc = NULL; | 705 | lockdownd_client_t client_loc = NULL; |
706 | plist_t pair_record = NULL; | ||
663 | char *host_id = NULL; | 707 | char *host_id = NULL; |
664 | char *type = NULL; | ||
665 | 708 | ||
666 | ret = lockdownd_client_new(device, &client_loc, label); | 709 | ret = lockdownd_client_new(device, &client_loc, label); |
667 | if (LOCKDOWN_E_SUCCESS != ret) { | 710 | if (LOCKDOWN_E_SUCCESS != ret) { |
@@ -670,60 +713,79 @@ lockdownd_error_t lockdownd_client_new_with_handshake(idevice_t device, lockdown | |||
670 | } | 713 | } |
671 | 714 | ||
672 | /* perform handshake */ | 715 | /* perform handshake */ |
673 | if (LOCKDOWN_E_SUCCESS != lockdownd_query_type(client_loc, &type)) { | 716 | userpref_error_t uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); |
674 | debug_info("QueryType failed in the lockdownd client."); | 717 | if (uerr == USERPREF_E_READ_ERROR) { |
675 | ret = LOCKDOWN_E_NOT_ENOUGH_DATA; | 718 | debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); |
676 | } else { | 719 | lockdownd_client_free(client_loc); |
677 | if (strcmp("com.apple.mobile.lockdown", type)) { | 720 | return LOCKDOWN_E_RECEIVE_TIMEOUT; |
678 | debug_info("Warning QueryType request returned \"%s\".", type); | ||
679 | } | ||
680 | if (type) | ||
681 | free(type); | ||
682 | } | 721 | } |
683 | 722 | if (pair_record) { | |
684 | ret = idevice_get_uuid(device, &client_loc->uuid); | 723 | pair_record_get_host_id(pair_record, &host_id); |
685 | if (LOCKDOWN_E_SUCCESS != ret) { | ||
686 | debug_info("failed to get device uuid."); | ||
687 | } | 724 | } |
688 | debug_info("device uuid: %s", client_loc->uuid); | 725 | if (LOCKDOWN_E_SUCCESS == ret && pair_record && !host_id) { |
689 | |||
690 | userpref_get_host_id(&host_id); | ||
691 | if (LOCKDOWN_E_SUCCESS == ret && !host_id) { | ||
692 | ret = LOCKDOWN_E_INVALID_CONF; | 726 | ret = LOCKDOWN_E_INVALID_CONF; |
693 | } | 727 | } |
694 | 728 | ||
695 | if (LOCKDOWN_E_SUCCESS == ret && !userpref_has_device_public_key(client_loc->uuid)) | 729 | if (LOCKDOWN_E_SUCCESS == ret && !pair_record) { |
730 | /* attempt pairing */ | ||
731 | free(host_id); | ||
732 | host_id = NULL; | ||
696 | ret = lockdownd_pair(client_loc, NULL); | 733 | ret = lockdownd_pair(client_loc, NULL); |
734 | } | ||
697 | 735 | ||
698 | /* in any case, we need to validate pairing to receive trusted host status */ | 736 | plist_free(pair_record); |
699 | ret = lockdownd_validate_pair(client_loc, NULL); | 737 | pair_record = NULL; |
700 | 738 | ||
701 | /* if not paired yet, let's do it now */ | 739 | if (device->version < IDEVICE_DEVICE_VERSION(7,0,0) && device->device_class != DEVICE_CLASS_WATCH) { |
702 | if (LOCKDOWN_E_INVALID_HOST_ID == ret) { | 740 | /* for older devices, we need to validate pairing to receive trusted host status */ |
703 | ret = lockdownd_pair(client_loc, NULL); | 741 | ret = lockdownd_validate_pair(client_loc, NULL); |
704 | if (LOCKDOWN_E_SUCCESS == ret) { | 742 | |
705 | ret = lockdownd_validate_pair(client_loc, NULL); | 743 | /* if not paired yet, let's do it now */ |
744 | if (LOCKDOWN_E_INVALID_HOST_ID == ret) { | ||
745 | free(host_id); | ||
746 | host_id = NULL; | ||
747 | ret = lockdownd_pair(client_loc, NULL); | ||
748 | if (LOCKDOWN_E_SUCCESS == ret) { | ||
749 | ret = lockdownd_validate_pair(client_loc, NULL); | ||
750 | } | ||
706 | } | 751 | } |
707 | } | 752 | } |
708 | 753 | ||
709 | if (LOCKDOWN_E_SUCCESS == ret) { | 754 | if (LOCKDOWN_E_SUCCESS == ret) { |
755 | if (!host_id) { | ||
756 | uerr = userpref_read_pair_record(client_loc->device->udid, &pair_record); | ||
757 | if (uerr == USERPREF_E_READ_ERROR) { | ||
758 | debug_info("ERROR: Failed to retrieve pair record for %s", client_loc->device->udid); | ||
759 | lockdownd_client_free(client_loc); | ||
760 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
761 | } else if (uerr == USERPREF_E_NOENT) { | ||
762 | debug_info("ERROR: No pair record for %s", client_loc->device->udid); | ||
763 | lockdownd_client_free(client_loc); | ||
764 | return LOCKDOWN_E_INVALID_CONF; | ||
765 | } else if (uerr != USERPREF_E_SUCCESS) { | ||
766 | debug_info("ERROR: Failed to retrieve or parse pair record for %s", client_loc->device->udid); | ||
767 | lockdownd_client_free(client_loc); | ||
768 | return LOCKDOWN_E_INVALID_CONF; | ||
769 | } | ||
770 | if (pair_record) { | ||
771 | pair_record_get_host_id(pair_record, &host_id); | ||
772 | plist_free(pair_record); | ||
773 | } | ||
774 | } | ||
775 | |||
710 | ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); | 776 | ret = lockdownd_start_session(client_loc, host_id, NULL, NULL); |
711 | if (LOCKDOWN_E_SUCCESS != ret) { | 777 | if (LOCKDOWN_E_SUCCESS != ret) { |
712 | debug_info("Session opening failed."); | 778 | debug_info("Session opening failed."); |
713 | } | 779 | } |
714 | 780 | ||
715 | if (host_id) { | ||
716 | free(host_id); | ||
717 | host_id = NULL; | ||
718 | } | ||
719 | } | 781 | } |
720 | 782 | ||
721 | if (LOCKDOWN_E_SUCCESS == ret) { | 783 | if (LOCKDOWN_E_SUCCESS == ret) { |
722 | *client = client_loc; | 784 | *client = client_loc; |
723 | } else { | 785 | } else { |
724 | lockdownd_client_free(client_loc); | 786 | lockdownd_client_free(client_loc); |
725 | } | 787 | } |
726 | 788 | free(host_id); | |
727 | return ret; | 789 | return ret; |
728 | } | 790 | } |
729 | 791 | ||
@@ -740,68 +802,78 @@ static plist_t lockdownd_pair_record_to_plist(lockdownd_pair_record_t pair_recor | |||
740 | if (!pair_record) | 802 | if (!pair_record) |
741 | return NULL; | 803 | return NULL; |
742 | 804 | ||
743 | char *host_id_loc = pair_record->host_id; | ||
744 | |||
745 | /* setup request plist */ | 805 | /* setup request plist */ |
746 | plist_t dict = plist_new_dict(); | 806 | plist_t dict = plist_new_dict(); |
747 | plist_dict_insert_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); | 807 | plist_dict_set_item(dict, "DeviceCertificate", plist_new_data(pair_record->device_certificate, strlen(pair_record->device_certificate))); |
748 | plist_dict_insert_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); | 808 | plist_dict_set_item(dict, "HostCertificate", plist_new_data(pair_record->host_certificate, strlen(pair_record->host_certificate))); |
749 | if (!pair_record->host_id) | 809 | plist_dict_set_item(dict, "HostID", plist_new_string(pair_record->host_id)); |
750 | userpref_get_host_id(&host_id_loc); | 810 | plist_dict_set_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate))); |
751 | plist_dict_insert_item(dict, "HostID", plist_new_string(host_id_loc)); | 811 | plist_dict_set_item(dict, "SystemBUID", plist_new_string(pair_record->system_buid)); |
752 | plist_dict_insert_item(dict, "RootCertificate", plist_new_data(pair_record->root_certificate, strlen(pair_record->root_certificate))); | ||
753 | |||
754 | if (!pair_record->host_id) | ||
755 | free(host_id_loc); | ||
756 | 812 | ||
757 | return dict; | 813 | return dict; |
758 | } | 814 | } |
759 | 815 | ||
760 | /** | 816 | /** |
761 | * Generates a new pairing record plist and required certificates for the | 817 | * Generates a pair record plist with required certificates for a specific |
762 | * supplied public key of the device and the host_id of the caller's host | 818 | * device. If a pairing exists, it is loaded from the computer instead of being |
763 | * computer. | 819 | * generated. |
764 | * | 820 | * |
765 | * @param public_key The public key of the device. | 821 | * @param pair_record_plist Holds the pair record. |
766 | * @param host_id The HostID to use for the pair record plist. | ||
767 | * @param pair_record_plist Holds the generated pair record. | ||
768 | * | 822 | * |
769 | * @return LOCKDOWN_E_SUCCESS on success | 823 | * @return LOCKDOWN_E_SUCCESS on success |
770 | */ | 824 | */ |
771 | static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, char *host_id, plist_t *pair_record_plist) | 825 | static lockdownd_error_t pair_record_generate(lockdownd_client_t client, plist_t *pair_record) |
772 | { | 826 | { |
773 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 827 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
774 | 828 | ||
775 | gnutls_datum_t device_cert = { NULL, 0 }; | 829 | key_data_t public_key = { NULL, 0 }; |
776 | gnutls_datum_t host_cert = { NULL, 0 }; | 830 | char* host_id = NULL; |
777 | gnutls_datum_t root_cert = { NULL, 0 }; | 831 | char* system_buid = NULL; |
778 | 832 | ||
779 | ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); | 833 | /* retrieve device public key */ |
834 | ret = lockdownd_get_device_public_key_as_key_data(client, &public_key); | ||
780 | if (ret != LOCKDOWN_E_SUCCESS) { | 835 | if (ret != LOCKDOWN_E_SUCCESS) { |
781 | return ret; | 836 | debug_info("device refused to send public key."); |
837 | goto leave; | ||
838 | } | ||
839 | debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); | ||
840 | |||
841 | *pair_record = plist_new_dict(); | ||
842 | |||
843 | /* generate keys and certificates into pair record */ | ||
844 | userpref_error_t uret = USERPREF_E_SUCCESS; | ||
845 | uret = pair_record_generate_keys_and_certs(*pair_record, public_key, client->device->version); | ||
846 | switch(uret) { | ||
847 | case USERPREF_E_INVALID_ARG: | ||
848 | ret = LOCKDOWN_E_INVALID_ARG; | ||
849 | break; | ||
850 | case USERPREF_E_INVALID_CONF: | ||
851 | ret = LOCKDOWN_E_INVALID_CONF; | ||
852 | break; | ||
853 | case USERPREF_E_SSL_ERROR: | ||
854 | ret = LOCKDOWN_E_SSL_ERROR; | ||
855 | break; | ||
856 | default: | ||
857 | break; | ||
782 | } | 858 | } |
783 | 859 | ||
784 | char *host_id_loc = host_id; | 860 | /* set SystemBUID */ |
861 | userpref_read_system_buid(&system_buid); | ||
862 | if (system_buid) { | ||
863 | plist_dict_set_item(*pair_record, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); | ||
864 | } | ||
785 | 865 | ||
786 | if (!host_id) | 866 | /* set HostID */ |
787 | userpref_get_host_id(&host_id_loc); | 867 | host_id = generate_uuid(); |
868 | pair_record_set_host_id(*pair_record, host_id); | ||
788 | 869 | ||
789 | /* setup request plist */ | 870 | leave: |
790 | *pair_record_plist = plist_new_dict(); | 871 | if (host_id) |
791 | plist_dict_insert_item(*pair_record_plist, "DeviceCertificate", plist_new_data((const char*)device_cert.data, device_cert.size)); | 872 | free(host_id); |
792 | plist_dict_insert_item(*pair_record_plist, "HostCertificate", plist_new_data((const char*)host_cert.data, host_cert.size)); | 873 | if (system_buid) |
793 | plist_dict_insert_item(*pair_record_plist, "HostID", plist_new_string(host_id_loc)); | 874 | free(system_buid); |
794 | plist_dict_insert_item(*pair_record_plist, "RootCertificate", plist_new_data((const char*)root_cert.data, root_cert.size)); | 875 | if (public_key.data) |
795 | 876 | free(public_key.data); | |
796 | if (!host_id) | ||
797 | free(host_id_loc); | ||
798 | |||
799 | if (device_cert.data) | ||
800 | free(device_cert.data); | ||
801 | if (host_cert.data) | ||
802 | free(host_cert.data); | ||
803 | if (root_cert.data) | ||
804 | free(root_cert.data); | ||
805 | 877 | ||
806 | return ret; | 878 | return ret; |
807 | } | 879 | } |
@@ -809,11 +881,13 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c | |||
809 | /** | 881 | /** |
810 | * Function used internally by lockdownd_pair() and lockdownd_validate_pair() | 882 | * Function used internally by lockdownd_pair() and lockdownd_validate_pair() |
811 | * | 883 | * |
812 | * @param client The lockdown client to pair with. | 884 | * @param client The lockdown client |
813 | * @param pair_record The pair record to use for pairing. If NULL is passed, then | 885 | * @param pair_record The pair record to use for pairing. If NULL is passed, then |
814 | * the pair records from the current machine are used. New records will be | 886 | * the pair records from the current machine are used. New records will be |
815 | * generated automatically when pairing is done for the first time. | 887 | * generated automatically when pairing is done for the first time. |
816 | * @param verb This is either "Pair", "ValidatePair" or "Unpair". | 888 | * @param verb This is either "Pair", "ValidatePair" or "Unpair". |
889 | * @param options The pairing options to pass. | ||
890 | * @param response If non-NULL a pointer to lockdownd's response dictionary is returned. | ||
817 | * | 891 | * |
818 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | 892 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, |
819 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | 893 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, |
@@ -821,73 +895,103 @@ static lockdownd_error_t generate_pair_record_plist(gnutls_datum_t public_key, c | |||
821 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | 895 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, |
822 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | 896 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id |
823 | */ | 897 | */ |
824 | static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record, const char *verb) | 898 | 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 | { | 899 | { |
826 | if (!client) | 900 | if (!client) |
827 | return LOCKDOWN_E_INVALID_ARG; | 901 | return LOCKDOWN_E_INVALID_ARG; |
828 | 902 | ||
829 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 903 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
830 | plist_t dict = NULL; | 904 | plist_t dict = NULL; |
831 | plist_t dict_record = NULL; | 905 | plist_t pair_record_plist = NULL; |
832 | gnutls_datum_t public_key = { NULL, 0 }; | 906 | plist_t wifi_node = NULL; |
833 | int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ | 907 | int pairing_mode = 0; /* 0 = libimobiledevice, 1 = external */ |
834 | 908 | ||
835 | if (pair_record && pair_record->host_id) { | 909 | if (pair_record && pair_record->system_buid && pair_record->host_id) { |
836 | /* valid pair_record passed? */ | 910 | /* valid pair_record passed? */ |
837 | if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { | 911 | if (!pair_record->device_certificate || !pair_record->host_certificate || !pair_record->root_certificate) { |
838 | return LOCKDOWN_E_PLIST_ERROR; | 912 | return LOCKDOWN_E_PLIST_ERROR; |
839 | } | 913 | } |
840 | 914 | ||
841 | /* use passed pair_record */ | 915 | /* use passed pair_record */ |
842 | dict_record = lockdownd_pair_record_to_plist(pair_record); | 916 | pair_record_plist = lockdownd_pair_record_to_plist(pair_record); |
843 | 917 | ||
844 | pairing_mode = 1; | 918 | pairing_mode = 1; |
845 | } else { | 919 | } else { |
846 | ret = lockdownd_get_device_public_key(client, &public_key); | 920 | /* generate a new pair record if pairing */ |
847 | if (ret != LOCKDOWN_E_SUCCESS) { | 921 | if (!strcmp("Pair", verb)) { |
848 | if (public_key.data) | 922 | ret = pair_record_generate(client, &pair_record_plist); |
849 | free(public_key.data); | 923 | |
850 | debug_info("device refused to send public key."); | 924 | if (ret != LOCKDOWN_E_SUCCESS) { |
851 | return ret; | 925 | if (pair_record_plist) |
852 | } | 926 | plist_free(pair_record_plist); |
853 | debug_info("device public key follows:\n%.*s", public_key.size, public_key.data); | 927 | return ret; |
854 | /* get libimobiledevice pair_record */ | 928 | } |
855 | ret = generate_pair_record_plist(public_key, NULL, &dict_record); | 929 | |
856 | if (ret != LOCKDOWN_E_SUCCESS) { | 930 | /* get wifi mac now, if we get it later we fail on iOS 7 which causes a reconnect */ |
857 | if (dict_record) | 931 | lockdownd_get_value(client, NULL, "WiFiAddress", &wifi_node); |
858 | plist_free(dict_record); | 932 | } else { |
859 | return ret; | 933 | /* use existing pair record */ |
934 | userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record_plist); | ||
935 | if (uerr == USERPREF_E_READ_ERROR) { | ||
936 | debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); | ||
937 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
938 | } else if (uerr == USERPREF_E_NOENT) { | ||
939 | debug_info("ERROR: No pair record for %s", client->device->udid); | ||
940 | return LOCKDOWN_E_INVALID_CONF; | ||
941 | } else if (uerr != USERPREF_E_SUCCESS) { | ||
942 | debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); | ||
943 | return LOCKDOWN_E_INVALID_CONF; | ||
944 | } | ||
860 | } | 945 | } |
861 | } | 946 | } |
862 | 947 | ||
863 | /* Setup Pair request plist */ | 948 | plist_t request_pair_record = plist_copy(pair_record_plist); |
949 | |||
950 | /* remove stuff that is private */ | ||
951 | plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); | ||
952 | plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); | ||
953 | |||
954 | /* setup pair request plist */ | ||
864 | dict = plist_new_dict(); | 955 | dict = plist_new_dict(); |
865 | plist_dict_add_label(dict, client->label); | 956 | plist_dict_add_label(dict, client->label); |
866 | plist_dict_insert_item(dict,"PairRecord", dict_record); | 957 | plist_dict_set_item(dict, "PairRecord", request_pair_record); |
867 | plist_dict_insert_item(dict, "Request", plist_new_string(verb)); | 958 | plist_dict_set_item(dict, "Request", plist_new_string(verb)); |
959 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_string(LOCKDOWN_PROTOCOL_VERSION)); | ||
960 | |||
961 | if (options) { | ||
962 | plist_dict_set_item(dict, "PairingOptions", plist_copy(options)); | ||
963 | } | ||
868 | 964 | ||
869 | /* send to device */ | 965 | /* send to device */ |
870 | ret = lockdownd_send(client, dict); | 966 | ret = lockdownd_send(client, dict); |
871 | plist_free(dict); | 967 | plist_free(dict); |
872 | dict = NULL; | 968 | dict = NULL; |
873 | 969 | ||
874 | if (ret != LOCKDOWN_E_SUCCESS) | 970 | if (ret != LOCKDOWN_E_SUCCESS) { |
971 | plist_free(pair_record_plist); | ||
972 | if (wifi_node) | ||
973 | plist_free(wifi_node); | ||
875 | return ret; | 974 | return ret; |
975 | } | ||
876 | 976 | ||
877 | /* Now get device's answer */ | 977 | /* Now get device's answer */ |
878 | ret = lockdownd_receive(client, &dict); | 978 | ret = lockdownd_receive(client, &dict); |
879 | 979 | ||
880 | if (ret != LOCKDOWN_E_SUCCESS) | 980 | if (ret != LOCKDOWN_E_SUCCESS) { |
981 | plist_free(pair_record_plist); | ||
982 | if (wifi_node) | ||
983 | plist_free(wifi_node); | ||
881 | return ret; | 984 | return ret; |
985 | } | ||
882 | 986 | ||
883 | if (strcmp(verb, "Unpair") == 0) { | 987 | if (strcmp(verb, "Unpair") == 0) { |
884 | /* workaround for Unpair giving back ValidatePair, | 988 | /* workaround for Unpair giving back ValidatePair, |
885 | * seems to be a bug in the device's fw */ | 989 | * seems to be a bug in the device's fw */ |
886 | if (lockdown_check_result(dict, NULL) != RESULT_SUCCESS) { | 990 | if (lockdown_check_result(dict, NULL) != LOCKDOWN_E_SUCCESS) { |
887 | ret = LOCKDOWN_E_PAIRING_FAILED; | 991 | ret = LOCKDOWN_E_PAIRING_FAILED; |
888 | } | 992 | } |
889 | } else { | 993 | } else { |
890 | if (lockdown_check_result(dict, verb) != RESULT_SUCCESS) { | 994 | if (lockdown_check_result(dict, verb) != LOCKDOWN_E_SUCCESS) { |
891 | ret = LOCKDOWN_E_PAIRING_FAILED; | 995 | ret = LOCKDOWN_E_PAIRING_FAILED; |
892 | } | 996 | } |
893 | } | 997 | } |
@@ -896,13 +1000,32 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ | |||
896 | if (ret == LOCKDOWN_E_SUCCESS) { | 1000 | if (ret == LOCKDOWN_E_SUCCESS) { |
897 | debug_info("%s success", verb); | 1001 | debug_info("%s success", verb); |
898 | if (!pairing_mode) { | 1002 | if (!pairing_mode) { |
1003 | debug_info("internal pairing mode"); | ||
899 | if (!strcmp("Unpair", verb)) { | 1004 | if (!strcmp("Unpair", verb)) { |
900 | /* remove public key from config */ | 1005 | /* remove public key from config */ |
901 | userpref_remove_device_public_key(client->uuid); | 1006 | userpref_delete_pair_record(client->device->udid); |
902 | } else { | 1007 | } else { |
903 | /* store public key in config */ | 1008 | if (!strcmp("Pair", verb)) { |
904 | userpref_set_device_public_key(client->uuid, public_key); | 1009 | /* add returned escrow bag if available */ |
1010 | plist_t extra_node = plist_dict_get_item(dict, USERPREF_ESCROW_BAG_KEY); | ||
1011 | if (extra_node && plist_get_node_type(extra_node) == PLIST_DATA) { | ||
1012 | debug_info("Saving EscrowBag from response in pair record"); | ||
1013 | plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(extra_node)); | ||
1014 | } | ||
1015 | |||
1016 | /* save previously retrieved wifi mac address in pair record */ | ||
1017 | if (wifi_node) { | ||
1018 | debug_info("Saving WiFiAddress from device in pair record"); | ||
1019 | plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, plist_copy(wifi_node)); | ||
1020 | plist_free(wifi_node); | ||
1021 | wifi_node = NULL; | ||
1022 | } | ||
1023 | |||
1024 | userpref_save_pair_record(client->device->udid, client->device->mux_id, pair_record_plist); | ||
1025 | } | ||
905 | } | 1026 | } |
1027 | } else { | ||
1028 | debug_info("external pairing mode"); | ||
906 | } | 1029 | } |
907 | } else { | 1030 | } else { |
908 | debug_info("%s failure", verb); | 1031 | debug_info("%s failure", verb); |
@@ -914,92 +1037,60 @@ static lockdownd_error_t lockdownd_do_pair(lockdownd_client_t client, lockdownd_ | |||
914 | plist_get_string_val(error_node, &value); | 1037 | plist_get_string_val(error_node, &value); |
915 | if (value) { | 1038 | if (value) { |
916 | /* the first pairing fails if the device is password protected */ | 1039 | /* the first pairing fails if the device is password protected */ |
917 | if (!strcmp(value, "PasswordProtected")) { | 1040 | ret = lockdownd_strtoerr(value); |
918 | ret = LOCKDOWN_E_PASSWORD_PROTECTED; | ||
919 | } else if (!strcmp(value, "InvalidHostID")) { | ||
920 | ret = LOCKDOWN_E_INVALID_HOST_ID; | ||
921 | } | ||
922 | free(value); | 1041 | free(value); |
923 | } | 1042 | } |
924 | |||
925 | plist_free(error_node); | ||
926 | error_node = NULL; | ||
927 | } | 1043 | } |
928 | } | 1044 | } |
929 | plist_free(dict); | 1045 | |
930 | dict = NULL; | 1046 | if (pair_record_plist) { |
931 | if (public_key.data) | 1047 | plist_free(pair_record_plist); |
932 | free(public_key.data); | 1048 | pair_record_plist = NULL; |
1049 | } | ||
1050 | |||
1051 | if (wifi_node) { | ||
1052 | plist_free(wifi_node); | ||
1053 | wifi_node = NULL; | ||
1054 | } | ||
1055 | |||
1056 | if (result) { | ||
1057 | *result = dict; | ||
1058 | } else { | ||
1059 | plist_free(dict); | ||
1060 | dict = NULL; | ||
1061 | } | ||
1062 | |||
933 | return ret; | 1063 | return ret; |
934 | } | 1064 | } |
935 | 1065 | ||
936 | /** | ||
937 | * Pairs the device using the supplied pair record. | ||
938 | * | ||
939 | * @param client The lockdown client to pair with. | ||
940 | * @param pair_record The pair record to use for pairing. If NULL is passed, then | ||
941 | * the pair records from the current machine are used. New records will be | ||
942 | * generated automatically when pairing is done for the first time. | ||
943 | * | ||
944 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
945 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | ||
946 | * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, | ||
947 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | ||
948 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | ||
949 | */ | ||
950 | lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) | 1066 | lockdownd_error_t lockdownd_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) |
951 | { | 1067 | { |
952 | return lockdownd_do_pair(client, pair_record, "Pair"); | 1068 | |
1069 | plist_t options = plist_new_dict(); | ||
1070 | plist_dict_set_item(options, "ExtendedPairingErrors", plist_new_bool(1)); | ||
1071 | |||
1072 | lockdownd_error_t ret = lockdownd_do_pair(client, pair_record, "Pair", options, NULL); | ||
1073 | |||
1074 | plist_free(options); | ||
1075 | |||
1076 | return ret; | ||
1077 | } | ||
1078 | |||
1079 | lockdownd_error_t lockdownd_pair_with_options(lockdownd_client_t client, lockdownd_pair_record_t pair_record, plist_t options, plist_t *response) | ||
1080 | { | ||
1081 | return lockdownd_do_pair(client, pair_record, "Pair", options, response); | ||
953 | } | 1082 | } |
954 | 1083 | ||
955 | /** | ||
956 | * Validates if the device is paired with the given HostID. If succeeded them | ||
957 | * specified host will become trusted host of the device indicated by the | ||
958 | * lockdownd preference named TrustedHostAttached. Otherwise the host must because | ||
959 | * paired using lockdownd_pair() first. | ||
960 | * | ||
961 | * @param client The lockdown client to pair with. | ||
962 | * @param pair_record The pair record to validate pairing with. If NULL is | ||
963 | * passed, then the pair record is read from the internal pairing record | ||
964 | * management. | ||
965 | * | ||
966 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
967 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | ||
968 | * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, | ||
969 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | ||
970 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | ||
971 | */ | ||
972 | lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) | 1084 | lockdownd_error_t lockdownd_validate_pair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) |
973 | { | 1085 | { |
974 | return lockdownd_do_pair(client, pair_record, "ValidatePair"); | 1086 | return lockdownd_do_pair(client, pair_record, "ValidatePair", NULL, NULL); |
975 | } | 1087 | } |
976 | 1088 | ||
977 | /** | ||
978 | * Unpairs the device with the given HostID and removes the pairing records | ||
979 | * from the device and host if the internal pairing record management is used. | ||
980 | * | ||
981 | * @param client The lockdown client to pair with. | ||
982 | * @param pair_record The pair record to use for unpair. If NULL is passed, then | ||
983 | * the pair records from the current machine are used. | ||
984 | * | ||
985 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
986 | * LOCKDOWN_E_PLIST_ERROR if the pair_record certificates are wrong, | ||
987 | * LOCKDOWN_E_PAIRING_FAILED if the pairing failed, | ||
988 | * LOCKDOWN_E_PASSWORD_PROTECTED if the device is password protected, | ||
989 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the caller's host id | ||
990 | */ | ||
991 | lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) | 1089 | lockdownd_error_t lockdownd_unpair(lockdownd_client_t client, lockdownd_pair_record_t pair_record) |
992 | { | 1090 | { |
993 | return lockdownd_do_pair(client, pair_record, "Unpair"); | 1091 | return lockdownd_do_pair(client, pair_record, "Unpair", NULL, NULL); |
994 | } | 1092 | } |
995 | 1093 | ||
996 | /** | ||
997 | * Tells the device to immediately enter recovery mode. | ||
998 | * | ||
999 | * @param client The lockdown client | ||
1000 | * | ||
1001 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL | ||
1002 | */ | ||
1003 | lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) | 1094 | lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) |
1004 | { | 1095 | { |
1005 | if (!client) | 1096 | if (!client) |
@@ -1009,7 +1100,7 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) | |||
1009 | 1100 | ||
1010 | plist_t dict = plist_new_dict(); | 1101 | plist_t dict = plist_new_dict(); |
1011 | plist_dict_add_label(dict, client->label); | 1102 | plist_dict_add_label(dict, client->label); |
1012 | plist_dict_insert_item(dict,"Request", plist_new_string("EnterRecovery")); | 1103 | plist_dict_set_item(dict,"Request", plist_new_string("EnterRecovery")); |
1013 | 1104 | ||
1014 | debug_info("telling device to enter recovery mode"); | 1105 | debug_info("telling device to enter recovery mode"); |
1015 | 1106 | ||
@@ -1019,23 +1110,17 @@ lockdownd_error_t lockdownd_enter_recovery(lockdownd_client_t client) | |||
1019 | 1110 | ||
1020 | ret = lockdownd_receive(client, &dict); | 1111 | ret = lockdownd_receive(client, &dict); |
1021 | 1112 | ||
1022 | if (lockdown_check_result(dict, "EnterRecovery") == RESULT_SUCCESS) { | 1113 | ret = lockdown_check_result(dict, "EnterRecovery"); |
1114 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
1023 | debug_info("success"); | 1115 | debug_info("success"); |
1024 | ret = LOCKDOWN_E_SUCCESS; | ||
1025 | } | 1116 | } |
1117 | |||
1026 | plist_free(dict); | 1118 | plist_free(dict); |
1027 | dict = NULL; | 1119 | dict = NULL; |
1120 | |||
1028 | return ret; | 1121 | return ret; |
1029 | } | 1122 | } |
1030 | 1123 | ||
1031 | /** | ||
1032 | * Sends the Goodbye request to lockdownd signaling the end of communication. | ||
1033 | * | ||
1034 | * @param client The lockdown client | ||
1035 | * | ||
1036 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
1037 | * LOCKDOWN_E_PLIST_ERROR if the device did not acknowledge the request | ||
1038 | */ | ||
1039 | lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) | 1124 | lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) |
1040 | { | 1125 | { |
1041 | if (!client) | 1126 | if (!client) |
@@ -1045,7 +1130,7 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) | |||
1045 | 1130 | ||
1046 | plist_t dict = plist_new_dict(); | 1131 | plist_t dict = plist_new_dict(); |
1047 | plist_dict_add_label(dict, client->label); | 1132 | plist_dict_add_label(dict, client->label); |
1048 | plist_dict_insert_item(dict,"Request", plist_new_string("Goodbye")); | 1133 | plist_dict_set_item(dict,"Request", plist_new_string("Goodbye")); |
1049 | 1134 | ||
1050 | debug_info("called"); | 1135 | debug_info("called"); |
1051 | 1136 | ||
@@ -1059,187 +1144,17 @@ lockdownd_error_t lockdownd_goodbye(lockdownd_client_t client) | |||
1059 | return LOCKDOWN_E_PLIST_ERROR; | 1144 | return LOCKDOWN_E_PLIST_ERROR; |
1060 | } | 1145 | } |
1061 | 1146 | ||
1062 | if (lockdown_check_result(dict, "Goodbye") == RESULT_SUCCESS) { | 1147 | ret = lockdown_check_result(dict, "Goodbye"); |
1148 | if (ret == LOCKDOWN_E_SUCCESS) { | ||
1063 | debug_info("success"); | 1149 | debug_info("success"); |
1064 | ret = LOCKDOWN_E_SUCCESS; | ||
1065 | } | 1150 | } |
1151 | |||
1066 | plist_free(dict); | 1152 | plist_free(dict); |
1067 | dict = NULL; | 1153 | dict = NULL; |
1068 | return ret; | ||
1069 | } | ||
1070 | |||
1071 | /** | ||
1072 | * Generates the device certificate from the public key as well as the host | ||
1073 | * and root certificates. | ||
1074 | * | ||
1075 | * @param public_key The public key of the device to use for generation. | ||
1076 | * @param odevice_cert Holds the generated device certificate. | ||
1077 | * @param ohost_cert Holds the generated host certificate. | ||
1078 | * @param oroot_cert Holds the generated root certificate. | ||
1079 | * | ||
1080 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a parameter is NULL, | ||
1081 | * LOCKDOWN_E_INVALID_CONF if the internal configuration system failed, | ||
1082 | * LOCKDOWN_E_SSL_ERROR if the certificates could not be generated | ||
1083 | */ | ||
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 | 1154 | ||
1227 | return ret; | 1155 | return ret; |
1228 | } | 1156 | } |
1229 | 1157 | ||
1230 | /** | ||
1231 | * Opens a session with lockdownd and switches to SSL mode if device wants it. | ||
1232 | * | ||
1233 | * @param client The lockdownd client | ||
1234 | * @param host_id The HostID of the computer | ||
1235 | * @param session_id The new session_id of the created session | ||
1236 | * @param ssl_enabled Whether SSL communication is used in the session | ||
1237 | * | ||
1238 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when a client or | ||
1239 | * host_id is NULL, LOCKDOWN_E_PLIST_ERROR if the response plist had errors, | ||
1240 | * LOCKDOWN_E_INVALID_HOST_ID if the device does not know the supplied HostID, | ||
1241 | * LOCKDOWN_E_SSL_ERROR if enabling SSL communication failed | ||
1242 | */ | ||
1243 | lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) | 1158 | lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char *host_id, char **session_id, int *ssl_enabled) |
1244 | { | 1159 | { |
1245 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; | 1160 | lockdownd_error_t ret = LOCKDOWN_E_SUCCESS; |
@@ -1251,14 +1166,28 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1251 | /* if we have a running session, stop current one first */ | 1166 | /* if we have a running session, stop current one first */ |
1252 | if (client->session_id) { | 1167 | if (client->session_id) { |
1253 | lockdownd_stop_session(client, client->session_id); | 1168 | lockdownd_stop_session(client, client->session_id); |
1254 | free(client->session_id); | ||
1255 | } | 1169 | } |
1256 | 1170 | ||
1257 | /* setup request plist */ | 1171 | /* setup request plist */ |
1258 | dict = plist_new_dict(); | 1172 | dict = plist_new_dict(); |
1259 | plist_dict_add_label(dict, client->label); | 1173 | plist_dict_add_label(dict, client->label); |
1260 | plist_dict_insert_item(dict,"HostID", plist_new_string(host_id)); | 1174 | plist_dict_set_item(dict,"Request", plist_new_string("StartSession")); |
1261 | plist_dict_insert_item(dict,"Request", plist_new_string("StartSession")); | 1175 | |
1176 | /* add host id */ | ||
1177 | if (host_id) { | ||
1178 | plist_dict_set_item(dict, "HostID", plist_new_string(host_id)); | ||
1179 | } | ||
1180 | |||
1181 | /* add system buid */ | ||
1182 | char *system_buid = NULL; | ||
1183 | userpref_read_system_buid(&system_buid); | ||
1184 | if (system_buid) { | ||
1185 | plist_dict_set_item(dict, "SystemBUID", plist_new_string(system_buid)); | ||
1186 | if (system_buid) { | ||
1187 | free(system_buid); | ||
1188 | system_buid = NULL; | ||
1189 | } | ||
1190 | } | ||
1262 | 1191 | ||
1263 | ret = lockdownd_send(client, dict); | 1192 | ret = lockdownd_send(client, dict); |
1264 | plist_free(dict); | 1193 | plist_free(dict); |
@@ -1272,17 +1201,8 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1272 | if (!dict) | 1201 | if (!dict) |
1273 | return LOCKDOWN_E_PLIST_ERROR; | 1202 | return LOCKDOWN_E_PLIST_ERROR; |
1274 | 1203 | ||
1275 | if (lockdown_check_result(dict, "StartSession") == RESULT_FAILURE) { | 1204 | ret = lockdown_check_result(dict, "StartSession"); |
1276 | plist_t error_node = plist_dict_get_item(dict, "Error"); | 1205 | if (ret == LOCKDOWN_E_SUCCESS) { |
1277 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { | ||
1278 | char *error = NULL; | ||
1279 | plist_get_string_val(error_node, &error); | ||
1280 | if (!strcmp(error, "InvalidHostID")) { | ||
1281 | ret = LOCKDOWN_E_INVALID_HOST_ID; | ||
1282 | } | ||
1283 | free(error); | ||
1284 | } | ||
1285 | } else { | ||
1286 | uint8_t use_ssl = 0; | 1206 | uint8_t use_ssl = 0; |
1287 | 1207 | ||
1288 | plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); | 1208 | plist_t enable_ssl = plist_dict_get_item(dict, "EnableSessionSSL"); |
@@ -1299,6 +1219,7 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1299 | if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) { | 1219 | if (session_node && (plist_get_node_type(session_node) == PLIST_STRING)) { |
1300 | plist_get_string_val(session_node, &client->session_id); | 1220 | plist_get_string_val(session_node, &client->session_id); |
1301 | } | 1221 | } |
1222 | |||
1302 | if (client->session_id) { | 1223 | if (client->session_id) { |
1303 | debug_info("SessionID: %s", client->session_id); | 1224 | debug_info("SessionID: %s", client->session_id); |
1304 | if (session_id != NULL) | 1225 | if (session_id != NULL) |
@@ -1306,18 +1227,15 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1306 | } else { | 1227 | } else { |
1307 | debug_info("Failed to get SessionID!"); | 1228 | debug_info("Failed to get SessionID!"); |
1308 | } | 1229 | } |
1309 | debug_info("Enable SSL Session: %s", (use_ssl?"true":"false")); | 1230 | |
1231 | debug_info("Enable SSL Session: %s", (use_ssl ? "true" : "false")); | ||
1232 | |||
1310 | if (use_ssl) { | 1233 | if (use_ssl) { |
1311 | ret = property_list_service_enable_ssl(client->parent); | 1234 | ret = lockdownd_error(property_list_service_enable_ssl(client->parent)); |
1312 | if (ret == PROPERTY_LIST_SERVICE_E_SUCCESS) { | 1235 | client->ssl_enabled = (ret == LOCKDOWN_E_SUCCESS ? 1 : 0); |
1313 | client->ssl_enabled = 1; | ||
1314 | } else { | ||
1315 | ret = LOCKDOWN_E_SSL_ERROR; | ||
1316 | client->ssl_enabled = 0; | ||
1317 | } | ||
1318 | } else { | 1236 | } else { |
1319 | client->ssl_enabled = 0; | ||
1320 | ret = LOCKDOWN_E_SUCCESS; | 1237 | ret = LOCKDOWN_E_SUCCESS; |
1238 | client->ssl_enabled = 0; | ||
1321 | } | 1239 | } |
1322 | } | 1240 | } |
1323 | 1241 | ||
@@ -1328,40 +1246,96 @@ lockdownd_error_t lockdownd_start_session(lockdownd_client_t client, const char | |||
1328 | } | 1246 | } |
1329 | 1247 | ||
1330 | /** | 1248 | /** |
1331 | * Requests to start a service and retrieve it's port on success. | 1249 | * Internal function used by lockdownd_do_start_service to create the |
1250 | * StartService request's plist. | ||
1332 | * | 1251 | * |
1333 | * @param client The lockdownd client | 1252 | * @param client The lockdownd client |
1334 | * @param service The name of the service to start | 1253 | * @param identifier The identifier of the service to start |
1335 | * @param port The port number the service was started on | 1254 | * @param send_escrow_bag Should we send the device's escrow bag with the request |
1336 | 1255 | * @param request The request's plist on success, NULL on failure | |
1337 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG if a parameter | 1256 | * |
1257 | * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_CONF on failure | ||
1258 | * to read the escrow bag from the device's record (when used). | ||
1259 | */ | ||
1260 | static lockdownd_error_t lockdownd_build_start_service_request(lockdownd_client_t client, const char *identifier, int send_escrow_bag, plist_t *request) | ||
1261 | { | ||
1262 | plist_t dict = plist_new_dict(); | ||
1263 | |||
1264 | /* create the basic request params */ | ||
1265 | plist_dict_add_label(dict, client->label); | ||
1266 | plist_dict_set_item(dict, "Request", plist_new_string("StartService")); | ||
1267 | plist_dict_set_item(dict, "Service", plist_new_string(identifier)); | ||
1268 | |||
1269 | /* if needed - get the escrow bag for the device and send it with the request */ | ||
1270 | if (send_escrow_bag) { | ||
1271 | /* get the pairing record */ | ||
1272 | plist_t pair_record = NULL; | ||
1273 | userpref_error_t uerr = userpref_read_pair_record(client->device->udid, &pair_record); | ||
1274 | if (uerr == USERPREF_E_READ_ERROR) { | ||
1275 | debug_info("ERROR: Failed to retrieve pair record for %s", client->device->udid); | ||
1276 | plist_free(dict); | ||
1277 | return LOCKDOWN_E_RECEIVE_TIMEOUT; | ||
1278 | } else if (uerr == USERPREF_E_NOENT) { | ||
1279 | debug_info("ERROR: No pair record for %s", client->device->udid); | ||
1280 | plist_free(dict); | ||
1281 | return LOCKDOWN_E_INVALID_CONF; | ||
1282 | } else if (uerr != USERPREF_E_SUCCESS) { | ||
1283 | debug_info("ERROR: Failed to retrieve or parse pair record for %s", client->device->udid); | ||
1284 | plist_free(dict); | ||
1285 | return LOCKDOWN_E_INVALID_CONF; | ||
1286 | } | ||
1287 | |||
1288 | /* try to read the escrow bag from the record */ | ||
1289 | plist_t escrow_bag = plist_dict_get_item(pair_record, USERPREF_ESCROW_BAG_KEY); | ||
1290 | if (!escrow_bag || (PLIST_DATA != plist_get_node_type(escrow_bag))) { | ||
1291 | debug_info("ERROR: Failed to retrieve the escrow bag from the device's record"); | ||
1292 | plist_free(dict); | ||
1293 | plist_free(pair_record); | ||
1294 | return LOCKDOWN_E_INVALID_CONF; | ||
1295 | } | ||
1296 | |||
1297 | debug_info("Adding escrow bag to StartService for %s", identifier); | ||
1298 | plist_dict_set_item(dict, USERPREF_ESCROW_BAG_KEY, plist_copy(escrow_bag)); | ||
1299 | plist_free(pair_record); | ||
1300 | } | ||
1301 | |||
1302 | *request = dict; | ||
1303 | return LOCKDOWN_E_SUCCESS; | ||
1304 | } | ||
1305 | |||
1306 | /** | ||
1307 | * Function used internally by lockdownd_start_service and lockdownd_start_service_with_escrow_bag. | ||
1308 | * | ||
1309 | * @param client The lockdownd client | ||
1310 | * @param identifier The identifier of the service to start | ||
1311 | * @param send_escrow_bag Should we send the device's escrow bag with the request | ||
1312 | * @param descriptor The service descriptor on success or NULL on failure | ||
1313 | * | ||
1314 | * @return LOCKDOWN_E_SUCCESS on success, LOCKDOWN_E_INVALID_ARG if a parameter | ||
1338 | * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known | 1315 | * is NULL, LOCKDOWN_E_INVALID_SERVICE if the requested service is not known |
1339 | * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because | 1316 | * by the device, LOCKDOWN_E_START_SERVICE_FAILED if the service could not because |
1340 | * started by the device | 1317 | * started by the device, LOCKDOWN_E_INVALID_CONF if the host id or escrow bag (when |
1318 | * used) are missing from the device record. | ||
1341 | */ | 1319 | */ |
1342 | lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char *service, uint16_t *port) | 1320 | 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 | { | 1321 | { |
1344 | if (!client || !service || !port) | 1322 | if (!client || !identifier || !service) |
1345 | return LOCKDOWN_E_INVALID_ARG; | 1323 | return LOCKDOWN_E_INVALID_ARG; |
1346 | 1324 | ||
1347 | char *host_id = NULL; | 1325 | if (*service) { |
1348 | userpref_get_host_id(&host_id); | 1326 | // reset fields if service descriptor is reused |
1349 | if (!host_id) | 1327 | (*service)->port = 0; |
1350 | return LOCKDOWN_E_INVALID_CONF; | 1328 | (*service)->ssl_enabled = 0; |
1351 | if (!client->session_id) | 1329 | } |
1352 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1353 | 1330 | ||
1354 | plist_t dict = NULL; | 1331 | plist_t dict = NULL; |
1355 | uint16_t port_loc = 0; | 1332 | uint16_t port_loc = 0; |
1356 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | 1333 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; |
1357 | 1334 | ||
1358 | free(host_id); | 1335 | /* create StartService request */ |
1359 | host_id = NULL; | 1336 | ret = lockdownd_build_start_service_request(client, identifier, send_escrow_bag, &dict); |
1360 | 1337 | if (LOCKDOWN_E_SUCCESS != ret) | |
1361 | dict = plist_new_dict(); | 1338 | return ret; |
1362 | plist_dict_add_label(dict, client->label); | ||
1363 | plist_dict_insert_item(dict,"Request", plist_new_string("StartService")); | ||
1364 | plist_dict_insert_item(dict,"Service", plist_new_string(service)); | ||
1365 | 1339 | ||
1366 | /* send to device */ | 1340 | /* send to device */ |
1367 | ret = lockdownd_send(client, dict); | 1341 | ret = lockdownd_send(client, dict); |
@@ -1379,57 +1353,63 @@ lockdownd_error_t lockdownd_start_service(lockdownd_client_t client, const char | |||
1379 | if (!dict) | 1353 | if (!dict) |
1380 | return LOCKDOWN_E_PLIST_ERROR; | 1354 | return LOCKDOWN_E_PLIST_ERROR; |
1381 | 1355 | ||
1382 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 1356 | ret = lockdown_check_result(dict, "StartService"); |
1383 | if (lockdown_check_result(dict, "StartService") == RESULT_SUCCESS) { | 1357 | if (ret == LOCKDOWN_E_SUCCESS) { |
1384 | plist_t port_value_node = plist_dict_get_item(dict, "Port"); | 1358 | if (*service == NULL) |
1385 | 1359 | *service = (lockdownd_service_descriptor_t)malloc(sizeof(struct lockdownd_service_descriptor)); | |
1386 | if (port_value_node && (plist_get_node_type(port_value_node) == PLIST_UINT)) { | 1360 | (*service)->port = 0; |
1361 | (*service)->ssl_enabled = 0; | ||
1362 | (*service)->identifier = strdup(identifier); | ||
1363 | |||
1364 | /* read service port number */ | ||
1365 | plist_t node = plist_dict_get_item(dict, "Port"); | ||
1366 | if (node && (plist_get_node_type(node) == PLIST_UINT)) { | ||
1387 | uint64_t port_value = 0; | 1367 | uint64_t port_value = 0; |
1388 | plist_get_uint_val(port_value_node, &port_value); | 1368 | plist_get_uint_val(node, &port_value); |
1389 | 1369 | ||
1390 | if (port_value) { | 1370 | if (port_value) { |
1391 | port_loc = port_value; | 1371 | port_loc = port_value; |
1392 | ret = LOCKDOWN_E_SUCCESS; | 1372 | ret = LOCKDOWN_E_SUCCESS; |
1393 | } | 1373 | } |
1394 | if (port && ret == LOCKDOWN_E_SUCCESS) | 1374 | if (port_loc && ret == LOCKDOWN_E_SUCCESS) { |
1395 | *port = port_loc; | 1375 | (*service)->port = port_loc; |
1376 | } | ||
1377 | } | ||
1378 | |||
1379 | /* check if the service requires SSL */ | ||
1380 | node = plist_dict_get_item(dict, "EnableServiceSSL"); | ||
1381 | if (node && (plist_get_node_type(node) == PLIST_BOOLEAN)) { | ||
1382 | uint8_t b = 0; | ||
1383 | plist_get_bool_val(node, &b); | ||
1384 | (*service)->ssl_enabled = b; | ||
1396 | } | 1385 | } |
1397 | } else { | 1386 | } else { |
1398 | ret = LOCKDOWN_E_START_SERVICE_FAILED; | ||
1399 | plist_t error_node = plist_dict_get_item(dict, "Error"); | 1387 | plist_t error_node = plist_dict_get_item(dict, "Error"); |
1400 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { | 1388 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { |
1401 | char *error = NULL; | 1389 | char *error = NULL; |
1402 | plist_get_string_val(error_node, &error); | 1390 | plist_get_string_val(error_node, &error); |
1403 | if (!strcmp(error, "InvalidService")) { | 1391 | ret = lockdownd_strtoerr(error); |
1404 | ret = LOCKDOWN_E_INVALID_SERVICE; | ||
1405 | } | ||
1406 | free(error); | 1392 | free(error); |
1407 | } | 1393 | } |
1408 | } | 1394 | } |
1409 | 1395 | ||
1410 | plist_free(dict); | 1396 | plist_free(dict); |
1411 | dict = NULL; | 1397 | dict = NULL; |
1398 | |||
1412 | return ret; | 1399 | return ret; |
1413 | } | 1400 | } |
1414 | 1401 | ||
1415 | /** | 1402 | 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. | 1403 | { |
1417 | * The ActivationRecord plist dictionary must be obtained using the | 1404 | return lockdownd_do_start_service(client, identifier, 0, service); |
1418 | * activation protocol requesting from Apple's https webservice. | 1405 | } |
1419 | * | 1406 | |
1420 | * @see http://iphone-docs.org/doku.php?id=docs:protocols:activation | 1407 | lockdownd_error_t lockdownd_start_service_with_escrow_bag(lockdownd_client_t client, const char *identifier, lockdownd_service_descriptor_t *service) |
1421 | * | 1408 | { |
1422 | * @param client The lockdown client | 1409 | return lockdownd_do_start_service(client, identifier, 1, service); |
1423 | * @param activation_record The activation record plist dictionary | 1410 | } |
1424 | * | 1411 | |
1425 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client or | 1412 | 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 | { | 1413 | { |
1434 | if (!client) | 1414 | if (!client) |
1435 | return LOCKDOWN_E_INVALID_ARG; | 1415 | return LOCKDOWN_E_INVALID_ARG; |
@@ -1444,8 +1424,8 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati | |||
1444 | 1424 | ||
1445 | plist_t dict = plist_new_dict(); | 1425 | plist_t dict = plist_new_dict(); |
1446 | plist_dict_add_label(dict, client->label); | 1426 | plist_dict_add_label(dict, client->label); |
1447 | plist_dict_insert_item(dict,"Request", plist_new_string("Activate")); | 1427 | plist_dict_set_item(dict,"Request", plist_new_string("Activate")); |
1448 | plist_dict_insert_item(dict,"ActivationRecord", plist_copy(activation_record)); | 1428 | plist_dict_set_item(dict,"ActivationRecord", plist_copy(activation_record)); |
1449 | 1429 | ||
1450 | ret = lockdownd_send(client, dict); | 1430 | ret = lockdownd_send(client, dict); |
1451 | plist_free(dict); | 1431 | plist_free(dict); |
@@ -1457,39 +1437,17 @@ lockdownd_error_t lockdownd_activate(lockdownd_client_t client, plist_t activati | |||
1457 | return LOCKDOWN_E_PLIST_ERROR; | 1437 | return LOCKDOWN_E_PLIST_ERROR; |
1458 | } | 1438 | } |
1459 | 1439 | ||
1460 | ret = LOCKDOWN_E_ACTIVATION_FAILED; | 1440 | ret = lockdown_check_result(dict, "Activate"); |
1461 | if (lockdown_check_result(dict, "Activate") == RESULT_SUCCESS) { | 1441 | if (ret == LOCKDOWN_E_SUCCESS) { |
1462 | debug_info("success"); | 1442 | debug_info("success"); |
1463 | ret = LOCKDOWN_E_SUCCESS; | ||
1464 | |||
1465 | } else { | ||
1466 | plist_t error_node = plist_dict_get_item(dict, "Error"); | ||
1467 | if (error_node && PLIST_STRING == plist_get_node_type(error_node)) { | ||
1468 | char *error = NULL; | ||
1469 | plist_get_string_val(error_node, &error); | ||
1470 | if (!strcmp(error, "InvalidActivationRecord")) { | ||
1471 | ret = LOCKDOWN_E_INVALID_ACTIVATION_RECORD; | ||
1472 | } | ||
1473 | free(error); | ||
1474 | } | ||
1475 | } | 1443 | } |
1476 | 1444 | ||
1477 | plist_free(dict); | 1445 | plist_free(dict); |
1478 | dict = NULL; | 1446 | dict = NULL; |
1479 | 1447 | ||
1480 | return ret; | 1448 | return ret; |
1481 | } | 1449 | } |
1482 | 1450 | ||
1483 | /** | ||
1484 | * Deactivates the device, returning it to the locked “Activate with iTunes” | ||
1485 | * screen. | ||
1486 | * | ||
1487 | * @param client The lockdown client | ||
1488 | * | ||
1489 | * @return LOCKDOWN_E_SUCCESS on success, NP_E_INVALID_ARG when client is NULL, | ||
1490 | * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open, | ||
1491 | * LOCKDOWN_E_PLIST_ERROR if the received plist is broken | ||
1492 | */ | ||
1493 | lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) | 1451 | lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) |
1494 | { | 1452 | { |
1495 | if (!client) | 1453 | if (!client) |
@@ -1502,7 +1460,7 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) | |||
1502 | 1460 | ||
1503 | plist_t dict = plist_new_dict(); | 1461 | plist_t dict = plist_new_dict(); |
1504 | plist_dict_add_label(dict, client->label); | 1462 | plist_dict_add_label(dict, client->label); |
1505 | plist_dict_insert_item(dict,"Request", plist_new_string("Deactivate")); | 1463 | plist_dict_set_item(dict,"Request", plist_new_string("Deactivate")); |
1506 | 1464 | ||
1507 | ret = lockdownd_send(client, dict); | 1465 | ret = lockdownd_send(client, dict); |
1508 | plist_free(dict); | 1466 | plist_free(dict); |
@@ -1514,11 +1472,11 @@ lockdownd_error_t lockdownd_deactivate(lockdownd_client_t client) | |||
1514 | return LOCKDOWN_E_PLIST_ERROR; | 1472 | return LOCKDOWN_E_PLIST_ERROR; |
1515 | } | 1473 | } |
1516 | 1474 | ||
1517 | ret = LOCKDOWN_E_UNKNOWN_ERROR; | 1475 | ret = lockdown_check_result(dict, "Deactivate"); |
1518 | if (lockdown_check_result(dict, "Deactivate") == RESULT_SUCCESS) { | 1476 | if (ret == LOCKDOWN_E_SUCCESS) { |
1519 | debug_info("success"); | 1477 | debug_info("success"); |
1520 | ret = LOCKDOWN_E_SUCCESS; | ||
1521 | } | 1478 | } |
1479 | |||
1522 | plist_free(dict); | 1480 | plist_free(dict); |
1523 | dict = NULL; | 1481 | dict = NULL; |
1524 | 1482 | ||
@@ -1537,19 +1495,6 @@ static void str_remove_spaces(char *source) | |||
1537 | *dest = 0; | 1495 | *dest = 0; |
1538 | } | 1496 | } |
1539 | 1497 | ||
1540 | /** | ||
1541 | * Calculates and returns the data classes the device supports from lockdownd. | ||
1542 | * | ||
1543 | * @param client An initialized lockdownd client. | ||
1544 | * @param classes A pointer to store an array of class names. The caller is responsible | ||
1545 | * for freeing the memory which can be done using mobilesync_data_classes_free(). | ||
1546 | * @param count The number of items in the classes array. | ||
1547 | * | ||
1548 | * @return LOCKDOWN_E_SUCCESS on success, | ||
1549 | * LOCKDOWN_E_INVALID_ARG when client is NULL, | ||
1550 | * LOCKDOWN_E_NO_RUNNING_SESSION if no session is open, | ||
1551 | * LOCKDOWN_E_PLIST_ERROR if the received plist is broken | ||
1552 | */ | ||
1553 | lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) | 1498 | lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, char ***classes, int *count) |
1554 | { | 1499 | { |
1555 | if (!client) | 1500 | if (!client) |
@@ -1583,14 +1528,16 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha | |||
1583 | } | 1528 | } |
1584 | 1529 | ||
1585 | while((value = plist_array_get_item(dict, *count)) != NULL) { | 1530 | while((value = plist_array_get_item(dict, *count)) != NULL) { |
1586 | plist_get_string_val(value, &val); | 1531 | plist_get_string_val(value, &val); |
1587 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); | 1532 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); |
1588 | str_remove_spaces(val); | 1533 | str_remove_spaces(val); |
1589 | asprintf(&newlist[*count], "com.apple.%s", val); | 1534 | if (asprintf(&newlist[*count], "com.apple.%s", val) < 0) { |
1590 | free(val); | 1535 | debug_info("ERROR: asprintf failed"); |
1591 | val = NULL; | 1536 | } |
1592 | *classes = newlist; | 1537 | free(val); |
1593 | *count = *count+1; | 1538 | val = NULL; |
1539 | *classes = newlist; | ||
1540 | *count = *count+1; | ||
1594 | } | 1541 | } |
1595 | 1542 | ||
1596 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); | 1543 | newlist = realloc(*classes, sizeof(char*) * (*count+1)); |
@@ -1603,14 +1550,6 @@ lockdownd_error_t lockdownd_get_sync_data_classes(lockdownd_client_t client, cha | |||
1603 | return LOCKDOWN_E_SUCCESS; | 1550 | return LOCKDOWN_E_SUCCESS; |
1604 | } | 1551 | } |
1605 | 1552 | ||
1606 | |||
1607 | /** | ||
1608 | * Frees memory of an allocated array of data classes as returned by lockdownd_get_sync_data_classes() | ||
1609 | * | ||
1610 | * @param classes An array of class names to free. | ||
1611 | * | ||
1612 | * @return LOCKDOWN_E_SUCCESS on success | ||
1613 | */ | ||
1614 | lockdownd_error_t lockdownd_data_classes_free(char **classes) | 1553 | lockdownd_error_t lockdownd_data_classes_free(char **classes) |
1615 | { | 1554 | { |
1616 | if (classes) { | 1555 | if (classes) { |
@@ -1622,3 +1561,51 @@ lockdownd_error_t lockdownd_data_classes_free(char **classes) | |||
1622 | } | 1561 | } |
1623 | return LOCKDOWN_E_SUCCESS; | 1562 | return LOCKDOWN_E_SUCCESS; |
1624 | } | 1563 | } |
1564 | |||
1565 | lockdownd_error_t lockdownd_service_descriptor_free(lockdownd_service_descriptor_t service) | ||
1566 | { | ||
1567 | if (service) { | ||
1568 | free(service->identifier); | ||
1569 | free(service); | ||
1570 | } | ||
1571 | |||
1572 | return LOCKDOWN_E_SUCCESS; | ||
1573 | } | ||
1574 | |||
1575 | const char* lockdownd_strerror(lockdownd_error_t err) | ||
1576 | { | ||
1577 | switch (err) { | ||
1578 | case LOCKDOWN_E_SUCCESS: | ||
1579 | return "Success"; | ||
1580 | case LOCKDOWN_E_INVALID_ARG: | ||
1581 | return "Invalid argument"; | ||
1582 | case LOCKDOWN_E_INVALID_CONF: | ||
1583 | return "Invalid configuration"; | ||
1584 | case LOCKDOWN_E_PLIST_ERROR: | ||
1585 | return "PropertyList error"; | ||
1586 | case LOCKDOWN_E_PAIRING_FAILED: | ||
1587 | return "Pairing failed"; | ||
1588 | case LOCKDOWN_E_SSL_ERROR: | ||
1589 | return "SSL error"; | ||
1590 | case LOCKDOWN_E_DICT_ERROR: | ||
1591 | return "Invalid dictionary"; | ||
1592 | case LOCKDOWN_E_RECEIVE_TIMEOUT: | ||
1593 | return "Receive timeout"; | ||
1594 | case LOCKDOWN_E_MUX_ERROR: | ||
1595 | return "Mux error"; | ||
1596 | case LOCKDOWN_E_NO_RUNNING_SESSION: | ||
1597 | return "No running session"; | ||
1598 | case LOCKDOWN_E_UNKNOWN_ERROR: | ||
1599 | return "Unknown Error"; | ||
1600 | default: { | ||
1601 | int i = 0; | ||
1602 | while (lockdownd_error_str_map[i].lockdown_errstr) { | ||
1603 | if (lockdownd_error_str_map[i].errcode == err) { | ||
1604 | return lockdownd_error_str_map[i].errstr; | ||
1605 | } | ||
1606 | i++; | ||
1607 | } | ||
1608 | } break; | ||
1609 | } | ||
1610 | return "Unknown Error"; | ||
1611 | } | ||
diff --git a/src/lockdown.h b/src/lockdown.h index a25e59d..ba291ec 100644 --- a/src/lockdown.h +++ b/src/lockdown.h | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * lockdownd.h | 2 | * lockdown.h |
3 | * Defines lockdown stuff, like the client struct. | 3 | * Defines lockdown stuff, like the client struct. |
4 | * | 4 | * |
5 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 6 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
6 | * | 7 | * |
7 | * This library is free software; you can redistribute it and/or | 8 | * This library is free software; you can redistribute it and/or |
@@ -19,24 +20,25 @@ | |||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 21 | */ |
21 | 22 | ||
22 | #ifndef LOCKDOWND_H | 23 | #ifndef __LOCKDOWND_H |
23 | #define LOCKDOWND_H | 24 | #define __LOCKDOWND_H |
24 | |||
25 | #include <gnutls/gnutls.h> | ||
26 | 25 | ||
26 | #include "idevice.h" | ||
27 | #include "libimobiledevice/lockdown.h" | 27 | #include "libimobiledevice/lockdown.h" |
28 | #include "property_list_service.h" | 28 | #include "property_list_service.h" |
29 | 29 | ||
30 | #define LOCKDOWN_PROTOCOL_VERSION "2" | ||
31 | |||
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..3fdca4d --- /dev/null +++ b/src/misagent.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * misagent.c | ||
3 | * com.apple.misagent service implementation. | ||
4 | * | ||
5 | * Copyright (c) 2012 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <stdio.h> | ||
28 | |||
29 | #ifndef _MSC_VER | ||
30 | #include <unistd.h> | ||
31 | #endif | ||
32 | |||
33 | #include <plist/plist.h> | ||
34 | |||
35 | #include "misagent.h" | ||
36 | #include "property_list_service.h" | ||
37 | #include "common/debug.h" | ||
38 | |||
39 | /** | ||
40 | * Convert a property_list_service_error_t value to a misagent_error_t | ||
41 | * value. Used internally to get correct error codes. | ||
42 | * | ||
43 | * @param err A property_list_service_error_t error code | ||
44 | * | ||
45 | * @return A matching misagent_error_t error code, | ||
46 | * MISAGENT_E_UNKNOWN_ERROR otherwise. | ||
47 | */ | ||
48 | static misagent_error_t misagent_error(property_list_service_error_t err) | ||
49 | { | ||
50 | switch (err) { | ||
51 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | ||
52 | return MISAGENT_E_SUCCESS; | ||
53 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | ||
54 | return MISAGENT_E_INVALID_ARG; | ||
55 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | ||
56 | return MISAGENT_E_PLIST_ERROR; | ||
57 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | ||
58 | return MISAGENT_E_CONN_FAILED; | ||
59 | default: | ||
60 | break; | ||
61 | } | ||
62 | return MISAGENT_E_UNKNOWN_ERROR; | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Checks the response from misagent to determine if the operation | ||
67 | * was successful or an error occurred. Internally used only. | ||
68 | * | ||
69 | * @param response a PLIST_DICT received from device's misagent | ||
70 | * @param status_code pointer to an int that will be set to the status code | ||
71 | * contained in the response | ||
72 | */ | ||
73 | static misagent_error_t misagent_check_result(plist_t response, int* status_code) | ||
74 | { | ||
75 | if (plist_get_node_type(response) != PLIST_DICT) { | ||
76 | return MISAGENT_E_PLIST_ERROR; | ||
77 | } | ||
78 | |||
79 | plist_t node = plist_dict_get_item(response, "Status"); | ||
80 | if (!node || (plist_get_node_type(node) != PLIST_UINT)) { | ||
81 | return MISAGENT_E_PLIST_ERROR; | ||
82 | } | ||
83 | |||
84 | uint64_t val = -1LL; | ||
85 | plist_get_uint_val(node, &val); | ||
86 | if ((int64_t)val == -1LL) { | ||
87 | return MISAGENT_E_PLIST_ERROR; | ||
88 | } | ||
89 | *status_code = (int)(val & 0xFFFFFFFF); | ||
90 | if (*status_code == 0) { | ||
91 | return MISAGENT_E_SUCCESS; | ||
92 | } | ||
93 | return MISAGENT_E_REQUEST_FAILED; | ||
94 | } | ||
95 | |||
96 | misagent_error_t misagent_client_new(idevice_t device, lockdownd_service_descriptor_t service, misagent_client_t *client) | ||
97 | { | ||
98 | property_list_service_client_t plistclient = NULL; | ||
99 | misagent_error_t err = misagent_error(property_list_service_client_new(device, service, &plistclient)); | ||
100 | if (err != MISAGENT_E_SUCCESS) { | ||
101 | return err; | ||
102 | } | ||
103 | |||
104 | misagent_client_t client_loc = (misagent_client_t) malloc(sizeof(struct misagent_client_private)); | ||
105 | client_loc->parent = plistclient; | ||
106 | client_loc->last_error = 0; | ||
107 | |||
108 | *client = client_loc; | ||
109 | return MISAGENT_E_SUCCESS; | ||
110 | } | ||
111 | |||
112 | misagent_error_t misagent_client_start_service(idevice_t device, misagent_client_t * client, const char* label) | ||
113 | { | ||
114 | misagent_error_t err = MISAGENT_E_UNKNOWN_ERROR; | ||
115 | service_client_factory_start_service(device, MISAGENT_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(misagent_client_new), &err); | ||
116 | return err; | ||
117 | } | ||
118 | |||
119 | misagent_error_t misagent_client_free(misagent_client_t client) | ||
120 | { | ||
121 | if (!client) | ||
122 | return MISAGENT_E_INVALID_ARG; | ||
123 | |||
124 | misagent_error_t err = MISAGENT_E_SUCCESS; | ||
125 | if (client->parent && client->parent->parent) { | ||
126 | misagent_error(property_list_service_client_free(client->parent)); | ||
127 | } | ||
128 | client->parent = NULL; | ||
129 | free(client); | ||
130 | |||
131 | return err; | ||
132 | } | ||
133 | |||
134 | misagent_error_t misagent_install(misagent_client_t client, plist_t profile) | ||
135 | { | ||
136 | if (!client || !client->parent || !profile || (plist_get_node_type(profile) != PLIST_DATA)) | ||
137 | return MISAGENT_E_INVALID_ARG; | ||
138 | |||
139 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
140 | |||
141 | plist_t dict = plist_new_dict(); | ||
142 | plist_dict_set_item(dict, "MessageType", plist_new_string("Install")); | ||
143 | plist_dict_set_item(dict, "Profile", plist_copy(profile)); | ||
144 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
145 | |||
146 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
147 | plist_free(dict); | ||
148 | dict = NULL; | ||
149 | |||
150 | if (res != MISAGENT_E_SUCCESS) { | ||
151 | debug_info("could not send plist, error %d", res); | ||
152 | return res; | ||
153 | } | ||
154 | |||
155 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
156 | if (res != MISAGENT_E_SUCCESS) { | ||
157 | debug_info("could not receive response, error %d", res); | ||
158 | return res; | ||
159 | } | ||
160 | if (!dict) { | ||
161 | debug_info("could not get response plist"); | ||
162 | return MISAGENT_E_UNKNOWN_ERROR; | ||
163 | } | ||
164 | |||
165 | res = misagent_check_result(dict, &client->last_error); | ||
166 | plist_free(dict); | ||
167 | |||
168 | return res; | ||
169 | } | ||
170 | |||
171 | misagent_error_t misagent_copy(misagent_client_t client, plist_t* profiles) | ||
172 | { | ||
173 | if (!client || !client->parent || !profiles) | ||
174 | return MISAGENT_E_INVALID_ARG; | ||
175 | |||
176 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
177 | |||
178 | plist_t dict = plist_new_dict(); | ||
179 | plist_dict_set_item(dict, "MessageType", plist_new_string("Copy")); | ||
180 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
181 | |||
182 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
183 | plist_free(dict); | ||
184 | dict = NULL; | ||
185 | |||
186 | if (res != MISAGENT_E_SUCCESS) { | ||
187 | debug_info("could not send plist, error %d", res); | ||
188 | return res; | ||
189 | } | ||
190 | |||
191 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
192 | if (res != MISAGENT_E_SUCCESS) { | ||
193 | debug_info("could not receive response, error %d", res); | ||
194 | return res; | ||
195 | } | ||
196 | if (!dict) { | ||
197 | debug_info("could not get response plist"); | ||
198 | return MISAGENT_E_UNKNOWN_ERROR; | ||
199 | } | ||
200 | |||
201 | res = misagent_check_result(dict, &client->last_error); | ||
202 | if (res == MISAGENT_E_SUCCESS) { | ||
203 | *profiles = plist_copy(plist_dict_get_item(dict, "Payload")); | ||
204 | } | ||
205 | plist_free(dict); | ||
206 | |||
207 | return res; | ||
208 | |||
209 | } | ||
210 | |||
211 | misagent_error_t misagent_copy_all(misagent_client_t client, plist_t* profiles) | ||
212 | { | ||
213 | if (!client || !client->parent || !profiles) | ||
214 | return MISAGENT_E_INVALID_ARG; | ||
215 | |||
216 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
217 | |||
218 | plist_t dict = plist_new_dict(); | ||
219 | plist_dict_set_item(dict, "MessageType", plist_new_string("CopyAll")); | ||
220 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
221 | |||
222 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
223 | plist_free(dict); | ||
224 | dict = NULL; | ||
225 | |||
226 | if (res != MISAGENT_E_SUCCESS) { | ||
227 | debug_info("could not send plist, error %d", res); | ||
228 | return res; | ||
229 | } | ||
230 | |||
231 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
232 | if (res != MISAGENT_E_SUCCESS) { | ||
233 | debug_info("could not receive response, error %d", res); | ||
234 | return res; | ||
235 | } | ||
236 | if (!dict) { | ||
237 | debug_info("could not get response plist"); | ||
238 | return MISAGENT_E_UNKNOWN_ERROR; | ||
239 | } | ||
240 | |||
241 | res = misagent_check_result(dict, &client->last_error); | ||
242 | if (res == MISAGENT_E_SUCCESS) { | ||
243 | *profiles = plist_copy(plist_dict_get_item(dict, "Payload")); | ||
244 | } | ||
245 | plist_free(dict); | ||
246 | |||
247 | return res; | ||
248 | |||
249 | } | ||
250 | |||
251 | misagent_error_t misagent_remove(misagent_client_t client, const char* profileID) | ||
252 | { | ||
253 | if (!client || !client->parent || !profileID) | ||
254 | return MISAGENT_E_INVALID_ARG; | ||
255 | |||
256 | client->last_error = MISAGENT_E_UNKNOWN_ERROR; | ||
257 | |||
258 | plist_t dict = plist_new_dict(); | ||
259 | plist_dict_set_item(dict, "MessageType", plist_new_string("Remove")); | ||
260 | plist_dict_set_item(dict, "ProfileID", plist_new_string(profileID)); | ||
261 | plist_dict_set_item(dict, "ProfileType", plist_new_string("Provisioning")); | ||
262 | |||
263 | misagent_error_t res = misagent_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
264 | plist_free(dict); | ||
265 | dict = NULL; | ||
266 | |||
267 | if (res != MISAGENT_E_SUCCESS) { | ||
268 | debug_info("could not send plist, error %d", res); | ||
269 | return res; | ||
270 | } | ||
271 | |||
272 | res = misagent_error(property_list_service_receive_plist(client->parent, &dict)); | ||
273 | if (res != MISAGENT_E_SUCCESS) { | ||
274 | debug_info("could not receive response, error %d", res); | ||
275 | return res; | ||
276 | } | ||
277 | if (!dict) { | ||
278 | debug_info("could not get response plist"); | ||
279 | return MISAGENT_E_UNKNOWN_ERROR; | ||
280 | } | ||
281 | |||
282 | res = misagent_check_result(dict, &client->last_error); | ||
283 | plist_free(dict); | ||
284 | |||
285 | return res; | ||
286 | } | ||
287 | |||
288 | int misagent_get_status_code(misagent_client_t client) | ||
289 | { | ||
290 | if (!client) { | ||
291 | return -1; | ||
292 | } | ||
293 | return client->last_error; | ||
294 | } | ||
diff --git a/src/misagent.h b/src/misagent.h new file mode 100644 index 0000000..e394087 --- /dev/null +++ b/src/misagent.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * misagent.h | ||
3 | * com.apple.misagent service header file. | ||
4 | * | ||
5 | * Copyright (c) 2012 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifndef __MISAGENT_H | ||
23 | #define __MISAGENT_H | ||
24 | |||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/misagent.h" | ||
27 | #include "property_list_service.h" | ||
28 | |||
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..6677882 100644 --- a/src/mobile_image_mounter.c +++ b/src/mobile_image_mounter.c | |||
@@ -2,31 +2,38 @@ | |||
2 | * mobile_image_mounter.c | 2 | * mobile_image_mounter.c |
3 | * com.apple.mobile.mobile_image_mounter service implementation. | 3 | * com.apple.mobile.mobile_image_mounter service implementation. |
4 | * | 4 | * |
5 | * Copyright (c) 2010 Nikias Bassen, All Rights Reserved. | 5 | * Copyright (c) 2010-2019 Nikias Bassen, All Rights Reserved. |
6 | * | 6 | * |
7 | * This library is free software; you can redistribute it and/or | 7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | |||
28 | #ifndef _MSC_VER | ||
24 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #endif | ||
31 | |||
25 | #include <plist/plist.h> | 32 | #include <plist/plist.h> |
26 | 33 | ||
27 | #include "mobile_image_mounter.h" | 34 | #include "mobile_image_mounter.h" |
28 | #include "property_list_service.h" | 35 | #include "property_list_service.h" |
29 | #include "debug.h" | 36 | #include "common/debug.h" |
30 | 37 | ||
31 | /** | 38 | /** |
32 | * Locks a mobile_image_mounter client, used for thread safety. | 39 | * Locks a mobile_image_mounter client, used for thread safety. |
@@ -35,17 +42,17 @@ | |||
35 | */ | 42 | */ |
36 | static void mobile_image_mounter_lock(mobile_image_mounter_client_t client) | 43 | static void mobile_image_mounter_lock(mobile_image_mounter_client_t client) |
37 | { | 44 | { |
38 | g_mutex_lock(client->mutex); | 45 | mutex_lock(&client->mutex); |
39 | } | 46 | } |
40 | 47 | ||
41 | /** | 48 | /** |
42 | * Unlocks a mobile_image_mounter client, used for thread safety. | 49 | * Unlocks a mobile_image_mounter client, used for thread safety. |
43 | * | 50 | * |
44 | * @param client mobile_image_mounter client to unlock | 51 | * @param client mobile_image_mounter client to unlock |
45 | */ | 52 | */ |
46 | static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client) | 53 | static void mobile_image_mounter_unlock(mobile_image_mounter_client_t client) |
47 | { | 54 | { |
48 | g_mutex_unlock(client->mutex); | 55 | mutex_unlock(&client->mutex); |
49 | } | 56 | } |
50 | 57 | ||
51 | /** | 58 | /** |
@@ -75,51 +82,30 @@ static mobile_image_mounter_error_t mobile_image_mounter_error(property_list_ser | |||
75 | return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; | 82 | return MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; |
76 | } | 83 | } |
77 | 84 | ||
78 | /** | 85 | 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 | { | 86 | { |
93 | /* makes sure thread environment is available */ | ||
94 | if (!g_thread_supported()) | ||
95 | g_thread_init(NULL); | ||
96 | |||
97 | if (!device) | ||
98 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
99 | |||
100 | property_list_service_client_t plistclient = NULL; | 87 | property_list_service_client_t plistclient = NULL; |
101 | if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 88 | mobile_image_mounter_error_t err = mobile_image_mounter_error(property_list_service_client_new(device, service, &plistclient)); |
102 | return MOBILE_IMAGE_MOUNTER_E_CONN_FAILED; | 89 | if (err != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { |
90 | return err; | ||
103 | } | 91 | } |
104 | 92 | ||
105 | mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_private)); | 93 | mobile_image_mounter_client_t client_loc = (mobile_image_mounter_client_t) malloc(sizeof(struct mobile_image_mounter_client_private)); |
106 | client_loc->parent = plistclient; | 94 | client_loc->parent = plistclient; |
107 | 95 | ||
108 | client_loc->mutex = g_mutex_new(); | 96 | mutex_init(&client_loc->mutex); |
109 | 97 | ||
110 | *client = client_loc; | 98 | *client = client_loc; |
111 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; | 99 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; |
112 | } | 100 | } |
113 | 101 | ||
114 | /** | 102 | 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 | 103 | { |
116 | * mobile_image_mounter client data. | 104 | mobile_image_mounter_error_t err = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; |
117 | * | 105 | service_client_factory_start_service(device, MOBILE_IMAGE_MOUNTER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(mobile_image_mounter_new), &err); |
118 | * @param client The mobile_image_mounter client to disconnect and free. | 106 | return err; |
119 | * | 107 | } |
120 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, | 108 | |
121 | * or MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is NULL. | ||
122 | */ | ||
123 | mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) | 109 | mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_client_t client) |
124 | { | 110 | { |
125 | if (!client) | 111 | if (!client) |
@@ -127,27 +113,12 @@ mobile_image_mounter_error_t mobile_image_mounter_free(mobile_image_mounter_clie | |||
127 | 113 | ||
128 | property_list_service_client_free(client->parent); | 114 | property_list_service_client_free(client->parent); |
129 | client->parent = NULL; | 115 | client->parent = NULL; |
130 | if (client->mutex) { | 116 | mutex_destroy(&client->mutex); |
131 | g_mutex_free(client->mutex); | ||
132 | } | ||
133 | free(client); | 117 | free(client); |
134 | 118 | ||
135 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; | 119 | return MOBILE_IMAGE_MOUNTER_E_SUCCESS; |
136 | } | 120 | } |
137 | 121 | ||
138 | /** | ||
139 | * Tells if the image of ImageType is already mounted. | ||
140 | * | ||
141 | * @param client The client use | ||
142 | * @param image_type The type of the image to look up | ||
143 | * @param result Pointer to a plist that will receive the result of the | ||
144 | * operation. | ||
145 | * | ||
146 | * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the | ||
147 | * operation has failed. Check the resulting plist for further information. | ||
148 | * | ||
149 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, or an error code on error | ||
150 | */ | ||
151 | mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result) | 122 | mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_mounter_client_t client, const char *image_type, plist_t *result) |
152 | { | 123 | { |
153 | if (!client || !image_type || !result) { | 124 | if (!client || !image_type || !result) { |
@@ -156,8 +127,8 @@ mobile_image_mounter_error_t mobile_image_mounter_lookup_image(mobile_image_moun | |||
156 | mobile_image_mounter_lock(client); | 127 | mobile_image_mounter_lock(client); |
157 | 128 | ||
158 | plist_t dict = plist_new_dict(); | 129 | plist_t dict = plist_new_dict(); |
159 | plist_dict_insert_item(dict,"Command", plist_new_string("LookupImage")); | 130 | plist_dict_set_item(dict,"Command", plist_new_string("LookupImage")); |
160 | plist_dict_insert_item(dict,"ImageType", plist_new_string(image_type)); | 131 | plist_dict_set_item(dict,"ImageType", plist_new_string(image_type)); |
161 | 132 | ||
162 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | 133 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); |
163 | plist_free(dict); | 134 | plist_free(dict); |
@@ -177,39 +148,139 @@ leave_unlock: | |||
177 | return res; | 148 | return res; |
178 | } | 149 | } |
179 | 150 | ||
180 | /** | 151 | static mobile_image_mounter_error_t process_result(plist_t result, const char *expected_status) |
181 | * Mounts an image on the device. | 152 | { |
182 | * | 153 | mobile_image_mounter_error_t res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; |
183 | * @param client The connected mobile_image_mounter client. | 154 | char* strval = NULL; |
184 | * @param image_path The absolute path of the image to mount. The image must | 155 | plist_t node; |
185 | * be present before calling this function. | 156 | |
186 | * @param image_signature Pointer to a buffer holding the images' signature | 157 | node = plist_dict_get_item(result, "Error"); |
187 | * @param signature_length Length of the signature image_signature points to | 158 | if (node && plist_get_node_type(node) == PLIST_STRING) { |
188 | * @param image_type Type of image to mount | 159 | plist_get_string_val(node, &strval); |
189 | * @param result Pointer to a plist that will receive the result of the | 160 | } |
190 | * operation. | 161 | if (strval) { |
191 | * | 162 | if (!strcmp(strval, "DeviceLocked")) { |
192 | * @note This function may return MOBILE_IMAGE_MOUNTER_E_SUCCESS even if the | 163 | debug_info("Device is locked, can't mount"); |
193 | * operation has failed. Check the resulting plist for further information. | 164 | res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED; |
194 | * Note that there is no unmounting function. The mount persists until the | 165 | } else { |
195 | * device is rebooted. | 166 | debug_info("Unhandled error '%s' received", strval); |
196 | * | 167 | } |
197 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, | 168 | free(strval); |
198 | * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if on ore more parameters are | 169 | return res; |
199 | * invalid, or another error code otherwise. | 170 | } |
200 | */ | 171 | |
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) | 172 | node = plist_dict_get_item(result, "Status"); |
173 | if (node && plist_get_node_type(node) == PLIST_STRING) { | ||
174 | plist_get_string_val(node, &strval); | ||
175 | } | ||
176 | if (!strval) { | ||
177 | debug_info("Error: Unexpected response received!"); | ||
178 | } else if (strcmp(strval, expected_status) == 0) { | ||
179 | res = MOBILE_IMAGE_MOUNTER_E_SUCCESS; | ||
180 | } else { | ||
181 | debug_info("Error: didn't get %s but %s", expected_status, strval); | ||
182 | } | ||
183 | free(strval); | ||
184 | |||
185 | return res; | ||
186 | } | ||
187 | |||
188 | 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) | ||
202 | { | 189 | { |
203 | if (!client || !image_path || !image_signature || (signature_length == 0) || !image_type || !result) { | 190 | if (!client || !image_type || (image_size == 0) || !upload_cb) { |
204 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | 191 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; |
205 | } | 192 | } |
206 | mobile_image_mounter_lock(client); | 193 | mobile_image_mounter_lock(client); |
194 | plist_t result = NULL; | ||
207 | 195 | ||
208 | plist_t dict = plist_new_dict(); | 196 | plist_t dict = plist_new_dict(); |
209 | plist_dict_insert_item(dict, "Command", plist_new_string("MountImage")); | 197 | plist_dict_set_item(dict, "Command", plist_new_string("ReceiveBytes")); |
210 | plist_dict_insert_item(dict, "ImagePath", plist_new_string(image_path)); | 198 | if (signature && signature_size != 0) |
211 | plist_dict_insert_item(dict, "ImageSignature", plist_new_data(image_signature, signature_length)); | 199 | plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size)); |
212 | plist_dict_insert_item(dict, "ImageType", plist_new_string(image_type)); | 200 | plist_dict_set_item(dict, "ImageSize", plist_new_uint(image_size)); |
201 | plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); | ||
202 | |||
203 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
204 | plist_free(dict); | ||
205 | |||
206 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
207 | debug_info("Error sending XML plist to device!"); | ||
208 | goto leave_unlock; | ||
209 | } | ||
210 | |||
211 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
212 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
213 | debug_info("Error receiving response from device!"); | ||
214 | goto leave_unlock; | ||
215 | } | ||
216 | res = process_result(result, "ReceiveBytesAck"); | ||
217 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
218 | goto leave_unlock; | ||
219 | } | ||
220 | |||
221 | size_t tx = 0; | ||
222 | size_t bufsize = 65536; | ||
223 | unsigned char *buf = (unsigned char*)malloc(bufsize); | ||
224 | if (!buf) { | ||
225 | debug_info("Out of memory"); | ||
226 | res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; | ||
227 | goto leave_unlock; | ||
228 | } | ||
229 | debug_info("uploading image (%d bytes)", (int)image_size); | ||
230 | while (tx < image_size) { | ||
231 | size_t remaining = image_size - tx; | ||
232 | size_t amount = (remaining < bufsize) ? remaining : bufsize; | ||
233 | ssize_t r = upload_cb(buf, amount, userdata); | ||
234 | if (r < 0) { | ||
235 | debug_info("upload_cb returned %d", (int)r); | ||
236 | break; | ||
237 | } | ||
238 | uint32_t sent = 0; | ||
239 | if (service_send(client->parent->parent, (const char*)buf, (uint32_t)r, &sent) != SERVICE_E_SUCCESS) { | ||
240 | debug_info("service_send failed"); | ||
241 | break; | ||
242 | } | ||
243 | tx += r; | ||
244 | } | ||
245 | free(buf); | ||
246 | if (tx < image_size) { | ||
247 | debug_info("Error: failed to upload image"); | ||
248 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
249 | goto leave_unlock; | ||
250 | } | ||
251 | debug_info("image uploaded"); | ||
252 | |||
253 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
254 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
255 | debug_info("Error receiving response from device!"); | ||
256 | goto leave_unlock; | ||
257 | } | ||
258 | res = process_result(result, "Complete"); | ||
259 | |||
260 | leave_unlock: | ||
261 | mobile_image_mounter_unlock(client); | ||
262 | if (result) | ||
263 | plist_free(result); | ||
264 | return res; | ||
265 | |||
266 | } | ||
267 | |||
268 | 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) | ||
269 | { | ||
270 | if (!client || !image_path || !image_type || !result) { | ||
271 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
272 | } | ||
273 | mobile_image_mounter_lock(client); | ||
274 | |||
275 | plist_t dict = plist_new_dict(); | ||
276 | plist_dict_set_item(dict, "Command", plist_new_string("MountImage")); | ||
277 | plist_dict_set_item(dict, "ImagePath", plist_new_string(image_path)); | ||
278 | if (signature && signature_size != 0) | ||
279 | plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size)); | ||
280 | plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); | ||
281 | if (PLIST_IS_DICT(options)) { | ||
282 | plist_dict_merge(&dict, options); | ||
283 | } | ||
213 | 284 | ||
214 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | 285 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); |
215 | plist_free(dict); | 286 | plist_free(dict); |
@@ -229,17 +300,56 @@ leave_unlock: | |||
229 | return res; | 300 | return res; |
230 | } | 301 | } |
231 | 302 | ||
232 | /** | 303 | 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. | 304 | { |
234 | * This functions has to be called before freeing up a mobile_image_mounter | 305 | return mobile_image_mounter_mount_image_with_options(client, image_path, signature, signature_size, image_type, NULL, result); |
235 | * instance. If not, errors appear in the device's syslog. | 306 | } |
236 | * | 307 | |
237 | * @param client The client to hang up | 308 | mobile_image_mounter_error_t mobile_image_mounter_unmount_image(mobile_image_mounter_client_t client, const char *mount_path) |
238 | * | 309 | { |
239 | * @return MOBILE_IMAGE_MOUNTER_E_SUCCESS on success, | 310 | if (!client || !mount_path) { |
240 | * MOBILE_IMAGE_MOUNTER_E_INVALID_ARG if client is invalid, | 311 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; |
241 | * or another error code otherwise. | 312 | } |
242 | */ | 313 | mobile_image_mounter_lock(client); |
314 | |||
315 | plist_t dict = plist_new_dict(); | ||
316 | plist_dict_set_item(dict, "Command", plist_new_string("UnmountImage")); | ||
317 | plist_dict_set_item(dict, "MountPath", plist_new_string(mount_path)); | ||
318 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
319 | plist_free(dict); | ||
320 | |||
321 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
322 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
323 | goto leave_unlock; | ||
324 | } | ||
325 | |||
326 | plist_t result = NULL; | ||
327 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
328 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
329 | debug_info("%s: Error receiving response from device!", __func__); | ||
330 | } else { | ||
331 | plist_t p_error = plist_dict_get_item(result, "Error"); | ||
332 | if (p_error) { | ||
333 | plist_t p_detailed = plist_dict_get_item(result, "DetailedError"); | ||
334 | const char* detailederr = (p_detailed) ? plist_get_string_ptr(p_detailed, NULL) : ""; | ||
335 | const char* errstr = plist_get_string_ptr(p_error, NULL); | ||
336 | if (errstr && !strcmp(errstr, "UnknownCommand")) { | ||
337 | res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; | ||
338 | } else if (errstr && !strcmp(errstr, "DeviceLocked")) { | ||
339 | res = MOBILE_IMAGE_MOUNTER_E_DEVICE_LOCKED; | ||
340 | } else if (strstr(detailederr, "no matching entry")) { | ||
341 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
342 | } else { | ||
343 | res = MOBILE_IMAGE_MOUNTER_E_UNKNOWN_ERROR; | ||
344 | } | ||
345 | } | ||
346 | } | ||
347 | |||
348 | leave_unlock: | ||
349 | mobile_image_mounter_unlock(client); | ||
350 | return res; | ||
351 | } | ||
352 | |||
243 | mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) | 353 | mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_client_t client) |
244 | { | 354 | { |
245 | if (!client) { | 355 | if (!client) { |
@@ -248,7 +358,7 @@ mobile_image_mounter_error_t mobile_image_mounter_hangup(mobile_image_mounter_cl | |||
248 | mobile_image_mounter_lock(client); | 358 | mobile_image_mounter_lock(client); |
249 | 359 | ||
250 | plist_t dict = plist_new_dict(); | 360 | plist_t dict = plist_new_dict(); |
251 | plist_dict_insert_item(dict, "Command", plist_new_string("Hangup")); | 361 | plist_dict_set_item(dict, "Command", plist_new_string("Hangup")); |
252 | 362 | ||
253 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | 363 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); |
254 | plist_free(dict); | 364 | plist_free(dict); |
@@ -272,3 +382,215 @@ leave_unlock: | |||
272 | mobile_image_mounter_unlock(client); | 382 | mobile_image_mounter_unlock(client); |
273 | return res; | 383 | return res; |
274 | } | 384 | } |
385 | |||
386 | mobile_image_mounter_error_t mobile_image_mounter_query_developer_mode_status(mobile_image_mounter_client_t client, plist_t *result) | ||
387 | { | ||
388 | if (!client || !result) { | ||
389 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
390 | } | ||
391 | mobile_image_mounter_lock(client); | ||
392 | |||
393 | plist_t dict = plist_new_dict(); | ||
394 | plist_dict_set_item(dict, "Command", plist_new_string("QueryDeveloperModeStatus")); | ||
395 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
396 | plist_free(dict); | ||
397 | |||
398 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
399 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
400 | goto leave_unlock; | ||
401 | } | ||
402 | |||
403 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, result)); | ||
404 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
405 | debug_info("%s: Error receiving response from device!", __func__); | ||
406 | } | ||
407 | |||
408 | leave_unlock: | ||
409 | mobile_image_mounter_unlock(client); | ||
410 | return res; | ||
411 | } | ||
412 | |||
413 | 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) | ||
414 | { | ||
415 | if (!client || !nonce || !nonce_size) { | ||
416 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
417 | } | ||
418 | mobile_image_mounter_lock(client); | ||
419 | |||
420 | plist_t dict = plist_new_dict(); | ||
421 | plist_dict_set_item(dict, "Command", plist_new_string("QueryNonce")); | ||
422 | if (image_type) { | ||
423 | plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); | ||
424 | } | ||
425 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
426 | plist_free(dict); | ||
427 | |||
428 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
429 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
430 | goto leave_unlock; | ||
431 | } | ||
432 | |||
433 | plist_t result = NULL; | ||
434 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
435 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
436 | debug_info("%s: Error receiving response from device!", __func__); | ||
437 | } else { | ||
438 | plist_t p_nonce = plist_dict_get_item(result, "PersonalizationNonce"); | ||
439 | if (!p_nonce) { | ||
440 | res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; | ||
441 | } else { | ||
442 | uint64_t nonce_size_ = 0; | ||
443 | plist_get_data_val(p_nonce, (char**)nonce, &nonce_size_); | ||
444 | if (*nonce) { | ||
445 | *nonce_size = (unsigned int)nonce_size_; | ||
446 | } else { | ||
447 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
448 | } | ||
449 | } | ||
450 | } | ||
451 | plist_free(result); | ||
452 | |||
453 | leave_unlock: | ||
454 | mobile_image_mounter_unlock(client); | ||
455 | return res; | ||
456 | } | ||
457 | |||
458 | mobile_image_mounter_error_t mobile_image_mounter_query_personalization_identifiers(mobile_image_mounter_client_t client, const char* image_type, plist_t *result) | ||
459 | { | ||
460 | if (!client || !result) { | ||
461 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
462 | } | ||
463 | mobile_image_mounter_lock(client); | ||
464 | |||
465 | plist_t dict = plist_new_dict(); | ||
466 | plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationIdentifiers")); | ||
467 | if (image_type) { | ||
468 | plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); | ||
469 | } | ||
470 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
471 | plist_free(dict); | ||
472 | |||
473 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
474 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
475 | goto leave_unlock; | ||
476 | } | ||
477 | |||
478 | plist_t _result = NULL; | ||
479 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &_result)); | ||
480 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
481 | debug_info("%s: Error receiving response from device!", __func__); | ||
482 | } | ||
483 | *result = plist_copy(plist_dict_get_item(_result, "PersonalizationIdentifiers")); | ||
484 | if (!*result) { | ||
485 | debug_info("%s: Response did not contain PersonalizationIdentifiers!", __func__); | ||
486 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
487 | } | ||
488 | |||
489 | leave_unlock: | ||
490 | mobile_image_mounter_unlock(client); | ||
491 | return res; | ||
492 | } | ||
493 | |||
494 | 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) | ||
495 | { | ||
496 | if (!client || !image_type || !signature || !signature_size || !manifest || !manifest_size) { | ||
497 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
498 | } | ||
499 | mobile_image_mounter_lock(client); | ||
500 | |||
501 | plist_t dict = plist_new_dict(); | ||
502 | plist_dict_set_item(dict, "Command", plist_new_string("QueryPersonalizationManifest")); | ||
503 | plist_dict_set_item(dict, "PersonalizedImageType", plist_new_string(image_type)); | ||
504 | plist_dict_set_item(dict, "ImageType", plist_new_string(image_type)); | ||
505 | plist_dict_set_item(dict, "ImageSignature", plist_new_data((char*)signature, signature_size)); | ||
506 | |||
507 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
508 | plist_free(dict); | ||
509 | |||
510 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
511 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
512 | goto leave_unlock; | ||
513 | } | ||
514 | |||
515 | plist_t result = NULL; | ||
516 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
517 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
518 | debug_info("%s: Error receiving response from device!", __func__); | ||
519 | } else { | ||
520 | plist_t p_manifest = plist_dict_get_item(result, "ImageSignature"); | ||
521 | if (!p_manifest) { | ||
522 | res = MOBILE_IMAGE_MOUNTER_E_NOT_SUPPORTED; | ||
523 | } else { | ||
524 | uint64_t manifest_size_ = 0; | ||
525 | plist_get_data_val(p_manifest, (char**)manifest, &manifest_size_); | ||
526 | if (*manifest) { | ||
527 | *manifest_size = (unsigned int)manifest_size_; | ||
528 | } else { | ||
529 | res = MOBILE_IMAGE_MOUNTER_E_COMMAND_FAILED; | ||
530 | } | ||
531 | } | ||
532 | } | ||
533 | plist_free(result); | ||
534 | |||
535 | leave_unlock: | ||
536 | mobile_image_mounter_unlock(client); | ||
537 | return res; | ||
538 | } | ||
539 | |||
540 | mobile_image_mounter_error_t mobile_image_mounter_roll_personalization_nonce(mobile_image_mounter_client_t client) | ||
541 | { | ||
542 | if (!client) { | ||
543 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
544 | } | ||
545 | mobile_image_mounter_lock(client); | ||
546 | |||
547 | plist_t dict = plist_new_dict(); | ||
548 | plist_dict_set_item(dict, "Command", plist_new_string("RollPersonalizationNonce")); | ||
549 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
550 | plist_free(dict); | ||
551 | |||
552 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
553 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
554 | goto leave_unlock; | ||
555 | } | ||
556 | |||
557 | plist_t result = NULL; | ||
558 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
559 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
560 | debug_info("%s: Error receiving response from device!", __func__); | ||
561 | } | ||
562 | plist_free(result); | ||
563 | |||
564 | leave_unlock: | ||
565 | mobile_image_mounter_unlock(client); | ||
566 | return res; | ||
567 | } | ||
568 | |||
569 | mobile_image_mounter_error_t mobile_image_mounter_roll_cryptex_nonce(mobile_image_mounter_client_t client) | ||
570 | { | ||
571 | if (!client) { | ||
572 | return MOBILE_IMAGE_MOUNTER_E_INVALID_ARG; | ||
573 | } | ||
574 | mobile_image_mounter_lock(client); | ||
575 | |||
576 | plist_t dict = plist_new_dict(); | ||
577 | plist_dict_set_item(dict, "Command", plist_new_string("RollCryptexNonce")); | ||
578 | mobile_image_mounter_error_t res = mobile_image_mounter_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
579 | plist_free(dict); | ||
580 | |||
581 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
582 | debug_info("%s: Error sending XML plist to device!", __func__); | ||
583 | goto leave_unlock; | ||
584 | } | ||
585 | |||
586 | plist_t result = NULL; | ||
587 | res = mobile_image_mounter_error(property_list_service_receive_plist(client->parent, &result)); | ||
588 | if (res != MOBILE_IMAGE_MOUNTER_E_SUCCESS) { | ||
589 | debug_info("%s: Error receiving response from device!", __func__); | ||
590 | } | ||
591 | plist_free(result); | ||
592 | |||
593 | leave_unlock: | ||
594 | mobile_image_mounter_unlock(client); | ||
595 | return res; | ||
596 | } | ||
diff --git a/src/mobile_image_mounter.h b/src/mobile_image_mounter.h index 2615dbc..9a8fcdd 100644 --- a/src/mobile_image_mounter.h +++ b/src/mobile_image_mounter.h | |||
@@ -8,27 +8,28 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef IMOBILE_IMAGE_MOUNTER_H | ||
22 | #define IMOBILE_IMAGE_MOUNTER_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __MOBILE_IMAGE_MOUNTER_H |
23 | #define __MOBILE_IMAGE_MOUNTER_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/mobile_image_mounter.h" | 26 | #include "libimobiledevice/mobile_image_mounter.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | #include <libimobiledevice-glue/thread.h> | ||
28 | 29 | ||
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..c7e4660 100644 --- a/src/notification_proxy.c +++ b/src/notification_proxy.c | |||
@@ -8,25 +8,37 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | |||
28 | #ifndef _MSC_VER | ||
24 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #endif | ||
31 | |||
25 | #include <plist/plist.h> | 32 | #include <plist/plist.h> |
26 | 33 | ||
27 | #include "notification_proxy.h" | 34 | #include "notification_proxy.h" |
28 | #include "property_list_service.h" | 35 | #include "property_list_service.h" |
29 | #include "debug.h" | 36 | #include "common/debug.h" |
37 | |||
38 | #ifdef _WIN32 | ||
39 | #include <windows.h> | ||
40 | #define sleep(x) Sleep(x*1000) | ||
41 | #endif | ||
30 | 42 | ||
31 | struct np_thread { | 43 | struct np_thread { |
32 | np_client_t client; | 44 | np_client_t client; |
@@ -41,19 +53,19 @@ struct np_thread { | |||
41 | */ | 53 | */ |
42 | static void np_lock(np_client_t client) | 54 | static void np_lock(np_client_t client) |
43 | { | 55 | { |
44 | debug_info("NP: Locked"); | 56 | debug_info("Locked"); |
45 | g_mutex_lock(client->mutex); | 57 | mutex_lock(&client->mutex); |
46 | } | 58 | } |
47 | 59 | ||
48 | /** | 60 | /** |
49 | * Unlocks a notification_proxy client, used for thread safety. | 61 | * Unlocks a notification_proxy client, used for thread safety. |
50 | * | 62 | * |
51 | * @param client notification_proxy client to unlock | 63 | * @param client notification_proxy client to unlock |
52 | */ | 64 | */ |
53 | static void np_unlock(np_client_t client) | 65 | static void np_unlock(np_client_t client) |
54 | { | 66 | { |
55 | debug_info("NP: Unlocked"); | 67 | debug_info("Unlocked"); |
56 | g_mutex_unlock(client->mutex); | 68 | mutex_unlock(&client->mutex); |
57 | } | 69 | } |
58 | 70 | ||
59 | /** | 71 | /** |
@@ -82,78 +94,85 @@ static np_error_t np_error(property_list_service_error_t err) | |||
82 | return NP_E_UNKNOWN_ERROR; | 94 | return NP_E_UNKNOWN_ERROR; |
83 | } | 95 | } |
84 | 96 | ||
85 | /** | 97 | 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 | { | 98 | { |
99 | /* makes sure thread environment is available */ | ||
100 | if (!g_thread_supported()) | ||
101 | g_thread_init(NULL); | ||
102 | |||
103 | if (!device) | ||
104 | return NP_E_INVALID_ARG; | ||
105 | |||
106 | property_list_service_client_t plistclient = NULL; | 99 | property_list_service_client_t plistclient = NULL; |
107 | if (property_list_service_client_new(device, port, &plistclient) != PROPERTY_LIST_SERVICE_E_SUCCESS) { | 100 | np_error_t err = np_error(property_list_service_client_new(device, service, &plistclient)); |
108 | return NP_E_CONN_FAILED; | 101 | if (err != NP_E_SUCCESS) { |
102 | return err; | ||
109 | } | 103 | } |
110 | 104 | ||
111 | np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); | 105 | np_client_t client_loc = (np_client_t) malloc(sizeof(struct np_client_private)); |
112 | client_loc->parent = plistclient; | 106 | client_loc->parent = plistclient; |
113 | 107 | ||
114 | client_loc->mutex = g_mutex_new(); | 108 | mutex_init(&client_loc->mutex); |
115 | 109 | client_loc->notifier = THREAD_T_NULL; | |
116 | client_loc->notifier = NULL; | ||
117 | 110 | ||
118 | *client = client_loc; | 111 | *client = client_loc; |
119 | return NP_E_SUCCESS; | 112 | return NP_E_SUCCESS; |
120 | } | 113 | } |
121 | 114 | ||
122 | /** | 115 | 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 | 116 | { |
124 | * notification_proxy client data. | 117 | int32_t err = NP_E_UNKNOWN_ERROR; |
125 | * | 118 | service_client_factory_start_service(device, NP_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(np_client_new), &err); |
126 | * @param client The notification_proxy client to disconnect and free. | 119 | return err; |
127 | * | 120 | } |
128 | * @return NP_E_SUCCESS on success, or NP_E_INVALID_ARG when client is NULL. | 121 | |
129 | */ | ||
130 | np_error_t np_client_free(np_client_t client) | 122 | np_error_t np_client_free(np_client_t client) |
131 | { | 123 | { |
124 | plist_t dict; | ||
125 | property_list_service_client_t parent; | ||
126 | |||
132 | if (!client) | 127 | if (!client) |
133 | return NP_E_INVALID_ARG; | 128 | return NP_E_INVALID_ARG; |
134 | 129 | ||
135 | property_list_service_client_free(client->parent); | 130 | dict = plist_new_dict(); |
131 | plist_dict_set_item(dict,"Command", plist_new_string("Shutdown")); | ||
132 | property_list_service_send_xml_plist(client->parent, dict); | ||
133 | plist_free(dict); | ||
134 | |||
135 | parent = client->parent; | ||
136 | /* notifies the client->notifier thread that it should terminate */ | ||
136 | client->parent = NULL; | 137 | client->parent = NULL; |
138 | |||
137 | if (client->notifier) { | 139 | if (client->notifier) { |
138 | debug_info("joining np callback"); | 140 | debug_info("joining np callback"); |
139 | g_thread_join(client->notifier); | 141 | thread_join(client->notifier); |
140 | } | 142 | thread_free(client->notifier); |
141 | if (client->mutex) { | 143 | client->notifier = THREAD_T_NULL; |
142 | g_mutex_free(client->mutex); | 144 | } else { |
145 | dict = NULL; | ||
146 | property_list_service_receive_plist(parent, &dict); | ||
147 | if (dict) { | ||
148 | #ifndef STRIP_DEBUG_CODE | ||
149 | char *cmd_value = NULL; | ||
150 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); | ||
151 | if (plist_get_node_type(cmd_value_node) == PLIST_STRING) { | ||
152 | plist_get_string_val(cmd_value_node, &cmd_value); | ||
153 | } | ||
154 | if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { | ||
155 | // this is the expected answer | ||
156 | } else { | ||
157 | debug_info("Did not get ProxyDeath but:"); | ||
158 | debug_plist(dict); | ||
159 | } | ||
160 | if (cmd_value) { | ||
161 | free(cmd_value); | ||
162 | } | ||
163 | #endif | ||
164 | plist_free(dict); | ||
165 | } | ||
143 | } | 166 | } |
167 | |||
168 | property_list_service_client_free(parent); | ||
169 | |||
170 | mutex_destroy(&client->mutex); | ||
144 | free(client); | 171 | free(client); |
145 | 172 | ||
146 | return NP_E_SUCCESS; | 173 | return NP_E_SUCCESS; |
147 | } | 174 | } |
148 | 175 | ||
149 | /** | ||
150 | * Sends a notification to the device's notification_proxy. | ||
151 | * | ||
152 | * @param client The client to send to | ||
153 | * @param notification The notification message to send | ||
154 | * | ||
155 | * @return NP_E_SUCCESS on success, or an error returned by np_plist_send | ||
156 | */ | ||
157 | np_error_t np_post_notification(np_client_t client, const char *notification) | 176 | np_error_t np_post_notification(np_client_t client, const char *notification) |
158 | { | 177 | { |
159 | if (!client || !notification) { | 178 | if (!client || !notification) { |
@@ -162,66 +181,24 @@ np_error_t np_post_notification(np_client_t client, const char *notification) | |||
162 | np_lock(client); | 181 | np_lock(client); |
163 | 182 | ||
164 | plist_t dict = plist_new_dict(); | 183 | plist_t dict = plist_new_dict(); |
165 | plist_dict_insert_item(dict,"Command", plist_new_string("PostNotification")); | 184 | plist_dict_set_item(dict,"Command", plist_new_string("PostNotification")); |
166 | plist_dict_insert_item(dict,"Name", plist_new_string(notification)); | 185 | plist_dict_set_item(dict,"Name", plist_new_string(notification)); |
167 | 186 | ||
168 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); | 187 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); |
169 | plist_free(dict); | 188 | plist_free(dict); |
170 | 189 | ||
171 | dict = plist_new_dict(); | ||
172 | plist_dict_insert_item(dict,"Command", plist_new_string("Shutdown")); | ||
173 | |||
174 | res = np_error(property_list_service_send_xml_plist(client->parent, dict)); | ||
175 | plist_free(dict); | ||
176 | |||
177 | if (res != NP_E_SUCCESS) { | 190 | if (res != NP_E_SUCCESS) { |
178 | debug_info("Error sending XML plist to device!"); | 191 | debug_info("Error sending XML plist to device!"); |
179 | } | 192 | } |
180 | |||
181 | // try to read an answer, we just ignore errors here | ||
182 | dict = NULL; | ||
183 | property_list_service_receive_plist(client->parent, &dict); | ||
184 | if (dict) { | ||
185 | #ifndef STRIP_DEBUG_CODE | ||
186 | char *cmd_value = NULL; | ||
187 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); | ||
188 | if (plist_get_node_type(cmd_value_node) == PLIST_STRING) { | ||
189 | plist_get_string_val(cmd_value_node, &cmd_value); | ||
190 | } | ||
191 | |||
192 | if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { | ||
193 | // this is the expected answer | ||
194 | } else { | ||
195 | debug_plist(dict); | ||
196 | } | ||
197 | g_free(cmd_value); | ||
198 | #endif | ||
199 | plist_free(dict); | ||
200 | } | ||
201 | |||
202 | np_unlock(client); | 193 | np_unlock(client); |
203 | return res; | 194 | return res; |
204 | } | 195 | } |
205 | 196 | ||
206 | /** | 197 | 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 | { | 198 | { |
217 | if (!client || !notification) { | ||
218 | return NP_E_INVALID_ARG; | ||
219 | } | ||
220 | np_lock(client); | ||
221 | |||
222 | plist_t dict = plist_new_dict(); | 199 | plist_t dict = plist_new_dict(); |
223 | plist_dict_insert_item(dict,"Command", plist_new_string("ObserveNotification")); | 200 | plist_dict_set_item(dict,"Command", plist_new_string("ObserveNotification")); |
224 | plist_dict_insert_item(dict,"Name", plist_new_string(notification)); | 201 | plist_dict_set_item(dict,"Name", plist_new_string(notification)); |
225 | 202 | ||
226 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); | 203 | np_error_t res = np_error(property_list_service_send_xml_plist(client->parent, dict)); |
227 | if (res != NP_E_SUCCESS) { | 204 | if (res != NP_E_SUCCESS) { |
@@ -229,21 +206,20 @@ np_error_t np_observe_notification( np_client_t client, const char *notification | |||
229 | } | 206 | } |
230 | plist_free(dict); | 207 | plist_free(dict); |
231 | 208 | ||
209 | return res; | ||
210 | } | ||
211 | |||
212 | np_error_t np_observe_notification( np_client_t client, const char *notification ) | ||
213 | { | ||
214 | if (!client || !notification) { | ||
215 | return NP_E_INVALID_ARG; | ||
216 | } | ||
217 | np_lock(client); | ||
218 | np_error_t res = internal_np_observe_notification(client, notification); | ||
232 | np_unlock(client); | 219 | np_unlock(client); |
233 | return res; | 220 | return res; |
234 | } | 221 | } |
235 | 222 | ||
236 | /** | ||
237 | * Tells the device to send a notification on specified events. | ||
238 | * | ||
239 | * @param client The client to send to | ||
240 | * @param notification_spec Specification of the notifications that should be | ||
241 | * observed. This is expected to be an array of const char* that MUST have a | ||
242 | * terminating NULL entry. | ||
243 | * | ||
244 | * @return NP_E_SUCCESS on success, NP_E_INVALID_ARG when client is null, | ||
245 | * or an error returned by np_observe_notification. | ||
246 | */ | ||
247 | np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) | 223 | np_error_t np_observe_notifications(np_client_t client, const char **notification_spec) |
248 | { | 224 | { |
249 | int i = 0; | 225 | int i = 0; |
@@ -258,13 +234,15 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio | |||
258 | return NP_E_INVALID_ARG; | 234 | return NP_E_INVALID_ARG; |
259 | } | 235 | } |
260 | 236 | ||
237 | np_lock(client); | ||
261 | while (notifications[i]) { | 238 | while (notifications[i]) { |
262 | res = np_observe_notification(client, notifications[i]); | 239 | res = internal_np_observe_notification(client, notifications[i]); |
263 | if (res != NP_E_SUCCESS) { | 240 | if (res != NP_E_SUCCESS) { |
264 | break; | 241 | break; |
265 | } | 242 | } |
266 | i++; | 243 | i++; |
267 | } | 244 | } |
245 | np_unlock(client); | ||
268 | 246 | ||
269 | return res; | 247 | return res; |
270 | } | 248 | } |
@@ -277,7 +255,7 @@ np_error_t np_observe_notifications(np_client_t client, const char **notificatio | |||
277 | * with the notification that has been received. | 255 | * with the notification that has been received. |
278 | * | 256 | * |
279 | * @return 0 if a notification has been received or nothing has been received, | 257 | * @return 0 if a notification has been received or nothing has been received, |
280 | * or a negative value if an error occured. | 258 | * or a negative value if an error occurred. |
281 | * | 259 | * |
282 | * @note You probably want to check out np_set_notify_callback | 260 | * @note You probably want to check out np_set_notify_callback |
283 | * @see np_set_notify_callback | 261 | * @see np_set_notify_callback |
@@ -292,11 +270,15 @@ static int np_get_notification(np_client_t client, char **notification) | |||
292 | 270 | ||
293 | np_lock(client); | 271 | np_lock(client); |
294 | 272 | ||
295 | property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); | 273 | property_list_service_error_t perr = property_list_service_receive_plist_with_timeout(client->parent, &dict, 500); |
296 | if (!dict) { | 274 | if (perr == PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT) { |
297 | debug_info("NotificationProxy: no notification received!"); | 275 | debug_info("NotificationProxy: no notification received!"); |
298 | res = 0; | 276 | res = 0; |
299 | } else { | 277 | } else if (perr != PROPERTY_LIST_SERVICE_E_SUCCESS) { |
278 | debug_info("NotificationProxy: error %d occurred!", perr); | ||
279 | res = perr; | ||
280 | } | ||
281 | if (dict) { | ||
300 | char *cmd_value = NULL; | 282 | char *cmd_value = NULL; |
301 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); | 283 | plist_t cmd_value_node = plist_dict_get_item(dict, "Command"); |
302 | 284 | ||
@@ -315,11 +297,11 @@ static int np_get_notification(np_client_t client, char **notification) | |||
315 | res = -2; | 297 | res = -2; |
316 | if (name_value_node && name_value) { | 298 | if (name_value_node && name_value) { |
317 | *notification = name_value; | 299 | *notification = name_value; |
318 | debug_info("got notification %s\n", __func__, name_value); | 300 | debug_info("got notification %s", __func__, name_value); |
319 | res = 0; | 301 | res = 0; |
320 | } | 302 | } |
321 | } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { | 303 | } else if (cmd_value && !strcmp(cmd_value, "ProxyDeath")) { |
322 | debug_info("ERROR: NotificationProxy died!"); | 304 | debug_info("NotificationProxy died!"); |
323 | res = -1; | 305 | res = -1; |
324 | } else if (cmd_value) { | 306 | } else if (cmd_value) { |
325 | debug_info("unknown NotificationProxy command '%s' received!", cmd_value); | 307 | debug_info("unknown NotificationProxy command '%s' received!", cmd_value); |
@@ -342,7 +324,7 @@ static int np_get_notification(np_client_t client, char **notification) | |||
342 | /** | 324 | /** |
343 | * Internally used thread function. | 325 | * Internally used thread function. |
344 | */ | 326 | */ |
345 | gpointer np_notifier( gpointer arg ) | 327 | void* np_notifier( void* arg ) |
346 | { | 328 | { |
347 | char *notification = NULL; | 329 | char *notification = NULL; |
348 | struct np_thread *npt = (struct np_thread*)arg; | 330 | struct np_thread *npt = (struct np_thread*)arg; |
@@ -351,7 +333,10 @@ gpointer np_notifier( gpointer arg ) | |||
351 | 333 | ||
352 | debug_info("starting callback."); | 334 | debug_info("starting callback."); |
353 | while (npt->client->parent) { | 335 | while (npt->client->parent) { |
354 | np_get_notification(npt->client, ¬ification); | 336 | if (np_get_notification(npt->client, ¬ification) < 0) { |
337 | npt->cbfunc("", npt->user_data); | ||
338 | break; | ||
339 | } | ||
355 | if (notification) { | 340 | if (notification) { |
356 | npt->cbfunc(notification, npt->user_data); | 341 | npt->cbfunc(notification, npt->user_data); |
357 | free(notification); | 342 | free(notification); |
@@ -366,25 +351,6 @@ gpointer np_notifier( gpointer arg ) | |||
366 | return NULL; | 351 | return NULL; |
367 | } | 352 | } |
368 | 353 | ||
369 | /** | ||
370 | * This function allows an application to define a callback function that will | ||
371 | * be called when a notification has been received. | ||
372 | * It will start a thread that polls for notifications and calls the callback | ||
373 | * function if a notification has been received. | ||
374 | * | ||
375 | * @param client the NP client | ||
376 | * @param notify_cb pointer to a callback function or NULL to de-register a | ||
377 | * previously set callback function. | ||
378 | * @param user_data Pointer that will be passed to the callback function as | ||
379 | * user data. If notify_cb is NULL, this parameter is ignored. | ||
380 | * | ||
381 | * @note Only one callback function can be registered at the same time; | ||
382 | * any previously set callback function will be removed automatically. | ||
383 | * | ||
384 | * @return NP_E_SUCCESS when the callback was successfully registered, | ||
385 | * NP_E_INVALID_ARG when client is NULL, or NP_E_UNKNOWN_ERROR when | ||
386 | * the callback thread could no be created. | ||
387 | */ | ||
388 | np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) | 354 | np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, void *user_data ) |
389 | { | 355 | { |
390 | if (!client) | 356 | if (!client) |
@@ -394,11 +360,12 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, | |||
394 | 360 | ||
395 | np_lock(client); | 361 | np_lock(client); |
396 | if (client->notifier) { | 362 | if (client->notifier) { |
397 | debug_info("callback already set, removing\n"); | 363 | debug_info("callback already set, removing"); |
398 | property_list_service_client_t parent = client->parent; | 364 | property_list_service_client_t parent = client->parent; |
399 | client->parent = NULL; | 365 | client->parent = NULL; |
400 | g_thread_join(client->notifier); | 366 | thread_join(client->notifier); |
401 | client->notifier = NULL; | 367 | thread_free(client->notifier); |
368 | client->notifier = THREAD_T_NULL; | ||
402 | client->parent = parent; | 369 | client->parent = parent; |
403 | } | 370 | } |
404 | 371 | ||
@@ -409,8 +376,7 @@ np_error_t np_set_notify_callback( np_client_t client, np_notify_cb_t notify_cb, | |||
409 | npt->cbfunc = notify_cb; | 376 | npt->cbfunc = notify_cb; |
410 | npt->user_data = user_data; | 377 | npt->user_data = user_data; |
411 | 378 | ||
412 | client->notifier = g_thread_create(np_notifier, npt, TRUE, NULL); | 379 | if (thread_new(&client->notifier, np_notifier, npt) == 0) { |
413 | if (client->notifier) { | ||
414 | res = NP_E_SUCCESS; | 380 | res = NP_E_SUCCESS; |
415 | } | 381 | } |
416 | } | 382 | } |
diff --git a/src/notification_proxy.h b/src/notification_proxy.h index 8d5cd24..595cb01 100644 --- a/src/notification_proxy.h +++ b/src/notification_proxy.h | |||
@@ -8,30 +8,31 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef INOTIFICATION_PROXY_H | ||
22 | #define INOTIFICATION_PROXY_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __NOTIFICATION_PROXY_H |
23 | #define __NOTIFICATION_PROXY_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/notification_proxy.h" | 26 | #include "libimobiledevice/notification_proxy.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | #include <libimobiledevice-glue/thread.h> | ||
28 | 29 | ||
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/ostrace.c b/src/ostrace.c new file mode 100644 index 0000000..68eb6bf --- /dev/null +++ b/src/ostrace.c | |||
@@ -0,0 +1,436 @@ | |||
1 | /* | ||
2 | * ostrace.c | ||
3 | * com.apple.os_trace_relay service implementation. | ||
4 | * | ||
5 | * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | |||
28 | #include <plist/plist.h> | ||
29 | |||
30 | #include "ostrace.h" | ||
31 | #include "lockdown.h" | ||
32 | #include "common/debug.h" | ||
33 | #include "endianness.h" | ||
34 | |||
35 | struct ostrace_worker_thread { | ||
36 | ostrace_client_t client; | ||
37 | ostrace_activity_cb_t cbfunc; | ||
38 | void *user_data; | ||
39 | }; | ||
40 | |||
41 | /** | ||
42 | * Convert a service_error_t value to a ostrace_error_t value. | ||
43 | * Used internally to get correct error codes. | ||
44 | * | ||
45 | * @param err An service_error_t error code | ||
46 | * | ||
47 | * @return A matching ostrace_error_t error code, | ||
48 | * OSTRACE_E_UNKNOWN_ERROR otherwise. | ||
49 | */ | ||
50 | static ostrace_error_t ostrace_error(service_error_t err) | ||
51 | { | ||
52 | switch (err) { | ||
53 | case SERVICE_E_SUCCESS: | ||
54 | return OSTRACE_E_SUCCESS; | ||
55 | case SERVICE_E_INVALID_ARG: | ||
56 | return OSTRACE_E_INVALID_ARG; | ||
57 | case SERVICE_E_MUX_ERROR: | ||
58 | return OSTRACE_E_MUX_ERROR; | ||
59 | case SERVICE_E_SSL_ERROR: | ||
60 | return OSTRACE_E_SSL_ERROR; | ||
61 | case SERVICE_E_NOT_ENOUGH_DATA: | ||
62 | return OSTRACE_E_NOT_ENOUGH_DATA; | ||
63 | case SERVICE_E_TIMEOUT: | ||
64 | return OSTRACE_E_TIMEOUT; | ||
65 | default: | ||
66 | break; | ||
67 | } | ||
68 | return OSTRACE_E_UNKNOWN_ERROR; | ||
69 | } | ||
70 | |||
71 | ostrace_error_t ostrace_client_new(idevice_t device, lockdownd_service_descriptor_t service, ostrace_client_t * client) | ||
72 | { | ||
73 | *client = NULL; | ||
74 | |||
75 | if (!device || !service || service->port == 0 || !client || *client) { | ||
76 | debug_info("Incorrect parameter passed to ostrace_client_new."); | ||
77 | return OSTRACE_E_INVALID_ARG; | ||
78 | } | ||
79 | |||
80 | debug_info("Creating ostrace_client, port = %d.", service->port); | ||
81 | |||
82 | service_client_t parent = NULL; | ||
83 | ostrace_error_t ret = ostrace_error(service_client_new(device, service, &parent)); | ||
84 | if (ret != OSTRACE_E_SUCCESS) { | ||
85 | debug_info("Creating base service client failed. Error: %i", ret); | ||
86 | return ret; | ||
87 | } | ||
88 | |||
89 | ostrace_client_t client_loc = (ostrace_client_t) malloc(sizeof(struct ostrace_client_private)); | ||
90 | client_loc->parent = parent; | ||
91 | client_loc->worker = THREAD_T_NULL; | ||
92 | |||
93 | *client = client_loc; | ||
94 | |||
95 | debug_info("ostrace_client successfully created."); | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | ostrace_error_t ostrace_client_start_service(idevice_t device, ostrace_client_t * client, const char* label) | ||
100 | { | ||
101 | ostrace_error_t err = OSTRACE_E_UNKNOWN_ERROR; | ||
102 | service_client_factory_start_service(device, OSTRACE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(ostrace_client_new), &err); | ||
103 | return err; | ||
104 | } | ||
105 | |||
106 | ostrace_error_t ostrace_client_free(ostrace_client_t client) | ||
107 | { | ||
108 | if (!client) | ||
109 | return OSTRACE_E_INVALID_ARG; | ||
110 | ostrace_stop_activity(client); | ||
111 | ostrace_error_t err = ostrace_error(service_client_free(client->parent)); | ||
112 | free(client); | ||
113 | |||
114 | return err; | ||
115 | } | ||
116 | |||
117 | static ostrace_error_t ostrace_send_plist(ostrace_client_t client, plist_t plist) | ||
118 | { | ||
119 | ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; | ||
120 | uint32_t blen = 0; | ||
121 | char* bin = NULL; | ||
122 | uint32_t sent = 0; | ||
123 | uint32_t swapped_len = 0; | ||
124 | |||
125 | if (!client || !plist) { | ||
126 | return OSTRACE_E_INVALID_ARG; | ||
127 | } | ||
128 | |||
129 | plist_to_bin(plist, &bin, &blen); | ||
130 | swapped_len = htobe32(blen); | ||
131 | |||
132 | res = ostrace_error(service_send(client->parent, (char*)&swapped_len, 4, &sent)); | ||
133 | if (res == OSTRACE_E_SUCCESS) { | ||
134 | res = ostrace_error(service_send(client->parent, bin, blen, &sent)); | ||
135 | } | ||
136 | free(bin); | ||
137 | return res; | ||
138 | } | ||
139 | |||
140 | static ostrace_error_t ostrace_receive_plist(ostrace_client_t client, plist_t *plist) | ||
141 | { | ||
142 | ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; | ||
143 | uint8_t msgtype = 0; | ||
144 | uint32_t received = 0; | ||
145 | res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received)); | ||
146 | if (res != OSTRACE_E_SUCCESS) { | ||
147 | debug_info("Failed to read message type from service"); | ||
148 | return res; | ||
149 | } | ||
150 | uint32_t rlen = 0; | ||
151 | res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received)); | ||
152 | if (res != OSTRACE_E_SUCCESS) { | ||
153 | debug_info("Failed to read message size from service"); | ||
154 | return res; | ||
155 | } | ||
156 | |||
157 | if (msgtype == 1) { | ||
158 | rlen = be32toh(rlen); | ||
159 | } else if (msgtype == 2) { | ||
160 | rlen = le32toh(rlen); | ||
161 | } else { | ||
162 | debug_info("Unexpected message type %d", msgtype); | ||
163 | return OSTRACE_E_UNKNOWN_ERROR; | ||
164 | } | ||
165 | debug_info("got length %d", rlen); | ||
166 | |||
167 | char* buf = (char*)malloc(rlen); | ||
168 | res = ostrace_error(service_receive(client->parent, buf, rlen, &received)); | ||
169 | if (res != OSTRACE_E_SUCCESS) { | ||
170 | return res; | ||
171 | } | ||
172 | |||
173 | plist_t reply = NULL; | ||
174 | plist_err_t perr = plist_from_memory(buf, received, &reply, NULL); | ||
175 | free(buf); | ||
176 | if (perr != PLIST_ERR_SUCCESS) { | ||
177 | return OSTRACE_E_UNKNOWN_ERROR; | ||
178 | } | ||
179 | *plist = reply; | ||
180 | return OSTRACE_E_SUCCESS; | ||
181 | } | ||
182 | |||
183 | static ostrace_error_t _ostrace_check_result(plist_t reply) | ||
184 | { | ||
185 | ostrace_error_t res = OSTRACE_E_REQUEST_FAILED; | ||
186 | if (!reply) { | ||
187 | return res; | ||
188 | } | ||
189 | plist_t p_status = plist_dict_get_item(reply, "Status"); | ||
190 | if (!p_status) { | ||
191 | return res; | ||
192 | } | ||
193 | const char* status = plist_get_string_ptr(p_status, NULL); | ||
194 | if (!status) { | ||
195 | return res; | ||
196 | } | ||
197 | if (!strcmp(status, "RequestSuccessful")) { | ||
198 | res = OSTRACE_E_SUCCESS; | ||
199 | } | ||
200 | return res; | ||
201 | } | ||
202 | |||
203 | void *ostrace_worker(void *arg) | ||
204 | { | ||
205 | ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; | ||
206 | struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)arg; | ||
207 | |||
208 | if (!oswt) | ||
209 | return NULL; | ||
210 | |||
211 | uint8_t msgtype = 0; | ||
212 | uint32_t received = 0; | ||
213 | |||
214 | debug_info("Running"); | ||
215 | |||
216 | while (oswt->client->parent) { | ||
217 | res = ostrace_error(service_receive_with_timeout(oswt->client->parent, (char*)&msgtype, 1, &received, 100)); | ||
218 | if (res == OSTRACE_E_TIMEOUT) { | ||
219 | continue; | ||
220 | } | ||
221 | if (res != OSTRACE_E_SUCCESS) { | ||
222 | debug_info("Failed to read message type from service"); | ||
223 | break; | ||
224 | } | ||
225 | uint32_t rlen = 0; | ||
226 | res = ostrace_error(service_receive(oswt->client->parent, (char*)&rlen, 4, &received)); | ||
227 | if (res != OSTRACE_E_SUCCESS) { | ||
228 | debug_info("Failed to read message size from service"); | ||
229 | break; | ||
230 | } | ||
231 | |||
232 | if (msgtype == 1) { | ||
233 | rlen = be32toh(rlen); | ||
234 | } else if (msgtype == 2) { | ||
235 | rlen = le32toh(rlen); | ||
236 | } else { | ||
237 | debug_info("Unexpected message type %d", msgtype); | ||
238 | break; | ||
239 | } | ||
240 | |||
241 | debug_info("got length %d", rlen); | ||
242 | |||
243 | void* buf = malloc(rlen); | ||
244 | res = ostrace_error(service_receive(oswt->client->parent, (char*)buf, rlen, &received)); | ||
245 | if (res != OSTRACE_E_SUCCESS) { | ||
246 | debug_info("Failed to receive %d bytes, error %d", rlen, res); | ||
247 | break; | ||
248 | } | ||
249 | if (received < rlen) { | ||
250 | debug_info("Failed to receive all data, got %d/%d", received, rlen); | ||
251 | break; | ||
252 | } | ||
253 | oswt->cbfunc(buf, received, oswt->user_data); | ||
254 | } | ||
255 | |||
256 | if (oswt) { | ||
257 | free(oswt); | ||
258 | } | ||
259 | |||
260 | debug_info("Exiting"); | ||
261 | |||
262 | return NULL; | ||
263 | } | ||
264 | |||
265 | ostrace_error_t ostrace_start_activity(ostrace_client_t client, plist_t options, ostrace_activity_cb_t callback, void* user_data) | ||
266 | { | ||
267 | if (!client || !callback) | ||
268 | return OSTRACE_E_INVALID_ARG; | ||
269 | |||
270 | ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; | ||
271 | |||
272 | if (client->worker) { | ||
273 | debug_info("Another ostrace activity thread appears to be running already."); | ||
274 | return res; | ||
275 | } | ||
276 | |||
277 | plist_t dict = plist_new_dict(); | ||
278 | plist_dict_set_item(dict, "Pid", plist_new_uint(0x0FFFFFFFF)); | ||
279 | plist_dict_set_item(dict, "MessageFilter", plist_new_uint(0xFFFF)); | ||
280 | plist_dict_set_item(dict, "StreamFlags", plist_new_uint(0x3C)); | ||
281 | if (options) { | ||
282 | plist_dict_merge(&dict, options); | ||
283 | } | ||
284 | plist_dict_set_item(dict, "Request", plist_new_string("StartActivity")); | ||
285 | |||
286 | res = ostrace_send_plist(client, dict); | ||
287 | plist_free(dict); | ||
288 | if (res != OSTRACE_E_SUCCESS) { | ||
289 | return res; | ||
290 | } | ||
291 | |||
292 | dict = NULL; | ||
293 | res = ostrace_receive_plist(client, &dict); | ||
294 | if (res != OSTRACE_E_SUCCESS) { | ||
295 | return res; | ||
296 | } | ||
297 | res = _ostrace_check_result(dict); | ||
298 | if (res != OSTRACE_E_SUCCESS) { | ||
299 | return res; | ||
300 | } | ||
301 | |||
302 | /* start worker thread */ | ||
303 | struct ostrace_worker_thread *oswt = (struct ostrace_worker_thread*)malloc(sizeof(struct ostrace_worker_thread)); | ||
304 | if (oswt) { | ||
305 | oswt->client = client; | ||
306 | oswt->cbfunc = callback; | ||
307 | oswt->user_data = user_data; | ||
308 | |||
309 | if (thread_new(&client->worker, ostrace_worker, oswt) == 0) { | ||
310 | res = OSTRACE_E_SUCCESS; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | return res; | ||
315 | } | ||
316 | |||
317 | ostrace_error_t ostrace_stop_activity(ostrace_client_t client) | ||
318 | { | ||
319 | if (client->worker) { | ||
320 | /* notify thread to finish */ | ||
321 | service_client_t parent = client->parent; | ||
322 | client->parent = NULL; | ||
323 | /* join thread to make it exit */ | ||
324 | thread_join(client->worker); | ||
325 | thread_free(client->worker); | ||
326 | client->worker = THREAD_T_NULL; | ||
327 | client->parent = parent; | ||
328 | } | ||
329 | |||
330 | return OSTRACE_E_SUCCESS; | ||
331 | } | ||
332 | |||
333 | ostrace_error_t ostrace_get_pid_list(ostrace_client_t client, plist_t* list) | ||
334 | { | ||
335 | ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; | ||
336 | plist_t dict = plist_new_dict(); | ||
337 | plist_dict_set_item(dict, "Request", plist_new_string("PidList")); | ||
338 | |||
339 | if (!client || !list) { | ||
340 | return OSTRACE_E_INVALID_ARG; | ||
341 | } | ||
342 | |||
343 | res = ostrace_send_plist(client, dict); | ||
344 | plist_free(dict); | ||
345 | if (res != OSTRACE_E_SUCCESS) { | ||
346 | return res; | ||
347 | } | ||
348 | |||
349 | plist_t reply = NULL; | ||
350 | res = ostrace_receive_plist(client, &reply); | ||
351 | if (res != OSTRACE_E_SUCCESS) { | ||
352 | return res; | ||
353 | } | ||
354 | res = _ostrace_check_result(reply); | ||
355 | if (res != OSTRACE_E_SUCCESS) { | ||
356 | return res; | ||
357 | } | ||
358 | |||
359 | plist_t payload = plist_dict_get_item(reply, "Payload"); | ||
360 | if (!payload) { | ||
361 | return OSTRACE_E_REQUEST_FAILED; | ||
362 | } | ||
363 | *list = plist_copy(payload); | ||
364 | plist_free(reply); | ||
365 | |||
366 | return OSTRACE_E_SUCCESS; | ||
367 | } | ||
368 | |||
369 | ostrace_error_t ostrace_create_archive(ostrace_client_t client, plist_t options, ostrace_archive_write_cb_t callback, void* user_data) | ||
370 | { | ||
371 | ostrace_error_t res = OSTRACE_E_UNKNOWN_ERROR; | ||
372 | if (!client || !callback) { | ||
373 | return OSTRACE_E_INVALID_ARG; | ||
374 | } | ||
375 | plist_t dict = plist_new_dict(); | ||
376 | if (options) { | ||
377 | plist_dict_merge(&dict, options); | ||
378 | } | ||
379 | plist_dict_set_item(dict, "Request", plist_new_string("CreateArchive")); | ||
380 | |||
381 | res = ostrace_send_plist(client, dict); | ||
382 | plist_free(dict); | ||
383 | if (res != OSTRACE_E_SUCCESS) { | ||
384 | return res; | ||
385 | } | ||
386 | |||
387 | plist_t reply = NULL; | ||
388 | res = ostrace_receive_plist(client, &reply); | ||
389 | if (res != OSTRACE_E_SUCCESS) { | ||
390 | return res; | ||
391 | } | ||
392 | |||
393 | res = _ostrace_check_result(reply); | ||
394 | if (res != OSTRACE_E_SUCCESS) { | ||
395 | return res; | ||
396 | } | ||
397 | |||
398 | debug_info("Receiving archive...\n"); | ||
399 | while (1) { | ||
400 | uint8_t msgtype = 0; | ||
401 | uint32_t received = 0; | ||
402 | res = ostrace_error(service_receive(client->parent, (char*)&msgtype, 1, &received)); | ||
403 | if (res != OSTRACE_E_SUCCESS) { | ||
404 | debug_info("Could not read message type from service: %d", res); | ||
405 | break; | ||
406 | } | ||
407 | if (msgtype != 3) { | ||
408 | debug_info("Unexpected packet type %d", msgtype); | ||
409 | return OSTRACE_E_REQUEST_FAILED; | ||
410 | } | ||
411 | uint32_t rlen = 0; | ||
412 | res = ostrace_error(service_receive(client->parent, (char*)&rlen, 4, &received)); | ||
413 | if (res != OSTRACE_E_SUCCESS) { | ||
414 | debug_info("Failed to read message size from service"); | ||
415 | break; | ||
416 | } | ||
417 | |||
418 | rlen = le32toh(rlen); | ||
419 | debug_info("got length %d", rlen); | ||
420 | |||
421 | unsigned char* buf = (unsigned char*)malloc(rlen); | ||
422 | res = ostrace_error(service_receive(client->parent, (char*)buf, rlen, &received)); | ||
423 | if (res != OSTRACE_E_SUCCESS) { | ||
424 | debug_info("Could not read data from service: %d", res); | ||
425 | break; | ||
426 | } | ||
427 | if (callback(buf, received, user_data) < 0) { | ||
428 | debug_info("Aborted through callback"); | ||
429 | return OSTRACE_E_REQUEST_FAILED; | ||
430 | } | ||
431 | } | ||
432 | debug_info("Done.\n"); | ||
433 | |||
434 | return OSTRACE_E_SUCCESS; | ||
435 | } | ||
436 | |||
diff --git a/src/ostrace.h b/src/ostrace.h new file mode 100644 index 0000000..dcc3e8d --- /dev/null +++ b/src/ostrace.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * ostrace.h | ||
3 | * com.apple.os_trace_relay service header file. | ||
4 | * | ||
5 | * Copyright (c) 2020-2025 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifndef _OSTRACE_H | ||
23 | #define _OSTRACE_H | ||
24 | |||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/ostrace.h" | ||
27 | #include "service.h" | ||
28 | #include <libimobiledevice-glue/thread.h> | ||
29 | |||
30 | struct ostrace_client_private { | ||
31 | service_client_t parent; | ||
32 | THREAD_T worker; | ||
33 | }; | ||
34 | |||
35 | void *ostrace_worker(void *arg); | ||
36 | |||
37 | #endif | ||
diff --git a/src/preboard.c b/src/preboard.c new file mode 100644 index 0000000..c3eff02 --- /dev/null +++ b/src/preboard.c | |||
@@ -0,0 +1,256 @@ | |||
1 | /* | ||
2 | * preboard.c | ||
3 | * com.apple.preboardservice_v2 service implementation. | ||
4 | * | ||
5 | * Copyright (c) 2019 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | #include <string.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <plist/plist.h> | ||
28 | |||
29 | #include "preboard.h" | ||
30 | #include "lockdown.h" | ||
31 | #include "common/debug.h" | ||
32 | |||
33 | /** | ||
34 | * Convert a property_list_service_error_t value to a preboard_error_t value. | ||
35 | * Used internally to get correct error codes. | ||
36 | * | ||
37 | * @param err An property_list_service_error_t error code | ||
38 | * | ||
39 | * @return A matching preboard_error_t error code, | ||
40 | * PREBOARD_E_UNKNOWN_ERROR otherwise. | ||
41 | */ | ||
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..5df5122 100644 --- a/src/sbservices.c +++ b/src/sbservices.c | |||
@@ -8,46 +8,53 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | |||
28 | #ifndef _MSC_VER | ||
24 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #endif | ||
31 | |||
25 | #include <plist/plist.h> | 32 | #include <plist/plist.h> |
26 | 33 | ||
27 | #include "sbservices.h" | 34 | #include "sbservices.h" |
28 | #include "property_list_service.h" | 35 | #include "property_list_service.h" |
29 | #include "debug.h" | 36 | #include "common/debug.h" |
30 | 37 | ||
31 | /** | 38 | /** |
32 | * Locks an sbservices client, used for thread safety. | 39 | * Locks an sbservices client, used for thread safety. |
33 | * | 40 | * |
34 | * @param client sbservices client to lock. | 41 | * @param client sbservices client to lock. |
35 | */ | 42 | */ |
36 | static void sbs_lock(sbservices_client_t client) | 43 | static void sbservices_lock(sbservices_client_t client) |
37 | { | 44 | { |
38 | debug_info("SBServices: Locked"); | 45 | debug_info("Locked"); |
39 | g_mutex_lock(client->mutex); | 46 | mutex_lock(&client->mutex); |
40 | } | 47 | } |
41 | 48 | ||
42 | /** | 49 | /** |
43 | * Unlocks an sbservices client, used for thread safety. | 50 | * Unlocks an sbservices client, used for thread safety. |
44 | * | 51 | * |
45 | * @param client sbservices client to unlock | 52 | * @param client sbservices client to unlock |
46 | */ | 53 | */ |
47 | static void sbs_unlock(sbservices_client_t client) | 54 | static void sbservices_unlock(sbservices_client_t client) |
48 | { | 55 | { |
49 | debug_info("SBServices: Unlocked"); | 56 | debug_info("Unlocked"); |
50 | g_mutex_unlock(client->mutex); | 57 | mutex_unlock(&client->mutex); |
51 | } | 58 | } |
52 | 59 | ||
53 | /** | 60 | /** |
@@ -61,64 +68,44 @@ static void sbs_unlock(sbservices_client_t client) | |||
61 | */ | 68 | */ |
62 | static sbservices_error_t sbservices_error(property_list_service_error_t err) | 69 | static sbservices_error_t sbservices_error(property_list_service_error_t err) |
63 | { | 70 | { |
64 | switch (err) { | 71 | switch (err) { |
65 | case PROPERTY_LIST_SERVICE_E_SUCCESS: | 72 | case PROPERTY_LIST_SERVICE_E_SUCCESS: |
66 | return SBSERVICES_E_SUCCESS; | 73 | return SBSERVICES_E_SUCCESS; |
67 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: | 74 | case PROPERTY_LIST_SERVICE_E_INVALID_ARG: |
68 | return SBSERVICES_E_INVALID_ARG; | 75 | return SBSERVICES_E_INVALID_ARG; |
69 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: | 76 | case PROPERTY_LIST_SERVICE_E_PLIST_ERROR: |
70 | return SBSERVICES_E_PLIST_ERROR; | 77 | return SBSERVICES_E_PLIST_ERROR; |
71 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: | 78 | case PROPERTY_LIST_SERVICE_E_MUX_ERROR: |
72 | return SBSERVICES_E_CONN_FAILED; | 79 | return SBSERVICES_E_CONN_FAILED; |
73 | default: | 80 | default: |
74 | break; | 81 | break; |
75 | } | 82 | } |
76 | return SBSERVICES_E_UNKNOWN_ERROR; | 83 | return SBSERVICES_E_UNKNOWN_ERROR; |
77 | } | 84 | } |
78 | 85 | ||
79 | /** | 86 | 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 | { | 87 | { |
92 | /* makes sure thread environment is available */ | ||
93 | if (!g_thread_supported()) | ||
94 | g_thread_init(NULL); | ||
95 | |||
96 | if (!device) | ||
97 | return SBSERVICES_E_INVALID_ARG; | ||
98 | |||
99 | property_list_service_client_t plistclient = NULL; | 88 | property_list_service_client_t plistclient = NULL; |
100 | sbservices_error_t err = sbservices_error(property_list_service_client_new(device, port, &plistclient)); | 89 | sbservices_error_t err = sbservices_error(property_list_service_client_new(device, service, &plistclient)); |
101 | if (err != SBSERVICES_E_SUCCESS) { | 90 | if (err != SBSERVICES_E_SUCCESS) { |
102 | return err; | 91 | return err; |
103 | } | 92 | } |
104 | 93 | ||
105 | sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private)); | 94 | sbservices_client_t client_loc = (sbservices_client_t) malloc(sizeof(struct sbservices_client_private)); |
106 | client_loc->parent = plistclient; | 95 | client_loc->parent = plistclient; |
107 | client_loc->mutex = g_mutex_new(); | 96 | mutex_init(&client_loc->mutex); |
108 | 97 | ||
109 | *client = client_loc; | 98 | *client = client_loc; |
110 | return SBSERVICES_E_SUCCESS; | 99 | return SBSERVICES_E_SUCCESS; |
111 | } | 100 | } |
112 | 101 | ||
113 | /** | 102 | 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 | 103 | { |
115 | * sbservices client data. | 104 | sbservices_error_t err = SBSERVICES_E_UNKNOWN_ERROR; |
116 | * | 105 | service_client_factory_start_service(device, SBSERVICES_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(sbservices_client_new), &err); |
117 | * @param client The sbservices client to disconnect and free. | 106 | return err; |
118 | * | 107 | } |
119 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | 108 | |
120 | * client is NULL, or an SBSERVICES_E_* error code otherwise. | ||
121 | */ | ||
122 | sbservices_error_t sbservices_client_free(sbservices_client_t client) | 109 | sbservices_error_t sbservices_client_free(sbservices_client_t client) |
123 | { | 110 | { |
124 | if (!client) | 111 | if (!client) |
@@ -126,28 +113,12 @@ sbservices_error_t sbservices_client_free(sbservices_client_t client) | |||
126 | 113 | ||
127 | sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent)); | 114 | sbservices_error_t err = sbservices_error(property_list_service_client_free(client->parent)); |
128 | client->parent = NULL; | 115 | client->parent = NULL; |
129 | if (client->mutex) { | 116 | mutex_destroy(&client->mutex); |
130 | g_mutex_free(client->mutex); | ||
131 | } | ||
132 | free(client); | 117 | free(client); |
133 | 118 | ||
134 | return err; | 119 | return err; |
135 | } | 120 | } |
136 | 121 | ||
137 | /** | ||
138 | * Gets the icon state of the connected device. | ||
139 | * | ||
140 | * @param client The connected sbservices client to use. | ||
141 | * @param state Pointer that will point to a newly allocated plist containing | ||
142 | * the current icon state. It is up to the caller to free the memory. | ||
143 | * @param format_version A string to be passed as formatVersion along with | ||
144 | * the request, or NULL if no formatVersion should be passed. This is only | ||
145 | * supported since iOS 4.0 so for older firmware versions this must be set | ||
146 | * to NULL. | ||
147 | * | ||
148 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
149 | * client or state is invalid, or an SBSERVICES_E_* error code otherwise. | ||
150 | */ | ||
151 | sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version) | 122 | sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t *state, const char *format_version) |
152 | { | 123 | { |
153 | if (!client || !client->parent || !state) | 124 | if (!client || !client->parent || !state) |
@@ -156,12 +127,12 @@ sbservices_error_t sbservices_get_icon_state(sbservices_client_t client, plist_t | |||
156 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 127 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
157 | 128 | ||
158 | plist_t dict = plist_new_dict(); | 129 | plist_t dict = plist_new_dict(); |
159 | plist_dict_insert_item(dict, "command", plist_new_string("getIconState")); | 130 | plist_dict_set_item(dict, "command", plist_new_string("getIconState")); |
160 | if (format_version) { | 131 | if (format_version) { |
161 | plist_dict_insert_item(dict, "formatVersion", plist_new_string(format_version)); | 132 | plist_dict_set_item(dict, "formatVersion", plist_new_string(format_version)); |
162 | } | 133 | } |
163 | 134 | ||
164 | sbs_lock(client); | 135 | sbservices_lock(client); |
165 | 136 | ||
166 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 137 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
167 | if (res != SBSERVICES_E_SUCCESS) { | 138 | if (res != SBSERVICES_E_SUCCESS) { |
@@ -184,19 +155,10 @@ leave_unlock: | |||
184 | if (dict) { | 155 | if (dict) { |
185 | plist_free(dict); | 156 | plist_free(dict); |
186 | } | 157 | } |
187 | sbs_unlock(client); | 158 | sbservices_unlock(client); |
188 | return res; | 159 | return res; |
189 | } | 160 | } |
190 | 161 | ||
191 | /** | ||
192 | * Sets the icon state of the connected device. | ||
193 | * | ||
194 | * @param client The connected sbservices client to use. | ||
195 | * @param newstate A plist containing the new iconstate. | ||
196 | * | ||
197 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
198 | * client or newstate is NULL, or an SBSERVICES_E_* error code otherwise. | ||
199 | */ | ||
200 | sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) | 162 | sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t newstate) |
201 | { | 163 | { |
202 | if (!client || !client->parent || !newstate) | 164 | if (!client || !client->parent || !newstate) |
@@ -205,39 +167,27 @@ sbservices_error_t sbservices_set_icon_state(sbservices_client_t client, plist_t | |||
205 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 167 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
206 | 168 | ||
207 | plist_t dict = plist_new_dict(); | 169 | plist_t dict = plist_new_dict(); |
208 | plist_dict_insert_item(dict, "command", plist_new_string("setIconState")); | 170 | plist_dict_set_item(dict, "command", plist_new_string("setIconState")); |
209 | plist_dict_insert_item(dict, "iconState", plist_copy(newstate)); | 171 | plist_dict_set_item(dict, "iconState", plist_copy(newstate)); |
210 | 172 | ||
211 | sbs_lock(client); | 173 | sbservices_lock(client); |
212 | 174 | ||
213 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 175 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
214 | if (res != SBSERVICES_E_SUCCESS) { | 176 | if (res != SBSERVICES_E_SUCCESS) { |
215 | debug_info("could not send plist, error %d", res); | 177 | debug_info("could not send plist, error %d", res); |
216 | } | 178 | } |
217 | /* NO RESPONSE */ | 179 | |
180 | uint32_t bytes = 0; | ||
181 | service_receive_with_timeout(client->parent->parent, malloc(4), 4, &bytes, 2000); | ||
182 | debug_info("setIconState response: %u", bytes); | ||
218 | 183 | ||
219 | if (dict) { | 184 | if (dict) { |
220 | plist_free(dict); | 185 | plist_free(dict); |
221 | } | 186 | } |
222 | sbs_unlock(client); | 187 | sbservices_unlock(client); |
223 | return res; | 188 | return res; |
224 | } | 189 | } |
225 | 190 | ||
226 | /** | ||
227 | * Get the icon of the specified app as PNG data. | ||
228 | * | ||
229 | * @param client The connected sbservices client to use. | ||
230 | * @param bundleId The bundle identifier of the app to retrieve the icon for. | ||
231 | * @param pngdata Pointer that will point to a newly allocated buffer | ||
232 | * containing the PNG data upon successful return. It is up to the caller | ||
233 | * to free the memory. | ||
234 | * @param pngsize Pointer to a uint64_t that will be set to the size of the | ||
235 | * buffer pngdata points to upon successful return. | ||
236 | * | ||
237 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
238 | * client, bundleId, or pngdata are invalid, or an SBSERVICES_E_* error | ||
239 | * code otherwise. | ||
240 | */ | ||
241 | sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) | 191 | sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const char *bundleId, char **pngdata, uint64_t *pngsize) |
242 | { | 192 | { |
243 | if (!client || !client->parent || !bundleId || !pngdata) | 193 | if (!client || !client->parent || !bundleId || !pngdata) |
@@ -246,10 +196,10 @@ sbservices_error_t sbservices_get_icon_pngdata(sbservices_client_t client, const | |||
246 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 196 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
247 | 197 | ||
248 | plist_t dict = plist_new_dict(); | 198 | plist_t dict = plist_new_dict(); |
249 | plist_dict_insert_item(dict, "command", plist_new_string("getIconPNGData")); | 199 | plist_dict_set_item(dict, "command", plist_new_string("getIconPNGData")); |
250 | plist_dict_insert_item(dict, "bundleId", plist_new_string(bundleId)); | 200 | plist_dict_set_item(dict, "bundleId", plist_new_string(bundleId)); |
251 | 201 | ||
252 | sbs_lock(client); | 202 | sbservices_lock(client); |
253 | 203 | ||
254 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 204 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
255 | if (res != SBSERVICES_E_SUCCESS) { | 205 | if (res != SBSERVICES_E_SUCCESS) { |
@@ -271,25 +221,48 @@ leave_unlock: | |||
271 | if (dict) { | 221 | if (dict) { |
272 | plist_free(dict); | 222 | plist_free(dict); |
273 | } | 223 | } |
274 | sbs_unlock(client); | 224 | sbservices_unlock(client); |
275 | return res; | 225 | return res; |
226 | } | ||
227 | |||
228 | sbservices_error_t sbservices_get_interface_orientation(sbservices_client_t client, sbservices_interface_orientation_t* interface_orientation) | ||
229 | { | ||
230 | if (!client || !client->parent || !interface_orientation) | ||
231 | return SBSERVICES_E_INVALID_ARG; | ||
232 | |||
233 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | ||
234 | |||
235 | plist_t dict = plist_new_dict(); | ||
236 | plist_dict_set_item(dict, "command", plist_new_string("getInterfaceOrientation")); | ||
276 | 237 | ||
238 | sbservices_lock(client); | ||
239 | |||
240 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | ||
241 | if (res != SBSERVICES_E_SUCCESS) { | ||
242 | debug_info("could not send plist, error %d", res); | ||
243 | goto leave_unlock; | ||
244 | } | ||
245 | plist_free(dict); | ||
246 | dict = NULL; | ||
247 | |||
248 | res = sbservices_error(property_list_service_receive_plist(client->parent, &dict)); | ||
249 | if (res == SBSERVICES_E_SUCCESS) { | ||
250 | plist_t node = plist_dict_get_item(dict, "interfaceOrientation"); | ||
251 | if (node) { | ||
252 | uint64_t value = SBSERVICES_INTERFACE_ORIENTATION_UNKNOWN; | ||
253 | plist_get_uint_val(node, &value); | ||
254 | *interface_orientation = (sbservices_interface_orientation_t)value; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | leave_unlock: | ||
259 | if (dict) { | ||
260 | plist_free(dict); | ||
261 | } | ||
262 | sbservices_unlock(client); | ||
263 | return res; | ||
277 | } | 264 | } |
278 | 265 | ||
279 | /** | ||
280 | * Get the home screen wallpaper as PNG data. | ||
281 | * | ||
282 | * @param client The connected sbservices client to use. | ||
283 | * @param pngdata Pointer that will point to a newly allocated buffer | ||
284 | * containing the PNG data upon successful return. It is up to the caller | ||
285 | * to free the memory. | ||
286 | * @param pngsize Pointer to a uint64_t that will be set to the size of the | ||
287 | * buffer pngdata points to upon successful return. | ||
288 | * | ||
289 | * @return SBSERVICES_E_SUCCESS on success, SBSERVICES_E_INVALID_ARG when | ||
290 | * client or pngdata are invalid, or an SBSERVICES_E_* error | ||
291 | * code otherwise. | ||
292 | */ | ||
293 | sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize) | 266 | sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_client_t client, char **pngdata, uint64_t *pngsize) |
294 | { | 267 | { |
295 | if (!client || !client->parent || !pngdata) | 268 | if (!client || !client->parent || !pngdata) |
@@ -298,9 +271,9 @@ sbservices_error_t sbservices_get_home_screen_wallpaper_pngdata(sbservices_clien | |||
298 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; | 271 | sbservices_error_t res = SBSERVICES_E_UNKNOWN_ERROR; |
299 | 272 | ||
300 | plist_t dict = plist_new_dict(); | 273 | plist_t dict = plist_new_dict(); |
301 | plist_dict_insert_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData")); | 274 | plist_dict_set_item(dict, "command", plist_new_string("getHomeScreenWallpaperPNGData")); |
302 | 275 | ||
303 | sbs_lock(client); | 276 | sbservices_lock(client); |
304 | 277 | ||
305 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); | 278 | res = sbservices_error(property_list_service_send_binary_plist(client->parent, dict)); |
306 | if (res != SBSERVICES_E_SUCCESS) { | 279 | if (res != SBSERVICES_E_SUCCESS) { |
@@ -322,6 +295,6 @@ leave_unlock: | |||
322 | if (dict) { | 295 | if (dict) { |
323 | plist_free(dict); | 296 | plist_free(dict); |
324 | } | 297 | } |
325 | sbs_unlock(client); | 298 | sbservices_unlock(client); |
326 | return res; | 299 | return res; |
327 | } | 300 | } |
diff --git a/src/sbservices.h b/src/sbservices.h index 3a4120f..b67281e 100644 --- a/src/sbservices.h +++ b/src/sbservices.h | |||
@@ -8,27 +8,28 @@ | |||
8 | * modify it under the terms of the GNU Lesser General Public | 8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | 9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. | 10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | 15 | * Lesser General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU Lesser General Public | 17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, write to the Free Software | 18 | * License along with this library; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | 20 | */ |
21 | #ifndef ISBSERVICES_H | ||
22 | #define ISBSERVICES_H | ||
23 | 21 | ||
24 | #include <glib.h> | 22 | #ifndef __SBSERVICES_H |
23 | #define __SBSERVICES_H | ||
25 | 24 | ||
25 | #include "idevice.h" | ||
26 | #include "libimobiledevice/sbservices.h" | 26 | #include "libimobiledevice/sbservices.h" |
27 | #include "property_list_service.h" | 27 | #include "property_list_service.h" |
28 | #include <libimobiledevice-glue/thread.h> | ||
28 | 29 | ||
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 | ||