diff options
Diffstat (limited to 'src/debugserver.c')
-rw-r--r-- | src/debugserver.c | 289 |
1 files changed, 160 insertions, 129 deletions
diff --git a/src/debugserver.c b/src/debugserver.c index 1e06233..74ade8a 100644 --- a/src/debugserver.c +++ b/src/debugserver.c @@ -2,7 +2,8 @@ * debugserver.c * com.apple.debugserver service implementation. * - * Copyright (c) 2014 Martin Szulecki All Rights Reserved. + * Copyright (c) 2019 Nikias Bassen, All Rights Reserved. + * Copyright (c) 2014-2015 Martin Szulecki All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,10 +29,11 @@ #define __USE_GNU 1 #include <stdio.h> +#include <libimobiledevice-glue/utils.h> + #include "debugserver.h" #include "lockdown.h" #include "common/debug.h" -#include "common/utils.h" #include "asprintf.h" /** @@ -54,13 +56,15 @@ static debugserver_error_t debugserver_error(service_error_t err) return DEBUGSERVER_E_MUX_ERROR; case SERVICE_E_SSL_ERROR: return DEBUGSERVER_E_SSL_ERROR; + case SERVICE_E_TIMEOUT: + return DEBUGSERVER_E_TIMEOUT; default: break; } return DEBUGSERVER_E_UNKNOWN_ERROR; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device, lockdownd_service_descriptor_t service, debugserver_client_t* client) +debugserver_error_t debugserver_client_new(idevice_t device, lockdownd_service_descriptor_t service, debugserver_client_t* client) { *client = NULL; @@ -77,11 +81,16 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device debug_info("Creating base service client failed. Error: %i", ret); return ret; } - service_disable_ssl(parent); + + if (service->identifier && (strcmp(service->identifier, DEBUGSERVER_SECURE_SERVICE_NAME) != 0)) { + service_disable_bypass_ssl(parent, 1); + } debugserver_client_t client_loc = (debugserver_client_t) malloc(sizeof(struct debugserver_client_private)); client_loc->parent = parent; client_loc->noack_mode = 0; + client_loc->cancel_receive = NULL; + client_loc->receive_loop_timeout = 1000; *client = client_loc; @@ -89,14 +98,18 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_new(idevice_t device return DEBUGSERVER_E_SUCCESS; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_start_service(idevice_t device, debugserver_client_t * client, const char* label) +debugserver_error_t debugserver_client_start_service(idevice_t device, debugserver_client_t * client, const char* label) { debugserver_error_t err = DEBUGSERVER_E_UNKNOWN_ERROR; - service_client_factory_start_service(device, DEBUGSERVER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); + service_client_factory_start_service(device, DEBUGSERVER_SECURE_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); + if (err != DEBUGSERVER_E_SUCCESS) { + err = DEBUGSERVER_E_UNKNOWN_ERROR; + service_client_factory_start_service(device, DEBUGSERVER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); + } return err; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_free(debugserver_client_t client) +debugserver_error_t debugserver_client_free(debugserver_client_t client) { if (!client) return DEBUGSERVER_E_INVALID_ARG; @@ -108,7 +121,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_free(debugserver_cli return err; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send(debugserver_client_t client, const char* data, uint32_t size, uint32_t *sent) +debugserver_error_t debugserver_client_send(debugserver_client_t client, const char* data, uint32_t size, uint32_t *sent) { debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; int bytes = 0; @@ -129,7 +142,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send(debugserver_cli return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) +debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) { debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; int bytes = 0; @@ -139,22 +152,27 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_with_timeout } res = debugserver_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout)); - if (bytes <= 0) { + if (bytes <= 0 && res != DEBUGSERVER_E_TIMEOUT) { debug_info("Could not read data, error %d", res); } if (received) { *received = (uint32_t)bytes; } - return res; + return (bytes > 0) ? DEBUGSERVER_E_SUCCESS : res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received) +debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received) { - return debugserver_client_receive_with_timeout(client, data, size, received, 1000); + debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; + do { + /* Is this allowed to return DEBUGSERVER_E_TIMEOUT and also set data and received? */ + res = debugserver_client_receive_with_timeout(client, data, size, received, client->receive_loop_timeout); + } while (res == DEBUGSERVER_E_TIMEOUT && client->cancel_receive != NULL && !client->cancel_receive()); + return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_command_new(const char* name, int argc, char* argv[], debugserver_command_t* command) +debugserver_error_t debugserver_command_new(const char* name, int argc, char* argv[], debugserver_command_t* command) { int i; debugserver_command_t tmp = (debugserver_command_t) malloc(sizeof(struct debugserver_command_private)); @@ -179,7 +197,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_command_new(const char* nam return DEBUGSERVER_E_SUCCESS; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_command_free(debugserver_command_t command) +debugserver_error_t debugserver_command_free(debugserver_command_t command) { int i; debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; @@ -221,10 +239,10 @@ static char debugserver_int2hex(int x) return hexchars[x]; } -#define DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(byte) debugserver_int2hex((byte >> 0x4) & 0xf) -#define DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(byte) debugserver_int2hex(byte & 0xf) -#define DEBUGSERVER_HEX_DECODE_FIRST_BYTE(byte) ((byte >> 0x4) & 0xf) -#define DEBUGSERVER_HEX_DECODE_SECOND_BYTE(byte) (byte & 0xf) +#define DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(byte) debugserver_int2hex(((byte) >> 0x4) & 0xf) +#define DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(byte) debugserver_int2hex((byte) & 0xf) +#define DEBUGSERVER_HEX_DECODE_FIRST_BYTE(byte) (((byte) >> 0x4) & 0xf) +#define DEBUGSERVER_HEX_DECODE_SECOND_BYTE(byte) ((byte) & 0xf) static uint32_t debugserver_get_checksum_for_buffer(const char* buffer, uint32_t size) { @@ -257,7 +275,7 @@ static int debugserver_response_is_checksum_valid(const char* response, uint32_t return 1; } -LIBIMOBILEDEVICE_API void debugserver_encode_string(const char* buffer, char** encoded_buffer, uint32_t* encoded_length) +void debugserver_encode_string(const char* buffer, char** encoded_buffer, uint32_t* encoded_length) { uint32_t position; uint32_t index; @@ -273,7 +291,7 @@ LIBIMOBILEDEVICE_API void debugserver_encode_string(const char* buffer, char** e } } -LIBIMOBILEDEVICE_API void debugserver_decode_string(const char *encoded_buffer, size_t encoded_length, char** buffer) +void debugserver_decode_string(const char *encoded_buffer, size_t encoded_length, char** buffer) { *buffer = malloc(sizeof(char) * ((encoded_length / 2)+1)); char* t = *buffer; @@ -332,7 +350,7 @@ static debugserver_error_t debugserver_client_send_noack(debugserver_client_t cl return debugserver_client_send(client, "-", sizeof(char), NULL); } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled) +debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled) { if (!client) return DEBUGSERVER_E_INVALID_ARG; @@ -344,119 +362,137 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_ack_mode(debugse return DEBUGSERVER_E_SUCCESS; } -static int debugserver_client_receive_internal_check(debugserver_client_t client, char* received_char) +debugserver_error_t debugserver_client_set_receive_params(debugserver_client_t client, int (*cancel_receive)(), int receive_loop_timeout) +{ + if (!client) + return DEBUGSERVER_E_INVALID_ARG; + + client->cancel_receive = cancel_receive; + client->receive_loop_timeout = receive_loop_timeout; + + debug_info("receive params: cancel_receive %s, receive_loop_timeout %dms", (client->cancel_receive == NULL ? "unset": "set"), client->receive_loop_timeout); + + return DEBUGSERVER_E_SUCCESS; +} + +static debugserver_error_t debugserver_client_receive_internal_char(debugserver_client_t client, char* received_char) { debugserver_error_t res = DEBUGSERVER_E_SUCCESS; - int did_receive_char = 0; - char buffer = 0; uint32_t bytes = 0; /* we loop here as we expect an answer */ - res = debugserver_client_receive_with_timeout(client, &buffer, sizeof(char), &bytes, 1000); - if (res == DEBUGSERVER_E_SUCCESS && received_char[0] != 0) { - if (memcmp(&buffer, received_char, sizeof(char)) == 0) { - did_receive_char = 1; - } - } else { - did_receive_char = 0; + res = debugserver_client_receive(client, received_char, sizeof(char), &bytes); + if (res != DEBUGSERVER_E_SUCCESS) { + return res; } - - if (!did_receive_char) { - memcpy(received_char, &buffer, sizeof(char)); + if (bytes != 1) { + debug_info("received %d bytes when asking for %d!", bytes, sizeof(char)); + return DEBUGSERVER_E_UNKNOWN_ERROR; } - - return did_receive_char; + return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_response(debugserver_client_t client, char** response) +debugserver_error_t debugserver_client_receive_response(debugserver_client_t client, char** response, size_t* response_size) { debugserver_error_t res = DEBUGSERVER_E_SUCCESS; - int should_receive = 1; + char data = '\0'; int skip_prefix = 0; - char* command_prefix = strdup("$"); - char* buffer = NULL; + char* buffer = malloc(1024); uint32_t buffer_size = 0; + uint32_t buffer_capacity = 1024; if (response) *response = NULL; if (!client->noack_mode) { - char ack[2] = {'+', '\0'}; - debug_info("attempting to receive ACK %c", *ack); - should_receive = debugserver_client_receive_internal_check(client, ack); - debug_info("received char: %c", *ack); - if (strncmp(ack, command_prefix, sizeof(char)) == 0) { - should_receive = 1; + debug_info("attempting to receive ACK (+)"); + res = debugserver_client_receive_internal_char(client, &data); + if (res != DEBUGSERVER_E_SUCCESS) { + goto cleanup; + } + if (data == '+') { + debug_info("received ACK (+)"); + } else if (data == '$') { + debug_info("received prefix ($)"); + buffer[0] = '$'; + buffer_size = 1; skip_prefix = 1; - buffer = strdup(command_prefix); - buffer_size += sizeof(char); - debug_info("received ACK"); + } else { + debug_info("unrecognized response when looking for ACK: %c", data); + goto cleanup; } } - debug_info("should_receive: %d, skip_prefix: %d", should_receive, skip_prefix); + debug_info("skip_prefix: %d", skip_prefix); - if (should_receive && !skip_prefix) { - debug_info("attempting to receive prefix"); - should_receive = debugserver_client_receive_internal_check(client, command_prefix); - debug_info("received command_prefix: %c", *command_prefix); - if (should_receive) { - if (buffer) { - memcpy(buffer, command_prefix, sizeof(char)); - } else { - buffer = strdup(command_prefix); - buffer_size += sizeof(char); - } + if (!skip_prefix) { + debug_info("attempting to receive prefix ($)"); + res = debugserver_client_receive_internal_char(client, &data); + if (res != DEBUGSERVER_E_SUCCESS) { + goto cleanup; + } + if (data == '$') { + debug_info("received prefix ($)"); + buffer[0] = '$'; + buffer_size = 1; + } else { + debug_info("unrecognized response when looking for prefix: %c", data); + goto cleanup; } } - debug_info("buffer: %*s, should_receive: %d, skip_prefix: %d", buffer_size, buffer, should_receive, skip_prefix); + uint32_t checksum_length = DEBUGSERVER_CHECKSUM_HASH_LENGTH; + int receiving_checksum_response = 0; + debug_info("attempting to read up response until checksum"); - if (should_receive) { - uint32_t checksum_length = DEBUGSERVER_CHECKSUM_HASH_LENGTH; - int receiving_checksum_response = 0; - debug_info("attempting to read up response until checksum"); - while ((checksum_length > 0)) { - char data[2] = {'#', '\0'}; - if (debugserver_client_receive_internal_check(client, data)) { - receiving_checksum_response = 1; - } - if (receiving_checksum_response) { - checksum_length--; + while ((checksum_length > 0)) { + res = debugserver_client_receive_internal_char(client, &data); + if (res != DEBUGSERVER_E_SUCCESS) { + goto cleanup; + } + if (data == '#') { + receiving_checksum_response = 1; + } + if (receiving_checksum_response) { + checksum_length--; + } + if (buffer_size + 1 >= buffer_capacity) { + char* newbuffer = realloc(buffer, buffer_capacity+1024); + if (!newbuffer) { + return DEBUGSERVER_E_UNKNOWN_ERROR; } - char* newbuffer = string_concat(buffer, data, NULL); - buffer_size += sizeof(char); - free(buffer); - buffer = NULL; buffer = newbuffer; - newbuffer = NULL; + buffer_capacity += 1024; } - debug_info("validating response checksum..."); - int valid_response = debugserver_response_is_checksum_valid(buffer, buffer_size); - if (valid_response) { - if (response) { - /* assemble response string */ - uint32_t response_size = sizeof(char) * (buffer_size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1); - *response = (char*)malloc(response_size + 1); - memcpy(*response, buffer + 1, response_size); - (*response)[response_size] = '\0'; - } - if (!client->noack_mode) { - /* confirm valid command */ - debugserver_client_send_ack(client); - } - } else { - /* response was invalid */ - res = DEBUGSERVER_E_RESPONSE_ERROR; - if (!client->noack_mode) { - /* report invalid command */ - debugserver_client_send_noack(client); - } + buffer[buffer_size] = data; + buffer_size += sizeof(char); + } + debug_info("validating response checksum..."); + if (client->noack_mode || debugserver_response_is_checksum_valid(buffer, buffer_size)) { + if (response) { + /* assemble response string */ + uint32_t resp_size = sizeof(char) * (buffer_size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1); + *response = (char*)malloc(resp_size + 1); + memcpy(*response, buffer + 1, resp_size); + (*response)[resp_size] = '\0'; + if (response_size) *response_size = resp_size; + } + if (!client->noack_mode) { + /* confirm valid command */ + debugserver_client_send_ack(client); + } + } else { + /* response was invalid */ + res = DEBUGSERVER_E_RESPONSE_ERROR; + if (!client->noack_mode) { + /* report invalid command */ + debugserver_client_send_noack(client); } } +cleanup: if (response) { debug_info("response: %s", *response); } @@ -464,13 +500,10 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_receive_response(deb if (buffer) free(buffer); - if (command_prefix) - free(command_prefix); - return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send_command(debugserver_client_t client, debugserver_command_t command, char** response) +debugserver_error_t debugserver_client_send_command(debugserver_client_t client, debugserver_command_t command, char** response, size_t* response_size) { debugserver_error_t res = DEBUGSERVER_E_SUCCESS; int i; @@ -482,25 +515,15 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send_command(debugse char* command_arguments = NULL; /* concat all arguments */ - char* tmp = NULL; - char* newtmp = NULL; for (i = 0; i < command->argc; i++) { debug_info("argv[%d]: %s", i, command->argv[i]); - if (!tmp) { - tmp = strdup(command->argv[i]); - } else { - newtmp = string_concat(tmp, command->argv[i], NULL); - free(tmp); - tmp = newtmp; - } + command_arguments = string_append(command_arguments, command->argv[i], NULL); } - command_arguments = tmp; - tmp = NULL; debug_info("command_arguments(%d): %s", command->argc, command_arguments); /* encode command arguments, add checksum if required and assemble entire command */ - debugserver_format_command("$", command->name, command_arguments, !client->noack_mode, &send_buffer, &send_buffer_size); + debugserver_format_command("$", command->name, command_arguments, 1, &send_buffer, &send_buffer_size); debug_info("sending encoded command: %s", send_buffer); @@ -511,7 +534,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_send_command(debugse } /* receive response */ - res = debugserver_client_receive_response(client, response); + res = debugserver_client_receive_response(client, response, response_size); debug_info("response result: %d", res); if (res != DEBUGSERVER_E_SUCCESS) { goto cleanup; @@ -536,7 +559,7 @@ cleanup: return res; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_environment_hex_encoded(debugserver_client_t client, const char* env, char** response) +debugserver_error_t debugserver_client_set_environment_hex_encoded(debugserver_client_t client, const char* env, char** response) { if (!client || !env) return DEBUGSERVER_E_INVALID_ARG; @@ -547,7 +570,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_environment_hex_ debugserver_command_t command = NULL; debugserver_command_new("QEnvironmentHexEncoded:", 1, env_arg, &command); - result = debugserver_client_send_command(client, command, response); + result = debugserver_client_send_command(client, command, response, NULL); debugserver_command_free(command); free(env_tmp); @@ -555,21 +578,25 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_environment_hex_ return result; } -LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_argv(debugserver_client_t client, int argc, char* argv[], char** response) +debugserver_error_t debugserver_client_set_argv(debugserver_client_t client, int argc, char* argv[], char** response) { if (!client || !argc) return DEBUGSERVER_E_INVALID_ARG; debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR; char *pkt = NULL; - int pkt_len = 0; + size_t pkt_len = 0; int i = 0; /* calculate total length */ while (i < argc && argv && argv[i]) { char *prefix = NULL; - asprintf(&prefix, ",%d,%d,", (int)strlen(argv[i]) * 2, i); - pkt_len += (int)strlen(prefix) + (int)strlen(argv[i]) * 2; + int ret = asprintf(&prefix, ",%zu,%d,", strlen(argv[i]) * 2, i); + if (ret < 0 || prefix == NULL) { + debug_info("asprintf failed, out of memory?"); + return DEBUGSERVER_E_UNKNOWN_ERROR; + } + pkt_len += strlen(prefix) + strlen(argv[i]) * 2; free(prefix); i++; } @@ -586,17 +613,21 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_argv(debugserver char *prefix = NULL; char *m = NULL; - int arg_len = strlen(argv[i]); - int arg_hexlen = arg_len * 2; + size_t arg_len = strlen(argv[i]); + size_t arg_hexlen = arg_len * 2; - asprintf(&prefix, ",%d,%d,", arg_hexlen, i); + int ret = asprintf(&prefix, ",%zu,%d,", arg_hexlen, i); + if (ret < 0 || prefix == NULL) { + debug_info("asprintf failed, out of memory?"); + return DEBUGSERVER_E_UNKNOWN_ERROR; + } m = (char *) malloc(arg_hexlen); char *p = m; char *q = (char*)argv[i]; while (*q) { - *p++ = debugserver_int2hex(*q >> 4); - *p++ = debugserver_int2hex(*q & 0xf); + *p++ = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(*q); + *p++ = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(*q); q++; } @@ -616,7 +647,7 @@ LIBIMOBILEDEVICE_API debugserver_error_t debugserver_client_set_argv(debugserver debugserver_command_t command = NULL; debugserver_command_new(pkt, 0, NULL, &command); - result = debugserver_client_send_command(client, command, response); + result = debugserver_client_send_command(client, command, response, NULL); debugserver_command_free(command); if (pkt) |