diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/companion_proxy.c | 381 | ||||
| -rw-r--r-- | src/companion_proxy.h | 34 |
3 files changed, 416 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 75c6dfe..feabf40 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
| @@ -30,6 +30,7 @@ libimobiledevice_la_SOURCES = idevice.c idevice.h \ | |||
| 30 | webinspector.c webinspector.h\ | 30 | webinspector.c webinspector.h\ |
| 31 | mobileactivation.c mobileactivation.h\ | 31 | mobileactivation.c mobileactivation.h\ |
| 32 | preboard.c preboard.h \ | 32 | preboard.c preboard.h \ |
| 33 | companion_proxy.c companion_proxy.h \ | ||
| 33 | syslog_relay.c syslog_relay.h | 34 | syslog_relay.c syslog_relay.h |
| 34 | 35 | ||
| 35 | if WIN32 | 36 | if WIN32 |
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 | */ | ||
| 43 | static 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 | |||
| 66 | LIBIMOBILEDEVICE_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 | |||
| 94 | LIBIMOBILEDEVICE_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 | |||
| 101 | LIBIMOBILEDEVICE_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 | |||
| 120 | LIBIMOBILEDEVICE_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 | |||
| 133 | LIBIMOBILEDEVICE_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 | |||
| 147 | LIBIMOBILEDEVICE_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 | |||
| 187 | struct compproxy_cb_data { | ||
| 188 | compproxy_client_t client; | ||
| 189 | compproxy_device_event_cb_t cbfunc; | ||
| 190 | void* user_data; | ||
| 191 | }; | ||
| 192 | |||
| 193 | static 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 | |||
| 230 | LIBIMOBILEDEVICE_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 | |||
| 256 | LIBIMOBILEDEVICE_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 | |||
| 270 | LIBIMOBILEDEVICE_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 | |||
| 314 | LIBIMOBILEDEVICE_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 | |||
| 357 | LIBIMOBILEDEVICE_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 | } | ||
diff --git a/src/companion_proxy.h b/src/companion_proxy.h new file mode 100644 index 0000000..1b520d4 --- /dev/null +++ b/src/companion_proxy.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* | ||
| 2 | * compproxy.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 "libimobiledevice/companion_proxy.h" | ||
| 26 | #include "property_list_service.h" | ||
| 27 | #include "common/thread.h" | ||
| 28 | |||
| 29 | struct compproxy_client_private { | ||
| 30 | property_list_service_client_t parent; | ||
| 31 | THREAD_T event_thread; | ||
| 32 | }; | ||
| 33 | |||
| 34 | #endif | ||
