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