summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2021-11-24 03:46:42 +0100
committerGravatar Nikias Bassen2021-11-24 03:46:42 +0100
commit2c6121db9ad84b8aad05b937e071ff7dcc9c8867 (patch)
treea255055506781da1bd6c0d3cf42f5996189d23a2
parentfa8bfb65c70edd4d2617fbbf970302beb9a4ced2 (diff)
downloadlibimobiledevice-2c6121db9ad84b8aad05b937e071ff7dcc9c8867.tar.gz
libimobiledevice-2c6121db9ad84b8aad05b937e071ff7dcc9c8867.tar.bz2
Add Reverse Proxy implementation
-rw-r--r--include/Makefile.am1
-rw-r--r--include/endianness.h12
-rw-r--r--include/libimobiledevice/reverse_proxy.h209
-rw-r--r--src/Makefile.am1
-rw-r--r--src/reverse_proxy.c799
-rw-r--r--src/reverse_proxy.h50
6 files changed, 1072 insertions, 0 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index e23b2a9..2abaf49 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -26,5 +26,6 @@ nobase_include_HEADERS = \
26 libimobiledevice/mobileactivation.h \ 26 libimobiledevice/mobileactivation.h \
27 libimobiledevice/preboard.h \ 27 libimobiledevice/preboard.h \
28 libimobiledevice/companion_proxy.h \ 28 libimobiledevice/companion_proxy.h \
29 libimobiledevice/reverse_proxy.h \
29 libimobiledevice/property_list_service.h \ 30 libimobiledevice/property_list_service.h \
30 libimobiledevice/service.h 31 libimobiledevice/service.h
diff --git a/include/endianness.h b/include/endianness.h
index 2d6ad0e..1d414b3 100644
--- a/include/endianness.h
+++ b/include/endianness.h
@@ -31,6 +31,18 @@
31#define htobe16 be16toh 31#define htobe16 be16toh
32#endif 32#endif
33 33
34#ifndef le16toh
35#if __BYTE_ORDER == __BIG_ENDIAN
36#define le16toh(x) ((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8))
37#else
38#define le16toh(x) (x)
39#endif
40#endif
41
42#ifndef htole16
43#define htole16 le16toh
44#endif
45
34#ifndef __bswap_32 46#ifndef __bswap_32
35#define __bswap_32(x) ((((x) & 0xFF000000) >> 24) \ 47#define __bswap_32(x) ((((x) & 0xFF000000) >> 24) \
36 | (((x) & 0x00FF0000) >> 8) \ 48 | (((x) & 0x00FF0000) >> 8) \
diff --git a/include/libimobiledevice/reverse_proxy.h b/include/libimobiledevice/reverse_proxy.h
new file mode 100644
index 0000000..2539bd9
--- /dev/null
+++ b/include/libimobiledevice/reverse_proxy.h
@@ -0,0 +1,209 @@
1/**
2 * @file libimobiledevice/reverse_proxy.h
3 * @brief Provide a reverse proxy to allow the device to communicate through,
4 * which is used during firmware restore.
5 *
6 * Copyright (c) 2021 Nikias Bassen, 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#ifndef IREVERSE_PROXY_H
24#define IREVERSE_PROXY_H
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30#include <libimobiledevice/libimobiledevice.h>
31
32#define REVERSE_PROXY_DEFAULT_PORT 1082
33
34/** Error Codes */
35typedef enum {
36 REVERSE_PROXY_E_SUCCESS = 0,
37 REVERSE_PROXY_E_INVALID_ARG = -1,
38 REVERSE_PROXY_E_PLIST_ERROR = -2,
39 REVERSE_PROXY_E_MUX_ERROR = -3,
40 REVERSE_PROXY_E_SSL_ERROR = -4,
41 REVERSE_PROXY_E_NOT_ENOUGH_DATA = -5,
42 REVERSE_PROXY_E_TIMEOUT = -6,
43 REVERSE_PROXY_E_UNKNOWN_ERROR = -256
44} reverse_proxy_error_t;
45
46typedef struct reverse_proxy_client_private reverse_proxy_client_private;
47typedef reverse_proxy_client_private *reverse_proxy_client_t; /**< The client handle. */
48
49typedef enum {
50 RP_TYPE_CTRL = 1, /**< control connection */
51 RP_TYPE_CONN /**< proxy connection */
52} reverse_proxy_client_type_t;
53
54typedef enum {
55 RP_STATUS_READY = 1, /**< proxy is ready */
56 RP_STATUS_TERMINATE, /**< proxy terminated */
57 RP_STATUS_CONNECT_REQ, /**< connection request received (only RP_TYPE_CTRL) */
58 RP_STATUS_SHUTDOWN_REQ, /**< shutdown request received (only RP_TYPE_CTRL) */
59 RP_STATUS_CONNECTED, /**< connection established (only RP_TYPE_CONN) */
60 RP_STATUS_DISCONNECTED, /**< connection closed (only RP_TYPE_CONN) */
61} reverse_proxy_status_t;
62
63typedef enum {
64 RP_DATA_DIRECTION_OUT = 1, /**< data going out to remote host */
65 RP_DATA_DIRECTION_IN /**< data coming in from remote host */
66} reverse_proxy_data_direction_t;
67
68/**
69 * Log callback function prototype.
70 *
71 * @param client The client that called the callback function
72 * @param log_msg The log message
73 * @param user_data The user_data pointer that was set when registering the callback
74 */
75typedef void (*reverse_proxy_log_cb_t) (reverse_proxy_client_t client, const char* log_msg, void* user_data);
76
77/**
78 * Data callback function prototype.
79 *
80 * @param client The client that called the callback function
81 * @param direction The direction of the data, either RP_DATA_DIRECTION_OUT or RP_DATA_DIRECTION_IN
82 * @param buffer The data buffer
83 * @param length The length of the data buffer
84 * @param user_data The user_data pointer that was set when registering the callback
85 */
86typedef void (*reverse_proxy_data_cb_t) (reverse_proxy_client_t client, reverse_proxy_data_direction_t direction, const char* buffer, uint32_t length, void* user_data);
87
88/**
89 * Status callback function prototype.
90 *
91 * @param client The client that called the callback function
92 * @param status The status the client is reporting
93 * @param status_msg A status message the client reports along with the status
94 * @param user_data The user_data pointer that was set when registering the callback
95 */
96typedef void (*reverse_proxy_status_cb_t) (reverse_proxy_client_t client, reverse_proxy_status_t status, const char* status_msg, void* user_data);
97
98/**
99 * Create a reverse proxy client using com.apple.PurpleReverseProxy.Ctrl and
100 * com.apple.PurpleReverseProxy.Conn lockdown services. This will open a port
101 * 1083 on the device that iOS apps could connect to; \b however that is
102 * only allowed if an app has the com.apple.private.PurpleReverseProxy.allowed
103 * entitlement, which currently only \c /usr/libexec/fdrhelper holds.
104 *
105 * @note This function only creates and initializes the reverse proxy;
106 * to make it operational, call reverse_proxy_client_start_proxy().
107 *
108 * @param device The device to connect to.
109 * @param client Pointer that will be set to a newly allocated #reverse_proxy_client_t
110 * upon successful return.
111 * @param label A label to pass to lockdownd when creating the service
112 * connections, usually the program name.
113 *
114 * @return REVERSE_PROXY_E_SUCCESS on success,
115 * or a REVERSE_PROXY_E_* error code otherwise.
116 */
117reverse_proxy_error_t reverse_proxy_client_create_with_service(idevice_t device, reverse_proxy_client_t* client, const char* label);
118
119/**
120 * Create a reverse proxy client using an open port on the device. This is
121 * used during firmware restores with the default port REVERSE_PROXY_DEFAULT_PORT (1082).
122 *
123 * @note This function only creates and initializes the reverse proxy;
124 * to make it operational, call reverse_proxy_client_start_proxy().
125 *
126 * @param device The device to connect to.
127 * @param client Pointer that will be set to a newly allocated reverse_proxy_client_t
128 * upon successful return.
129 * @param device_port An open port on the device. Unless it's being used for
130 * a custom implementation, pass REVERSE_PROXY_DEFAULT_PORT here.
131 *
132 * @return REVERSE_PROXY_E_SUCCESS on success,
133 * or a REVERSE_PROXY_E_* error code otherwise.
134 */
135reverse_proxy_error_t reverse_proxy_client_create_with_port(idevice_t device, reverse_proxy_client_t* client, uint16_t device_port);
136
137/**
138 * Disconnects a reverse proxy client and frees up the client data.
139 *
140 * @param client The reverse proxy client to disconnect and free.
141 */
142reverse_proxy_error_t reverse_proxy_client_free(reverse_proxy_client_t client);
143
144/**
145 * Make an initialized reverse proxy client operational, i.e. start the actual proxy.
146 *
147 * @param client The reverse proxy client to start.
148 * @param control_protocol_version The control protocol version to use.
149 * This is either 1 or 2. Recent devices use 2.
150 *
151 * @return REVERSE_PROXY_E_SUCCESS on success,
152 * or a REVERSE_PROXY_E_* error code otherwise.
153 */
154reverse_proxy_error_t reverse_proxy_client_start_proxy(reverse_proxy_client_t client, int control_protocol_version);
155
156/**
157 * Set a status callback function. This allows to report the status of the
158 * reverse proxy, like Ready, Connect Request, Connected, etc.
159 *
160 * @note Set the callback before calling reverse_proxy_client_start_proxy().
161 *
162 * @param client The reverse proxy client
163 * @param callback The status callback function that will be called
164 * when the status of the reverse proxy changes.
165 * @param user_data A pointer that will be passed to the callback function.
166 */
167void reverse_proxy_client_set_status_callback(reverse_proxy_client_t client, reverse_proxy_status_cb_t callback, void* user_data);
168
169/**
170 * Set a log callback function. Useful for debugging or verbosity.
171 *
172 * @note Set the callback before calling reverse_proxy_client_start_proxy().
173 *
174 * @param client The reverse proxy client
175 * @param callback The log callback function that will be called
176 * when the reverse proxy logs something.
177 * @param user_data A pointer that will be passed to the callback function.
178 */
179void reverse_proxy_client_set_log_callback(reverse_proxy_client_t client, reverse_proxy_log_cb_t callback, void* user_data);
180
181/**
182 * Set a data callback function. Useful for debugging or extra verbosity.
183 *
184 * @note Set the callback before calling reverse_proxy_client_start_proxy().
185 *
186 * @param client The reverse proxy client
187 * @param callback The status callback function that will be called
188 * when the status of the reverse proxy changes.
189 * @param user_data A pointer that will be passed to the callback function.
190 */
191
192void reverse_proxy_client_set_data_callback(reverse_proxy_client_t client, reverse_proxy_data_cb_t callback, void* user_data);
193
194/**
195 * Helper function to return the type of a given reverse proxy client, which
196 * is either RP_TYPE_CTRL or RP_TYPE_CONN. Useful for callback functions.
197 * @see reverse_proxy_client_type_t
198 *
199 * @param client The reverse proxy client
200 *
201 * @return The type of the rerverse proxy client
202 */
203reverse_proxy_client_type_t reverse_proxy_get_type(reverse_proxy_client_t client);
204
205#ifdef __cplusplus
206}
207#endif
208
209#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 183a745..106eef7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ libimobiledevice_1_0_la_SOURCES = \
47 mobileactivation.c mobileactivation.h \ 47 mobileactivation.c mobileactivation.h \
48 preboard.c preboard.h \ 48 preboard.c preboard.h \
49 companion_proxy.c companion_proxy.h \ 49 companion_proxy.c companion_proxy.h \
50 reverse_proxy.c reverse_proxy.h \
50 syslog_relay.c syslog_relay.h 51 syslog_relay.c syslog_relay.h
51 52
52if WIN32 53if WIN32
diff --git a/src/reverse_proxy.c b/src/reverse_proxy.c
new file mode 100644
index 0000000..fd6f1a2
--- /dev/null
+++ b/src/reverse_proxy.c
@@ -0,0 +1,799 @@
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#include <errno.h>
29
30#include <plist/plist.h>
31#include <libimobiledevice-glue/thread.h>
32#include <libimobiledevice-glue/socket.h>
33
34
35#include "reverse_proxy.h"
36#include "lockdown.h"
37#include "common/debug.h"
38#include "endianness.h"
39
40#define CTRL_PORT 1082
41#define CTRLCMD "BeginCtrl"
42#define HELLOCTRLCMD "HelloCtrl"
43#define HELLOCMD "HelloConn"
44
45#define RP_SYNC_MSG 0x1
46#define RP_PROXY_MSG 0x105
47#define RP_PLIST_MSG 0xbbaa
48
49/**
50 * Convert a service_error_t value to a reverse_proxy_error_t value.
51 * Used internally to get correct error codes.
52 *
53 * @param err A service_error_t error code
54 *
55 * @return A matching reverse_proxy_error_t error code,
56 * REVERSE_PROXY_E_UNKNOWN_ERROR otherwise.
57 */
58static reverse_proxy_error_t reverse_proxy_error(service_error_t err)
59{
60 switch (err) {
61 case SERVICE_E_SUCCESS:
62 return REVERSE_PROXY_E_SUCCESS;
63 case SERVICE_E_INVALID_ARG:
64 return REVERSE_PROXY_E_INVALID_ARG;
65 case SERVICE_E_MUX_ERROR:
66 return REVERSE_PROXY_E_MUX_ERROR;
67 case SERVICE_E_SSL_ERROR:
68 return REVERSE_PROXY_E_SSL_ERROR;
69 case SERVICE_E_NOT_ENOUGH_DATA:
70 return REVERSE_PROXY_E_NOT_ENOUGH_DATA;
71 case SERVICE_E_TIMEOUT:
72 return REVERSE_PROXY_E_TIMEOUT;
73 default:
74 break;
75 }
76 return REVERSE_PROXY_E_UNKNOWN_ERROR;
77}
78
79static void _reverse_proxy_log(reverse_proxy_client_t client, const char* format, ...)
80{
81 if (!client || !client->log_cb) {
82 return;
83 }
84 va_list args;
85 va_start(args, format);
86 char* buffer = NULL;
87 (void)vasprintf(&buffer, format, args);
88 va_end(args);
89 client->log_cb(client, buffer, client->log_cb_user_data);
90 free(buffer);
91}
92
93static void _reverse_proxy_data(reverse_proxy_client_t client, int direction, char* buffer, uint32_t length)
94{
95 if (!client || !client->data_cb) {
96 return;
97 }
98 client->data_cb(client, direction, buffer, length, client->data_cb_user_data);
99}
100
101static void _reverse_proxy_status(reverse_proxy_client_t client, int status, const char* format, ...)
102{
103 if (!client || !client->status_cb) {
104 return;
105 }
106 va_list args;
107 va_start(args, format);
108 char* buffer = NULL;
109 (void)vasprintf(&buffer, format, args);
110 va_end(args);
111 client->status_cb(client, status, buffer, client->status_cb_user_data);
112 free(buffer);
113}
114
115static int _reverse_proxy_handle_proxy_cmd(reverse_proxy_client_t client)
116{
117 reverse_proxy_error_t err = REVERSE_PROXY_E_SUCCESS;
118 char *buf = NULL;
119 size_t bufsize = 1048576;
120 uint32_t sent = 0, bytes = 0;
121 uint32_t sent_total = 0;
122 uint32_t recv_total = 0;
123 char *host = NULL;
124 uint16_t port = 0;
125
126 buf = malloc(bufsize);
127 if (!buf) {
128 _reverse_proxy_log(client, "ERROR: Failed to allocate buffer");
129 return -1;
130 }
131
132 err = reverse_proxy_receive(client, buf, bufsize, &bytes);
133 if (err != REVERSE_PROXY_E_SUCCESS) {
134 free(buf);
135 _reverse_proxy_log(client, "ERROR: Unable to read data for proxy command");
136 return -1;
137 }
138 _reverse_proxy_log(client, "Handling proxy command");
139
140 /* Just return success here unconditionally because we don't know
141 * anything else and we will eventually abort on failure anyway */
142 uint16_t ack = 5;
143 err = reverse_proxy_send(client, (char *)&ack, sizeof(ack), &sent);
144 if (err != REVERSE_PROXY_E_SUCCESS || sent != sizeof(ack)) {
145 free(buf);
146 _reverse_proxy_log(client, "ERROR: Unable to send ack. Sent %u of %u bytes.", sent, (uint32_t)sizeof(ack));
147 return -1;
148 }
149
150 if (bytes < 3) {
151 free(buf);
152 _reverse_proxy_log(client, "Proxy command data too short, retrying");
153 return 0;
154 }
155
156 /* ack command data too */
157 err = reverse_proxy_send(client, buf, bytes, &sent);
158 if (err != REVERSE_PROXY_E_SUCCESS || sent != bytes) {
159 free(buf);
160 _reverse_proxy_log(client, "ERROR: Unable to send data. Sent %u of %u bytes.", sent, bytes);
161 return -1;
162 }
163
164 /* Now try to handle actual messages */
165 /* Connect: 0 3 hostlen <host> <port> */
166 if (buf[0] == 0 && buf[1] == 3) {
167 uint16_t *p = (uint16_t *)&buf[bytes - 2];
168 port = be16toh(*p);
169 buf[bytes - 2] = '\0';
170 host = strdup(&buf[3]);
171 _reverse_proxy_log(client, "Connect request to %s:%u", host, port);
172 }
173
174 if (!host || !buf[2]) {
175 /* missing or zero length host name */
176 free(buf);
177 return 0;
178 }
179
180 /* else wait for messages and forward them */
181 int sockfd = socket_connect(host, port);
182 free(host);
183 if (sockfd < 0) {
184 free(buf);
185 _reverse_proxy_log(client, "ERROR: Connection to %s:%u failed: %s", host, port, strerror(errno));
186 return -1;
187 }
188
189 _reverse_proxy_status(client, RP_STATUS_CONNECTED, "Connected to %s:%u", host, port);
190
191 int res = 0, bytes_ret;
192 while (1) {
193 bytes = 0;
194 err = reverse_proxy_receive_with_timeout(client, buf, bufsize, &bytes, 100);
195 if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && !bytes)) {
196 /* just a timeout condition */
197 }
198 else if (err != REVERSE_PROXY_E_SUCCESS) {
199 _reverse_proxy_log(client, "Connection closed");
200 res = -1;
201 break;
202 }
203 if (bytes) {
204 _reverse_proxy_log(client, "Proxying %u bytes of data", bytes);
205 _reverse_proxy_data(client, RP_DATA_DIRECTION_OUT, buf, bytes);
206 sent = 0;
207 while (sent < bytes) {
208 int s = socket_send(sockfd, buf + sent, bytes - sent);
209 if (s < 0) {
210 break;
211 }
212 sent += s;
213 }
214 sent_total += sent;
215 if (sent != bytes) {
216 _reverse_proxy_log(client, "ERROR: Sending proxy payload failed: %s. Sent %u of %u bytes.", strerror(errno), sent, bytes);
217 socket_close(sockfd);
218 res = -1;
219 break;
220 }
221 }
222 bytes_ret = socket_receive_timeout(sockfd, buf, bufsize, 0, 100);
223 if (bytes_ret == -ETIMEDOUT) {
224 bytes_ret = 0;
225 } else if (bytes_ret == -ECONNRESET) {
226 res = 1;
227 break;
228 } else if (bytes_ret < 0) {
229 _reverse_proxy_log(client, "ERROR: Failed to receive from host: %s", strerror(-bytes_ret));
230 break;
231 }
232
233 bytes = bytes_ret;
234 if (bytes) {
235 _reverse_proxy_log(client, "Received %u bytes reply data, sending to device\n", bytes);
236 _reverse_proxy_data(client, RP_DATA_DIRECTION_IN, buf, bytes);
237 recv_total += bytes;
238 sent = 0;
239 while (sent < bytes) {
240 uint32_t s;
241 err = reverse_proxy_send(client, buf + sent, bytes - sent, &s);
242 if (err != REVERSE_PROXY_E_SUCCESS) {
243 break;
244 }
245 sent += s;
246 }
247 if (err != REVERSE_PROXY_E_SUCCESS || bytes != sent) {
248 _reverse_proxy_log(client, "ERROR: Unable to send data (%d). Sent %u of %u bytes.", err, sent, bytes);
249 res = -1;
250 break;
251 }
252 }
253 }
254 socket_close(sockfd);
255 free(buf);
256
257 _reverse_proxy_status(client, RP_STATUS_DISCONNECTED, "Disconnected (out: %u / in: %u)", sent_total, recv_total);
258
259 return res;
260}
261
262static int _reverse_proxy_handle_plist_cmd(reverse_proxy_client_t client)
263{
264 plist_t dict;
265 reverse_proxy_error_t err;
266
267 err = reverse_proxy_receive_plist(client, &dict);
268 if (err != REVERSE_PROXY_E_SUCCESS) {
269 _reverse_proxy_log(client, "ERROR: Unable to receive plist command, error", err);
270 return -1;
271 }
272 plist_t node = plist_dict_get_item(dict, "Command");
273 if (!node || (plist_get_node_type(node) != PLIST_STRING)) {
274 _reverse_proxy_log(client, "ERROR: No 'Command' in reply", err);
275 plist_free(dict);
276 return -1;
277 }
278 char *command = NULL;
279 plist_get_string_val(node, &command);
280 plist_free(dict);
281
282 if (!command) {
283 _reverse_proxy_log(client, "ERROR: Empty 'Command' string");
284 return -1;
285 }
286
287 if (!strcmp(command, "Ping")) {
288 _reverse_proxy_log(client, "Received Ping command, replying with Pong");
289 dict = plist_new_dict();
290 plist_dict_set_item(dict, "Pong", plist_new_bool(1));
291 err = reverse_proxy_send_plist(client, dict);
292 plist_free(dict);
293 if (err) {
294 _reverse_proxy_log(client, "ERROR: Unable to send Ping command reply");
295 free(command);
296 return -1;
297 }
298 } else {
299 _reverse_proxy_log(client, "WARNING: Received unhandled plist command '%s'", command);
300 free(command);
301 return -1;
302 }
303
304 free(command);
305 /* reverse proxy connection will be terminated remotely. Next receive will get nothing, error and terminate this worker thread. */
306 return 0;
307}
308
309static reverse_proxy_error_t reverse_proxy_client_new(idevice_t device, lockdownd_service_descriptor_t service, reverse_proxy_client_t * client)
310{
311 *client = NULL;
312
313 if (!device || !service || service->port == 0 || !client || *client) {
314 return REVERSE_PROXY_E_INVALID_ARG;
315 }
316
317 debug_info("Creating reverse_proxy_client, port = %d.", service->port);
318
319 service_client_t sclient = NULL;
320 reverse_proxy_error_t ret = reverse_proxy_error(service_client_new(device, service, &sclient));
321 if (ret != REVERSE_PROXY_E_SUCCESS) {
322 debug_info("Creating service client failed. Error: %i", ret);
323 return ret;
324 }
325
326 reverse_proxy_client_t client_loc = (reverse_proxy_client_t) calloc(1, sizeof(struct reverse_proxy_client_private));
327 client_loc->parent = sclient;
328 client_loc->th_ctrl = THREAD_T_NULL;
329 *client = client_loc;
330
331 return 0;
332}
333
334static void* _reverse_proxy_connection_thread(void *cdata)
335{
336 reverse_proxy_client_t client = (reverse_proxy_client_t)cdata;
337 uint32_t bytes = 0;
338 reverse_proxy_client_t conn_client = NULL;
339 reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR;
340
341 if (client->conn_port == 0) {
342 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);
343 if (!conn_client) {
344 _reverse_proxy_log(client, "ERROR: Failed to start proxy connection service, error %d", err);
345 }
346 } else {
347 struct lockdownd_service_descriptor svc;
348 svc.port = client->conn_port;
349 svc.ssl_enabled = 0;
350 svc.identifier = NULL;
351 err = reverse_proxy_client_new(client->parent->connection->device, &svc, &conn_client);
352 if (!conn_client) {
353 _reverse_proxy_log(client, "ERROR: Failed to connect to proxy connection port %u, error %d", client->conn_port, err);
354 }
355 }
356 if (!conn_client) {
357 goto leave;
358 }
359 conn_client->type = RP_TYPE_CONN;
360 conn_client->protoversion = client->protoversion;
361 conn_client->log_cb = client->log_cb;
362 conn_client->log_cb_user_data = client->log_cb_user_data;
363 conn_client->status_cb = client->status_cb;
364 conn_client->status_cb_user_data = client->status_cb_user_data;
365
366 err = reverse_proxy_send(conn_client, HELLOCMD, sizeof(HELLOCMD), &bytes);
367 if (err != REVERSE_PROXY_E_SUCCESS || bytes != sizeof(HELLOCMD)) {
368 _reverse_proxy_log(conn_client, "ERROR: Unable to send " HELLOCMD " (sent %u/%u bytes)", bytes, sizeof(HELLOCMD));
369 goto leave;
370 }
371
372 if (conn_client->protoversion == 2) {
373 plist_t reply = NULL;
374 err = reverse_proxy_receive_plist(conn_client, &reply);
375 if (err != REVERSE_PROXY_E_SUCCESS) {
376 _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " reply, error %d", err);
377 goto leave;
378 }
379 char* identifier = NULL;
380 char* cmd = NULL;
381 plist_t node = NULL;
382 node = plist_dict_get_item(reply, "Command");
383 if (node) {
384 plist_get_string_val(node, &cmd);
385 }
386 node = plist_dict_get_item(reply, "Identifier");
387 if (node) {
388 plist_get_string_val(node, &identifier);
389 }
390 plist_free(reply);
391
392 if (!cmd || (strcmp(cmd, HELLOCMD) != 0)) {
393 free(cmd);
394 free(identifier);
395 _reverse_proxy_log(conn_client, "ERROR: Unexpected reply to " HELLOCMD " received");
396 goto leave;
397 }
398 free(cmd);
399
400 if (identifier) {
401 _reverse_proxy_log(conn_client, "Got device identifier %s", identifier);
402 free(identifier);
403 }
404 } else {
405 char buf[16];
406 memset(buf, '\0', sizeof(buf));
407 bytes = 0;
408 err = reverse_proxy_receive(conn_client, buf, sizeof(HELLOCMD), &bytes);
409 if (err != REVERSE_PROXY_E_SUCCESS) {
410 _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " reply, error %d", err);
411 goto leave;
412 }
413 if (memcmp(buf, HELLOCMD, sizeof(HELLOCMD)) != 0) {
414 _reverse_proxy_log(conn_client, "ERROR: Did not receive " HELLOCMD " as reply, but %.*s", (int)bytes, buf);
415 goto leave;
416 }
417 }
418
419 _reverse_proxy_status(conn_client, RP_STATUS_READY, "Ready");
420
421 int running = 1;
422 while (client->th_ctrl != THREAD_T_NULL && conn_client && running) {
423 uint16_t cmd = 0;
424 bytes = 0;
425 err = reverse_proxy_receive_with_timeout(conn_client, (char*)&cmd, sizeof(cmd), &bytes, 1000);
426 if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && bytes != sizeof(cmd))) {
427 continue;
428 } else if (err != REVERSE_PROXY_E_SUCCESS) {
429 _reverse_proxy_log(conn_client, "Connection closed");
430 break;
431 }
432 cmd = le16toh(cmd);
433 switch (cmd) {
434 case 0xBBAA:
435 /* plist command */
436 if (_reverse_proxy_handle_plist_cmd(conn_client) < 0) {
437 running = 0;
438 }
439 break;
440 case 0x105:
441 /* proxy command */
442 if (_reverse_proxy_handle_proxy_cmd(conn_client) < 0) {
443 running = 0;
444 }
445 break;
446 default:
447 /* unknown */
448 debug_info("ERROR: Unknown request 0x%x", cmd);
449 _reverse_proxy_log(conn_client, "ERROR: Unknown request 0x%x", cmd);
450 running = 0;
451 break;
452 }
453 }
454
455leave:
456 _reverse_proxy_status(conn_client, RP_STATUS_TERMINATE, "Terminated");
457 if (conn_client) {
458 reverse_proxy_client_free(conn_client);
459 }
460
461 return NULL;
462}
463
464static void* _reverse_proxy_control_thread(void *cdata)
465{
466 reverse_proxy_client_t client = (reverse_proxy_client_t)cdata;
467 THREAD_T th_conn = THREAD_T_NULL;
468 int running = 1;
469 _reverse_proxy_status(client, RP_STATUS_READY, "Ready");
470 while (client && client->parent && running) {
471 uint32_t cmd = 0;
472 uint32_t bytes = 0;
473 reverse_proxy_error_t err = reverse_proxy_receive_with_timeout(client, (char*)&cmd, sizeof(cmd), &bytes, 1000);
474 if (err == REVERSE_PROXY_E_TIMEOUT || (err == REVERSE_PROXY_E_SUCCESS && bytes != sizeof(cmd))) {
475 continue;
476 } else if (err != REVERSE_PROXY_E_SUCCESS) {
477 _reverse_proxy_log(client, "Connection closed");
478 break;
479 }
480 cmd = le32toh(cmd);
481 switch (cmd) {
482 case 1:
483 /* connection request */
484 debug_info("ReverseProxy<%p> got connect request", client);
485 _reverse_proxy_status(client, RP_STATUS_CONNECT_REQ, "Connect Request");
486 if (thread_new(&th_conn, _reverse_proxy_connection_thread, client) != 0) {
487 debug_info("ERROR: Failed to start connection thread");
488 th_conn = THREAD_T_NULL;
489 running = 0;
490 }
491 break;
492 case 2:
493 /* shutdown request */
494 debug_info("ReverseProxy<%p> got shutdown request", client);
495 _reverse_proxy_status(client, RP_STATUS_SHUTDOWN_REQ, "Shutdown Request");
496 running = 0;
497 break;
498 default:
499 /* unknown */
500 debug_info("ERROR: Unknown request 0x%x", cmd);
501 _reverse_proxy_log(client, "ERROR: Unknown request 0x%x", cmd);
502 running = 0;
503 break;
504 }
505 }
506 _reverse_proxy_log(client, "Terminating");
507
508 client->th_ctrl = THREAD_T_NULL;
509 if (th_conn) {
510 debug_info("joining connection thread");
511 thread_join(th_conn);
512 thread_free(th_conn);
513 }
514
515 _reverse_proxy_status(client, RP_STATUS_TERMINATE, "Terminated");
516
517 return NULL;
518}
519
520LIBIMOBILEDEVICE_API reverse_proxy_error_t reverse_proxy_client_start_proxy(reverse_proxy_client_t client, int control_protocol_version)
521{
522 char buf[16] = {0, };
523 uint32_t bytes = 0;
524 reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR;
525
526 if (!client) {
527 return REVERSE_PROXY_E_INVALID_ARG;
528 }
529 if (control_protocol_version < 1 || control_protocol_version > 2) {
530 debug_info("invalid protocol version %d, must be 1 or 2", control_protocol_version);
531 return REVERSE_PROXY_E_INVALID_ARG;
532 }
533
534 if (control_protocol_version == 2) {
535 err = reverse_proxy_send(client, CTRLCMD, sizeof(CTRLCMD), &bytes);
536 if (err != REVERSE_PROXY_E_SUCCESS) {
537 _reverse_proxy_log(client, "ERROR: Failed to send " CTRLCMD " to device, error %d", err);
538 return err;
539 }
540 plist_t dict = plist_new_dict();
541 plist_dict_set_item(dict, "Command", plist_new_string(CTRLCMD));
542 plist_dict_set_item(dict, "CtrlProtoVersion", plist_new_uint(client->protoversion));
543 err = reverse_proxy_send_plist(client, dict);
544 plist_free(dict);
545 if (err != REVERSE_PROXY_E_SUCCESS) {
546 _reverse_proxy_log(client, "ERROR: Could not send " CTRLCMD " plist command, error %d", err);
547 return err;
548 }
549 dict = NULL;
550 err = reverse_proxy_receive_plist(client, &dict);
551 if (err != REVERSE_PROXY_E_SUCCESS) {
552 _reverse_proxy_log(client, "ERROR: Could not receive " CTRLCMD " plist reply, error %d", err);
553 return err;
554 }
555 plist_t node = plist_dict_get_item(dict, "ConnPort");
556 if (node && plist_get_node_type(node) == PLIST_UINT) {
557 uint64_t u64val = 0;
558 plist_get_uint_val(node, &u64val);
559 client->conn_port = (uint16_t)u64val;
560 } else {
561 _reverse_proxy_log(client, "ERROR: Could not get ConnPort value");
562 return REVERSE_PROXY_E_UNKNOWN_ERROR;
563 }
564 client->protoversion = 2;
565 } else {
566 err = reverse_proxy_send(client, HELLOCTRLCMD, sizeof(HELLOCTRLCMD), &bytes);
567 if (err != REVERSE_PROXY_E_SUCCESS) {
568 _reverse_proxy_log(client, "ERROR: Failed to send " HELLOCTRLCMD " to device, error %d", err);
569 return err;
570 }
571
572 bytes = 0;
573 err = reverse_proxy_receive(client, buf, sizeof(HELLOCTRLCMD)-1, &bytes);
574 if (err != REVERSE_PROXY_E_SUCCESS) {
575 _reverse_proxy_log(client, "ERROR: Could not receive " HELLOCTRLCMD " reply, error %d", err);
576 return err;
577 }
578
579 uint16_t cport = 0;
580 bytes = 0;
581 err = reverse_proxy_receive(client, (char*)&cport, 2, &bytes);
582 if (err != REVERSE_PROXY_E_SUCCESS) {
583 _reverse_proxy_log(client, "ERROR: Failed to receive connection port, error %d", err);
584 return err;
585 }
586 client->conn_port = le16toh(cport);
587 client->protoversion = 1;
588 }
589
590 if (thread_new(&(client->th_ctrl), _reverse_proxy_control_thread, client) != 0) {
591 _reverse_proxy_log(client, "ERROR: Failed to start control thread");
592 client->th_ctrl = THREAD_T_NULL; /* undefined after failure */
593 err = REVERSE_PROXY_E_UNKNOWN_ERROR;
594 }
595
596 return err;
597}
598
599LIBIMOBILEDEVICE_API reverse_proxy_error_t reverse_proxy_client_create_with_service(idevice_t device, reverse_proxy_client_t* client, const char* label)
600{
601 reverse_proxy_error_t err = REVERSE_PROXY_E_UNKNOWN_ERROR;
602 service_client_factory_start_service(device, "com.apple.PurpleReverseProxy.Ctrl", (void**)client, label, SERVICE_CONSTRUCTOR(reverse_proxy_client_new), &err);
603 if (!*client) {
604 return err;
605 }
606 (*client)->label = strdup(label);
607 (*client)->type = RP_TYPE_CTRL;
608
609 return REVERSE_PROXY_E_SUCCESS;
610}
611
612LIBIMOBILEDEVICE_API reverse_proxy_error_t reverse_proxy_client_create_with_port(idevice_t device, reverse_proxy_client_t* client, uint16_t device_port)
613{
614 reverse_proxy_client_t client_loc = NULL;
615 reverse_proxy_error_t err;
616
617 struct lockdownd_service_descriptor svc;
618 svc.port = device_port;
619 svc.ssl_enabled = 0;
620 svc.identifier = NULL;
621
622 err = reverse_proxy_client_new(device, &svc, &client_loc);
623 if (err != REVERSE_PROXY_E_SUCCESS) {
624 return err;
625 }
626
627 client_loc->type = RP_TYPE_CTRL;
628 *client = client_loc;
629
630 return REVERSE_PROXY_E_SUCCESS;
631}
632
633LIBIMOBILEDEVICE_API reverse_proxy_error_t reverse_proxy_client_free(reverse_proxy_client_t client)
634{
635 if (!client)
636 return REVERSE_PROXY_E_INVALID_ARG;
637 service_client_t parent = client->parent;
638 client->parent = NULL;
639 if (client->th_ctrl) {
640 debug_info("joining control thread");
641 thread_join(client->th_ctrl);
642 thread_free(client->th_ctrl);
643 client->th_ctrl = THREAD_T_NULL;
644 }
645 reverse_proxy_error_t err = reverse_proxy_error(service_client_free(parent));
646 free(client->label);
647 free(client);
648
649 return err;
650}
651
652LIBIMOBILEDEVICE_API reverse_proxy_client_type_t reverse_proxy_get_type(reverse_proxy_client_t client)
653{
654 if (!client)
655 return 0;
656 return client->type;
657}
658
659LIBIMOBILEDEVICE_API void reverse_proxy_client_set_status_callback(reverse_proxy_client_t client, reverse_proxy_status_cb_t status_callback, void* user_data)
660{
661 if (!client) {
662 return;
663 }
664 client->status_cb = status_callback;
665 client->status_cb_user_data = user_data;
666}
667
668LIBIMOBILEDEVICE_API void reverse_proxy_client_set_log_callback(reverse_proxy_client_t client, reverse_proxy_log_cb_t log_callback, void* user_data)
669{
670 if (!client) {
671 return;
672 }
673 client->log_cb = log_callback;
674 client->log_cb_user_data = user_data;
675}
676
677LIBIMOBILEDEVICE_API void reverse_proxy_client_set_data_callback(reverse_proxy_client_t client, reverse_proxy_data_cb_t data_callback, void* user_data)
678{
679 if (!client) {
680 return;
681 }
682 client->data_cb = data_callback;
683 client->data_cb_user_data = user_data;
684}
685
686reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent)
687{
688 reverse_proxy_error_t err = reverse_proxy_error(service_send(client->parent, data, len, sent));
689 return err;
690}
691
692reverse_proxy_error_t reverse_proxy_receive_with_timeout(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received, unsigned int timeout)
693{
694 if (!client)
695 return REVERSE_PROXY_E_INVALID_ARG;
696 return reverse_proxy_error(service_receive_with_timeout(client->parent, buffer, len, received, timeout));
697}
698
699reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received)
700{
701 return reverse_proxy_receive_with_timeout(client, buffer, len, received, 20000);
702}
703
704reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist)
705{
706 reverse_proxy_error_t err;
707 uint32_t len = 0;
708 char* buf = NULL;
709 uint32_t bytes = 0;
710
711 plist_to_bin(plist, &buf, &len);
712
713 if (!buf) {
714 return REVERSE_PROXY_E_INVALID_ARG;
715 }
716
717 debug_info("Sending %u bytes", len);
718
719 uint32_t slen = htole32(len);
720 err = reverse_proxy_send(client, (char*)&slen, sizeof(slen), &bytes);
721 if (err != REVERSE_PROXY_E_SUCCESS) {
722 free(buf);
723 debug_info("ERROR: Unable to send data length, error %d. Sent %u/%u bytes.", err, bytes, (uint32_t)sizeof(slen));
724 return err;
725 }
726 uint32_t done = 0;
727 do {
728 bytes = 0;
729 err = reverse_proxy_send(client, buf+done, len-done, &bytes);
730 if (err != REVERSE_PROXY_E_SUCCESS) {
731 break;
732 }
733 done += bytes;
734 } while (done < len);
735 free(buf);
736 if (err != REVERSE_PROXY_E_SUCCESS || done != len) {
737 debug_info("ERROR: Unable to send data, error %d. Sent %u/%u bytes.", err, done, len);
738 return err;
739 }
740
741 debug_info("Sent %u bytes", len);
742
743 return REVERSE_PROXY_E_SUCCESS;
744}
745
746reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist)
747{
748 return reverse_proxy_receive_plist_with_timeout(client, plist, 20000);
749}
750
751reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms)
752{
753 uint32_t len;
754 uint32_t bytes;
755 reverse_proxy_error_t err;
756
757 err = reverse_proxy_receive_with_timeout(client, (char*)&len, sizeof(len), &bytes, timeout_ms);
758 if (err != REVERSE_PROXY_E_SUCCESS) {
759 if (err != REVERSE_PROXY_E_TIMEOUT) {
760 debug_info("ERROR: Unable to receive packet length, error %d\n", err);
761 }
762 return err;
763 }
764
765 len = le32toh(len);
766 char* buf = calloc(1, len);
767 if (!buf) {
768 debug_info("ERROR: Out of memory");
769 return REVERSE_PROXY_E_UNKNOWN_ERROR;
770 }
771
772 uint32_t done = 0;
773 do {
774 bytes = 0;
775 err = reverse_proxy_receive_with_timeout(client, buf+done, len-done, &bytes, timeout_ms);
776 if (err != REVERSE_PROXY_E_SUCCESS) {
777 break;
778 }
779 done += bytes;
780 } while (done < len);
781
782 if (err != REVERSE_PROXY_E_SUCCESS || done != len) {
783 free(buf);
784 debug_info("ERROR: Unable to receive data, error %d. Received %u/%u bytes.", err, done, len);
785 return err;
786 }
787
788 debug_info("Received %u bytes", len);
789
790 plist_from_bin(buf, len, plist);
791 free(buf);
792
793 if (!(*plist)) {
794 debug_info("ERROR: Failed to convert buffer to plist");
795 return REVERSE_PROXY_E_PLIST_ERROR;
796 }
797
798 return REVERSE_PROXY_E_SUCCESS;
799}
diff --git a/src/reverse_proxy.h b/src/reverse_proxy.h
new file mode 100644
index 0000000..17eabac
--- /dev/null
+++ b/src/reverse_proxy.h
@@ -0,0 +1,50 @@
1/*
2 * reverse_proxy.h
3 * com.apple.PurpleReverseProxy service header file.
4 *
5 * Copyright (c) 2021 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 __REVERSE_PROXY_H
23#define __REVERSE_PROXY_H
24
25#include "libimobiledevice/reverse_proxy.h"
26#include "service.h"
27
28struct reverse_proxy_client_private {
29 service_client_t parent;
30 char* label;
31 int type;
32 int protoversion;
33 THREAD_T th_ctrl;
34 uint16_t conn_port;
35 reverse_proxy_log_cb_t log_cb;
36 void* log_cb_user_data;
37 reverse_proxy_data_cb_t data_cb;
38 void* data_cb_user_data;
39 reverse_proxy_status_cb_t status_cb;
40 void* status_cb_user_data;
41};
42
43reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent);
44reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received);
45reverse_proxy_error_t reverse_proxy_receive_with_timeout(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received, unsigned int timeout);
46reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist);
47reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist);
48reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms);
49
50#endif