diff options
author | 2021-11-24 03:46:42 +0100 | |
---|---|---|
committer | 2021-11-24 03:46:42 +0100 | |
commit | 2c6121db9ad84b8aad05b937e071ff7dcc9c8867 (patch) | |
tree | a255055506781da1bd6c0d3cf42f5996189d23a2 | |
parent | fa8bfb65c70edd4d2617fbbf970302beb9a4ced2 (diff) | |
download | libimobiledevice-2c6121db9ad84b8aad05b937e071ff7dcc9c8867.tar.gz libimobiledevice-2c6121db9ad84b8aad05b937e071ff7dcc9c8867.tar.bz2 |
Add Reverse Proxy implementation
-rw-r--r-- | include/Makefile.am | 1 | ||||
-rw-r--r-- | include/endianness.h | 12 | ||||
-rw-r--r-- | include/libimobiledevice/reverse_proxy.h | 209 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/reverse_proxy.c | 799 | ||||
-rw-r--r-- | src/reverse_proxy.h | 50 |
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 | ||
27 | extern "C" { | ||
28 | #endif | ||
29 | |||
30 | #include <libimobiledevice/libimobiledevice.h> | ||
31 | |||
32 | #define REVERSE_PROXY_DEFAULT_PORT 1082 | ||
33 | |||
34 | /** Error Codes */ | ||
35 | typedef 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 | |||
46 | typedef struct reverse_proxy_client_private reverse_proxy_client_private; | ||
47 | typedef reverse_proxy_client_private *reverse_proxy_client_t; /**< The client handle. */ | ||
48 | |||
49 | typedef enum { | ||
50 | RP_TYPE_CTRL = 1, /**< control connection */ | ||
51 | RP_TYPE_CONN /**< proxy connection */ | ||
52 | } reverse_proxy_client_type_t; | ||
53 | |||
54 | typedef 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 | |||
63 | typedef 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 | */ | ||
75 | typedef 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 | */ | ||
86 | typedef 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 | */ | ||
96 | typedef 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 | */ | ||
117 | reverse_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 | */ | ||
135 | reverse_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 | */ | ||
142 | reverse_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 | */ | ||
154 | reverse_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 | */ | ||
167 | void 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 | */ | ||
179 | void 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 | |||
192 | void 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 | */ | ||
203 | reverse_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 | ||
52 | if WIN32 | 53 | if 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 | */ | ||
58 | static 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 | |||
79 | static 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 | |||
93 | static 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 | |||
101 | static 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 | |||
115 | static 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 | |||
262 | static 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 | |||
309 | static 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 | |||
334 | static 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 | |||
455 | leave: | ||
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 | |||
464 | static 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 | |||
520 | LIBIMOBILEDEVICE_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 | |||
599 | LIBIMOBILEDEVICE_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 | |||
612 | LIBIMOBILEDEVICE_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 | |||
633 | LIBIMOBILEDEVICE_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 | |||
652 | LIBIMOBILEDEVICE_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 | |||
659 | LIBIMOBILEDEVICE_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 | |||
668 | LIBIMOBILEDEVICE_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 | |||
677 | LIBIMOBILEDEVICE_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 | |||
686 | reverse_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 | |||
692 | 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) | ||
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 | |||
699 | reverse_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 | |||
704 | reverse_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 | |||
746 | reverse_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 | |||
751 | reverse_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 | |||
28 | struct 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 | |||
43 | reverse_proxy_error_t reverse_proxy_send(reverse_proxy_client_t client, const char* data, uint32_t len, uint32_t* sent); | ||
44 | reverse_proxy_error_t reverse_proxy_receive(reverse_proxy_client_t client, char* buffer, uint32_t len, uint32_t* received); | ||
45 | 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); | ||
46 | reverse_proxy_error_t reverse_proxy_send_plist(reverse_proxy_client_t client, plist_t plist); | ||
47 | reverse_proxy_error_t reverse_proxy_receive_plist(reverse_proxy_client_t client, plist_t* plist); | ||
48 | reverse_proxy_error_t reverse_proxy_receive_plist_with_timeout(reverse_proxy_client_t client, plist_t * plist, uint32_t timeout_ms); | ||
49 | |||
50 | #endif | ||