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 
GLOBALS(char * filename;long quit_delay;char * source_address;long port;long idle;long wait;)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 
set_alarm(int seconds)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.
lookup_name(char * name,uint32_t * result)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.
lookup_port(char * str,uint16_t * port)82 static void lookup_port(char *str, uint16_t *port)
83 {
84   *port = SWAP_BE16(atoi(str));
85 }
86 
netcat_main(void)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