summaryrefslogtreecommitdiffstats
path: root/tools/idevicedebugserverproxy.c
diff options
context:
space:
mode:
authorGravatar Martin Szulecki2012-09-04 00:36:40 +0200
committerGravatar Martin Szulecki2012-10-21 14:19:50 +0200
commitd09e25b69d2744ba7a6a88cc58d9cc8fc4810d2a (patch)
treee9caed03fb33d4eb3eadda59fdaa89cb0041471e /tools/idevicedebugserverproxy.c
parentb3a679c7c6a2ab30aaac27abd79428b557511bf2 (diff)
downloadlibimobiledevice-d09e25b69d2744ba7a6a88cc58d9cc8fc4810d2a.tar.gz
libimobiledevice-d09e25b69d2744ba7a6a88cc58d9cc8fc4810d2a.tar.bz2
Add new idevicedebugserverproxy tool
Diffstat (limited to 'tools/idevicedebugserverproxy.c')
-rw-r--r--tools/idevicedebugserverproxy.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/tools/idevicedebugserverproxy.c b/tools/idevicedebugserverproxy.c
new file mode 100644
index 0000000..4487647
--- /dev/null
+++ b/tools/idevicedebugserverproxy.c
@@ -0,0 +1,374 @@
1/*
2 * idevicedebugserverproxy.c
3 * Proxy a debugserver connection from device for remote debugging
4 *
5 * Copyright (c) 2012 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#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <errno.h>
26#include <signal.h>
27
28#include <libimobiledevice/libimobiledevice.h>
29#include <libimobiledevice/lockdown.h>
30
31#include "socket.h"
32#include "thread.h"
33
34#define info(...) fprintf(stdout, __VA_ARGS__); fflush(stdout)
35#define debug(...) if(debug_mode) fprintf(stdout, __VA_ARGS__)
36
37static int debug_mode = 0;
38static int quit_flag = 0;
39
40typedef struct {
41 int server_fd;
42 int client_fd;
43 uint16_t local_port;
44 uint16_t remote_port;
45 idevice_connection_t device_connection;
46 volatile int stop_ctod;
47 volatile int stop_dtoc;
48} socket_info_t;
49
50static void clean_exit(int sig)
51{
52 fprintf(stderr, "Exiting...\n");
53 quit_flag++;
54}
55
56static void print_usage(int argc, char **argv)
57{
58 char *name = NULL;
59
60 name = strrchr(argv[0], '/');
61 printf("Usage: %s [OPTIONS] <PORT>\n", (name ? name + 1: argv[0]));
62 printf("Proxy debugserver connection from device to a local socket at PORT.\n\n");
63 printf(" -d, --debug\t\tenable communication debugging\n");
64 printf(" -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n");
65 printf(" -h, --help\t\tprints usage information\n");
66 printf("\n");
67}
68
69static void *thread_device_to_client(void *data)
70{
71 socket_info_t* socket_info = (socket_info_t*)data;
72 idevice_error_t res = IDEVICE_E_UNKNOWN_ERROR;
73
74 int recv_len;
75 int sent;
76 char buffer[131072];
77
78 debug("%s: started thread...\n", __func__);
79
80 debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
81 debug("%s: server fd = %d\n", __func__, socket_info->server_fd);
82
83 while (!quit_flag && !socket_info->stop_dtoc && socket_info->client_fd > 0 && socket_info->server_fd > 0) {
84 debug("%s: receiving data from device...\n", __func__);
85
86 res = idevice_connection_receive_timeout(socket_info->device_connection, buffer, sizeof(buffer), (uint32_t*)&recv_len, 5000);
87
88 if (recv_len <= 0) {
89 if (recv_len == 0 && res == IDEVICE_E_SUCCESS) {
90 // try again
91 continue;
92 } else {
93 fprintf(stderr, "recv failed: %s\n", strerror(errno));
94 break;
95 }
96 } else {
97 /* send to device */
98 debug("%s: sending data to device...\n", __func__);
99 sent = socket_send(socket_info->client_fd, buffer, recv_len);
100 if (sent < recv_len) {
101 if (sent <= 0) {
102 fprintf(stderr, "send failed: %s\n", strerror(errno));
103 break;
104 } else {
105 fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
106 }
107 } else {
108 // sending succeeded, receive from device
109 debug("%s: pushed %d bytes to client\n", __func__, sent);
110 }
111 }
112 }
113
114 debug("%s: shutting down...\n", __func__);
115
116 socket_shutdown(socket_info->client_fd, SHUT_RDWR);
117 socket_close(socket_info->client_fd);
118
119 socket_info->client_fd = -1;
120 socket_info->stop_ctod = 1;
121
122 return NULL;
123}
124
125static void *thread_client_to_device(void *data)
126{
127 socket_info_t* socket_info = (socket_info_t*)data;
128 idevice_error_t res = IDEVICE_E_UNKNOWN_ERROR;
129
130 int recv_len;
131 int sent;
132 char buffer[131072];
133 thread_t dtoc;
134
135 debug("%s: started thread...\n", __func__);
136
137 debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
138 debug("%s: server_fd = %d\n", __func__, socket_info->server_fd);
139
140 /* spawn server to client thread */
141 socket_info->stop_dtoc = 0;
142 if (thread_create(&dtoc, thread_device_to_client, data) != 0) {
143 fprintf(stderr, "Failed to start device to client thread...\n");
144 }
145
146 while (!quit_flag && !socket_info->stop_ctod && socket_info->client_fd > 0 && socket_info->server_fd > 0) {
147 debug("%s: receiving data from client...\n", __func__);
148
149 /* attempt to read incoming data from client */
150 recv_len = socket_receive_timeout(socket_info->client_fd, buffer, sizeof(buffer), 0, 5000);
151
152 /* any data received? */
153 if (recv_len <= 0) {
154 if (recv_len == 0) {
155 /* try again */
156 continue;
157 } else {
158 fprintf(stderr, "Receive failed: %s\n", strerror(errno));
159 break;
160 }
161 } else {
162 /* forward data to device */
163 debug("%s: sending data to device...\n", __func__);
164 res = idevice_connection_send(socket_info->device_connection, buffer, recv_len, (uint32_t*)&sent);
165
166 if (sent < recv_len || res != IDEVICE_E_SUCCESS) {
167 if (sent <= 0) {
168 fprintf(stderr, "send failed: %s\n", strerror(errno));
169 break;
170 } else {
171 fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
172 }
173 } else {
174 // sending succeeded, receive from device
175 debug("%s: sent %d bytes to device\n", __func__, sent);
176 }
177 }
178 }
179
180 debug("%s: shutting down...\n", __func__);
181
182 socket_shutdown(socket_info->client_fd, SHUT_RDWR);
183 socket_close(socket_info->client_fd);
184
185 socket_info->client_fd = -1;
186 socket_info->stop_dtoc = 1;
187
188 /* join other thread to allow it to stop */
189 thread_join(dtoc);
190
191 return NULL;
192}
193
194static void* connection_handler(void* data)
195{
196 socket_info_t* socket_info = (socket_info_t*)data;
197 thread_t ctod;
198
199 debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
200
201 /* spawn client to device thread */
202 socket_info->stop_ctod = 0;
203 if (thread_create(&ctod, thread_client_to_device, data) != 0) {
204 fprintf(stderr, "Failed to start client to device thread...\n");
205 }
206
207 /* join the fun */
208 thread_join(ctod);
209
210 /* shutdown client socket */
211 socket_shutdown(socket_info->client_fd, SHUT_RDWR);
212 socket_close(socket_info->client_fd);
213
214 /* shutdown server socket if we have to terminate to unblock the server loop */
215 if (quit_flag) {
216 socket_shutdown(socket_info->server_fd, SHUT_RDWR);
217 socket_close(socket_info->server_fd);
218 }
219
220 return NULL;
221}
222
223int main(int argc, char *argv[])
224{
225 lockdownd_client_t lockdown = NULL;
226 idevice_t device = NULL;
227 idevice_connection_t connection = NULL;
228 idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
229 thread_t th;
230 char udid[41];
231 udid[0] = 0;
232 uint16_t port = 0;
233 uint16_t local_port = 0;
234 int result = EXIT_SUCCESS;
235 int i;
236
237 /* bind signals */
238 signal(SIGINT, clean_exit);
239 signal(SIGTERM, clean_exit);
240#ifndef WIN32
241 signal(SIGQUIT, clean_exit);
242 signal(SIGPIPE, SIG_IGN);
243#endif
244
245 /* parse cmdline arguments */
246 for (i = 1; i < argc; i++) {
247 if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
248 debug_mode = 1;
249 idevice_set_debug_level(1);
250 socket_set_verbose(3);
251 continue;
252 }
253 else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
254 i++;
255 if (!argv[i] || (strlen(argv[i]) != 40)) {
256 print_usage(argc, argv);
257 return 0;
258 }
259 strcpy(udid, argv[i]);
260 continue;
261 }
262 else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
263 print_usage(argc, argv);
264 return EXIT_SUCCESS;
265 }
266 else if (atoi(argv[i]) > 0) {
267 local_port = atoi(argv[i]);
268 continue;
269 }
270 else {
271 print_usage(argc, argv);
272 return EXIT_SUCCESS;
273 }
274 }
275
276 /* a PORT is mandatory */
277 if (!local_port) {
278 fprintf(stderr, "Please specify a PORT.\n");
279 print_usage(argc, argv);
280 goto leave_cleanup;
281 }
282
283 /* start services and connect to device */
284 if (udid[0] != 0) {
285 ret = idevice_new(&device, udid);
286 if (ret != IDEVICE_E_SUCCESS) {
287 fprintf(stderr, "No device found with udid %s, is it plugged in?\n", udid);
288 result = EXIT_FAILURE;
289 goto leave_cleanup;
290 }
291 }
292 else
293 {
294 ret = idevice_new(&device, NULL);
295 if (ret != IDEVICE_E_SUCCESS) {
296 fprintf(stderr, "No device found, is it plugged in?\n");
297 result = EXIT_FAILURE;
298 goto leave_cleanup;
299 }
300 }
301
302 if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lockdown, "idevicedebugserverproxy")) {
303 fprintf(stderr, "Could not connect to lockdownd. Exiting.\n");
304 result = EXIT_FAILURE;
305 goto leave_cleanup;
306 }
307
308 if ((lockdownd_start_service(lockdown, "com.apple.debugserver", &port) != LOCKDOWN_E_SUCCESS) || !port) {
309 fprintf(stderr, "Could not start com.apple.debugserver!\nPlease make sure to mount the developer disk image first.\n");
310 result = EXIT_FAILURE;
311 goto leave_cleanup;
312 }
313
314 if (idevice_connect(device, port, &connection) != IDEVICE_E_SUCCESS) {
315 fprintf(stderr, "Connection to debugserver port %d failed!\n", (int)&port);
316 result = EXIT_FAILURE;
317 goto leave_cleanup;
318 }
319
320 /* free lockdown connection if running as it is not needed anymore */
321 if (lockdown) {
322 lockdownd_client_free(lockdown);
323 lockdown = NULL;
324 }
325
326 /* setup and create socket endpoint */
327 socket_info_t socket_info;
328
329 socket_info.device_connection = connection;
330 socket_info.local_port = local_port;
331 socket_info.remote_port = port;
332
333 /* create local socket */
334 socket_info.server_fd = socket_create(socket_info.local_port);
335 if (socket_info.server_fd < 0) {
336 fprintf(stderr, "Could not create socket\n");
337 result = EXIT_FAILURE;
338 goto leave_cleanup;
339 }
340
341 while (!quit_flag) {
342 debug("%s: Waiting for connection on local port %d\n", __func__, socket_info.local_port);
343
344 /* wait for client */
345 socket_info.client_fd = socket_accept(socket_info.server_fd, socket_info.local_port);
346 if (socket_info.client_fd < 0) {
347 debug("%s: Continuing...\n", __func__);
348 continue;
349 }
350
351 debug("%s: Handling new client connection...\n", __func__);
352
353 if (thread_create(&th, connection_handler, (void*)&socket_info) != 0) {
354 fprintf(stderr, "Could not start connection handler.\n");
355 socket_shutdown(socket_info.server_fd, SHUT_RDWR);
356 socket_close(socket_info.server_fd);
357 }
358 }
359
360 debug("%s: Shutting down debugserver proxy...\n", __func__);
361
362leave_cleanup:
363 if (connection) {
364 idevice_disconnect(connection);
365 }
366 if (lockdown) {
367 lockdownd_client_free(lockdown);
368 }
369 if (device) {
370 idevice_free(device);
371 }
372
373 return result;
374}