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 
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 
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 
200 cleanup:
201   if (CFG_TOYBOX_FREE) {
202     close(in1);
203     close(sockfd);
204   }
205 }
206