diff options
| author | 2013-02-23 18:42:04 +0100 | |
|---|---|---|
| committer | 2015-06-10 19:59:54 +0200 | |
| commit | 69f49d0760b6b5743ec0eaef5a63917d72772fc8 (patch) | |
| tree | d07d093d8d8b3096e293a3790c5571eef78ec69a | |
| parent | 1ff3448d2e27f1bac8d2f0af8b8e952854860278 (diff) | |
| download | libimobiledevice-69f49d0760b6b5743ec0eaef5a63917d72772fc8.tar.gz libimobiledevice-69f49d0760b6b5743ec0eaef5a63917d72772fc8.tar.bz2  | |
idevicewebinspectorproxy: Add tool to proxy WebKit remote protocol locally
| -rw-r--r-- | tools/Makefile.am | 7 | ||||
| -rw-r--r-- | tools/idevicewebinspectorproxy.c | 367 | 
2 files changed, 373 insertions, 1 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am index db929bb..0db8621 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)  AM_CFLAGS = $(GLOBAL_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgcrypt_CFLAGS) $(openssl_CFLAGS) $(libplist_CFLAGS) $(LFS_CFLAGS)  AM_LDFLAGS = $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgcrypt_LIBS) $(openssl_LIBS) $(libplist_LIBS) -bin_PROGRAMS = idevice_id ideviceinfo idevicename idevicepair idevicesyslog ideviceimagemounter idevicescreenshot ideviceenterrecovery idevicedate idevicebackup idevicebackup2 ideviceprovision idevicedebugserverproxy idevicediagnostics idevicedebug idevicenotificationproxy idevicecrashreport +bin_PROGRAMS = idevice_id ideviceinfo idevicename idevicepair idevicesyslog ideviceimagemounter idevicescreenshot ideviceenterrecovery idevicedate idevicebackup idevicebackup2 ideviceprovision idevicedebugserverproxy idevicediagnostics idevicedebug idevicenotificationproxy idevicecrashreport idevicewebinspectorproxy  ideviceinfo_SOURCES = ideviceinfo.c  ideviceinfo_CFLAGS = $(AM_CFLAGS) @@ -89,3 +89,8 @@ idevicecrashreport_SOURCES = idevicecrashreport.c  idevicecrashreport_CFLAGS = -I$(top_srcdir) $(AM_CFLAGS)  idevicecrashreport_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS)  idevicecrashreport_LDADD = $(top_builddir)/src/libimobiledevice.la + +idevicewebinspectorproxy_SOURCES = idevicewebinspectorproxy.c +idevicewebinspectorproxy_CFLAGS = -I$(top_srcdir) $(AM_CFLAGS) +idevicewebinspectorproxy_LDFLAGS = $(top_builddir)/common/libinternalcommon.la $(AM_LDFLAGS) +idevicewebinspectorproxy_LDADD = $(top_builddir)/src/libimobiledevice.la diff --git a/tools/idevicewebinspectorproxy.c b/tools/idevicewebinspectorproxy.c new file mode 100644 index 0000000..9d7e3e2 --- /dev/null +++ b/tools/idevicewebinspectorproxy.c @@ -0,0 +1,367 @@ +/* + * idevicewebinspectorproxy.c + * Proxy a webinspector connection from device for remote debugging + * + * Copyright (c) 2013 Yury Melnichek 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <time.h> + +#include <libimobiledevice/libimobiledevice.h> +#include <libimobiledevice/webinspector.h> + +#include "socket.h" +#include "thread.h" + +#define info(...) fprintf(stdout, __VA_ARGS__); fflush(stdout) +#define debug(...) if(debug_mode) fprintf(stdout, __VA_ARGS__) + +static int debug_mode = 0; +static int quit_flag = 0; + +typedef struct { +	int server_fd; +	int client_fd; +	uint16_t local_port; +	webinspector_client_t inspector; +	uint32_t timeout; +	volatile int stop_ctod; +	volatile int stop_dtoc; +} socket_info_t; + +static void clean_exit(int sig) +{ +	fprintf(stderr, "Exiting...\n"); +	quit_flag++; +} + +static void print_usage(int argc, char **argv) +{ +	char *name = NULL; + +	name = strrchr(argv[0], '/'); +	printf("Usage: %s [OPTIONS] <PORT>\n", (name ? name + 1: argv[0])); +	printf("Proxy webinspector connection from device to a local socket at PORT.\n"); +	printf("  -d, --debug\t\tenable communication debugging\n"); +	printf("  -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n"); +	printf("  -h, --help\t\tprints usage information\n"); +	printf("  -t, --timeout MSEC\t\tchange timeout when receiving data\n"); +	printf("\n"); +} + +static void *thread_device_to_client(void *data) +{ +	socket_info_t* socket_info = (socket_info_t*)data; +	webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR; + +	int recv_len; +	int sent; +	char * buf = NULL; +	plist_t message = NULL; + +	debug("%s: started thread...\n", __func__); + +	debug("%s: client_fd = %d\n", __func__, socket_info->client_fd); +	debug("%s: server fd = %d\n", __func__, socket_info->server_fd); + +	while (!quit_flag && !socket_info->stop_dtoc && socket_info->client_fd > 0 && socket_info->server_fd > 0) { +		debug("%s: receiving data from device...\n", __func__); + +		res = webinspector_receive_with_timeout(socket_info->inspector, &message, socket_info->timeout); +		if (res != WEBINSPECTOR_E_SUCCESS) { +			fprintf(stderr, "webinspector_receive_with_timeout failed: %d\n", res); +			continue; +		} else { +			plist_to_bin(message, &buf, (uint32_t*)&recv_len); +			if (!buf || recv_len == 0) { +				debug("Error converting plist to binary."); +				break; +			} + +			if (message) { +				plist_free(message); +				message = NULL; +			} + +			/* send to client */ +			debug("%s: sending data to client...\n", __func__); + +			sent = socket_send(socket_info->client_fd, buf, recv_len); +			if (sent < recv_len) { +				if (sent <= 0) { +					fprintf(stderr, "send failed: %s\n", strerror(errno)); +					break; +				} else { +					fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len); +				} +			} else { +				// sending succeeded, receive from device +				debug("%s: pushed %d bytes to client\n", __func__, sent); +			} + +			if (buf) { +				free(buf); +				buf = NULL; +			} +		} +	} + +	debug("%s: shutting down...\n", __func__); + +	socket_shutdown(socket_info->client_fd, SHUT_RDWR); +	socket_close(socket_info->client_fd); + +	socket_info->client_fd = -1; +	socket_info->stop_ctod = 1; + +	return NULL; +} + +static void *thread_client_to_device(void *data) +{ +	socket_info_t* socket_info = (socket_info_t*)data; +	webinspector_error_t res = WEBINSPECTOR_E_UNKNOWN_ERROR; + +	int recv_len; +	char buffer[131072]; +	plist_t message = NULL; +	thread_t dtoc; + +	debug("%s: started thread...\n", __func__); + +	debug("%s: client_fd = %d\n", __func__, socket_info->client_fd); +	debug("%s: server_fd = %d\n", __func__, socket_info->server_fd); + +	/* spawn server to client thread */ +	socket_info->stop_dtoc = 0; +	if (thread_create(&dtoc, thread_device_to_client, data) != 0) { +		fprintf(stderr, "Failed to start device to client thread...\n"); +	} + +	while (!quit_flag && !socket_info->stop_ctod && socket_info->client_fd > 0 && socket_info->server_fd > 0) { +		debug("%s: receiving data from client...\n", __func__); + +		/* attempt to read incoming data from client */ +		recv_len = socket_receive_timeout(socket_info->client_fd, buffer, sizeof(buffer), 0, socket_info->timeout); + +		/* any data received? */ +		if (recv_len <= 0) { +			if (recv_len == 0 || recv_len == -11) { +				/* try again */ +				continue; +			} else { +				fprintf(stderr, "Receive failed: %s %d %d\n", strerror(errno), errno, recv_len); +				break; +			} +		} else { +			/* convert data to a buffer */ +			plist_from_bin(buffer, (uint32_t)recv_len, &message); + +			/* forward data to device */ +			debug("%s: sending data to device...\n", __func__); + +			res = webinspector_send(socket_info->inspector, message); +			if (res != WEBINSPECTOR_E_SUCCESS) { +				fprintf(stderr, "send failed: %s\n", strerror(errno)); +				break; +			} else { +				// sending succeeded, receive from device +				debug("%s: sent %d bytes to device\n", __func__, recv_len); +			} +		} +	} + +	debug("%s: shutting down...\n", __func__); + +	socket_shutdown(socket_info->client_fd, SHUT_RDWR); +	socket_close(socket_info->client_fd); + +	socket_info->client_fd = -1; +	socket_info->stop_dtoc = 1; + +	/* join other thread to allow it to stop */ +	thread_join(dtoc); + +	return NULL; +} + +static void* connection_handler(void* data) +{ +	socket_info_t* socket_info = (socket_info_t*)data; +	thread_t ctod; + +	debug("%s: client_fd = %d\n", __func__, socket_info->client_fd); + +	/* spawn client to device thread */ +	socket_info->stop_ctod = 0; +	if (thread_create(&ctod, thread_client_to_device, data) != 0) { +		fprintf(stderr, "Failed to start client to device thread...\n"); +	} + +	/* join the fun */ +	thread_join(ctod); + +	/* shutdown client socket */ +	socket_shutdown(socket_info->client_fd, SHUT_RDWR); +	socket_close(socket_info->client_fd); + +	/* shutdown server socket if we have to terminate to unblock the server loop */ +	if (quit_flag) { +		socket_shutdown(socket_info->server_fd, SHUT_RDWR); +		socket_close(socket_info->server_fd); +	} + +	return NULL; +} + +int main(int argc, char **argv) +{ +	idevice_t device = NULL; +	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR; +	thread_t th; +	const char* udid = NULL; +	uint16_t local_port = 0; +	int result = EXIT_SUCCESS; +	int i; +	uint32_t timeout = 1000; + +	/* bind signals */ +	signal(SIGINT, clean_exit); +	signal(SIGTERM, clean_exit); +#ifndef WIN32 +	signal(SIGQUIT, clean_exit); +	signal(SIGPIPE, SIG_IGN); +#endif + +	/* parse cmdline arguments */ +	for (i = 1; i < argc; i++) { +		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { +			debug_mode = 1; +			idevice_set_debug_level(1); +			socket_set_verbose(3); +			continue; +		} +		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) { +			i++; +			if (!argv[i] || (strlen(argv[i]) != 40)) { +				print_usage(argc, argv); +				return 0; +			} +			udid = argv[i]; +			continue; +		} +		else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--timeout")) { +			i++; +			if (!argv[i] || (atoi(argv[i]) <= 0)) { +				print_usage(argc, argv); +				return 0; +			} +			timeout = atoi(argv[i]); +			continue; +		} +		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { +			print_usage(argc, argv); +			return EXIT_SUCCESS; +		} +		else if (atoi(argv[i]) > 0) { +			local_port = atoi(argv[i]); +			continue; +		} +		else { +			print_usage(argc, argv); +			return EXIT_SUCCESS; +		} +	} + +	/* a PORT is mandatory */ +	if (!local_port) { +		fprintf(stderr, "Please specify a PORT.\n"); +		print_usage(argc, argv); +		goto leave_cleanup; +	} + +	/* start services and connect to device */ +	ret = idevice_new(&device, udid); +	if (ret != IDEVICE_E_SUCCESS) { +		if (udid) { +			fprintf(stderr, "No device found with udid %s, is it plugged in?\n", udid); +		} else { +			fprintf(stderr, "No device found, is it plugged in?\n"); +		} +		result = EXIT_FAILURE; +		goto leave_cleanup; +	} + +	webinspector_client_t inspector = NULL; +	webinspector_error_t error = webinspector_start_service(device, &inspector); +	if (error != WEBINSPECTOR_E_SUCCESS) { +		printf("Could not connect to the webinspector! Error: %i\n", error); +		result = EXIT_FAILURE; +		goto leave_cleanup; +	} + +	/* setup and create socket endpoint */ +	socket_info_t socket_info; + +	socket_info.inspector = inspector; +	socket_info.local_port = local_port; +	socket_info.timeout = timeout; + +	/* create local socket */ +	socket_info.server_fd = socket_create(socket_info.local_port); +	if (socket_info.server_fd < 0) { +		fprintf(stderr, "Could not create socket\n"); +		result = EXIT_FAILURE; +		goto leave_cleanup; +	} + +	while (!quit_flag) { +		debug("%s: Waiting for connection on local port %d\n", __func__, socket_info.local_port); + +		/* wait for client */ +		socket_info.client_fd = socket_accept(socket_info.server_fd, socket_info.local_port); +		if (socket_info.client_fd < 0) { +			debug("%s: Continuing...\n", __func__); +			continue; +		} + +		debug("%s: Handling new client connection...\n", __func__); + +		if (thread_create(&th, connection_handler, (void*)&socket_info) != 0) { +			fprintf(stderr, "Could not start connection handler.\n"); +			socket_shutdown(socket_info.server_fd, SHUT_RDWR); +			socket_close(socket_info.server_fd); +		} +	} + +	debug("%s: Shutting down webinspector proxy...\n", __func__); + +leave_cleanup: +	if (inspector) { +		webinspector_client_free(inspector); +	} +	if (device) { +		idevice_free(device); +	} + +	return result; +}  | 
