diff options
Diffstat (limited to 'src/reverse_proxy.c')
-rw-r--r-- | src/reverse_proxy.c | 810 |
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 | */ | ||
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 | } | ||