diff options
Diffstat (limited to 'src/debugserver.c')
| -rw-r--r-- | src/debugserver.c | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/src/debugserver.c b/src/debugserver.c new file mode 100644 index 0000000..1e38698 --- /dev/null +++ b/src/debugserver.c | |||
| @@ -0,0 +1,620 @@ | |||
| 1 | /* | ||
| 2 | * debugserver.c | ||
| 3 | * com.apple.debugserver service implementation. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2014 Martin Szulecki 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 | #ifdef HAVE_CONFIG_H | ||
| 23 | #include <config.h> | ||
| 24 | #endif | ||
| 25 | #include <string.h> | ||
| 26 | #include <stdlib.h> | ||
| 27 | #define _GNU_SOURCE 1 | ||
| 28 | #define __USE_GNU 1 | ||
| 29 | #include <stdio.h> | ||
| 30 | |||
| 31 | #include "debugserver.h" | ||
| 32 | #include "lockdown.h" | ||
| 33 | #include "common/debug.h" | ||
| 34 | #include "common/utils.h" | ||
| 35 | #include "asprintf.h" | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Convert a service_error_t value to a debugserver_error_t value. | ||
| 39 | * Used internally to get correct error codes. | ||
| 40 | * | ||
| 41 | * @param err An service_error_t error code | ||
| 42 | * | ||
| 43 | * @return A matching debugserver_error_t error code, | ||
| 44 | * DEBUGSERVER_E_UNKNOWN_ERROR otherwise. | ||
| 45 | */ | ||
| 46 | static debugserver_error_t debugserver_error(service_error_t err) | ||
| 47 | { | ||
| 48 | switch (err) { | ||
| 49 | case SERVICE_E_SUCCESS: | ||
| 50 | return DEBUGSERVER_E_SUCCESS; | ||
| 51 | case SERVICE_E_INVALID_ARG: | ||
| 52 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 53 | case SERVICE_E_MUX_ERROR: | ||
| 54 | return DEBUGSERVER_E_MUX_ERROR; | ||
| 55 | case SERVICE_E_SSL_ERROR: | ||
| 56 | return DEBUGSERVER_E_SSL_ERROR; | ||
| 57 | default: | ||
| 58 | break; | ||
| 59 | } | ||
| 60 | return DEBUGSERVER_E_UNKNOWN_ERROR; | ||
| 61 | } | ||
| 62 | |||
| 63 | debugserver_error_t debugserver_client_new(idevice_t device, lockdownd_service_descriptor_t service, debugserver_client_t* client) | ||
| 64 | { | ||
| 65 | *client = NULL; | ||
| 66 | |||
| 67 | if (!device || !service || service->port == 0 || !client || *client) { | ||
| 68 | debug_info("Incorrect parameter passed to debugserver_client_new."); | ||
| 69 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 70 | } | ||
| 71 | |||
| 72 | debug_info("Creating debugserver_client, port = %d.", service->port); | ||
| 73 | |||
| 74 | service_client_t parent = NULL; | ||
| 75 | debugserver_error_t ret = debugserver_error(service_client_new(device, service, &parent)); | ||
| 76 | if (ret != DEBUGSERVER_E_SUCCESS) { | ||
| 77 | debug_info("Creating base service client failed. Error: %i", ret); | ||
| 78 | return ret; | ||
| 79 | } | ||
| 80 | |||
| 81 | debugserver_client_t client_loc = (debugserver_client_t) malloc(sizeof(struct debugserver_client_private)); | ||
| 82 | client_loc->parent = parent; | ||
| 83 | client_loc->noack_mode = 0; | ||
| 84 | |||
| 85 | *client = client_loc; | ||
| 86 | |||
| 87 | debug_info("debugserver_client successfully created."); | ||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | debugserver_error_t debugserver_client_start_service(idevice_t device, debugserver_client_t * client, const char* label) | ||
| 92 | { | ||
| 93 | debugserver_error_t err = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
| 94 | service_client_factory_start_service(device, DEBUGSERVER_SERVICE_NAME, (void**)client, label, SERVICE_CONSTRUCTOR(debugserver_client_new), &err); | ||
| 95 | return err; | ||
| 96 | } | ||
| 97 | |||
| 98 | debugserver_error_t debugserver_client_free(debugserver_client_t client) | ||
| 99 | { | ||
| 100 | if (!client) | ||
| 101 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 102 | |||
| 103 | debugserver_error_t err = debugserver_error(service_client_free(client->parent)); | ||
| 104 | client->parent = NULL; | ||
| 105 | free(client); | ||
| 106 | |||
| 107 | return err; | ||
| 108 | } | ||
| 109 | |||
| 110 | debugserver_error_t debugserver_client_send(debugserver_client_t client, const char* data, uint32_t size, uint32_t *sent) | ||
| 111 | { | ||
| 112 | debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
| 113 | int bytes = 0; | ||
| 114 | |||
| 115 | if (!client || !data || (size == 0)) { | ||
| 116 | return SERVICE_E_INVALID_ARG; | ||
| 117 | } | ||
| 118 | |||
| 119 | debug_info("sending %d bytes", size); | ||
| 120 | res = debugserver_error(service_send(client->parent, data, size, (uint32_t*)&bytes)); | ||
| 121 | if (bytes <= 0) { | ||
| 122 | debug_info("ERROR: sending to device failed."); | ||
| 123 | } | ||
| 124 | if (sent) { | ||
| 125 | *sent = (uint32_t)bytes; | ||
| 126 | } | ||
| 127 | |||
| 128 | return res; | ||
| 129 | } | ||
| 130 | |||
| 131 | debugserver_error_t debugserver_client_receive_with_timeout(debugserver_client_t client, char* data, uint32_t size, uint32_t *received, unsigned int timeout) | ||
| 132 | { | ||
| 133 | debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
| 134 | int bytes = 0; | ||
| 135 | |||
| 136 | if (!client || !data || (size == 0)) { | ||
| 137 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 138 | } | ||
| 139 | |||
| 140 | res = debugserver_error(service_receive_with_timeout(client->parent, data, size, (uint32_t*)&bytes, timeout)); | ||
| 141 | if (bytes <= 0) { | ||
| 142 | debug_info("Could not read data, error %d", res); | ||
| 143 | } | ||
| 144 | if (received) { | ||
| 145 | *received = (uint32_t)bytes; | ||
| 146 | } | ||
| 147 | |||
| 148 | return res; | ||
| 149 | } | ||
| 150 | |||
| 151 | debugserver_error_t debugserver_client_receive(debugserver_client_t client, char* data, uint32_t size, uint32_t *received) | ||
| 152 | { | ||
| 153 | return debugserver_client_receive_with_timeout(client, data, size, received, 1000); | ||
| 154 | } | ||
| 155 | |||
| 156 | debugserver_error_t debugserver_command_new(const char* name, int argc, const char* argv[], debugserver_command_t* command) | ||
| 157 | { | ||
| 158 | int i; | ||
| 159 | debugserver_command_t tmp = (debugserver_command_t) malloc(sizeof(struct debugserver_command_private)); | ||
| 160 | |||
| 161 | /* copy name */ | ||
| 162 | tmp->name = strdup(name); | ||
| 163 | |||
| 164 | /* copy arguments */ | ||
| 165 | tmp->argc = argc; | ||
| 166 | tmp->argv = NULL; | ||
| 167 | if (argc > 0) { | ||
| 168 | tmp->argv = malloc(sizeof(char*) * (argc + 2)); | ||
| 169 | for (i = 0; i < argc; i++) { | ||
| 170 | tmp->argv[i] = strdup(argv[i]); | ||
| 171 | } | ||
| 172 | tmp->argv[i+1] = NULL; | ||
| 173 | } | ||
| 174 | |||
| 175 | /* return */ | ||
| 176 | *command = tmp; | ||
| 177 | |||
| 178 | return DEBUGSERVER_E_SUCCESS; | ||
| 179 | } | ||
| 180 | |||
| 181 | debugserver_error_t debugserver_command_free(debugserver_command_t command) | ||
| 182 | { | ||
| 183 | int i; | ||
| 184 | debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
| 185 | |||
| 186 | if (!command) | ||
| 187 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 188 | |||
| 189 | if (command) { | ||
| 190 | if (command->name) | ||
| 191 | free(command->name); | ||
| 192 | if (command->argv && command->argc) { | ||
| 193 | for (i = 0; i < command->argc; i++) { | ||
| 194 | free(command->argv[i]); | ||
| 195 | } | ||
| 196 | free(command->argv); | ||
| 197 | } | ||
| 198 | free(command); | ||
| 199 | res = DEBUGSERVER_E_SUCCESS; | ||
| 200 | } | ||
| 201 | |||
| 202 | return res; | ||
| 203 | } | ||
| 204 | |||
| 205 | static int debugserver_hex2int(char c) | ||
| 206 | { | ||
| 207 | if (c >= '0' && c <= '9') | ||
| 208 | return c - '0'; | ||
| 209 | else if (c >= 'a' && c <= 'f') | ||
| 210 | return 10 + c - 'a'; | ||
| 211 | else if (c >= 'A' && c <= 'F') | ||
| 212 | return 10 + c - 'A'; | ||
| 213 | else | ||
| 214 | return c; | ||
| 215 | } | ||
| 216 | |||
| 217 | static char debugserver_int2hex(int x) | ||
| 218 | { | ||
| 219 | const char *hexchars = "0123456789ABCDEF"; | ||
| 220 | return hexchars[x]; | ||
| 221 | } | ||
| 222 | |||
| 223 | #define DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(byte) debugserver_int2hex((byte >> 0x4) & 0xf) | ||
| 224 | #define DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(byte) debugserver_int2hex(byte & 0xf) | ||
| 225 | #define DEBUGSERVER_HEX_DECODE_FIRST_BYTE(byte) ((byte >> 0x4) & 0xf) | ||
| 226 | #define DEBUGSERVER_HEX_DECODE_SECOND_BYTE(byte) (byte & 0xf) | ||
| 227 | |||
| 228 | static uint32_t debugserver_get_checksum_for_buffer(const char* buffer, uint32_t size) | ||
| 229 | { | ||
| 230 | uint32_t checksum = 0; | ||
| 231 | uint32_t i; | ||
| 232 | |||
| 233 | for (i = 0; i < size; i++) { | ||
| 234 | checksum += buffer[i]; | ||
| 235 | } | ||
| 236 | |||
| 237 | return checksum; | ||
| 238 | } | ||
| 239 | |||
| 240 | static int debugserver_response_is_checksum_valid(const char* response, uint32_t size) | ||
| 241 | { | ||
| 242 | uint32_t checksum = 0; | ||
| 243 | if ((size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1) > 0) | ||
| 244 | checksum = debugserver_get_checksum_for_buffer(&response[1], size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1); | ||
| 245 | |||
| 246 | debug_info("checksum: 0x%x", checksum); | ||
| 247 | |||
| 248 | if ((unsigned)debugserver_hex2int(response[size - 2]) != DEBUGSERVER_HEX_DECODE_FIRST_BYTE(checksum)) | ||
| 249 | return 0; | ||
| 250 | |||
| 251 | if ((unsigned)debugserver_hex2int(response[size - 1]) != DEBUGSERVER_HEX_DECODE_SECOND_BYTE(checksum)) | ||
| 252 | return 0; | ||
| 253 | |||
| 254 | debug_info("valid checksum"); | ||
| 255 | |||
| 256 | return 1; | ||
| 257 | } | ||
| 258 | |||
| 259 | void debugserver_encode_string(const char* buffer, char** encoded_buffer, uint32_t* encoded_length) | ||
| 260 | { | ||
| 261 | uint32_t position; | ||
| 262 | uint32_t index; | ||
| 263 | uint32_t length = strlen(buffer); | ||
| 264 | *encoded_length = (2 * length) + DEBUGSERVER_CHECKSUM_HASH_LENGTH + 1; | ||
| 265 | |||
| 266 | *encoded_buffer = malloc(sizeof(char) * (*encoded_length)); | ||
| 267 | memset(*encoded_buffer, '\0', *encoded_length); | ||
| 268 | for (position = 0, index = 0; index < length; index++) { | ||
| 269 | position = (index * (2 * sizeof(char))); | ||
| 270 | (*encoded_buffer)[position] = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(buffer[index]); | ||
| 271 | (*encoded_buffer)[position + 1] = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(buffer[index]); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | void debugserver_decode_string(const char *encoded_buffer, size_t encoded_length, char** buffer) | ||
| 276 | { | ||
| 277 | *buffer = malloc(sizeof(char) * ((encoded_length / 2)+1)); | ||
| 278 | char* t = *buffer; | ||
| 279 | const char *f = encoded_buffer; | ||
| 280 | const char *fend = f + encoded_length; | ||
| 281 | while (f < fend) { | ||
| 282 | *t++ = debugserver_hex2int(*f) << 4 | debugserver_hex2int(f[1]); | ||
| 283 | f += 2; | ||
| 284 | } | ||
| 285 | *t = '\0'; | ||
| 286 | } | ||
| 287 | |||
| 288 | static void debugserver_format_command(const char* prefix, const char* command, const char* arguments, int calculate_checksum, char** buffer, uint32_t* size) | ||
| 289 | { | ||
| 290 | char checksum_hash[DEBUGSERVER_CHECKSUM_HASH_LENGTH + 1] = {'#', '0', '0', '\0'}; | ||
| 291 | char* encoded = NULL; | ||
| 292 | uint32_t encoded_length = 0; | ||
| 293 | |||
| 294 | if (arguments) { | ||
| 295 | /* arguments must be hex encoded */ | ||
| 296 | debugserver_encode_string(arguments, &encoded, &encoded_length); | ||
| 297 | } else { | ||
| 298 | encoded = NULL; | ||
| 299 | } | ||
| 300 | |||
| 301 | char* encoded_command = string_concat(command, encoded, NULL); | ||
| 302 | encoded_length = strlen(encoded_command); | ||
| 303 | |||
| 304 | if (calculate_checksum) { | ||
| 305 | uint32_t checksum = debugserver_get_checksum_for_buffer(encoded_command, encoded_length); | ||
| 306 | checksum_hash[1] = DEBUGSERVER_HEX_ENCODE_FIRST_BYTE(checksum); | ||
| 307 | checksum_hash[2] = DEBUGSERVER_HEX_ENCODE_SECOND_BYTE(checksum); | ||
| 308 | } | ||
| 309 | |||
| 310 | *buffer = string_concat(prefix, encoded_command, checksum_hash, NULL); | ||
| 311 | *size = strlen(prefix) + strlen(encoded_command) + DEBUGSERVER_CHECKSUM_HASH_LENGTH; | ||
| 312 | |||
| 313 | debug_info("formatted command: %s size: %d checksum: 0x%s", *buffer, *size, checksum_hash); | ||
| 314 | |||
| 315 | if (encoded_command) | ||
| 316 | free(encoded_command); | ||
| 317 | |||
| 318 | if (encoded) | ||
| 319 | free(encoded); | ||
| 320 | } | ||
| 321 | |||
| 322 | static debugserver_error_t debugserver_client_send_ack(debugserver_client_t client) | ||
| 323 | { | ||
| 324 | debug_info("sending ACK"); | ||
| 325 | return debugserver_client_send(client, "+", sizeof(char), NULL); | ||
| 326 | } | ||
| 327 | |||
| 328 | static debugserver_error_t debugserver_client_send_noack(debugserver_client_t client) | ||
| 329 | { | ||
| 330 | debug_info("sending !ACK"); | ||
| 331 | return debugserver_client_send(client, "-", sizeof(char), NULL); | ||
| 332 | } | ||
| 333 | |||
| 334 | static debugserver_error_t debugserver_client_set_ack_mode(debugserver_client_t client, int enabled) | ||
| 335 | { | ||
| 336 | if (!client) | ||
| 337 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 338 | |||
| 339 | client->noack_mode = (enabled == 0)? 1: 0; | ||
| 340 | |||
| 341 | debug_info("ack mode: %s", client->noack_mode == 0 ? "on": "off"); | ||
| 342 | |||
| 343 | return DEBUGSERVER_E_SUCCESS; | ||
| 344 | } | ||
| 345 | |||
| 346 | static int debugserver_client_receive_internal_check(debugserver_client_t client, char* received_char) | ||
| 347 | { | ||
| 348 | debugserver_error_t res = DEBUGSERVER_E_SUCCESS; | ||
| 349 | int did_receive_char = 0; | ||
| 350 | char buffer = 0; | ||
| 351 | uint32_t bytes = 0; | ||
| 352 | |||
| 353 | /* we loop here as we expect an answer */ | ||
| 354 | res = debugserver_client_receive_with_timeout(client, &buffer, sizeof(char), &bytes, 1000); | ||
| 355 | if (res == DEBUGSERVER_E_SUCCESS && received_char[0] != 0) { | ||
| 356 | if (memcmp(&buffer, received_char, sizeof(char)) == 0) { | ||
| 357 | did_receive_char = 1; | ||
| 358 | } | ||
| 359 | } else { | ||
| 360 | did_receive_char = 0; | ||
| 361 | } | ||
| 362 | |||
| 363 | if (!did_receive_char) { | ||
| 364 | memcpy(received_char, &buffer, sizeof(char)); | ||
| 365 | } | ||
| 366 | |||
| 367 | return did_receive_char; | ||
| 368 | } | ||
| 369 | |||
| 370 | debugserver_error_t debugserver_client_receive_response(debugserver_client_t client, char** response) | ||
| 371 | { | ||
| 372 | debugserver_error_t res = DEBUGSERVER_E_SUCCESS; | ||
| 373 | |||
| 374 | int should_receive = 1; | ||
| 375 | int skip_prefix = 0; | ||
| 376 | char* command_prefix = strdup("$"); | ||
| 377 | |||
| 378 | char* buffer = NULL; | ||
| 379 | uint32_t buffer_size = 0; | ||
| 380 | |||
| 381 | if (response) | ||
| 382 | *response = NULL; | ||
| 383 | |||
| 384 | if (!client->noack_mode) { | ||
| 385 | char ack[2] = {'+', '\0'}; | ||
| 386 | debug_info("attempting to receive ACK %c", *ack); | ||
| 387 | should_receive = debugserver_client_receive_internal_check(client, ack); | ||
| 388 | debug_info("received char: %c", *ack); | ||
| 389 | if (strncmp(ack, command_prefix, sizeof(char)) == 0) { | ||
| 390 | should_receive = 1; | ||
| 391 | skip_prefix = 1; | ||
| 392 | buffer = strdup(command_prefix); | ||
| 393 | buffer_size += sizeof(char); | ||
| 394 | debug_info("received ACK"); | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | debug_info("should_receive: %d, skip_prefix: %d", should_receive, skip_prefix); | ||
| 399 | |||
| 400 | if (should_receive && !skip_prefix) { | ||
| 401 | debug_info("attempting to receive prefix"); | ||
| 402 | should_receive = debugserver_client_receive_internal_check(client, command_prefix); | ||
| 403 | debug_info("received command_prefix: %c", *command_prefix); | ||
| 404 | if (should_receive) { | ||
| 405 | if (buffer) { | ||
| 406 | memcpy(buffer, command_prefix, sizeof(char)); | ||
| 407 | } else { | ||
| 408 | buffer = strdup(command_prefix); | ||
| 409 | buffer_size += sizeof(char); | ||
| 410 | } | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | debug_info("buffer: %*s, should_receive: %d, skip_prefix: %d", buffer_size, buffer, should_receive, skip_prefix); | ||
| 415 | |||
| 416 | if (should_receive) { | ||
| 417 | uint32_t checksum_length = DEBUGSERVER_CHECKSUM_HASH_LENGTH; | ||
| 418 | int receiving_checksum_response = 0; | ||
| 419 | debug_info("attempting to read up response until checksum"); | ||
| 420 | while ((checksum_length > 0)) { | ||
| 421 | char data[2] = {'#', '\0'}; | ||
| 422 | if (debugserver_client_receive_internal_check(client, data)) { | ||
| 423 | receiving_checksum_response = 1; | ||
| 424 | } | ||
| 425 | if (receiving_checksum_response) { | ||
| 426 | checksum_length--; | ||
| 427 | } | ||
| 428 | char* newbuffer = string_concat(buffer, data, NULL); | ||
| 429 | buffer_size += sizeof(char); | ||
| 430 | free(buffer); | ||
| 431 | buffer = NULL; | ||
| 432 | buffer = newbuffer; | ||
| 433 | newbuffer = NULL; | ||
| 434 | } | ||
| 435 | debug_info("validating response checksum..."); | ||
| 436 | int valid_response = debugserver_response_is_checksum_valid(buffer, buffer_size); | ||
| 437 | if (valid_response) { | ||
| 438 | if (response) { | ||
| 439 | /* assemble response string */ | ||
| 440 | uint32_t response_size = sizeof(char) * (buffer_size - DEBUGSERVER_CHECKSUM_HASH_LENGTH - 1); | ||
| 441 | *response = (char*)malloc(response_size + 1); | ||
| 442 | memcpy(*response, buffer + 1, response_size); | ||
| 443 | (*response)[response_size] = '\0'; | ||
| 444 | } | ||
| 445 | if (!client->noack_mode) { | ||
| 446 | /* confirm valid command */ | ||
| 447 | debugserver_client_send_ack(client); | ||
| 448 | } | ||
| 449 | } else { | ||
| 450 | /* response was invalid */ | ||
| 451 | res = DEBUGSERVER_E_RESPONSE_ERROR; | ||
| 452 | if (!client->noack_mode) { | ||
| 453 | /* report invalid command */ | ||
| 454 | debugserver_client_send_noack(client); | ||
| 455 | } | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | if (response) | ||
| 460 | debug_info("response: %s", *response); | ||
| 461 | |||
| 462 | if (buffer) | ||
| 463 | free(buffer); | ||
| 464 | |||
| 465 | if (command_prefix) | ||
| 466 | free(command_prefix); | ||
| 467 | |||
| 468 | return res; | ||
| 469 | } | ||
| 470 | |||
| 471 | debugserver_error_t debugserver_client_send_command(debugserver_client_t client, debugserver_command_t command, char** response) | ||
| 472 | { | ||
| 473 | debugserver_error_t res = DEBUGSERVER_E_SUCCESS; | ||
| 474 | int i; | ||
| 475 | uint32_t bytes = 0; | ||
| 476 | |||
| 477 | char* send_buffer = NULL; | ||
| 478 | uint32_t send_buffer_size = 0; | ||
| 479 | |||
| 480 | char* command_arguments = NULL; | ||
| 481 | |||
| 482 | /* concat all arguments */ | ||
| 483 | char* tmp = NULL; | ||
| 484 | char* newtmp = NULL; | ||
| 485 | for (i = 0; i < command->argc; i++) { | ||
| 486 | debug_info("argv[%d]: %s", i, command->argv[i]); | ||
| 487 | if (!tmp) { | ||
| 488 | tmp = strdup(command->argv[i]); | ||
| 489 | } else { | ||
| 490 | newtmp = string_concat(tmp, command->argv[i], NULL); | ||
| 491 | free(tmp); | ||
| 492 | tmp = newtmp; | ||
| 493 | } | ||
| 494 | } | ||
| 495 | command_arguments = tmp; | ||
| 496 | tmp = NULL; | ||
| 497 | |||
| 498 | debug_info("command_arguments(%d): %s", command->argc, command_arguments); | ||
| 499 | |||
| 500 | /* encode command arguments, add checksum if required and assemble entire command */ | ||
| 501 | debugserver_format_command("$", command->name, command_arguments, !client->noack_mode, &send_buffer, &send_buffer_size); | ||
| 502 | |||
| 503 | debug_info("sending encoded command: %s", send_buffer); | ||
| 504 | |||
| 505 | res = debugserver_client_send(client, send_buffer, send_buffer_size, &bytes); | ||
| 506 | debug_info("command result: %d", res); | ||
| 507 | if (res != DEBUGSERVER_E_SUCCESS) { | ||
| 508 | goto cleanup; | ||
| 509 | } | ||
| 510 | |||
| 511 | /* receive response */ | ||
| 512 | res = debugserver_client_receive_response(client, response); | ||
| 513 | debug_info("response result: %d", res); | ||
| 514 | if (res != DEBUGSERVER_E_SUCCESS) { | ||
| 515 | goto cleanup; | ||
| 516 | } | ||
| 517 | |||
| 518 | if (response) | ||
| 519 | debug_info("received response: %s", *response); | ||
| 520 | |||
| 521 | /* disable sending ack on the client */ | ||
| 522 | if (!strncmp(command->name, "QStartNoAckMode", 16)) { | ||
| 523 | debugserver_client_set_ack_mode(client, 0); | ||
| 524 | } | ||
| 525 | |||
| 526 | cleanup: | ||
| 527 | if (command_arguments) | ||
| 528 | free(command_arguments); | ||
| 529 | |||
| 530 | if (send_buffer) | ||
| 531 | free(send_buffer); | ||
| 532 | |||
| 533 | return res; | ||
| 534 | } | ||
| 535 | |||
| 536 | debugserver_error_t debugserver_client_set_environment_hex_encoded(debugserver_client_t client, const char* env, char** response) | ||
| 537 | { | ||
| 538 | if (!client || !env) | ||
| 539 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 540 | |||
| 541 | debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
| 542 | const char* env_arg[] = { env, NULL }; | ||
| 543 | |||
| 544 | debugserver_command_t command = NULL; | ||
| 545 | debugserver_command_new("QEnvironmentHexEncoded:", 1, env_arg, &command); | ||
| 546 | result = debugserver_client_send_command(client, command, response); | ||
| 547 | debugserver_command_free(command); | ||
| 548 | |||
| 549 | return result; | ||
| 550 | } | ||
| 551 | |||
| 552 | debugserver_error_t debugserver_client_set_argv(debugserver_client_t client, int argc, char* argv[], char** response) | ||
| 553 | { | ||
| 554 | if (!client || !argc) | ||
| 555 | return DEBUGSERVER_E_INVALID_ARG; | ||
| 556 | |||
| 557 | debugserver_error_t result = DEBUGSERVER_E_UNKNOWN_ERROR; | ||
| 558 | char *pkt = NULL; | ||
| 559 | int pkt_len = 0; | ||
| 560 | int i = 0; | ||
| 561 | |||
| 562 | /* calculate total length */ | ||
| 563 | while (i < argc && argv && argv[i]) { | ||
| 564 | char *prefix = NULL; | ||
| 565 | asprintf(&prefix, ",%d,%d,", (int)strlen(argv[i]) * 2, i); | ||
| 566 | pkt_len += (int)strlen(prefix) + (int)strlen(argv[i]) * 2; | ||
| 567 | free(prefix); | ||
| 568 | i++; | ||
| 569 | } | ||
| 570 | |||
| 571 | /* allocate packet and initialize it */ | ||
| 572 | pkt = (char *) malloc(pkt_len + 1); | ||
| 573 | memset(pkt, 0, pkt_len + 1); | ||
| 574 | |||
| 575 | char *pktp = pkt; | ||
| 576 | |||
| 577 | i = 0; | ||
| 578 | while (i < argc && argv && argv[i]) { | ||
| 579 | debug_info("argv[%d] = \"%s\"", i, argv[i]); | ||
| 580 | |||
| 581 | char *prefix = NULL; | ||
| 582 | char *m = NULL; | ||
| 583 | int arg_len = strlen(argv[i]); | ||
| 584 | int arg_hexlen = arg_len * 2; | ||
| 585 | |||
| 586 | asprintf(&prefix, ",%d,%d,", arg_hexlen, i); | ||
| 587 | |||
| 588 | m = (char *) malloc(arg_hexlen); | ||
| 589 | char *p = m; | ||
| 590 | char *q = (char*)argv[i]; | ||
| 591 | while (*q) { | ||
| 592 | *p++ = debugserver_int2hex(*q >> 4); | ||
| 593 | *p++ = debugserver_int2hex(*q & 0xf); | ||
| 594 | q++; | ||
| 595 | } | ||
| 596 | |||
| 597 | memcpy(pktp, prefix, strlen(prefix)); | ||
| 598 | pktp += strlen(prefix); | ||
| 599 | |||
| 600 | memcpy(pktp, m, arg_hexlen); | ||
| 601 | pktp += arg_hexlen; | ||
| 602 | |||
| 603 | free(prefix); | ||
| 604 | free(m); | ||
| 605 | |||
| 606 | i++; | ||
| 607 | } | ||
| 608 | |||
| 609 | pkt[0] = 'A'; | ||
| 610 | |||
| 611 | debugserver_command_t command = NULL; | ||
| 612 | debugserver_command_new(pkt, 0, NULL, &command); | ||
| 613 | result = debugserver_client_send_command(client, command, response); | ||
| 614 | debugserver_command_free(command); | ||
| 615 | |||
| 616 | if (pkt) | ||
| 617 | free(pkt); | ||
| 618 | |||
| 619 | return result; | ||
| 620 | } | ||
