toybox/toys/net/netcat.c
<<
>>
Prefs
   1/* netcat.c - Forward stdin/stdout to a file or network connection.
   2 *
   3 * Copyright 2007 Rob Landley <rob@landley.net>
   4 *
   5 * TODO: udp, ipv6, genericize for telnet/microcom/tail-f
   6
   7USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
   8USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#W#p#s:q#f:"USE_NETCAT_LISTEN("[!tlL][!Lw]"), TOYFLAG_BIN))
   9
  10config 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
  26config 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
  49GLOBALS(
  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
  58static 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
  65static 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.
  72static 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.
  82static void lookup_port(char *str, uint16_t *port)
  83{
  84  *port = SWAP_BE16(atoi(str));
  85}
  86
  87void 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
 130      // We have a connection. Disarm timeout.
 131      set_alarm(0);
 132
 133      in1 = out2 = sockfd;
 134
 135      pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay);
 136    } else {
 137      // Listen for incoming connections
 138      socklen_t len = sizeof(*address);
 139
 140      if (listen(sockfd, 5)) error_exit("listen");
 141      if (!TT.port) {
 142        getsockname(sockfd, (struct sockaddr *)address, &len);
 143        printf("%d\n", SWAP_BE16(address->sin_port));
 144        fflush(stdout);
 145        // Return immediately if no -p and -Ll has arguments, so wrapper
 146        // script can use port number.
 147        if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup;
 148      }
 149
 150      do {
 151        child = 0;
 152        len = sizeof(*address); // gcc's insane optimizer can overwrite this
 153        in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len);
 154
 155        if (in1<0) perror_exit("accept");
 156
 157        // We can't exit this loop or the optimizer's "liveness analysis"
 158        // combines badly with vfork() to corrupt or local variables
 159        // (the child's call stack gets trimmed and the next function call
 160        // stops the variables the parent tries to re-use next loop)
 161        // So there's a bit of redundancy here
 162
 163        // We have a connection. Disarm timeout.
 164        set_alarm(0);
 165
 166        if (toys.optc) {
 167          // Do we need a tty?
 168
 169// TODO nommu, and -t only affects server mode...? Only do -t with optc
 170//        if (CFG_TOYBOX_FORK && (toys.optflags&FLAG_t))
 171//          child = forkpty(&fdout, NULL, NULL, NULL);
 172//        else
 173
 174          // Do we need to fork and/or redirect for exec?
 175
 176          if (toys.optflags&FLAG_L) {
 177            toys.stacktop = 0;
 178            child = vfork();
 179          }
 180          if (child<0) error_msg("vfork failed\n");
 181          else {
 182            if (child) {
 183              close(in1);
 184              continue;
 185            }
 186            dup2(in1, 0);
 187            dup2(in1, 1);
 188            if (toys.optflags&FLAG_L) dup2(in1, 2);
 189            if (in1>2) close(in1);
 190            xexec(toys.optargs);
 191          }
 192        }
 193
 194        pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay);
 195        close(in1);
 196      } while (!(toys.optflags&FLAG_l));
 197    }
 198  }
 199
 200cleanup:
 201  if (CFG_TOYBOX_FREE) {
 202    close(in1);
 203    close(sockfd);
 204  }
 205}
 206