summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/debugserver.c620
-rw-r--r--src/debugserver.h41
3 files changed, 662 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 287b4e0..e45437b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,6 +26,7 @@ libimobiledevice_la_SOURCES = idevice.c idevice.h \
26 restore.c restore.h\ 26 restore.c restore.h\
27 diagnostics_relay.c diagnostics_relay.h\ 27 diagnostics_relay.c diagnostics_relay.h\
28 heartbeat.c heartbeat.h\ 28 heartbeat.c heartbeat.h\
29 debugserver.c debugserver.h\
29 webinspector.c webinspector.h\ 30 webinspector.c webinspector.h\
30 syslog_relay.c syslog_relay.h 31 syslog_relay.c syslog_relay.h
31 32
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 */
46static 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
63debugserver_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
91debugserver_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
98debugserver_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
110debugserver_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
131debugserver_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
151debugserver_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
156debugserver_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
181debugserver_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
205static 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
217static 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
228static 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
240static 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
259void 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
275void 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
288static 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
322static 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
328static 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
334static 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
346static 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
370debugserver_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
471debugserver_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
526cleanup:
527 if (command_arguments)
528 free(command_arguments);
529
530 if (send_buffer)
531 free(send_buffer);
532
533 return res;
534}
535
536debugserver_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
552debugserver_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}
diff --git a/src/debugserver.h b/src/debugserver.h
new file mode 100644
index 0000000..8608167
--- /dev/null
+++ b/src/debugserver.h
@@ -0,0 +1,41 @@
1/*
2 * debugserver.h
3 * com.apple.debugserver service header file.
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#ifndef _DEBUGSERVER_H
23#define _DEBUGSERVER_H
24
25#include "libimobiledevice/debugserver.h"
26#include "service.h"
27
28#define DEBUGSERVER_CHECKSUM_HASH_LENGTH 0x3
29
30struct debugserver_client_private {
31 service_client_t parent;
32 int noack_mode;
33};
34
35struct debugserver_command_private {
36 char* name;
37 int argc;
38 char** argv;
39};
40
41#endif