summaryrefslogtreecommitdiffstats
path: root/src/reverse_proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reverse_proxy.c')
-rw-r--r--src/reverse_proxy.c810
1 files changed, 810 insertions, 0 deletions
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 */
68static 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
89static 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
103static 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
111static 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
125static 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
273static 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
320static 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
345static 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
466leave:
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
475static 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
531reverse_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
610reverse_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
623reverse_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
644reverse_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
663reverse_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
670void 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
679void 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
688void 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
697reverse_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
703reverse_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
710reverse_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
715reverse_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
757reverse_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
762reverse_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}