Home | History | Annotate | Download | only in net
      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#W#p#s:q#f:"USE_NETCAT_LISTEN("[!tlL][!Lw]"), 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	quit SECONDS after EOF on stdin, even if stdout hasn't closed yet.
     19     -s	local source address
     20     -w	SECONDS timeout to establish connection
     21     -W	SECONDS timeout for idle connection
     22 
     23     Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
     24     netcat -f to connect to a serial port.
     25 
     26 config NETCAT_LISTEN
     27   bool "netcat server options (-let)"
     28   default y
     29   depends on NETCAT
     30   help
     31     usage: netcat [-t] [-lL COMMAND...]
     32 
     33     -l	listen for one incoming connection
     34     -L	listen for multiple incoming connections (server mode)
     35     -t	allocate tty (must come before -l or -L)
     36 
     37     The command line after -l or -L is executed (as a child process) to handle
     38     each incoming connection. If blank -l waits for a connection and forwards
     39     it to stdin/stdout. If no -p specified, -l prints port it bound to and
     40     backgrounds itself (returning immediately).
     41 
     42     For a quick-and-dirty server, try something like:
     43     netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l
     44 */
     45 
     46 #define FOR_netcat
     47 #include "toys.h"
     48 
     49 GLOBALS(
     50   char *filename;        // -f read from filename instead of network
     51   long quit_delay;       // -q Exit after EOF from stdin after # seconds.
     52   char *source_address;  // -s Bind to a specific source address.
     53   long port;             // -p Bind to a specific source port.
     54   long idle;             // -W Wait # seconds for more data
     55   long wait;             // -w Wait # seconds for a connection.
     56 )
     57 
     58 static void timeout(int signum)
     59 {
     60   if (TT.wait) error_exit("Timeout");
     61   // This should be xexit() but would need siglongjmp()...
     62   exit(0);
     63 }
     64 
     65 static void set_alarm(int seconds)
     66 {
     67   xsignal(SIGALRM, seconds ? timeout : SIG_DFL);
     68   alarm(seconds);
     69 }
     70 
     71 // Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name.
     72 static void lookup_name(char *name, uint32_t *result)
     73 {
     74   struct hostent *hostbyname;
     75 
     76   hostbyname = gethostbyname(name); // getaddrinfo
     77   if (!hostbyname) error_exit("no host '%s'", name);
     78   *result = *(uint32_t *)*hostbyname->h_addr_list;
     79 }
     80 
     81 // Worry about a fancy lookup later.
     82 static void lookup_port(char *str, uint16_t *port)
     83 {
     84   *port = SWAP_BE16(atoi(str));
     85 }
     86 
     87 void netcat_main(void)
     88 {
     89   struct sockaddr_in *address = (void *)toybuf;
     90   int sockfd=-1, in1 = 0, in2 = 0, out1 = 1, out2 = 1;
     91   pid_t child;
     92 
     93   // Addjust idle and quit_delay to miliseconds or -1 for no timeout
     94   TT.idle = TT.idle ? TT.idle*1000 : -1;
     95   TT.quit_delay = TT.quit_delay ? TT.quit_delay*1000 : -1;
     96 
     97   set_alarm(TT.wait);
     98 
     99   // The argument parsing logic can't make "<2" conditional on other
    100   // arguments like -f and -l, so we do it by hand here.
    101   if ((toys.optflags&FLAG_f) ? toys.optc :
    102       (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2))
    103         help_exit("bad argument count");
    104 
    105   if (TT.filename) in1 = out2 = xopen(TT.filename, O_RDWR);
    106   else {
    107     // Setup socket
    108     sockfd = xsocket(AF_INET, SOCK_STREAM, 0);
    109     fcntl(sockfd, F_SETFD, FD_CLOEXEC);
    110     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &out1, sizeof(out1));
    111     address->sin_family = AF_INET;
    112     if (TT.source_address || TT.port) {
    113       address->sin_port = SWAP_BE16(TT.port);
    114       if (TT.source_address)
    115         lookup_name(TT.source_address, (uint32_t *)&(address->sin_addr));
    116       if (bind(sockfd, (struct sockaddr *)address, sizeof(*address)))
    117         perror_exit("bind");
    118     }
    119 
    120     // Dial out
    121 
    122     if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) {
    123       // Figure out where to dial out to.
    124       lookup_name(*toys.optargs, (uint32_t *)&(address->sin_addr));
    125       lookup_port(toys.optargs[1], &(address->sin_port));
    126 // TODO xconnect
    127       if (connect(sockfd, (struct sockaddr *)address, sizeof(*address))<0)
    128         perror_exit("connect");
    129       in1 = out2 = sockfd;
    130 
    131     // Listen for incoming connections
    132 
    133     } else {
    134       socklen_t len = sizeof(*address);
    135 
    136       if (listen(sockfd, 5)) error_exit("listen");
    137       if (!TT.port) {
    138         getsockname(sockfd, (struct sockaddr *)address, &len);
    139         printf("%d\n", SWAP_BE16(address->sin_port));
    140         fflush(stdout);
    141         // Return immediately if no -p and -Ll has arguments, so wrapper
    142         // script can use port number.
    143         if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup;
    144       }
    145 
    146       for (;;) {
    147         child = 0;
    148         len = sizeof(*address); // gcc's insane optimizer can overwrite this
    149         in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len);
    150 
    151         if (in1<0) perror_exit("accept");
    152 
    153         // We can't exit this loop or the optimizer's "liveness analysis"
    154         // combines badly with vfork() to corrupt or local variables
    155         // (the child's call stack gets trimmed and the next function call
    156         // stops the variables the parent tries to re-use next loop)
    157         // So there's a bit of redundancy here
    158 
    159         // We have a connection. Disarm timeout.
    160         set_alarm(0);
    161 
    162         if (toys.optc) {
    163           // Do we need a tty?
    164 
    165 // TODO nommu, and -t only affects server mode...? Only do -t with optc
    166 //        if (CFG_TOYBOX_FORK && (toys.optflags&FLAG_t))
    167 //          child = forkpty(&fdout, NULL, NULL, NULL);
    168 //        else
    169 
    170           // Do we need to fork and/or redirect for exec?
    171 
    172           if (toys.optflags&FLAG_L) {
    173             toys.stacktop = 0;
    174             child = vfork();
    175           }
    176           if (child<0) error_msg("vfork failed\n");
    177           else {
    178             if (child) {
    179               close(in1);
    180               continue;
    181             }
    182             dup2(in1, 0);
    183             dup2(in1, 1);
    184             if (toys.optflags&FLAG_L) dup2(in1, 2);
    185             if (in1>2) close(in1);
    186             xexec(toys.optargs);
    187           }
    188         }
    189 
    190         pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay);
    191         close(in1);
    192       }
    193     }
    194   }
    195 
    196   // We have a connection. Disarm timeout.
    197   set_alarm(0);
    198 
    199   pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay);
    200 
    201 cleanup:
    202   if (CFG_TOYBOX_FREE) {
    203     close(in1);
    204     close(sockfd);
    205   }
    206 }
    207