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