idevicedebugserverproxy.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <errno.h>
  25. #include <signal.h>
  26. #include <libimobiledevice/libimobiledevice.h>
  27. #include <libimobiledevice/debugserver.h>
  28. #include "common/socket.h"
  29. #include "common/thread.h"
  30. #define info(...) fprintf(stdout, __VA_ARGS__); fflush(stdout)
  31. #define debug(...) if(debug_mode) fprintf(stdout, __VA_ARGS__)
  32. static int debug_mode = 0;
  33. static int quit_flag = 0;
  34. typedef struct {
  35. int client_fd;
  36. idevice_t device;
  37. debugserver_client_t debugserver_client;
  38. volatile int stop_ctod;
  39. volatile int stop_dtoc;
  40. } socket_info_t;
  41. struct thread_info {
  42. thread_t th;
  43. struct thread_info *next;
  44. };
  45. typedef struct thread_info thread_info_t;
  46. static void clean_exit(int sig)
  47. {
  48. fprintf(stderr, "Exiting...\n");
  49. quit_flag++;
  50. }
  51. static void print_usage(int argc, char **argv)
  52. {
  53. char *name = NULL;
  54. name = strrchr(argv[0], '/');
  55. printf("Usage: %s [OPTIONS] <PORT>\n", (name ? name + 1: argv[0]));
  56. printf("Proxy debugserver connection from device to a local socket at PORT.\n\n");
  57. printf(" -d, --debug\t\tenable communication debugging\n");
  58. printf(" -u, --udid UDID\ttarget specific device by its 40-digit device UDID\n");
  59. printf(" -h, --help\t\tprints usage information\n");
  60. printf("\n");
  61. printf("Homepage: <http://libimobiledevice.org>\n");
  62. }
  63. static void *thread_device_to_client(void *data)
  64. {
  65. socket_info_t* socket_info = (socket_info_t*)data;
  66. debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
  67. int recv_len;
  68. int sent;
  69. char buffer[131072];
  70. debug("%s: started thread...\n", __func__);
  71. debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
  72. while (!quit_flag && !socket_info->stop_dtoc && socket_info->client_fd > 0) {
  73. debug("%s: receiving data from device...\n", __func__);
  74. res = debugserver_client_receive_with_timeout(socket_info->debugserver_client, buffer, sizeof(buffer), (uint32_t*)&recv_len, 5000);
  75. if (recv_len <= 0) {
  76. if (recv_len == 0 && res == DEBUGSERVER_E_SUCCESS) {
  77. // try again
  78. continue;
  79. } else {
  80. fprintf(stderr, "recv failed: %s\n", strerror(errno));
  81. break;
  82. }
  83. } else {
  84. /* send to device */
  85. debug("%s: sending data to client...\n", __func__);
  86. sent = socket_send(socket_info->client_fd, buffer, recv_len);
  87. if (sent < recv_len) {
  88. if (sent <= 0) {
  89. fprintf(stderr, "send failed: %s\n", strerror(errno));
  90. break;
  91. } else {
  92. fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
  93. }
  94. } else {
  95. // sending succeeded, receive from device
  96. debug("%s: pushed %d bytes to client\n", __func__, sent);
  97. }
  98. }
  99. }
  100. debug("%s: shutting down...\n", __func__);
  101. socket_shutdown(socket_info->client_fd, SHUT_RDWR);
  102. socket_close(socket_info->client_fd);
  103. socket_info->client_fd = -1;
  104. socket_info->stop_ctod = 1;
  105. return NULL;
  106. }
  107. static void *thread_client_to_device(void *data)
  108. {
  109. socket_info_t* socket_info = (socket_info_t*)data;
  110. debugserver_error_t res = DEBUGSERVER_E_UNKNOWN_ERROR;
  111. int recv_len;
  112. int sent;
  113. char buffer[131072];
  114. thread_t dtoc;
  115. debug("%s: started thread...\n", __func__);
  116. debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
  117. /* spawn server to client thread */
  118. socket_info->stop_dtoc = 0;
  119. if (thread_new(&dtoc, thread_device_to_client, data) != 0) {
  120. fprintf(stderr, "Failed to start device to client thread...\n");
  121. }
  122. while (!quit_flag && !socket_info->stop_ctod && socket_info->client_fd > 0) {
  123. debug("%s: receiving data from client...\n", __func__);
  124. /* attempt to read incoming data from client */
  125. recv_len = socket_receive_timeout(socket_info->client_fd, buffer, sizeof(buffer), 0, 5000);
  126. /* any data received? */
  127. if (recv_len <= 0) {
  128. if (recv_len == 0) {
  129. /* try again */
  130. continue;
  131. } else {
  132. fprintf(stderr, "Receive failed: %s\n", strerror(errno));
  133. break;
  134. }
  135. } else {
  136. /* forward data to device */
  137. debug("%s: sending data to device...\n", __func__);
  138. res = debugserver_client_send(socket_info->debugserver_client, buffer, recv_len, (uint32_t*)&sent);
  139. if (sent < recv_len || res != DEBUGSERVER_E_SUCCESS) {
  140. if (sent <= 0) {
  141. fprintf(stderr, "send failed: %s\n", strerror(errno));
  142. break;
  143. } else {
  144. fprintf(stderr, "only sent %d from %d bytes\n", sent, recv_len);
  145. }
  146. } else {
  147. // sending succeeded, receive from device
  148. debug("%s: sent %d bytes to device\n", __func__, sent);
  149. }
  150. }
  151. }
  152. debug("%s: shutting down...\n", __func__);
  153. socket_shutdown(socket_info->client_fd, SHUT_RDWR);
  154. socket_close(socket_info->client_fd);
  155. socket_info->client_fd = -1;
  156. socket_info->stop_dtoc = 1;
  157. /* join other thread to allow it to stop */
  158. thread_join(dtoc);
  159. thread_free(dtoc);
  160. return NULL;
  161. }
  162. static void* connection_handler(void* data)
  163. {
  164. debugserver_error_t derr = DEBUGSERVER_E_SUCCESS;
  165. socket_info_t* socket_info = (socket_info_t*)data;
  166. thread_t ctod;
  167. debug("%s: client_fd = %d\n", __func__, socket_info->client_fd);
  168. derr = debugserver_client_start_service(socket_info->device, &socket_info->debugserver_client, "idevicedebugserverproxy");
  169. if (derr != DEBUGSERVER_E_SUCCESS) {
  170. fprintf(stderr, "Could not start debugserver on device!\nPlease make sure to mount a developer disk image first.\n");
  171. return NULL;
  172. }
  173. /* spawn client to device thread */
  174. socket_info->stop_ctod = 0;
  175. if (thread_new(&ctod, thread_client_to_device, data) != 0) {
  176. fprintf(stderr, "Failed to start client to device thread...\n");
  177. }
  178. /* join the fun */
  179. thread_join(ctod);
  180. thread_free(ctod);
  181. debug("%s: shutting down...\n", __func__);
  182. debugserver_client_free(socket_info->debugserver_client);
  183. socket_info->debugserver_client = NULL;
  184. /* shutdown client socket */
  185. socket_shutdown(socket_info->client_fd, SHUT_RDWR);
  186. socket_close(socket_info->client_fd);
  187. return NULL;
  188. }
  189. int main(int argc, char *argv[])
  190. {
  191. idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  192. idevice_t device = NULL;
  193. thread_info_t *thread_list = NULL;
  194. const char* udid = NULL;
  195. uint16_t local_port = 0;
  196. int server_fd;
  197. int result = EXIT_SUCCESS;
  198. int i;
  199. #ifndef WIN32
  200. struct sigaction sa;
  201. struct sigaction si;
  202. memset(&sa, '\0', sizeof(struct sigaction));
  203. memset(&si, '\0', sizeof(struct sigaction));
  204. sa.sa_handler = clean_exit;
  205. sigemptyset(&sa.sa_mask);
  206. si.sa_handler = SIG_IGN;
  207. sigemptyset(&si.sa_mask);
  208. sigaction(SIGINT, &sa, NULL);
  209. sigaction(SIGTERM, &sa, NULL);
  210. sigaction(SIGQUIT, &sa, NULL);
  211. sigaction(SIGPIPE, &si, NULL);
  212. #else
  213. /* bind signals */
  214. signal(SIGINT, clean_exit);
  215. signal(SIGTERM, clean_exit);
  216. #endif
  217. /* parse cmdline arguments */
  218. for (i = 1; i < argc; i++) {
  219. if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
  220. debug_mode = 1;
  221. idevice_set_debug_level(1);
  222. socket_set_verbose(3);
  223. continue;
  224. }
  225. else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
  226. i++;
  227. if (!argv[i] || (strlen(argv[i]) != 40)) {
  228. print_usage(argc, argv);
  229. return 0;
  230. }
  231. udid = argv[i];
  232. continue;
  233. }
  234. else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
  235. print_usage(argc, argv);
  236. return EXIT_SUCCESS;
  237. }
  238. else if (atoi(argv[i]) > 0) {
  239. local_port = atoi(argv[i]);
  240. continue;
  241. }
  242. else {
  243. print_usage(argc, argv);
  244. return EXIT_SUCCESS;
  245. }
  246. }
  247. /* a PORT is mandatory */
  248. if (!local_port) {
  249. fprintf(stderr, "Please specify a PORT.\n");
  250. print_usage(argc, argv);
  251. goto leave_cleanup;
  252. }
  253. /* start services and connect to device */
  254. ret = idevice_new(&device, udid);
  255. if (ret != IDEVICE_E_SUCCESS) {
  256. if (udid) {
  257. fprintf(stderr, "No device found with udid %s, is it plugged in?\n", udid);
  258. } else {
  259. fprintf(stderr, "No device found, is it plugged in?\n");
  260. }
  261. result = EXIT_FAILURE;
  262. goto leave_cleanup;
  263. }
  264. /* create local socket */
  265. server_fd = socket_create(local_port);
  266. if (server_fd < 0) {
  267. fprintf(stderr, "Could not create socket\n");
  268. result = EXIT_FAILURE;
  269. goto leave_cleanup;
  270. }
  271. while (!quit_flag) {
  272. debug("%s: Waiting for connection on local port %d\n", __func__, local_port);
  273. /* wait for client */
  274. int client_fd = socket_accept(server_fd, local_port);
  275. if (client_fd < 0) {
  276. continue;
  277. }
  278. debug("%s: Handling new client connection...\n", __func__);
  279. thread_info_t *el = (thread_info_t*)malloc(sizeof(thread_info_t));
  280. if (!el) {
  281. fprintf(stderr, "Out of memory\n");
  282. exit(EXIT_FAILURE);
  283. }
  284. el->next = NULL;
  285. if (thread_list) {
  286. thread_list->next = el;
  287. } else {
  288. thread_list = el;
  289. }
  290. socket_info_t *sinfo = (socket_info_t*)malloc(sizeof(socket_info_t));
  291. if (!sinfo) {
  292. fprintf(stderr, "Out of memory\n");
  293. exit(EXIT_FAILURE);
  294. }
  295. sinfo->client_fd = client_fd;
  296. sinfo->device = device;
  297. if (thread_new(&(el->th), connection_handler, (void*)sinfo) != 0) {
  298. fprintf(stderr, "Could not start connection handler.\n");
  299. socket_shutdown(server_fd, SHUT_RDWR);
  300. socket_close(server_fd);
  301. continue;
  302. }
  303. }
  304. debug("%s: Shutting down debugserver proxy...\n", __func__);
  305. /* join and clean up threads */
  306. while (thread_list) {
  307. thread_info_t *el = thread_list;
  308. thread_join(el->th);
  309. thread_free(el->th);
  310. thread_list = el->next;
  311. free(el);
  312. }
  313. leave_cleanup:
  314. if (device) {
  315. idevice_free(device);
  316. }
  317. return result;
  318. }