inetcat.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * inetcat.c -- simple netcat-like tool that enables service access to iOS devices
  3. *
  4. * Copyright (C) 2017 Adrien Guinet <adrien@guinet.me>
  5. *
  6. * Based on iproxy which is based upon iTunnel source code, Copyright (c)
  7. * 2008 Jing Su. http://www.cs.toronto.edu/~jingsu/itunnel/
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. */
  23. #ifdef HAVE_CONFIG_H
  24. #include <config.h>
  25. #endif
  26. #define TOOL_NAME "inetcat"
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <stdint.h>
  30. #include <string.h>
  31. #include <stddef.h>
  32. #include <errno.h>
  33. #ifdef _WIN32
  34. #include <winsock2.h>
  35. #include <windows.h>
  36. #ifndef _MSC_VER
  37. #include <unistd.h>
  38. #endif
  39. #else
  40. #include <fcntl.h>
  41. #include <unistd.h>
  42. #include <sys/select.h>
  43. #include <sys/socket.h>
  44. #include <sys/un.h>
  45. #include <sys/ioctl.h>
  46. #include <signal.h>
  47. #endif
  48. #ifdef _MSC_VER
  49. #include <basetsd.h>
  50. typedef SSIZE_T ssize_t;
  51. #define STDIN_FILENO _fileno(stdin)
  52. #define STDOUT_FILENO _fileno(stdout)
  53. #endif
  54. #include <getopt.h>
  55. #include <libimobiledevice-glue/socket.h>
  56. #include "usbmuxd.h"
  57. static int debug_level = 0;
  58. static size_t read_data_socket(int fd, uint8_t* buf, size_t bufsize)
  59. {
  60. #ifdef _WIN32
  61. u_long bytesavailable = 0;
  62. if (fd == STDIN_FILENO) {
  63. bytesavailable = bufsize;
  64. } else if (ioctlsocket(fd, FIONREAD, &bytesavailable) != 0) {
  65. perror("ioctlsocket FIONREAD failed");
  66. exit(1);
  67. }
  68. #else
  69. size_t bytesavailable = 0;
  70. if (ioctl(fd, FIONREAD, &bytesavailable) != 0) {
  71. perror("ioctl FIONREAD failed");
  72. exit(1);
  73. }
  74. #endif
  75. size_t bufread = (bytesavailable >= bufsize) ? bufsize:bytesavailable;
  76. ssize_t ret = read(fd, buf, bufread);
  77. if (ret < 0) {
  78. perror("read failed");
  79. exit(1);
  80. }
  81. return (size_t)ret;
  82. }
  83. static void print_usage(int argc, char **argv, int is_error)
  84. {
  85. char *name = NULL;
  86. name = strrchr(argv[0], '/');
  87. fprintf(is_error ? stderr : stdout, "Usage: %s [OPTIONS] DEVICE_PORT\n", (name ? name + 1: argv[0]));
  88. fprintf(is_error ? stderr : stdout,
  89. "\n" \
  90. "Opens a read/write interface via STDIN/STDOUT to a TCP port on a usbmux device.\n" \
  91. "\n" \
  92. "OPTIONS:\n" \
  93. " -u, --udid UDID target specific device by UDID\n" \
  94. " -n, --network connect to network device\n" \
  95. " -l, --local connect to USB device (default)\n" \
  96. " -h, --help prints usage information\n" \
  97. " -d, --debug increase debug level\n" \
  98. " -v, --version prints version information\n" \
  99. "\n" \
  100. "Homepage: <" PACKAGE_URL ">\n"
  101. "Bug Reports: <" PACKAGE_BUGREPORT ">\n"
  102. );
  103. }
  104. int main(int argc, char **argv)
  105. {
  106. const struct option longopts[] = {
  107. { "debug", no_argument, NULL, 'd' },
  108. { "help", no_argument, NULL, 'h' },
  109. { "udid", required_argument, NULL, 'u' },
  110. { "local", no_argument, NULL, 'l' },
  111. { "network", no_argument, NULL, 'n' },
  112. { "version", no_argument, NULL, 'v' },
  113. { NULL, 0, NULL, 0}
  114. };
  115. char* device_udid = NULL;
  116. static enum usbmux_lookup_options lookup_opts = 0;
  117. int c = 0;
  118. while ((c = getopt_long(argc, argv, "dhu:lnv", longopts, NULL)) != -1) {
  119. switch (c) {
  120. case 'd':
  121. libusbmuxd_set_debug_level(++debug_level);
  122. break;
  123. case 'u':
  124. if (!*optarg) {
  125. fprintf(stderr, "ERROR: UDID must not be empty!\n");
  126. print_usage(argc, argv, 1);
  127. return 2;
  128. }
  129. free(device_udid);
  130. device_udid = strdup(optarg);
  131. break;
  132. case 'l':
  133. lookup_opts |= DEVICE_LOOKUP_USBMUX;
  134. break;
  135. case 'n':
  136. lookup_opts |= DEVICE_LOOKUP_NETWORK;
  137. break;
  138. case 'h':
  139. print_usage(argc, argv, 0);
  140. return 0;
  141. case 'v':
  142. printf("%s %s\n", TOOL_NAME, libusbmuxd_version());
  143. return 0;
  144. default:
  145. print_usage(argc, argv, 1);
  146. return 2;
  147. }
  148. }
  149. if (lookup_opts == 0) {
  150. lookup_opts = DEVICE_LOOKUP_USBMUX;
  151. }
  152. argc -= optind;
  153. argv += optind;
  154. if (argc < 1) {
  155. print_usage(argc + optind, argv - optind, 1);
  156. return 2;
  157. }
  158. int device_port = atoi(argv[0]);
  159. if (!device_port) {
  160. fprintf(stderr, "Invalid device_port specified!\n");
  161. return -EINVAL;
  162. }
  163. #ifndef _WIN32
  164. signal(SIGPIPE, SIG_IGN);
  165. #endif
  166. usbmuxd_device_info_t *dev_list = NULL;
  167. usbmuxd_device_info_t *dev = NULL;
  168. usbmuxd_device_info_t muxdev;
  169. if (device_udid) {
  170. if (usbmuxd_get_device(device_udid, &muxdev, lookup_opts) > 0) {
  171. dev = &muxdev;
  172. }
  173. } else {
  174. int count;
  175. if ((count = usbmuxd_get_device_list(&dev_list)) < 0) {
  176. printf("Connecting to usbmuxd failed, terminating.\n");
  177. free(dev_list);
  178. return 1;
  179. }
  180. if (dev_list == NULL || dev_list[0].handle == 0) {
  181. fprintf(stderr, "No connected device found, terminating.\n");
  182. free(dev_list);
  183. return 1;
  184. }
  185. int i;
  186. for (i = 0; i < count; i++) {
  187. if (dev_list[i].conn_type == CONNECTION_TYPE_USB && (lookup_opts & DEVICE_LOOKUP_USBMUX)) {
  188. dev = &(dev_list[i]);
  189. break;
  190. }
  191. if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK && (lookup_opts & DEVICE_LOOKUP_NETWORK)) {
  192. dev = &(dev_list[i]);
  193. break;
  194. }
  195. }
  196. }
  197. if (dev == NULL || dev->handle == 0) {
  198. fprintf(stderr, "No connected/matching device found, disconnecting client.\n");
  199. free(dev_list);
  200. return 1;
  201. }
  202. int devfd = -1;
  203. if (dev->conn_type == CONNECTION_TYPE_NETWORK) {
  204. unsigned char saddr_[32];
  205. memset(saddr_, '\0', sizeof(saddr_));
  206. struct sockaddr* saddr = (struct sockaddr*)&saddr_[0];
  207. if (dev->conn_data[1] == 0x02) { // AF_INET
  208. saddr->sa_family = AF_INET;
  209. memcpy(&saddr->sa_data[0], (uint8_t*)dev->conn_data+2, 14);
  210. }
  211. else if (dev->conn_data[1] == 0x1E) { //AF_INET6 (bsd)
  212. #ifdef AF_INET6
  213. saddr->sa_family = AF_INET6;
  214. memcpy(&saddr->sa_data[0], (uint8_t*)dev->conn_data+2, 26);
  215. #else
  216. fprintf(stderr, "ERROR: Got an IPv6 address but this system doesn't support IPv6\n");
  217. free(dev_list);
  218. return 1;
  219. #endif
  220. }
  221. else {
  222. fprintf(stderr, "Unsupported address family 0x%02x\n", dev->conn_data[1]);
  223. free(dev_list);
  224. return 1;
  225. }
  226. char addrtxt[48];
  227. addrtxt[0] = '\0';
  228. if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) {
  229. fprintf(stderr, "Failed to convert network address: %d (%s)\n", errno, strerror(errno));
  230. }
  231. devfd = socket_connect_addr(saddr, device_port);
  232. if (devfd < 0) {
  233. devfd = -errno;
  234. }
  235. } else if (dev->conn_type == CONNECTION_TYPE_USB) {
  236. devfd = usbmuxd_connect(dev->handle, device_port);
  237. }
  238. free(dev_list);
  239. if (devfd < 0) {
  240. fprintf(stderr, "Error connecting to device: %s\n", strerror(-devfd));
  241. return 1;
  242. }
  243. fd_set fds;
  244. FD_ZERO(&fds);
  245. FD_SET(STDIN_FILENO, &fds);
  246. FD_SET(devfd, &fds);
  247. int ret = 0;
  248. uint8_t buf[4096];
  249. while (1) {
  250. fd_set read_fds = fds;
  251. int ret_sel = select(devfd+1, &read_fds, NULL, NULL, NULL);
  252. if (ret_sel < 0) {
  253. perror("select");
  254. ret = 1;
  255. break;
  256. }
  257. if (FD_ISSET(STDIN_FILENO, &read_fds)) {
  258. size_t n = read_data_socket(STDIN_FILENO, buf, sizeof(buf));
  259. if (n == 0) {
  260. break;
  261. }
  262. write(devfd, buf, n);
  263. }
  264. if (FD_ISSET(devfd, &read_fds)) {
  265. size_t n = read_data_socket(devfd, buf, sizeof(buf));
  266. if (n == 0) {
  267. break;
  268. }
  269. write(STDOUT_FILENO, buf, n);
  270. }
  271. }
  272. socket_close(devfd);
  273. return ret;
  274. }