summaryrefslogtreecommitdiffstats
path: root/src/companion_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/companion_proxy.c')
-rw-r--r--src/companion_proxy.c381
1 files changed, 381 insertions, 0 deletions
diff --git a/src/companion_proxy.c b/src/companion_proxy.c
new file mode 100644
index 0000000..f4fe96c
--- /dev/null
+++ b/src/companion_proxy.c
@@ -0,0 +1,381 @@
1/*
2 * compproxy.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#include "common/thread.h"
33
34/**
35 * Convert a property_list_service_error_t value to a compproxy_error_t value.
36 * Used internally to get correct error codes.
37 *
38 * @param err An property_list_service_error_t error code
39 *
40 * @return A matching compproxy_error_t error code,
41 * COMPPROXY_E_UNKNOWN_ERROR otherwise.
42 */
43static compproxy_error_t compproxy_error(property_list_service_error_t err)
44{
45 switch (err) {
46 case PROPERTY_LIST_SERVICE_E_SUCCESS:
47 return COMPPROXY_E_SUCCESS;
48 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
49 return COMPPROXY_E_INVALID_ARG;
50 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
51 return COMPPROXY_E_PLIST_ERROR;
52 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
53 return COMPPROXY_E_MUX_ERROR;
54 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
55 return COMPPROXY_E_SSL_ERROR;
56 case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA:
57 return COMPPROXY_E_NOT_ENOUGH_DATA;
58 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
59 return COMPPROXY_E_TIMEOUT;
60 default:
61 break;
62 }
63 return COMPPROXY_E_UNKNOWN_ERROR;
64}
65
66LIBIMOBILEDEVICE_API compproxy_error_t compproxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, compproxy_client_t * client)
67{
68 *client = NULL;
69
70 if (!device || !service || service->port == 0 || !client || *client) {
71 debug_info("Incorrect parameter passed to compproxy_client_new.");
72 return COMPPROXY_E_INVALID_ARG;
73 }
74
75 debug_info("Creating compproxy_client, port = %d.", service->port);
76
77 property_list_service_client_t plclient = NULL;
78 compproxy_error_t ret = compproxy_error(property_list_service_client_new(device, service, &plclient));
79 if (ret != COMPPROXY_E_SUCCESS) {
80 debug_info("Creating a property list client failed. Error: %i", ret);
81 return ret;
82 }
83
84 compproxy_client_t client_loc = (compproxy_client_t) malloc(sizeof(struct compproxy_client_private));
85 client_loc->parent = plclient;
86 client_loc->event_thread = THREAD_T_NULL;
87
88 *client = client_loc;
89
90 debug_info("compproxy_client successfully created.");
91 return COMPPROXY_E_SUCCESS;
92}
93
94LIBIMOBILEDEVICE_API compproxy_error_t compproxy_client_start_service(idevice_t device, compproxy_client_t * client, const char* label)
95{
96 compproxy_error_t err = COMPPROXY_E_UNKNOWN_ERROR;
97 service_client_factory_start_service(device, COMPPROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(compproxy_client_new), &err);
98 return err;
99}
100
101LIBIMOBILEDEVICE_API compproxy_error_t compproxy_client_free(compproxy_client_t client)
102{
103 if (!client)
104 return COMPPROXY_E_INVALID_ARG;
105
106 property_list_service_client_t parent = client->parent;
107 client->parent = NULL;
108 if (client->event_thread) {
109 debug_info("joining event thread");
110 thread_join(client->event_thread);
111 thread_free(client->event_thread);
112 client->event_thread = THREAD_T_NULL;
113 }
114 compproxy_error_t err = compproxy_error(property_list_service_client_free(parent));
115 free(client);
116
117 return err;
118}
119
120LIBIMOBILEDEVICE_API compproxy_error_t compproxy_send(compproxy_client_t client, plist_t plist)
121{
122 compproxy_error_t res = COMPPROXY_E_UNKNOWN_ERROR;
123
124 res = compproxy_error(property_list_service_send_binary_plist(client->parent, plist));
125 if (res != COMPPROXY_E_SUCCESS) {
126 debug_info("Sending plist failed with error %d", res);
127 return res;
128 }
129
130 return res;
131}
132
133LIBIMOBILEDEVICE_API compproxy_error_t compproxy_receive(compproxy_client_t client, plist_t * plist)
134{
135 compproxy_error_t res = COMPPROXY_E_UNKNOWN_ERROR;
136 plist_t outplist = NULL;
137 res = compproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, 10000));
138 if (res != COMPPROXY_E_SUCCESS && res != COMPPROXY_E_TIMEOUT) {
139 debug_info("Could not receive plist, error %d", res);
140 plist_free(outplist);
141 } else if (res == COMPPROXY_E_SUCCESS) {
142 *plist = outplist;
143 }
144 return res;
145}
146
147LIBIMOBILEDEVICE_API compproxy_error_t compproxy_get_device_registry(compproxy_client_t client, plist_t* paired_devices)
148{
149 if (!client || !paired_devices) {
150 return COMPPROXY_E_INVALID_ARG;
151 }
152
153 plist_t dict = plist_new_dict();
154 plist_dict_set_item(dict, "Command", plist_new_string("GetDeviceRegistry"));
155
156 compproxy_error_t res = compproxy_send(client, dict);
157 plist_free(dict);
158 dict = NULL;
159 if (res != COMPPROXY_E_SUCCESS) {
160 return res;
161 }
162
163 res = compproxy_receive(client, &dict);
164 if (res != COMPPROXY_E_SUCCESS) {
165 return res;
166 }
167 if (!dict || !PLIST_IS_DICT(dict)) {
168 return COMPPROXY_E_PLIST_ERROR;
169 }
170 plist_t val = plist_dict_get_item(dict, "PairedDevicesArray");
171 if (val) {
172 *paired_devices = plist_copy(val);
173 res = COMPPROXY_E_SUCCESS;
174 } else {
175 res = COMPPROXY_E_UNKNOWN_ERROR;
176 val = plist_dict_get_item(dict, "Error");
177 if (val) {
178 if (plist_string_val_compare(val, "NoPairedWatches")) {
179 res = COMPPROXY_E_NO_DEVICES;
180 }
181 }
182 }
183 plist_free(dict);
184 return res;
185}
186
187struct compproxy_cb_data {
188 compproxy_client_t client;
189 compproxy_device_event_cb_t cbfunc;
190 void* user_data;
191};
192
193static void* compproxy_event_thread(void* arg)
194{
195 struct compproxy_cb_data* data = (struct compproxy_cb_data*)arg;
196 compproxy_client_t client = data->client;
197 compproxy_error_t res;
198
199 plist_t command = plist_new_dict();
200 plist_dict_set_item(command, "Command", plist_new_string("StartListeningForDevices"));
201 res = compproxy_send(client, command);
202 plist_free(command);
203
204 if (res != COMPPROXY_E_SUCCESS) {
205 free(data);
206 client->event_thread = THREAD_T_NULL;
207 return NULL;
208 }
209
210 while (client && client->parent) {
211 plist_t node = NULL;
212 res = compproxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000));
213 if (res != COMPPROXY_E_SUCCESS && res != COMPPROXY_E_TIMEOUT) {
214 debug_info("could not receive plist, error %d", res);
215 break;
216 }
217
218 if (node) {
219 data->cbfunc(node, data->user_data);
220 }
221 plist_free(node);
222 }
223
224 client->event_thread = THREAD_T_NULL;
225 free(data);
226
227 return NULL;
228}
229
230LIBIMOBILEDEVICE_API compproxy_error_t compproxy_start_listening_for_devices(compproxy_client_t client, compproxy_device_event_cb_t callback, void* userdata)
231{
232 if (!client || !client->parent || !callback) {
233 return COMPPROXY_E_INVALID_ARG;
234 }
235
236 if (client->event_thread) {
237 return COMPPROXY_E_OP_IN_PROGRESS;
238 }
239
240 compproxy_error_t res = COMPPROXY_E_UNKNOWN_ERROR;
241 struct compproxy_cb_data *data = (struct compproxy_cb_data*)malloc(sizeof(struct compproxy_cb_data));
242 if (data) {
243 data->client = client;
244 data->cbfunc = callback;
245 data->user_data = userdata;
246
247 if (thread_new(&client->event_thread, compproxy_event_thread, data) == 0) {
248 res = COMPPROXY_E_SUCCESS;
249 } else {
250 free(data);
251 }
252 }
253 return res;
254}
255
256LIBIMOBILEDEVICE_API compproxy_error_t compproxy_stop_listening_for_devices(compproxy_client_t client)
257{
258 property_list_service_client_t parent = client->parent;
259 client->parent = NULL;
260 if (client->event_thread) {
261 debug_info("joining event thread");
262 thread_join(client->event_thread);
263 thread_free(client->event_thread);
264 client->event_thread = THREAD_T_NULL;
265 }
266 client->parent = parent;
267 return COMPPROXY_E_SUCCESS;
268}
269
270LIBIMOBILEDEVICE_API compproxy_error_t compproxy_get_value_from_registry(compproxy_client_t client, const char* companion_udid, const char* key, plist_t* value)
271{
272 if (!client || !companion_udid || !key || !value) {
273 return COMPPROXY_E_INVALID_ARG;
274 }
275
276 plist_t dict = plist_new_dict();
277 plist_dict_set_item(dict, "Command", plist_new_string("GetValueFromRegistry"));
278 plist_dict_set_item(dict, "GetValueGizmoUDIDKey", plist_new_string(companion_udid));
279 plist_dict_set_item(dict, "GetValueKeyKey", plist_new_string(key));
280
281 compproxy_error_t res = compproxy_send(client, dict);
282 plist_free(dict);
283 dict = NULL;
284 if (res != COMPPROXY_E_SUCCESS) {
285 return res;
286 }
287
288 res = compproxy_receive(client, &dict);
289 if (res != COMPPROXY_E_SUCCESS) {
290 return res;
291 }
292 if (!dict || !PLIST_IS_DICT(dict)) {
293 return COMPPROXY_E_PLIST_ERROR;
294 }
295 plist_t val = plist_dict_get_item(dict, "RetrievedValueDictionary");
296 if (val) {
297 *value = plist_copy(val);
298 res = COMPPROXY_E_SUCCESS;
299 } else {
300 res = COMPPROXY_E_UNKNOWN_ERROR;
301 val = plist_dict_get_item(dict, "Error");
302 if (val) {
303 if (!plist_string_val_compare(val, "UnsupportedWatchKey")) {
304 res = COMPPROXY_E_UNSUPPORTED_KEY;
305 } else if (plist_string_val_compare(val, "TimeoutReply")) {
306 res = COMPPROXY_E_TIMEOUT_REPLY;
307 }
308 }
309 }
310 plist_free(dict);
311 return res;
312}
313
314LIBIMOBILEDEVICE_API compproxy_error_t compproxy_start_forwarding_service_port(compproxy_client_t client, uint16_t remote_port, const char* service_name, uint16_t* forward_port, plist_t options)
315{
316 if (!client) {
317 return COMPPROXY_E_INVALID_ARG;
318 }
319
320 plist_t dict = plist_new_dict();
321 plist_dict_set_item(dict, "Command", plist_new_string("StartForwardingServicePort"));
322 plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port));
323 if (service_name) {
324 plist_dict_set_item(dict, "ForwardedServiceName", plist_new_string(service_name));
325 }
326 plist_dict_set_item(dict, "IsServiceLowPriority", plist_new_bool(0));
327 plist_dict_set_item(dict, "PreferWifi", plist_new_bool(0));
328 if (options) {
329 plist_dict_merge(dict, options);
330 }
331
332 compproxy_error_t res = compproxy_send(client, dict);
333 plist_free(dict);
334 dict = NULL;
335 if (res != COMPPROXY_E_SUCCESS) {
336 return res;
337 }
338
339 res = compproxy_receive(client, &dict);
340 if (res != COMPPROXY_E_SUCCESS) {
341 return res;
342 }
343 plist_t val = plist_dict_get_item(dict, "CompanionProxyServicePort");
344 if (val) {
345 uint64_t u64val = 0;
346 plist_get_uint_val(val, &u64val);
347 *forward_port = (uint16_t)u64val;
348 res = COMPPROXY_E_SUCCESS;
349 } else {
350 res = COMPPROXY_E_UNKNOWN_ERROR;
351 }
352 plist_free(dict);
353
354 return res;
355}
356
357LIBIMOBILEDEVICE_API compproxy_error_t compproxy_stop_forwarding_service_port(compproxy_client_t client, uint16_t remote_port)
358{
359 if (!client) {
360 return COMPPROXY_E_INVALID_ARG;
361 }
362
363 plist_t dict = plist_new_dict();
364 plist_dict_set_item(dict, "Command", plist_new_string("StopForwardingServicePort"));
365 plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port));
366
367 compproxy_error_t res = compproxy_send(client, dict);
368 plist_free(dict);
369 dict = NULL;
370 if (res != COMPPROXY_E_SUCCESS) {
371 return res;
372 }
373
374 res = compproxy_receive(client, &dict);
375 if (res != COMPPROXY_E_SUCCESS) {
376 return res;
377 }
378 plist_free(dict);
379
380 return res;
381}