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