busybox/networking/nc.c
<<
>>
Prefs
   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 tarball for details.
   8 */
   9
  10#include "libbb.h"
  11
  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"
  47#else
  48
  49//usage:#if !ENABLE_NC_110_COMPAT
  50//usage:
  51//usage:#if ENABLE_NC_SERVER || ENABLE_NC_EXTRA
  52//usage:#define NC_OPTIONS_STR "\n\nOptions:"
  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
  95/* Lots of small differences in features
  96 * when compared to "standard" nc
  97 */
  98
  99static void timeout(int signum UNUSED_PARAM)
 100{
 101        bb_error_msg_and_die("timed out");
 102}
 103
 104int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 105int nc_main(int argc, char **argv)
 106{
 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;
 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;)
 116        fd_set readfds, testfds;
 117        int opt; /* must be signed (getopt returns -1) */
 118
 119        if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
 120                /* getopt32 is _almost_ usable:
 121                ** it cannot handle "... -e PROG -prog-opt" */
 122                while ((opt = getopt(argc, argv,
 123                        "" IF_NC_SERVER("lp:") IF_NC_EXTRA("w:i:f:e:") )) > 0
 124                ) {
 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) {
 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
 139                                ** (think "nc -w 60 host port -e PROG"). */
 140                                IF_NC_EXTRA(
 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
 152                                ** possibly present on "-e PROG ARGS" and will not
 153                                ** include them into final value of optind
 154                                ** which is to be used ...  */
 155                        } else bb_show_usage();
 156                }
 157                argv += optind; /* ... here! */
 158                argc -= optind;
 159                // -l and -f don't mix
 160                if (do_listen && cfd) bb_show_usage();
 161                // File mode needs need zero arguments, listen mode needs zero or one,
 162                // client mode needs one or two
 163                if (cfd) {
 164                        if (argc) bb_show_usage();
 165                } else if (do_listen) {
 166                        if (argc > 1) bb_show_usage();
 167                } else {
 168                        if (!argc || argc > 2) bb_show_usage();
 169                }
 170        } else {
 171                if (argc != 3) bb_show_usage();
 172                argc--;
 173                argv++;
 174        }
 175
 176        if (wsecs) {
 177                signal(SIGALRM, timeout);
 178                alarm(wsecs);
 179        }
 180
 181        if (!cfd) {
 182                if (do_listen) {
 183                        sfd = create_and_bind_stream_or_die(argv[0], lport);
 184                        xlisten(sfd, do_listen); /* can be > 1 */
 185#if 0  /* nc-1.10 does not do this (without -v) */
 186                        /* If we didn't specify a port number,
 187                         * query and print it after listen() */
 188                        if (!lport) {
 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);
 193                                fdprintf(2, "%d\n", ntohs(lport));
 194                        }
 195#endif
 196                        close_on_exec_on(sfd);
 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                }
 207        }
 208
 209        if (wsecs) {
 210                alarm(0);
 211                /* Non-ignored signals revert to SIG_DFL on exec anyway */
 212                /*signal(SIGALRM, SIG_DFL);*/
 213        }
 214
 215        /* -e given? */
 216        if (execparam) {
 217                pid_t pid;
 218                /* With more than one -l, repeatedly act as server */
 219                if (do_listen > 1 && (pid = xvfork()) != 0) {
 220                        /* parent */
 221                        /* prevent zombies */
 222                        signal(SIGCHLD, SIG_IGN);
 223                        close(cfd);
 224                        goto accept_again;
 225                }
 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... */
 232                _exit(127);
 233        }
 234
 235        /* Select loop copying stdin to cfd, and cfd to stdout */
 236
 237        FD_ZERO(&readfds);
 238        FD_SET(cfd, &readfds);
 239        FD_SET(STDIN_FILENO, &readfds);
 240
 241        for (;;) {
 242                int fd;
 243                int ofd;
 244                int nread;
 245
 246                testfds = readfds;
 247
 248                if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0)
 249                        bb_perror_msg_and_die("select");
 250
 251#define iobuf bb_common_bufsiz1
 252                fd = STDIN_FILENO;
 253                while (1) {
 254                        if (FD_ISSET(fd, &testfds)) {
 255                                nread = safe_read(fd, iobuf, sizeof(iobuf));
 256                                if (fd == cfd) {
 257                                        if (nread < 1)
 258                                                exit(EXIT_SUCCESS);
 259                                        ofd = STDOUT_FILENO;
 260                                } else {
 261                                        if (nread < 1) {
 262                                                /* Close outgoing half-connection so they get EOF,
 263                                                 * but leave incoming alone so we can see response */
 264                                                shutdown(cfd, 1);
 265                                                FD_CLR(STDIN_FILENO, &readfds);
 266                                        }
 267                                        ofd = cfd;
 268                                }
 269                                xwrite(ofd, iobuf, nread);
 270                                if (delay > 0)
 271                                        sleep(delay);
 272                        }
 273                        if (fd == cfd)
 274                                break;
 275                        fd = cfd;
 276                }
 277        }
 278}
 279#endif
 280