xref: /aosp_15_r20/external/toybox/toys/net/netcat.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* netcat.c - Forward stdin/stdout to a file or network connection.
2  *
3  * Copyright 2007 Rob Landley <[email protected]>
4  *
5  * TODO: genericize for telnet/microcom/tail-f, fix -t with login_tty()
6 
7 USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO]", TOYFLAG_BIN))
8 USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN))
9 
10 config NETCAT
11   bool "netcat"
12   default y
13   help
14     usage: netcat [-46ELlntUu] [-pqWw #] [-s addr] [-o FILE] {IPADDR PORTNUM|-f FILENAME|COMMAND...}
15 
16     Forward stdin/stdout to a file or network connection.
17 
18     -4	Force IPv4
19     -6	Force IPv6
20     -E	Forward stderr
21     -f	Use FILENAME (ala /dev/ttyS0) instead of network
22     -L	Listen and background each incoming connection (server mode)
23     -l	Listen for one incoming connection, then exit
24     -n	No DNS lookup
25     -o	Hex dump to FILE (show packets, -o- writes hex only to stdout)
26     -O	Hex dump to FILE (streaming mode)
27     -p	Local port number
28     -q	Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet
29     -s	Local source address
30     -t	Allocate tty
31     -u	Use UDP
32     -U	Use a UNIX domain socket
33     -W	SECONDS timeout for more data on an idle connection
34     -w	SECONDS timeout to establish connection
35     -z	zero-I/O mode [used for scanning]
36 
37     When listening the COMMAND line is executed as a child process to handle
38     an incoming connection. With no COMMAND -l forwards the connection
39     to stdin/stdout. If no -p specified, -l prints the 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 sh -l
44 
45     Or use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
46     netcat -f to connect to a serial port.
47 */
48 
49 #define FOR_netcat
50 #include "toys.h"
51 
52 GLOBALS(
53   char *f, *s, *o, *O;
54   long q, p, W, w;
55 
56   unsigned ofd, olast, opos, ocount[2];
57   char obuf[16];
58 )
59 
timeout(int signum)60 static void timeout(int signum)
61 {
62   if (TT.w) error_exit("Timeout");
63   xexit();
64 }
65 
66 // open AF_UNIX socket
usock(char * name,int type,int out)67 static int usock(char *name, int type, int out)
68 {
69   int sockfd;
70   struct sockaddr_un sockaddr;
71 
72   memset(&sockaddr, 0, sizeof(struct sockaddr_un));
73 
74   if (strlen(name) + 1 > sizeof(sockaddr.sun_path))
75     error_exit("socket path too long %s", name);
76   strcpy(sockaddr.sun_path, name);
77   sockaddr.sun_family = AF_UNIX;
78 
79   sockfd = xsocket(AF_UNIX, type, 0);
80   (out?xconnect:xbind)(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
81 
82   return sockfd;
83 }
84 
85 // Hex dump accumulated buffer data
oflush(void)86 void oflush(void)
87 {
88   char *s = toybuf;
89   unsigned *oc = TT.ocount+(TT.olast==1), uu;
90 
91   if (!TT.opos) return;
92   s += sprintf(toybuf, "%c %08x", 60+2*(TT.olast==1), *oc);
93   for (uu = 0; uu<16; uu++) {
94     s += sprintf(s, uu<TT.opos ? " %02x" : "   ", TT.obuf[uu]);
95     if (TT.obuf[uu]-32u>95) TT.obuf[uu] = '.';
96   }
97   dprintf(TT.ofd, "%s # %.*s\n", toybuf, TT.opos, TT.obuf);
98   *oc += TT.opos;
99   TT.opos = 0;
100 }
101 
102 // Write data to output, and hex dump to -o if enabled.
ohexwrite(int fd,void * buf,size_t len)103 void ohexwrite(int fd, void *buf, size_t len)
104 {
105   // Hex dump if -o specified. Output is always to fd 1, input != 1.
106   if (TT.ofd) {
107     int i = 0, j;
108 
109     if (TT.olast != fd) oflush();
110     TT.olast = fd;
111 
112     while (i<len) {
113       j = minof(16-TT.opos, len-i);
114       memcpy(TT.obuf+TT.opos, buf+i, j);
115       TT.opos += j;
116       i += j;
117       if (TT.opos==16 || !TT.O) oflush();
118     }
119 
120     // Don't write data to stdout when -o goes to stdout.
121     if (TT.ofd==1 && fd==1) return;
122   }
123 
124   // Pass along raw data
125   xwrite(fd, buf, len);
126 }
127 
netcat_main(void)128 void netcat_main(void)
129 {
130   int sockfd = -1, in1 = 0, in2 = 0, out1 = 1, out2 = 1, family = AF_UNSPEC,
131     type = FLAG(u) ? SOCK_DGRAM : SOCK_STREAM;
132   socklen_t len;
133   pid_t child;
134 
135   // -o - disables normal writes to stdout, just gives hex dump.
136   if (TT.O) TT.o = TT.O;
137   if (TT.o) {
138     if (!strcmp(TT.o, "-")) TT.ofd = 1;
139     else TT.ofd = xcreate(TT.o, O_CREAT|O_TRUNC|O_WRONLY, 0666);
140     sigatexit(oflush);
141   }
142 
143   // Adjust idle and quit_delay to ms or -1 for no timeout
144   TT.W = TT.W ? TT.W*1000 : -1;
145   TT.q = TT.q ? TT.q*1000 : -1;
146 
147   xsignal(SIGCHLD, SIG_IGN);
148   if (TT.w) {
149     xsignal(SIGALRM, timeout);
150     alarm(TT.w);
151   }
152 
153   // The argument parsing logic can't make "<2" conditional on other
154   // arguments like -f and -l, so do it by hand here.
155   if (FLAG(f) ? toys.optc : (!FLAG(l) && !FLAG(L) && toys.optc!=2-FLAG(U)))
156     help_exit("bad argument count");
157 
158   if (FLAG(4)) family = AF_INET;
159   else if (FLAG(6)) family = AF_INET6;
160   else if (FLAG(U)) family = AF_UNIX;
161 
162   if (TT.f) {
163     in1 = out2 = xopen(TT.f, O_RDWR);
164     alarm(0);
165     pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
166   } else {
167     // Setup socket
168     if (!FLAG(l) && !FLAG(L)) {
169       if (FLAG(U)) sockfd = usock(toys.optargs[0], type, 1);
170       else sockfd = xconnectany(xgetaddrinfo(toys.optargs[0], toys.optargs[1],
171         family, type, 0, AI_NUMERICHOST*FLAG(n)));
172 
173       // Do not perform any I/O in zero mode
174       if (FLAG(z)) goto cleanup;
175 
176       // We have a connection. Disarm timeout and start poll/send loop.
177       alarm(0);
178       in1 = out2 = sockfd;
179       pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
180     } else {
181       // Listen for incoming connections
182       if (FLAG(U)) {
183         if (!FLAG(s)) error_exit("-s must be provided if using -U with -L/-l");
184         sockfd = usock(TT.s, type, 0);
185       } else {
186         sprintf(toybuf, "%ld", TT.p);
187         sockfd = xbindany(xgetaddrinfo(TT.s, toybuf, family, type, 0, 0));
188       }
189 
190       if (!FLAG(u) && listen(sockfd, 5)) perror_exit("listen");
191       if (!TT.p && !FLAG(U)) {
192         struct sockaddr* address = (void*)toybuf;
193         short port_be;
194 
195         len = sizeof(struct sockaddr_storage);
196         getsockname(sockfd, address, &len);
197         if (address->sa_family == AF_INET)
198           port_be = ((struct sockaddr_in*)address)->sin_port;
199         else if (address->sa_family == AF_INET6)
200           port_be = ((struct sockaddr_in6*)address)->sin6_port;
201         else perror_exit("getsockname: bad family");
202 
203         dprintf(1, "%d\n", SWAP_BE16(port_be));
204         // Return immediately if no -p and -Ll has arguments, so wrapper
205         // script can use port number.
206         if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup;
207       }
208 
209       do {
210         len = sizeof(struct sockaddr_storage);
211         if (FLAG(u)) {
212           if (-1 == recvfrom(in1 = dup(sockfd), &child, 1, MSG_PEEK,
213             (void *)toybuf, &len)) perror_exit("recvfrom");
214         } else if ((in1 = accept(sockfd, 0, 0))<0) perror_exit("accept");
215         out2 = in1;
216         child = 0;
217 
218         // We have a connection. Disarm timeout.
219         alarm(0);
220 
221         // Fork a child as necessary. Parent cleans up and continues here.
222         if (toys.optc && FLAG(L)) NOEXIT(child = XVFORK());
223         if (child) {
224           close(in1);
225           continue;
226         }
227 
228         if (FLAG(u))
229           xconnect(in1, (void *)toybuf, sizeof(struct sockaddr_storage));
230 
231         // Cleanup and redirect for exec
232         if (toys.optc) {
233           // Do we need a tty?
234 // TODO nommu and -t only affects server mode...
235 //        if (FLAG(t)) child = forkpty(&fdout, NULL, NULL, NULL);
236 
237           close(sockfd);
238           dup2(in1, 0);
239           dup2(in1, 1);
240           if (FLAG(E)) dup2(in1, 2);
241           if (in1>2) close(in1);
242           xexec(toys.optargs);
243 
244         // Copy stdin/out
245         } else {
246           pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q);
247           close(in1);
248         }
249       } while (FLAG(L));
250     }
251   }
252 
253 cleanup:
254   if (CFG_TOYBOX_FREE) {
255     close(in1);
256     close(sockfd);
257   }
258 }
259