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