Home | History | Annotate | Download | only in other
      1 /* netcat.c - Forward stdin/stdout to a file or network connection.
      2  *
      3  * Copyright 2007 Rob Landley <rob (at) landley.net>
      4  *
      5  * TODO: udp, ipv6, genericize for telnet/microcom/tail-f
      6 
      7 USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
      8 USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#p#s:q#f:", TOYFLAG_BIN))
      9 
     10 config NETCAT
     11   bool "netcat"
     12   default y
     13   help
     14     usage: netcat [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME}
     15 
     16     -f	use FILENAME (ala /dev/ttyS0) instead of network
     17     -p	local port number
     18     -q	SECONDS quit this many seconds after EOF on stdin.
     19     -s	local ipv4 address
     20     -w	SECONDS timeout for connection
     21 
     22     Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
     23     netcat -f to connect to a serial port.
     24 
     25 config NETCAT_LISTEN
     26   bool "netcat server options (-let)"
     27   default y
     28   depends on NETCAT
     29   depends on TOYBOX_FORK
     30   help
     31     usage: netcat [-lL COMMAND...]
     32 
     33     -l	listen for one incoming connection.
     34     -L	listen for multiple incoming connections (server mode).
     35 
     36     The command line after -l or -L is executed to handle each incoming
     37     connection. If none, the connection is forwarded to stdin/stdout.
     38 
     39     For a quick-and-dirty server, try something like:
     40     netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l
     41 
     42 config NETCAT_LISTEN_TTY
     43   bool
     44   default y
     45   depends on NETCAT_LISTEN
     46   depends on TOYBOX_FORK
     47   help
     48     usage: netcat [-t]
     49 
     50     -t	allocate tty (must come before -l or -L)
     51 */
     52 
     53 #define FOR_netcat
     54 #include "toys.h"
     55 
     56 GLOBALS(
     57   char *filename;        // -f read from filename instead of network
     58   long quit_delay;       // -q Exit after EOF from stdin after # seconds.
     59   char *source_address;  // -s Bind to a specific source address.
     60   long port;             // -p Bind to a specific source port.
     61   long wait;             // -w Wait # seconds for a connection.
     62 )
     63 
     64 static void timeout(int signum)
     65 {
     66   if (TT.wait) error_exit("Timeout");
     67   // This should be xexit() but would need siglongjmp()...
     68   exit(0);
     69 }
     70 
     71 static void set_alarm(int seconds)
     72 {
     73   xsignal(SIGALRM, seconds ? timeout : SIG_DFL);
     74   alarm(seconds);
     75 }
     76 
     77 // Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name.
     78 static void lookup_name(char *name, uint32_t *result)
     79 {
     80   struct hostent *hostbyname;
     81 
     82   hostbyname = gethostbyname(name); // getaddrinfo
     83   if (!hostbyname) error_exit("no host '%s'", name);
     84   *result = *(uint32_t *)*hostbyname->h_addr_list;
     85 }
     86 
     87 // Worry about a fancy lookup later.
     88 static void lookup_port(char *str, uint16_t *port)
     89 {
     90   *port = SWAP_BE16(atoi(str));
     91 }
     92 
     93 void netcat_main(void)
     94 {
     95   int sockfd=-1, pollcount=2;
     96   struct pollfd pollfds[2];
     97 
     98   memset(pollfds, 0, 2*sizeof(struct pollfd));
     99   pollfds[0].events = pollfds[1].events = POLLIN;
    100   set_alarm(TT.wait);
    101 
    102   // The argument parsing logic can't make "<2" conditional on other
    103   // arguments like -f and -l, so we do it by hand here.
    104   if ((toys.optflags&FLAG_f) ? toys.optc :
    105       (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2))
    106         help_exit("Argument count wrong");
    107 
    108   if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR);
    109   else {
    110     int temp;
    111     struct sockaddr_in address;
    112 
    113     // Setup socket
    114     sockfd = xsocket(AF_INET, SOCK_STREAM, 0);
    115     fcntl(sockfd, F_SETFD, FD_CLOEXEC);
    116     temp = 1;
    117     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp));
    118     memset(&address, 0, sizeof(address));
    119     address.sin_family = AF_INET;
    120     if (TT.source_address || TT.port) {
    121       address.sin_port = SWAP_BE16(TT.port);
    122       if (TT.source_address)
    123         lookup_name(TT.source_address, (uint32_t *)&address.sin_addr);
    124       if (bind(sockfd, (struct sockaddr *)&address, sizeof(address)))
    125         perror_exit("bind");
    126     }
    127 
    128     // Dial out
    129 
    130     if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) {
    131       // Figure out where to dial out to.
    132       lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr);
    133       lookup_port(toys.optargs[1], &address.sin_port);
    134       temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
    135       if (temp<0) perror_exit("connect");
    136       pollfds[0].fd = sockfd;
    137 
    138     // Listen for incoming connections
    139 
    140     } else {
    141       socklen_t len = sizeof(address);
    142 
    143       if (listen(sockfd, 5)) error_exit("listen");
    144       if (!TT.port) {
    145         getsockname(sockfd, (struct sockaddr *)&address, &len);
    146         printf("%d\n", SWAP_BE16(address.sin_port));
    147         fflush(stdout);
    148       }
    149       // Do we need to return immediately because -l has arguments?
    150 
    151       if ((toys.optflags & FLAG_l) && toys.optc) {
    152         if (CFG_TOYBOX_FORK && xfork()) goto cleanup;
    153         close(0);
    154         close(1);
    155         close(2);
    156       }
    157 
    158       for (;;) {
    159         pid_t child = 0;
    160 
    161         // For -l, call accept from the _new_ process.
    162 
    163         pollfds[0].fd = accept(sockfd, (struct sockaddr *)&address, &len);
    164         if (pollfds[0].fd<0) perror_exit("accept");
    165 
    166         // Do we need a tty?
    167 
    168         if (toys.optflags&FLAG_t)
    169           child = forkpty(&(pollfds[1].fd), NULL, NULL, NULL);
    170 
    171         // Do we need to fork and/or redirect for exec?
    172 
    173         else {
    174           if (toys.optflags&FLAG_L) {
    175             toys.stacktop = 0;
    176             child = vfork();
    177           }
    178           if (!child && toys.optc) {
    179             int fd = pollfds[0].fd;
    180 
    181             dup2(fd, 0);
    182             dup2(fd, 1);
    183             if (toys.optflags&FLAG_L) dup2(fd, 2);
    184             if (fd>2) close(fd);
    185           }
    186         }
    187 
    188         if (child<0) error_msg("Fork failed\n");
    189         if (child<1) break;
    190         close(pollfds[0].fd);
    191       }
    192     }
    193   }
    194 
    195   // We have a connection.  Disarm timeout.
    196   // (Does not play well with -L, but what _should_ that do?)
    197   set_alarm(0);
    198 
    199   if (CFG_NETCAT_LISTEN && ((toys.optflags&(FLAG_L|FLAG_l)) && toys.optc))
    200     xexec(toys.optargs);
    201 
    202   // Poll loop copying stdin->socket and socket->stdout.
    203   for (;;) {
    204     int i;
    205 
    206     if (0>poll(pollfds, pollcount, -1)) perror_exit("poll");
    207 
    208     for (i=0; i<pollcount; i++) {
    209       if (pollfds[i].revents & POLLIN) {
    210         int len = read(pollfds[i].fd, toybuf, sizeof(toybuf));
    211         if (len<1) goto dohupnow;
    212         xwrite(i ? pollfds[0].fd : 1, toybuf, len);
    213       } else if (pollfds[i].revents & POLLHUP) {
    214 dohupnow:
    215         // Close half-connection.  This is needed for things like
    216         // "echo GET / | netcat landley.net 80"
    217         if (i) {
    218           shutdown(pollfds[0].fd, SHUT_WR);
    219           pollcount--;
    220           set_alarm(TT.quit_delay);
    221         } else goto cleanup;
    222       }
    223     }
    224   }
    225 cleanup:
    226   if (CFG_TOYBOX_FREE) {
    227     close(pollfds[0].fd);
    228     close(sockfd);
    229   }
    230 }
    231