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