nc.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /* vi: set sw=4 ts=4: */
  2. /* nc: mini-netcat - built from the ground up for LRP
  3. *
  4. * Copyright (C) 1998, 1999 Charles P. Wright
  5. * Copyright (C) 1998 Dave Cinege
  6. *
  7. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  8. */
  9. //config:config NC
  10. //config: bool "nc"
  11. //config: default y
  12. //config: help
  13. //config: A simple Unix utility which reads and writes data across network
  14. //config: connections.
  15. //config:
  16. //config:config NC_SERVER
  17. //config: bool "Netcat server options (-l)"
  18. //config: default y
  19. //config: depends on NC
  20. //config: help
  21. //config: Allow netcat to act as a server.
  22. //config:
  23. //config:config NC_EXTRA
  24. //config: bool "Netcat extensions (-eiw and -f FILE)"
  25. //config: default y
  26. //config: depends on NC
  27. //config: help
  28. //config: Add -e (support for executing the rest of the command line after
  29. //config: making or receiving a successful connection), -i (delay interval for
  30. //config: lines sent), -w (timeout for initial connection).
  31. //config:
  32. //config:config NC_110_COMPAT
  33. //config: bool "Netcat 1.10 compatibility (+2.5k)"
  34. //config: default n # off specially for Rob
  35. //config: depends on NC
  36. //config: help
  37. //config: This option makes nc closely follow original nc-1.10.
  38. //config: The code is about 2.5k bigger. It enables
  39. //config: -s ADDR, -n, -u, -v, -o FILE, -z options, but loses
  40. //config: busybox-specific extensions: -f FILE.
  41. //applet:IF_NC(APPLET(nc, BB_DIR_USR_BIN, BB_SUID_DROP))
  42. //kbuild:lib-$(CONFIG_NC) += nc.o
  43. #include "libbb.h"
  44. #include "common_bufsiz.h"
  45. #if ENABLE_NC_110_COMPAT
  46. # include "nc_bloaty.c"
  47. #else
  48. //usage:#if !ENABLE_NC_110_COMPAT
  49. //usage:
  50. //usage:#if ENABLE_NC_SERVER || ENABLE_NC_EXTRA
  51. //usage:#define NC_OPTIONS_STR "\n"
  52. //usage:#else
  53. //usage:#define NC_OPTIONS_STR
  54. //usage:#endif
  55. //usage:
  56. //usage:#define nc_trivial_usage
  57. //usage: IF_NC_EXTRA("[-iN] [-wN] ")IF_NC_SERVER("[-l] [-p PORT] ")
  58. //usage: "["IF_NC_EXTRA("-f FILE|")"IPADDR PORT]"IF_NC_EXTRA(" [-e PROG]")
  59. //usage:#define nc_full_usage "\n\n"
  60. //usage: "Open a pipe to IP:PORT" IF_NC_EXTRA(" or FILE")
  61. //usage: NC_OPTIONS_STR
  62. //usage: IF_NC_SERVER(
  63. //usage: "\n -l Listen mode, for inbound connects"
  64. //usage: IF_NC_EXTRA(
  65. //usage: "\n (use -ll with -e for persistent server)"
  66. //usage: )
  67. //usage: "\n -p PORT Local port"
  68. //usage: )
  69. //usage: IF_NC_EXTRA(
  70. //usage: "\n -w SEC Connect timeout"
  71. //usage: "\n -i SEC Delay interval for lines sent"
  72. //usage: "\n -f FILE Use file (ala /dev/ttyS0) instead of network"
  73. //usage: "\n -e PROG Run PROG after connect"
  74. //usage: )
  75. //usage:
  76. //usage:#define nc_notes_usage ""
  77. //usage: IF_NC_EXTRA(
  78. //usage: "To use netcat as a terminal emulator on a serial port:\n\n"
  79. //usage: "$ stty 115200 -F /dev/ttyS0\n"
  80. //usage: "$ stty raw -echo -ctlecho && nc -f /dev/ttyS0\n"
  81. //usage: )
  82. //usage:
  83. //usage:#define nc_example_usage
  84. //usage: "$ nc foobar.somedomain.com 25\n"
  85. //usage: "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n"
  86. //usage: "help\n"
  87. //usage: "214-Commands supported:\n"
  88. //usage: "214- HELO EHLO MAIL RCPT DATA AUTH\n"
  89. //usage: "214 NOOP QUIT RSET HELP\n"
  90. //usage: "quit\n"
  91. //usage: "221 foobar closing connection\n"
  92. //usage:
  93. //usage:#endif
  94. /* Lots of small differences in features
  95. * when compared to "standard" nc
  96. */
  97. static void timeout(int signum UNUSED_PARAM)
  98. {
  99. bb_error_msg_and_die("timed out");
  100. }
  101. int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  102. int nc_main(int argc, char **argv)
  103. {
  104. /* sfd sits _here_ only because of "repeat" option (-l -l). */
  105. int sfd = sfd; /* for gcc */
  106. int cfd = 0;
  107. unsigned lport = 0;
  108. IF_NOT_NC_SERVER(const) unsigned do_listen = 0;
  109. IF_NOT_NC_EXTRA (const) unsigned wsecs = 0;
  110. IF_NOT_NC_EXTRA (const) unsigned delay = 0;
  111. IF_NOT_NC_EXTRA (const int execparam = 0;)
  112. IF_NC_EXTRA (char **execparam = NULL;)
  113. struct pollfd pfds[2];
  114. int opt; /* must be signed (getopt returns -1) */
  115. if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
  116. /* getopt32 is _almost_ usable:
  117. ** it cannot handle "... -e PROG -prog-opt" */
  118. while ((opt = getopt(argc, argv,
  119. "" IF_NC_SERVER("lp:") IF_NC_EXTRA("w:i:f:e:") )) > 0
  120. ) {
  121. if (ENABLE_NC_SERVER && opt == 'l')
  122. IF_NC_SERVER(do_listen++);
  123. else if (ENABLE_NC_SERVER && opt == 'p')
  124. IF_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0));
  125. else if (ENABLE_NC_EXTRA && opt == 'w')
  126. IF_NC_EXTRA( wsecs = xatou(optarg));
  127. else if (ENABLE_NC_EXTRA && opt == 'i')
  128. IF_NC_EXTRA( delay = xatou(optarg));
  129. else if (ENABLE_NC_EXTRA && opt == 'f')
  130. IF_NC_EXTRA( cfd = xopen(optarg, O_RDWR));
  131. else if (ENABLE_NC_EXTRA && opt == 'e' && optind <= argc) {
  132. /* We cannot just 'break'. We should let getopt finish.
  133. ** Or else we won't be able to find where
  134. ** 'host' and 'port' params are
  135. ** (think "nc -w 60 host port -e PROG"). */
  136. IF_NC_EXTRA(
  137. char **p;
  138. // +2: one for progname (optarg) and one for NULL
  139. execparam = xzalloc(sizeof(char*) * (argc - optind + 2));
  140. p = execparam;
  141. *p++ = optarg;
  142. while (optind < argc) {
  143. *p++ = argv[optind++];
  144. }
  145. )
  146. /* optind points to argv[argc] (NULL) now.
  147. ** FIXME: we assume that getopt will not count options
  148. ** possibly present on "-e PROG ARGS" and will not
  149. ** include them into final value of optind
  150. ** which is to be used ... */
  151. } else bb_show_usage();
  152. }
  153. argv += optind; /* ... here! */
  154. argc -= optind;
  155. // -l and -f don't mix
  156. if (do_listen && cfd) bb_show_usage();
  157. // File mode needs need zero arguments, listen mode needs zero or one,
  158. // client mode needs one or two
  159. if (cfd) {
  160. if (argc) bb_show_usage();
  161. } else if (do_listen) {
  162. if (argc > 1) bb_show_usage();
  163. } else {
  164. if (!argc || argc > 2) bb_show_usage();
  165. }
  166. } else {
  167. if (argc != 3) bb_show_usage();
  168. argc--;
  169. argv++;
  170. }
  171. if (wsecs) {
  172. signal(SIGALRM, timeout);
  173. alarm(wsecs);
  174. }
  175. if (!cfd) {
  176. if (do_listen) {
  177. sfd = create_and_bind_stream_or_die(argv[0], lport);
  178. xlisten(sfd, do_listen); /* can be > 1 */
  179. #if 0 /* nc-1.10 does not do this (without -v) */
  180. /* If we didn't specify a port number,
  181. * query and print it after listen() */
  182. if (!lport) {
  183. len_and_sockaddr lsa;
  184. lsa.len = LSA_SIZEOF_SA;
  185. getsockname(sfd, &lsa.u.sa, &lsa.len);
  186. lport = get_nport(&lsa.u.sa);
  187. fdprintf(2, "%d\n", ntohs(lport));
  188. }
  189. #endif
  190. close_on_exec_on(sfd);
  191. accept_again:
  192. cfd = accept(sfd, NULL, 0);
  193. if (cfd < 0)
  194. bb_perror_msg_and_die("accept");
  195. if (!execparam)
  196. close(sfd);
  197. } else {
  198. cfd = create_and_connect_stream_or_die(argv[0],
  199. argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0);
  200. }
  201. }
  202. if (wsecs) {
  203. alarm(0);
  204. /* Non-ignored signals revert to SIG_DFL on exec anyway */
  205. /*signal(SIGALRM, SIG_DFL);*/
  206. }
  207. /* -e given? */
  208. if (execparam) {
  209. pid_t pid;
  210. /* With more than one -l, repeatedly act as server */
  211. if (do_listen > 1 && (pid = xvfork()) != 0) {
  212. /* parent */
  213. /* prevent zombies */
  214. signal(SIGCHLD, SIG_IGN);
  215. close(cfd);
  216. goto accept_again;
  217. }
  218. /* child, or main thread if only one -l */
  219. xmove_fd(cfd, 0);
  220. xdup2(0, 1);
  221. /*xdup2(0, 2); - original nc 1.10 does this, we don't */
  222. IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
  223. IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);)
  224. }
  225. /* loop copying stdin to cfd, and cfd to stdout */
  226. pfds[0].fd = STDIN_FILENO;
  227. pfds[0].events = POLLIN;
  228. pfds[1].fd = cfd;
  229. pfds[1].events = POLLIN;
  230. #define iobuf bb_common_bufsiz1
  231. setup_common_bufsiz();
  232. for (;;) {
  233. int fdidx;
  234. int ofd;
  235. int nread;
  236. if (safe_poll(pfds, 2, -1) < 0)
  237. bb_perror_msg_and_die("poll");
  238. fdidx = 0;
  239. while (1) {
  240. if (pfds[fdidx].revents) {
  241. nread = safe_read(pfds[fdidx].fd, iobuf, COMMON_BUFSIZE);
  242. if (fdidx != 0) {
  243. if (nread < 1)
  244. exit(EXIT_SUCCESS);
  245. ofd = STDOUT_FILENO;
  246. } else {
  247. if (nread < 1) {
  248. /* Close outgoing half-connection so they get EOF,
  249. * but leave incoming alone so we can see response */
  250. shutdown(cfd, SHUT_WR);
  251. pfds[0].fd = -1;
  252. }
  253. ofd = cfd;
  254. }
  255. xwrite(ofd, iobuf, nread);
  256. if (delay > 0)
  257. sleep(delay);
  258. }
  259. if (fdidx == 1)
  260. break;
  261. fdidx++;
  262. }
  263. }
  264. }
  265. #endif