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 | ||
