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.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/src/companion_proxy.c b/src/companion_proxy.c
new file mode 100644
index 0000000..421fa9a
--- /dev/null
+++ b/src/companion_proxy.c
@@ -0,0 +1,380 @@
1/*
2 * companion_proxy.c
3 * com.apple.companion_proxy service implementation.
4 *
5 * Copyright (c) 2019-2020 Nikias Bassen, All Rights Reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25#include <string.h>
26#include <stdlib.h>
27#include <plist/plist.h>
28
29#include "companion_proxy.h"
30#include "lockdown.h"
31#include "common/debug.h"
32
33/**
34 * Convert a property_list_service_error_t value to a companion_proxy_error_t value.
35 * Used internally to get correct error codes.
36 *
37 * @param err An property_list_service_error_t error code
38 *
39 * @return A matching companion_proxy_error_t error code,
40 * COMPANION_PROXY_E_UNKNOWN_ERROR otherwise.
41 */
42static companion_proxy_error_t companion_proxy_error(property_list_service_error_t err)
43{
44 switch (err) {
45 case PROPERTY_LIST_SERVICE_E_SUCCESS:
46 return COMPANION_PROXY_E_SUCCESS;
47 case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
48 return COMPANION_PROXY_E_INVALID_ARG;
49 case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
50 return COMPANION_PROXY_E_PLIST_ERROR;
51 case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
52 return COMPANION_PROXY_E_MUX_ERROR;
53 case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
54 return COMPANION_PROXY_E_SSL_ERROR;
55 case PROPERTY_LIST_SERVICE_E_NOT_ENOUGH_DATA:
56 return COMPANION_PROXY_E_NOT_ENOUGH_DATA;
57 case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
58 return COMPANION_PROXY_E_TIMEOUT;
59 default:
60 break;
61 }
62 return COMPANION_PROXY_E_UNKNOWN_ERROR;
63}
64
65companion_proxy_error_t companion_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, companion_proxy_client_t * client)
66{
67 *client = NULL;
68
69 if (!device || !service || service->port == 0 || !client || *client) {
70 debug_info("Incorrect parameter passed to companion_proxy_client_new.");
71 return COMPANION_PROXY_E_INVALID_ARG;
72 }
73
74 debug_info("Creating companion_proxy_client, port = %d.", service->port);
75
76 property_list_service_client_t plclient = NULL;
77 companion_proxy_error_t ret = companion_proxy_error(property_list_service_client_new(device, service, &plclient));
78 if (ret != COMPANION_PROXY_E_SUCCESS) {
79 debug_info("Creating a property list client failed. Error: %i", ret);
80 return ret;
81 }
82
83 companion_proxy_client_t client_loc = (companion_proxy_client_t) malloc(sizeof(struct companion_proxy_client_private));
84 client_loc->parent = plclient;
85 client_loc->event_thread = THREAD_T_NULL;
86
87 *client = client_loc;
88
89 debug_info("Created companion_proxy_client successfully.");
90 return COMPANION_PROXY_E_SUCCESS;
91}
92
93companion_proxy_error_t companion_proxy_client_start_service(idevice_t device, companion_proxy_client_t * client, const char* label)
94{
95 companion_proxy_error_t err = COMPANION_PROXY_E_UNKNOWN_ERROR;
96 service_client_factory_start_service(device, COMPANION_PROXY_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(companion_proxy_client_new), &err);
97 return err;
98}
99
100companion_proxy_error_t companion_proxy_client_free(companion_proxy_client_t client)
101{
102 if (!client)
103 return COMPANION_PROXY_E_INVALID_ARG;
104
105 property_list_service_client_t parent = client->parent;
106 client->parent = NULL;
107 if (client->event_thread) {
108 debug_info("joining event thread");
109 thread_join(client->event_thread);
110 thread_free(client->event_thread);
111 client->event_thread = THREAD_T_NULL;
112 }
113 companion_proxy_error_t err = companion_proxy_error(property_list_service_client_free(parent));
114 free(client);
115
116 return err;
117}
118
119companion_proxy_error_t companion_proxy_send(companion_proxy_client_t client, plist_t plist)
120{
121 companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR;
122
123 res = companion_proxy_error(property_list_service_send_binary_plist(client->parent, plist));
124 if (res != COMPANION_PROXY_E_SUCCESS) {
125 debug_info("Sending plist failed with error %d", res);
126 return res;
127 }
128
129 return res;
130}
131
132companion_proxy_error_t companion_proxy_receive(companion_proxy_client_t client, plist_t * plist)
133{
134 companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR;
135 plist_t outplist = NULL;
136 res = companion_proxy_error(property_list_service_receive_plist_with_timeout(client->parent, &outplist, 10000));
137 if (res != COMPANION_PROXY_E_SUCCESS && res != COMPANION_PROXY_E_TIMEOUT) {
138 debug_info("Could not receive plist, error %d", res);
139 plist_free(outplist);
140 } else if (res == COMPANION_PROXY_E_SUCCESS) {
141 *plist = outplist;
142 }
143 return res;
144}
145
146companion_proxy_error_t companion_proxy_get_device_registry(companion_proxy_client_t client, plist_t* paired_devices)
147{
148 if (!client || !paired_devices) {
149 return COMPANION_PROXY_E_INVALID_ARG;
150 }
151
152 plist_t dict = plist_new_dict();
153 plist_dict_set_item(dict, "Command", plist_new_string("GetDeviceRegistry"));
154
155 companion_proxy_error_t res = companion_proxy_send(client, dict);
156 plist_free(dict);
157 dict = NULL;
158 if (res != COMPANION_PROXY_E_SUCCESS) {
159 return res;
160 }
161
162 res = companion_proxy_receive(client, &dict);
163 if (res != COMPANION_PROXY_E_SUCCESS) {
164 return res;
165 }
166 if (!dict || !PLIST_IS_DICT(dict)) {
167 return COMPANION_PROXY_E_PLIST_ERROR;
168 }
169 plist_t val = plist_dict_get_item(dict, "PairedDevicesArray");
170 if (val) {
171 *paired_devices = plist_copy(val);
172 res = COMPANION_PROXY_E_SUCCESS;
173 } else {
174 res = COMPANION_PROXY_E_UNKNOWN_ERROR;
175 val = plist_dict_get_item(dict, "Error");
176 if (val) {
177 if (plist_string_val_compare(val, "NoPairedWatches")) {
178 res = COMPANION_PROXY_E_NO_DEVICES;
179 }
180 }
181 }
182 plist_free(dict);
183 return res;
184}
185
186struct companion_proxy_cb_data {
187 companion_proxy_client_t client;
188 companion_proxy_device_event_cb_t cbfunc;
189 void* user_data;
190};
191
192static void* companion_proxy_event_thread(void* arg)
193{
194 struct companion_proxy_cb_data* data = (struct companion_proxy_cb_data*)arg;
195 companion_proxy_client_t client = data->client;
196 companion_proxy_error_t res;
197
198 plist_t command = plist_new_dict();
199 plist_dict_set_item(command, "Command", plist_new_string("StartListeningForDevices"));
200 res = companion_proxy_send(client, command);
201 plist_free(command);
202
203 if (res != COMPANION_PROXY_E_SUCCESS) {
204 free(data);
205 client->event_thread = THREAD_T_NULL;
206 return NULL;
207 }
208
209 while (client && client->parent) {
210 plist_t node = NULL;
211 res = companion_proxy_error(property_list_service_receive_plist_with_timeout(client->parent, &node, 1000));
212 if (res != COMPANION_PROXY_E_SUCCESS && res != COMPANION_PROXY_E_TIMEOUT) {
213 debug_info("could not receive plist, error %d", res);
214 break;
215 }
216
217 if (node) {
218 data->cbfunc(node, data->user_data);
219 }
220 plist_free(node);
221 }
222
223 client->event_thread = THREAD_T_NULL;
224 free(data);
225
226 return NULL;
227}
228
229companion_proxy_error_t companion_proxy_start_listening_for_devices(companion_proxy_client_t client, companion_proxy_device_event_cb_t callback, void* userdata)
230{
231 if (!client || !client->parent || !callback) {
232 return COMPANION_PROXY_E_INVALID_ARG;
233 }
234
235 if (client->event_thread) {
236 return COMPANION_PROXY_E_OP_IN_PROGRESS;
237 }
238
239 companion_proxy_error_t res = COMPANION_PROXY_E_UNKNOWN_ERROR;
240 struct companion_proxy_cb_data *data = (struct companion_proxy_cb_data*)malloc(sizeof(struct companion_proxy_cb_data));
241 if (data) {
242 data->client = client;
243 data->cbfunc = callback;
244 data->user_data = userdata;
245
246 if (thread_new(&client->event_thread, companion_proxy_event_thread, data) == 0) {
247 res = COMPANION_PROXY_E_SUCCESS;
248 } else {
249 free(data);
250 }
251 }
252 return res;
253}
254
255companion_proxy_error_t companion_proxy_stop_listening_for_devices(companion_proxy_client_t client)
256{
257 property_list_service_client_t parent = client->parent;
258 client->parent = NULL;
259 if (client->event_thread) {
260 debug_info("joining event thread");
261 thread_join(client->event_thread);
262 thread_free(client->event_thread);
263 client->event_thread = THREAD_T_NULL;
264 }
265 client->parent = parent;
266 return COMPANION_PROXY_E_SUCCESS;
267}
268
269companion_proxy_error_t companion_proxy_get_value_from_registry(companion_proxy_client_t client, const char* companion_udid, const char* key, plist_t* value)
270{
271 if (!client || !companion_udid || !key || !value) {
272 return COMPANION_PROXY_E_INVALID_ARG;
273 }
274
275 plist_t dict = plist_new_dict();
276 plist_dict_set_item(dict, "Command", plist_new_string("GetValueFromRegistry"));
277 plist_dict_set_item(dict, "GetValueGizmoUDIDKey", plist_new_string(companion_udid));
278 plist_dict_set_item(dict, "GetValueKeyKey", plist_new_string(key));
279
280 companion_proxy_error_t res = companion_proxy_send(client, dict);
281 plist_free(dict);
282 dict = NULL;
283 if (res != COMPANION_PROXY_E_SUCCESS) {
284 return res;
285 }
286
287 res = companion_proxy_receive(client, &dict);
288 if (res != COMPANION_PROXY_E_SUCCESS) {
289 return res;
290 }
291 if (!dict || !PLIST_IS_DICT(dict)) {
292 return COMPANION_PROXY_E_PLIST_ERROR;
293 }
294 plist_t val = plist_dict_get_item(dict, "RetrievedValueDictionary");
295 if (val) {
296 *value = plist_copy(val);
297 res = COMPANION_PROXY_E_SUCCESS;
298 } else {
299 res = COMPANION_PROXY_E_UNKNOWN_ERROR;
300 val = plist_dict_get_item(dict, "Error");
301 if (val) {
302 if (!plist_string_val_compare(val, "UnsupportedWatchKey")) {
303 res = COMPANION_PROXY_E_UNSUPPORTED_KEY;
304 } else if (plist_string_val_compare(val, "TimeoutReply")) {
305 res = COMPANION_PROXY_E_TIMEOUT_REPLY;
306 }
307 }
308 }
309 plist_free(dict);
310 return res;
311}
312
313companion_proxy_error_t companion_proxy_start_forwarding_service_port(companion_proxy_client_t client, uint16_t remote_port, const char* service_name, uint16_t* forward_port, plist_t options)
314{
315 if (!client) {
316 return COMPANION_PROXY_E_INVALID_ARG;
317 }
318
319 plist_t dict = plist_new_dict();
320 plist_dict_set_item(dict, "Command", plist_new_string("StartForwardingServicePort"));
321 plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port));
322 if (service_name) {
323 plist_dict_set_item(dict, "ForwardedServiceName", plist_new_string(service_name));
324 }
325 plist_dict_set_item(dict, "IsServiceLowPriority", plist_new_bool(0));
326 plist_dict_set_item(dict, "PreferWifi", plist_new_bool(0));
327 if (options) {
328 plist_dict_merge(&dict, options);
329 }
330
331 companion_proxy_error_t res = companion_proxy_send(client, dict);
332 plist_free(dict);
333 dict = NULL;
334 if (res != COMPANION_PROXY_E_SUCCESS) {
335 return res;
336 }
337
338 res = companion_proxy_receive(client, &dict);
339 if (res != COMPANION_PROXY_E_SUCCESS) {
340 return res;
341 }
342 plist_t val = plist_dict_get_item(dict, "CompanionProxyServicePort");
343 if (val) {
344 uint64_t u64val = 0;
345 plist_get_uint_val(val, &u64val);
346 *forward_port = (uint16_t)u64val;
347 res = COMPANION_PROXY_E_SUCCESS;
348 } else {
349 res = COMPANION_PROXY_E_UNKNOWN_ERROR;
350 }
351 plist_free(dict);
352
353 return res;
354}
355
356companion_proxy_error_t companion_proxy_stop_forwarding_service_port(companion_proxy_client_t client, uint16_t remote_port)
357{
358 if (!client) {
359 return COMPANION_PROXY_E_INVALID_ARG;
360 }
361
362 plist_t dict = plist_new_dict();
363 plist_dict_set_item(dict, "Command", plist_new_string("StopForwardingServicePort"));
364 plist_dict_set_item(dict, "GizmoRemotePortNumber", plist_new_uint(remote_port));
365
366 companion_proxy_error_t res = companion_proxy_send(client, dict);
367 plist_free(dict);
368 dict = NULL;
369 if (res != COMPANION_PROXY_E_SUCCESS) {
370 return res;
371 }
372
373 res = companion_proxy_receive(client, &dict);
374 if (res != COMPANION_PROXY_E_SUCCESS) {
375 return res;
376 }
377 plist_free(dict);
378
379 return res;
380}