blob: 00ba6f114657cf7660132fb670b4b53637e61aab [file] [log] [blame]
Denis Vlasenko29fe7262007-04-05 20:26:28 +00001/* Based on netcat 1.10 RELEASE 960320 written by hobbit@avian.org.
2 * Released into public domain by the author.
3 *
Denis Vlasenko7cff01e2008-02-02 16:23:43 +00004 * Copyright (C) 2007 Denys Vlasenko.
Denis Vlasenko29fe7262007-04-05 20:26:28 +00005 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko29fe7262007-04-05 20:26:28 +00007 */
8
9/* Author's comments from nc 1.10:
10 * =====================
11 * Netcat is entirely my own creation, although plenty of other code was used as
12 * examples. It is freely given away to the Internet community in the hope that
13 * it will be useful, with no restrictions except giving credit where it is due.
14 * No GPLs, Berkeley copyrights or any of that nonsense. The author assumes NO
15 * responsibility for how anyone uses it. If netcat makes you rich somehow and
16 * you're feeling generous, mail me a check. If you are affiliated in any way
17 * with Microsoft Network, get a life. Always ski in control. Comments,
18 * questions, and patches to hobbit@avian.org.
19 * ...
20 * Netcat and the associated package is a product of Avian Research, and is freely
21 * available in full source form with no restrictions save an obligation to give
22 * credit where due.
23 * ...
24 * A damn useful little "backend" utility begun 950915 or thereabouts,
25 * as *Hobbit*'s first real stab at some sockets programming. Something that
26 * should have and indeed may have existed ten years ago, but never became a
27 * standard Unix utility. IMHO, "nc" could take its place right next to cat,
28 * cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things.
29 * =====================
30 *
31 * Much of author's comments are still retained in the code.
32 *
33 * Functionality removed (rationale):
34 * - miltiple-port ranges, randomized port scanning (use nmap)
35 * - telnet support (use telnet)
36 * - source routing
37 * - multiple DNS checks
38 * Functionalty which is different from nc 1.10:
Denys Vlasenko7d1201c2010-06-28 04:17:06 +020039 * - PROG in '-e PROG' can have ARGS (and options).
Denis Vlasenko29fe7262007-04-05 20:26:28 +000040 * Because of this -e option must be last.
Denys Vlasenko7d1201c2010-06-28 04:17:06 +020041//TODO: remove -e incompatibility?
42 * - we don't redirect stderr to the network socket for the -e PROG.
43 * (PROG can do it itself if needed, but sometimes it is NOT wanted!)
Denis Vlasenko19507f02007-04-06 10:41:05 +000044 * - numeric addresses are printed in (), not [] (IPv6 looks better),
45 * port numbers are inside (): (1.2.3.4:5678)
46 * - network read errors are reported on verbose levels > 1
47 * (nc 1.10 treats them as EOF)
48 * - TCP connects from wrong ip/ports (if peer ip:port is specified
49 * on the command line, but accept() says that it came from different addr)
Denys Vlasenko7d1201c2010-06-28 04:17:06 +020050 * are closed, but we don't exit - we continue to listen/accept.
Denis Vlasenko29fe7262007-04-05 20:26:28 +000051 */
52
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000053/* done in nc.c: #include "libbb.h" */
Denis Vlasenko29fe7262007-04-05 20:26:28 +000054
Denys Vlasenko77cc2c52010-06-27 04:22:02 +020055//usage:#if ENABLE_NC_110_COMPAT
56//usage:
57//usage:#define nc_trivial_usage
58//usage: "[OPTIONS] HOST PORT - connect"
59//usage: IF_NC_SERVER("\n"
Denys Vlasenko3b2acb72010-10-09 21:10:32 +020060//usage: "nc [OPTIONS] -l -p PORT [HOST] [PORT] - listen"
61//usage: )
Denys Vlasenko77cc2c52010-06-27 04:22:02 +020062//usage:#define nc_full_usage "\n\n"
Denys Vlasenko66426762011-06-05 03:58:28 +020063//usage: " -e PROG Run PROG after connect (must be last)"
Denys Vlasenko77cc2c52010-06-27 04:22:02 +020064//usage: IF_NC_SERVER(
65//usage: "\n -l Listen mode, for inbound connects"
Denys Vlasenkode6f1482013-02-28 12:20:06 +010066//usage: "\n -lk With -e, provides persistent server"
67/* -ll does the same as -lk, but its our extension, while -k is BSD'd,
68 * presumably more widely known. Therefore we advertise it, not -ll.
69 * I would like to drop -ll support, but our "small" nc supports it,
70 * and Rob uses it.
71 */
Denys Vlasenko77cc2c52010-06-27 04:22:02 +020072//usage: )
73//usage: "\n -p PORT Local port"
74//usage: "\n -s ADDR Local address"
75//usage: "\n -w SEC Timeout for connects and final net reads"
76//usage: IF_NC_EXTRA(
77//usage: "\n -i SEC Delay interval for lines sent" /* ", ports scanned" */
78//usage: )
79//usage: "\n -n Don't do DNS resolution"
80//usage: "\n -u UDP mode"
81//usage: "\n -v Verbose"
82//usage: IF_NC_EXTRA(
83//usage: "\n -o FILE Hex dump traffic"
84//usage: "\n -z Zero-I/O mode (scanning)"
85//usage: )
86//usage:#endif
87
88/* "\n -r Randomize local and remote ports" */
89/* "\n -g gateway Source-routing hop point[s], up to 8" */
90/* "\n -G num Source-routing pointer: 4, 8, 12, ..." */
91/* "\nport numbers can be individual or ranges: lo-hi [inclusive]" */
92
93/* -e PROG can take ARGS too: "nc ... -e ls -l", but we don't document it
94 * in help text: nc 1.10 does not allow that. We don't want to entice
95 * users to use this incompatibility */
96
Denis Vlasenko19507f02007-04-06 10:41:05 +000097enum {
98 SLEAZE_PORT = 31337, /* for UDP-scan RTT trick, change if ya want */
99 BIGSIZ = 8192, /* big buffers */
100
101 netfd = 3,
102 ofd = 4,
103};
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000104
105struct globals {
Denis Vlasenko19507f02007-04-06 10:41:05 +0000106 /* global cmd flags: */
107 unsigned o_verbose;
108 unsigned o_wait;
109#if ENABLE_NC_EXTRA
110 unsigned o_interval;
111#endif
112
113 /*int netfd;*/
114 /*int ofd;*/ /* hexdump output fd */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000115#if ENABLE_LFS
116#define SENT_N_RECV_M "sent %llu, rcvd %llu\n"
117 unsigned long long wrote_out; /* total stdout bytes */
118 unsigned long long wrote_net; /* total net bytes */
119#else
Denis Vlasenko19507f02007-04-06 10:41:05 +0000120#define SENT_N_RECV_M "sent %u, rcvd %u\n"
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000121 unsigned wrote_out; /* total stdout bytes */
122 unsigned wrote_net; /* total net bytes */
123#endif
Denys Vlasenko5e896482012-03-19 01:17:36 +0100124 char *proggie0saved;
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000125 /* ouraddr is never NULL and goes through three states as we progress:
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000126 1 - local address before bind (IP/port possibly zero)
127 2 - local address after bind (port is nonzero)
128 3 - local address after connect??/recv/accept (IP and port are nonzero) */
129 struct len_and_sockaddr *ouraddr;
130 /* themaddr is NULL if no peer hostname[:port] specified on command line */
131 struct len_and_sockaddr *themaddr;
132 /* remend is set after connect/recv/accept to the actual ip:port of peer */
133 struct len_and_sockaddr remend;
134
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000135 jmp_buf jbuf; /* timer crud */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000136
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000137 fd_set ding1; /* for select loop */
138 fd_set ding2;
139 char bigbuf_in[BIGSIZ]; /* data buffers */
140 char bigbuf_net[BIGSIZ];
141};
142
143#define G (*ptr_to_globals)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000144#define wrote_out (G.wrote_out )
145#define wrote_net (G.wrote_net )
146#define ouraddr (G.ouraddr )
147#define themaddr (G.themaddr )
148#define remend (G.remend )
149#define jbuf (G.jbuf )
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000150#define ding1 (G.ding1 )
151#define ding2 (G.ding2 )
152#define bigbuf_in (G.bigbuf_in )
153#define bigbuf_net (G.bigbuf_net)
154#define o_verbose (G.o_verbose )
155#define o_wait (G.o_wait )
156#if ENABLE_NC_EXTRA
157#define o_interval (G.o_interval)
158#else
159#define o_interval 0
160#endif
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000161#define INIT_G() do { \
162 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
163} while (0)
164
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000165
166/* Must match getopt32 call! */
167enum {
Denys Vlasenko5e896482012-03-19 01:17:36 +0100168 OPT_n = (1 << 0),
169 OPT_p = (1 << 1),
170 OPT_s = (1 << 2),
171 OPT_u = (1 << 3),
172 OPT_v = (1 << 4),
173 OPT_w = (1 << 5),
174 OPT_l = (1 << 6) * ENABLE_NC_SERVER,
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100175 OPT_k = (1 << 7) * ENABLE_NC_SERVER,
176 OPT_i = (1 << (7+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
177 OPT_o = (1 << (8+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
178 OPT_z = (1 << (9+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000179};
180
181#define o_nflag (option_mask32 & OPT_n)
182#define o_udpmode (option_mask32 & OPT_u)
Denis Vlasenko19507f02007-04-06 10:41:05 +0000183#if ENABLE_NC_EXTRA
184#define o_ofile (option_mask32 & OPT_o)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000185#define o_zero (option_mask32 & OPT_z)
186#else
Denis Vlasenko19507f02007-04-06 10:41:05 +0000187#define o_ofile 0
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000188#define o_zero 0
189#endif
190
Denis Vlasenko19507f02007-04-06 10:41:05 +0000191/* Debug: squirt whatever message and sleep a bit so we can see it go by. */
192/* Beware: writes to stdOUT... */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000193#if 0
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100194#define Debug(...) do { printf(__VA_ARGS__); printf("\n"); fflush_all(); sleep(1); } while (0)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000195#else
Denis Vlasenko51742f42007-04-12 00:32:05 +0000196#define Debug(...) do { } while (0)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000197#endif
198
Denis Vlasenko51742f42007-04-12 00:32:05 +0000199#define holler_error(...) do { if (o_verbose) bb_error_msg(__VA_ARGS__); } while (0)
200#define holler_perror(...) do { if (o_verbose) bb_perror_msg(__VA_ARGS__); } while (0)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000201
202/* catch: no-brainer interrupt handler */
203static void catch(int sig)
204{
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000205 if (o_verbose > 1) /* normally we don't care */
206 fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
207 fprintf(stderr, "punt!\n");
Denis Vlasenko400d8bb2008-02-24 13:36:01 +0000208 kill_myself_with_sig(sig);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000209}
210
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000211/* unarm */
212static void unarm(void)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000213{
214 signal(SIGALRM, SIG_IGN);
215 alarm(0);
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000216}
217
218/* timeout and other signal handling cruft */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000219static void tmtravel(int sig UNUSED_PARAM)
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000220{
221 unarm();
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000222 longjmp(jbuf, 1);
223}
224
225/* arm: set the timer. */
226static void arm(unsigned secs)
227{
228 signal(SIGALRM, tmtravel);
229 alarm(secs);
230}
231
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000232/* findline:
233 find the next newline in a buffer; return inclusive size of that "line",
234 or the entire buffer size, so the caller knows how much to then write().
235 Not distinguishing \n vs \r\n for the nonce; it just works as is... */
236static unsigned findline(char *buf, unsigned siz)
237{
238 char * p;
239 int x;
240 if (!buf) /* various sanity checks... */
241 return 0;
242 if (siz > BIGSIZ)
243 return 0;
244 x = siz;
245 for (p = buf; x > 0; x--) {
246 if (*p == '\n') {
247 x = (int) (p - buf);
248 x++; /* 'sokay if it points just past the end! */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000249Debug("findline returning %d", x);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000250 return x;
251 }
252 p++;
253 } /* for */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000254Debug("findline returning whole thing: %d", siz);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000255 return siz;
256} /* findline */
257
258/* doexec:
259 fiddle all the file descriptors around, and hand off to another prog. Sort
260 of like a one-off "poor man's inetd". This is the only section of code
261 that would be security-critical, which is why it's ifdefed out by default.
262 Use at your own hairy risk; if you leave shells lying around behind open
263 listening ports you deserve to lose!! */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000264static int doexec(char **proggie) NORETURN;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000265static int doexec(char **proggie)
266{
Denys Vlasenko5e896482012-03-19 01:17:36 +0100267 if (G.proggie0saved)
268 proggie[0] = G.proggie0saved;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000269 xmove_fd(netfd, 0);
270 dup2(0, 1);
271 /* dup2(0, 2); - do we *really* want this? NO!
272 * exec'ed prog can do it yourself, if needed */
Denys Vlasenko1c31e9e2010-11-28 04:34:09 +0100273 BB_EXECVP_or_die(proggie);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000274}
275
276/* connect_w_timeout:
277 return an fd for one of
278 an open outbound TCP connection, a UDP stub-socket thingie, or
279 an unconnected TCP or UDP socket to listen on.
280 Examines various global o_blah flags to figure out what to do.
281 lad can be NULL, then socket is not bound to any local ip[:port] */
282static int connect_w_timeout(int fd)
283{
284 int rr;
285
286 /* wrap connect inside a timer, and hit it */
287 arm(o_wait);
288 if (setjmp(jbuf) == 0) {
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000289 rr = connect(fd, &themaddr->u.sa, themaddr->len);
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000290 unarm();
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000291 } else { /* setjmp: connect failed... */
292 rr = -1;
293 errno = ETIMEDOUT; /* fake it */
294 }
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000295 return rr;
296}
297
298/* dolisten:
299 listens for
300 incoming and returns an open connection *from* someplace. If we were
301 given host/port args, any connections from elsewhere are rejected. This
302 in conjunction with local-address binding should limit things nicely... */
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100303static void dolisten(int is_persistent, char **proggie)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000304{
305 int rr;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000306
307 if (!o_udpmode)
308 xlisten(netfd, 1); /* TCP: gotta listen() before we can get */
309
310 /* Various things that follow temporarily trash bigbuf_net, which might contain
311 a copy of any recvfrom()ed packet, but we'll read() another copy later. */
312
313 /* I can't believe I have to do all this to get my own goddamn bound address
314 and port number. It should just get filled in during bind() or something.
315 All this is only useful if we didn't say -p for listening, since if we
316 said -p we *know* what port we're listening on. At any rate we won't bother
317 with it all unless we wanted to see it, although listening quietly on a
318 random unknown port is probably not very useful without "netstat". */
319 if (o_verbose) {
320 char *addr;
Denis Vlasenkoa771e7c2009-04-21 23:48:38 +0000321 getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
322 //if (rr < 0)
323 // bb_perror_msg_and_die("getsockname after bind");
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000324 addr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000325 fprintf(stderr, "listening on %s ...\n", addr);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000326 free(addr);
327 }
328
329 if (o_udpmode) {
330 /* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
331 party's particulars all at once, listen() and accept() don't apply.
332 At least in the BSD universe, however, recvfrom/PEEK is enough to tell
333 us something came in, and we can set things up so straight read/write
334 actually does work after all. Yow. YMMV on strange platforms! */
335
336 /* I'm not completely clear on how this works -- BSD seems to make UDP
337 just magically work in a connect()ed context, but we'll undoubtedly run
338 into systems this deal doesn't work on. For now, we apparently have to
339 issue a connect() on our just-tickled socket so we can write() back.
340 Again, why the fuck doesn't it just get filled in and taken care of?!
341 This hack is anything but optimal. Basically, if you want your listener
342 to also be able to send data back, you need this connect() line, which
343 also has the side effect that now anything from a different source or even a
344 different port on the other end won't show up and will cause ICMP errors.
345 I guess that's what they meant by "connect".
346 Let's try to remember what the "U" is *really* for, eh? */
347
348 /* If peer address is specified, connect to it */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000349 remend.len = LSA_SIZEOF_SA;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000350 if (themaddr) {
351 remend = *themaddr;
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000352 xconnect(netfd, &themaddr->u.sa, themaddr->len);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000353 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000354 /* peek first packet and remember peer addr */
355 arm(o_wait); /* might as well timeout this, too */
356 if (setjmp(jbuf) == 0) { /* do timeout for initial connect */
357 /* (*ouraddr) is prefilled with "default" address */
358 /* and here we block... */
359 rr = recv_from_to(netfd, NULL, 0, MSG_PEEK, /*was bigbuf_net, BIGSIZ*/
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000360 &remend.u.sa, &ouraddr->u.sa, ouraddr->len);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000361 if (rr < 0)
362 bb_perror_msg_and_die("recvfrom");
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000363 unarm();
Denis Vlasenko19507f02007-04-06 10:41:05 +0000364 } else
365 bb_error_msg_and_die("timeout");
Denis Vlasenko19507f02007-04-06 10:41:05 +0000366/* Now we learned *to which IP* peer has connected, and we want to anchor
367our socket on it, so that our outbound packets will have correct local IP.
368Unfortunately, bind() on already bound socket will fail now (EINVAL):
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000369 xbind(netfd, &ouraddr->u.sa, ouraddr->len);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000370Need to read the packet, save data, close this socket and
371create new one, and bind() it. TODO */
372 if (!themaddr)
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000373 xconnect(netfd, &remend.u.sa, ouraddr->len);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000374 } else {
375 /* TCP */
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100376 another:
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000377 arm(o_wait); /* wrap this in a timer, too; 0 = forever */
378 if (setjmp(jbuf) == 0) {
Denis Vlasenko19507f02007-04-06 10:41:05 +0000379 again:
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000380 remend.len = LSA_SIZEOF_SA;
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000381 rr = accept(netfd, &remend.u.sa, &remend.len);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000382 if (rr < 0)
383 bb_perror_msg_and_die("accept");
Denys Vlasenko866710a2010-01-08 16:09:45 +0100384 if (themaddr) {
385 int sv_port, port, r;
386
387 sv_port = get_nport(&remend.u.sa); /* save */
388 port = get_nport(&themaddr->u.sa);
389 if (port == 0) {
390 /* "nc -nl -p LPORT RHOST" (w/o RPORT!):
391 * we should accept any remote port */
Denys Vlasenkoca183112011-04-07 17:52:20 +0200392 set_nport(&remend.u.sa, 0); /* blot out remote port# */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000393 }
Denys Vlasenko866710a2010-01-08 16:09:45 +0100394 r = memcmp(&remend.u.sa, &themaddr->u.sa, remend.len);
Denys Vlasenkoca183112011-04-07 17:52:20 +0200395 set_nport(&remend.u.sa, sv_port); /* restore */
Denys Vlasenko866710a2010-01-08 16:09:45 +0100396 if (r != 0) {
397 /* nc 1.10 bails out instead, and its error message
398 * is not suppressed by o_verbose */
399 if (o_verbose) {
400 char *remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
401 bb_error_msg("connect from wrong ip/port %s ignored", remaddr);
402 free(remaddr);
403 }
404 close(rr);
405 goto again;
406 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000407 }
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000408 unarm();
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000409 } else
410 bb_error_msg_and_die("timeout");
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100411
412 if (is_persistent && proggie) {
413 /* -l -k -e PROG */
414 signal(SIGCHLD, SIG_IGN); /* no zombies please */
415 if (xvfork() != 0) {
416 /* parent: go back and accept more connections */
417 close(rr);
418 goto another;
419 }
420 /* child */
421 signal(SIGCHLD, SIG_DFL);
422 }
423
Denis Vlasenko19507f02007-04-06 10:41:05 +0000424 xmove_fd(rr, netfd); /* dump the old socket, here's our new one */
425 /* find out what address the connection was *to* on our end, in case we're
426 doing a listen-on-any on a multihomed machine. This allows one to
427 offer different services via different alias addresses, such as the
428 "virtual web site" hack. */
Denis Vlasenkoa771e7c2009-04-21 23:48:38 +0000429 getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
430 //if (rr < 0)
431 // bb_perror_msg_and_die("getsockname after accept");
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000432 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000433
434 if (o_verbose) {
435 char *lcladdr, *remaddr, *remhostname;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000436
437#if ENABLE_NC_EXTRA && defined(IP_OPTIONS)
438 /* If we can, look for any IP options. Useful for testing the receiving end of
439 such things, and is a good exercise in dealing with it. We do this before
440 the connect message, to ensure that the connect msg is uniformly the LAST
441 thing to emerge after all the intervening crud. Doesn't work for UDP on
442 any machines I've tested, but feel free to surprise me. */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000443 char optbuf[40];
Denis Vlasenkoc4f12f52008-05-12 14:35:56 +0000444 socklen_t x = sizeof(optbuf);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000445
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000446 rr = getsockopt(netfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
Denis Vlasenkof6b46852009-04-25 13:16:53 +0000447 if (rr >= 0 && x) { /* we've got options, lessee em... */
Denys Vlasenko2ea73b52011-10-19 22:31:01 +0200448 *bin2hex(bigbuf_net, optbuf, x) = '\0';
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000449 fprintf(stderr, "IP options: %s\n", bigbuf_net);
450 }
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000451#endif
452
453 /* now check out who it is. We don't care about mismatched DNS names here,
454 but any ADDR and PORT we specified had better fucking well match the caller.
455 Converting from addr to inet_ntoa and back again is a bit of a kludge, but
456 gethostpoop wants a string and there's much gnarlier code out there already,
457 so I don't feel bad.
458 The *real* question is why BFD sockets wasn't designed to allow listens for
459 connections *from* specific hosts/ports, instead of requiring the caller to
460 accept the connection and then reject undesireable ones by closing.
461 In other words, we need a TCP MSG_PEEK. */
462 /* bbox: removed most of it */
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000463 lcladdr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
464 remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
465 remhostname = o_nflag ? remaddr : xmalloc_sockaddr2host(&remend.u.sa);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000466 fprintf(stderr, "connect to %s from %s (%s)\n",
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000467 lcladdr, remhostname, remaddr);
468 free(lcladdr);
469 free(remaddr);
470 if (!o_nflag)
471 free(remhostname);
472 }
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100473
474 if (proggie)
475 doexec(proggie);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000476}
477
478/* udptest:
479 fire a couple of packets at a UDP target port, just to see if it's really
480 there. On BSD kernels, ICMP host/port-unreachable errors get delivered to
481 our socket as ECONNREFUSED write errors. On SV kernels, we lose; we'll have
482 to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
483 backend. Guess where one could swipe the appropriate code from...
484
485 Use the time delay between writes if given, otherwise use the "tcp ping"
486 trick for getting the RTT. [I got that idea from pluvius, and warped it.]
487 Return either the original fd, or clean up and return -1. */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000488#if ENABLE_NC_EXTRA
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000489static int udptest(void)
490{
491 int rr;
492
493 rr = write(netfd, bigbuf_in, 1);
494 if (rr != 1)
495 bb_perror_msg("udptest first write");
496
497 if (o_wait)
Denis Vlasenko19507f02007-04-06 10:41:05 +0000498 sleep(o_wait); // can be interrupted! while (t) nanosleep(&t)?
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000499 else {
500 /* use the tcp-ping trick: try connecting to a normally refused port, which
501 causes us to block for the time that SYN gets there and RST gets back.
502 Not completely reliable, but it *does* mostly work. */
503 /* Set a temporary connect timeout, so packet filtration doesnt cause
504 us to hang forever, and hit it */
505 o_wait = 5; /* enough that we'll notice?? */
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000506 rr = xsocket(ouraddr->u.sa.sa_family, SOCK_STREAM, 0);
Denys Vlasenkoca183112011-04-07 17:52:20 +0200507 set_nport(&themaddr->u.sa, htons(SLEAZE_PORT));
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000508 connect_w_timeout(rr);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000509 /* don't need to restore themaddr's port, it's not used anymore */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000510 close(rr);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000511 o_wait = 0; /* restore */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000512 }
513
514 rr = write(netfd, bigbuf_in, 1);
515 return (rr != 1); /* if rr == 1, return 0 (success) */
516}
Denis Vlasenko19507f02007-04-06 10:41:05 +0000517#else
518int udptest(void);
519#endif
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000520
521/* oprint:
522 Hexdump bytes shoveled either way to a running logfile, in the format:
523 D offset - - - - --- 16 bytes --- - - - - # .... ascii .....
524 where "which" sets the direction indicator, D:
525 0 -- sent to network, or ">"
526 1 -- rcvd and printed to stdout, or "<"
527 and "buf" and "n" are data-block and length. If the current block generates
528 a partial line, so be it; we *want* that lockstep indication of who sent
529 what when. Adapted from dgaudet's original example -- but must be ripping
530 *fast*, since we don't want to be too disk-bound... */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000531#if ENABLE_NC_EXTRA
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000532static void oprint(int direction, unsigned char *p, unsigned bc)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000533{
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000534 unsigned obc; /* current "global" offset */
535 unsigned x;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000536 unsigned char *op; /* out hexdump ptr */
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000537 unsigned char *ap; /* out asc-dump ptr */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000538 unsigned char stage[100];
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000539
Denis Vlasenko19507f02007-04-06 10:41:05 +0000540 if (bc == 0)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000541 return;
542
Denis Vlasenko19507f02007-04-06 10:41:05 +0000543 obc = wrote_net; /* use the globals! */
544 if (direction == '<')
545 obc = wrote_out;
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000546 stage[0] = direction;
547 stage[59] = '#'; /* preload separator */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000548 stage[60] = ' ';
549
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000550 do { /* for chunk-o-data ... */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000551 x = 16;
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000552 if (bc < 16) {
553 /* memset(&stage[bc*3 + 11], ' ', 16*3 - bc*3); */
554 memset(&stage[11], ' ', 16*3);
555 x = bc;
556 }
Denis Vlasenkoc4f12f52008-05-12 14:35:56 +0000557 sprintf((char *)&stage[1], " %8.8x ", obc); /* xxx: still slow? */
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000558 bc -= x; /* fix current count */
559 obc += x; /* fix current offset */
560 op = &stage[11]; /* where hex starts */
561 ap = &stage[61]; /* where ascii starts */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000562
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000563 do { /* for line of dump, however long ... */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000564 *op++ = 0x20 | bb_hexdigits_upcase[*p >> 4];
565 *op++ = 0x20 | bb_hexdigits_upcase[*p & 0x0f];
566 *op++ = ' ';
567 if ((*p > 31) && (*p < 127))
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000568 *ap = *p; /* printing */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000569 else
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000570 *ap = '.'; /* nonprinting, loose def */
571 ap++;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000572 p++;
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000573 } while (--x);
574 *ap++ = '\n'; /* finish the line */
575 xwrite(ofd, stage, ap - stage);
576 } while (bc);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000577}
Denis Vlasenko19507f02007-04-06 10:41:05 +0000578#else
Denis Vlasenko828e2a92007-07-13 12:37:31 +0000579void oprint(int direction, unsigned char *p, unsigned bc);
Denis Vlasenko19507f02007-04-06 10:41:05 +0000580#endif
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000581
582/* readwrite:
583 handle stdin/stdout/network I/O. Bwahaha!! -- the select loop from hell.
584 In this instance, return what might become our exit status. */
585static int readwrite(void)
586{
587 int rr;
588 char *zp = zp; /* gcc */ /* stdin buf ptr */
589 char *np = np; /* net-in buf ptr */
590 unsigned rzleft;
591 unsigned rnleft;
592 unsigned netretry; /* net-read retry counter */
593 unsigned wretry; /* net-write sanity counter */
594 unsigned wfirst; /* one-shot flag to skip first net read */
595
596 /* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
597 either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
598 FD_SET(netfd, &ding1); /* global: the net is open */
599 netretry = 2;
600 wfirst = 0;
601 rzleft = rnleft = 0;
602 if (o_interval)
603 sleep(o_interval); /* pause *before* sending stuff, too */
604
605 errno = 0; /* clear from sleep, close, whatever */
606 /* and now the big ol' select shoveling loop ... */
607 while (FD_ISSET(netfd, &ding1)) { /* i.e. till the *net* closes! */
608 wretry = 8200; /* more than we'll ever hafta write */
609 if (wfirst) { /* any saved stdin buffer? */
610 wfirst = 0; /* clear flag for the duration */
611 goto shovel; /* and go handle it first */
612 }
613 ding2 = ding1; /* FD_COPY ain't portable... */
614 /* some systems, notably linux, crap into their select timers on return, so
615 we create a expendable copy and give *that* to select. */
616 if (o_wait) {
617 struct timeval tmp_timer;
618 tmp_timer.tv_sec = o_wait;
619 tmp_timer.tv_usec = 0;
Denis Vlasenko19507f02007-04-06 10:41:05 +0000620 /* highest possible fd is netfd (3) */
621 rr = select(netfd+1, &ding2, NULL, NULL, &tmp_timer);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000622 } else
Denis Vlasenko19507f02007-04-06 10:41:05 +0000623 rr = select(netfd+1, &ding2, NULL, NULL, NULL);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000624 if (rr < 0 && errno != EINTR) { /* might have gotten ^Zed, etc */
625 holler_perror("select");
626 close(netfd);
627 return 1;
628 }
629 /* if we have a timeout AND stdin is closed AND we haven't heard anything
630 from the net during that time, assume it's dead and close it too. */
631 if (rr == 0) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000632 if (!FD_ISSET(STDIN_FILENO, &ding1))
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000633 netretry--; /* we actually try a coupla times. */
634 if (!netretry) {
635 if (o_verbose > 1) /* normally we don't care */
636 fprintf(stderr, "net timeout\n");
637 close(netfd);
638 return 0; /* not an error! */
639 }
640 } /* select timeout */
641 /* xxx: should we check the exception fds too? The read fds seem to give
642 us the right info, and none of the examples I found bothered. */
643
644 /* Ding!! Something arrived, go check all the incoming hoppers, net first */
645 if (FD_ISSET(netfd, &ding2)) { /* net: ding! */
646 rr = read(netfd, bigbuf_net, BIGSIZ);
647 if (rr <= 0) {
Denis Vlasenko19507f02007-04-06 10:41:05 +0000648 if (rr < 0 && o_verbose > 1) {
649 /* nc 1.10 doesn't do this */
650 bb_perror_msg("net read");
651 }
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000652 FD_CLR(netfd, &ding1); /* net closed, we'll finish up... */
653 rzleft = 0; /* can't write anymore: broken pipe */
654 } else {
655 rnleft = rr;
656 np = bigbuf_net;
657 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000658Debug("got %d from the net, errno %d", rr, errno);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000659 } /* net:ding */
660
661 /* if we're in "slowly" mode there's probably still stuff in the stdin
662 buffer, so don't read unless we really need MORE INPUT! MORE INPUT! */
663 if (rzleft)
664 goto shovel;
665
666 /* okay, suck more stdin */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000667 if (FD_ISSET(STDIN_FILENO, &ding2)) { /* stdin: ding! */
668 rr = read(STDIN_FILENO, bigbuf_in, BIGSIZ);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000669 /* Considered making reads here smaller for UDP mode, but 8192-byte
670 mobygrams are kinda fun and exercise the reassembler. */
671 if (rr <= 0) { /* at end, or fukt, or ... */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000672 FD_CLR(STDIN_FILENO, &ding1); /* disable and close stdin */
Denis Vlasenkof6b46852009-04-25 13:16:53 +0000673 close(STDIN_FILENO);
674// Does it make sense to shutdown(net_fd, SHUT_WR)
675// to let other side know that we won't write anything anymore?
676// (and what about keeping compat if we do that?)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000677 } else {
678 rzleft = rr;
679 zp = bigbuf_in;
680 }
681 } /* stdin:ding */
682 shovel:
683 /* now that we've dingdonged all our thingdings, send off the results.
684 Geez, why does this look an awful lot like the big loop in "rsh"? ...
685 not sure if the order of this matters, but write net -> stdout first. */
686
687 /* sanity check. Works because they're both unsigned... */
688 if ((rzleft > 8200) || (rnleft > 8200)) {
689 holler_error("bogus buffers: %u, %u", rzleft, rnleft);
690 rzleft = rnleft = 0;
691 }
692 /* net write retries sometimes happen on UDP connections */
693 if (!wretry) { /* is something hung? */
694 holler_error("too many output retries");
695 return 1;
696 }
697 if (rnleft) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000698 rr = write(STDOUT_FILENO, np, rnleft);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000699 if (rr > 0) {
Denis Vlasenkoc4f12f52008-05-12 14:35:56 +0000700 if (o_ofile) /* log the stdout */
701 oprint('<', (unsigned char *)np, rr);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000702 np += rr; /* fix up ptrs and whatnot */
703 rnleft -= rr; /* will get sanity-checked above */
704 wrote_out += rr; /* global count */
705 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000706Debug("wrote %d to stdout, errno %d", rr, errno);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000707 } /* rnleft */
708 if (rzleft) {
709 if (o_interval) /* in "slowly" mode ?? */
710 rr = findline(zp, rzleft);
711 else
712 rr = rzleft;
713 rr = write(netfd, zp, rr); /* one line, or the whole buffer */
714 if (rr > 0) {
Denis Vlasenkoc4f12f52008-05-12 14:35:56 +0000715 if (o_ofile) /* log what got sent */
716 oprint('>', (unsigned char *)zp, rr);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000717 zp += rr;
718 rzleft -= rr;
719 wrote_net += rr; /* global count */
720 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000721Debug("wrote %d to net, errno %d", rr, errno);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000722 } /* rzleft */
723 if (o_interval) { /* cycle between slow lines, or ... */
724 sleep(o_interval);
725 errno = 0; /* clear from sleep */
726 continue; /* ...with hairy select loop... */
727 }
728 if ((rzleft) || (rnleft)) { /* shovel that shit till they ain't */
729 wretry--; /* none left, and get another load */
730 goto shovel;
731 }
732 } /* while ding1:netfd is open */
733
734 /* XXX: maybe want a more graceful shutdown() here, or screw around with
735 linger times?? I suspect that I don't need to since I'm always doing
736 blocking reads and writes and my own manual "last ditch" efforts to read
737 the net again after a timeout. I haven't seen any screwups yet, but it's
738 not like my test network is particularly busy... */
739 close(netfd);
740 return 0;
741} /* readwrite */
742
743/* main: now we pull it all together... */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000744int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denys Vlasenko2ec91ae2010-01-04 14:15:38 +0100745int nc_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000746{
Denis Vlasenko1d426652008-03-17 09:09:09 +0000747 char *str_p, *str_s;
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000748 IF_NC_EXTRA(char *str_i, *str_o;)
Denys Vlasenko5e896482012-03-19 01:17:36 +0100749 char *themdotted = themdotted; /* for compiler */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000750 char **proggie;
751 int x;
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100752 unsigned cnt_l = 0;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000753 unsigned o_lport = 0;
754
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000755 INIT_G();
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000756
757 /* catch a signal or two for cleanup */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000758 bb_signals(0
759 + (1 << SIGINT)
760 + (1 << SIGQUIT)
761 + (1 << SIGTERM)
762 , catch);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000763 /* and suppress others... */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000764 bb_signals(0
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000765#ifdef SIGURG
Denis Vlasenko25591c32008-02-16 22:58:56 +0000766 + (1 << SIGURG)
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000767#endif
Denis Vlasenko25591c32008-02-16 22:58:56 +0000768 + (1 << SIGPIPE) /* important! */
769 , SIG_IGN);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000770
771 proggie = argv;
772 while (*++proggie) {
773 if (strcmp(*proggie, "-e") == 0) {
774 *proggie = NULL;
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000775 proggie++;
776 goto e_found;
777 }
Denys Vlasenko5e896482012-03-19 01:17:36 +0100778 /* -<other_opts>e PROG [ARGS] ? */
779 /* (aboriginal linux uses this form) */
780 if (proggie[0][0] == '-') {
781 char *optpos = *proggie + 1;
782 /* Skip all valid opts w/o params */
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100783 optpos = optpos + strspn(optpos, "nuv"IF_NC_SERVER("lk")IF_NC_EXTRA("z"));
Denys Vlasenko5e896482012-03-19 01:17:36 +0100784 if (*optpos == 'e' && !optpos[1]) {
785 *optpos = '\0';
786 proggie++;
787 G.proggie0saved = *proggie;
788 *proggie = NULL; /* terminate argv for getopt32 */
789 goto e_found;
790 }
791 }
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000792 }
793 proggie = NULL;
794 e_found:
Denis Vlasenko4b924f32007-05-30 00:29:55 +0000795
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000796 // -g -G -t -r deleted, unimplemented -a deleted too
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100797 opt_complementary = "?2:vv:ll:w+"; /* max 2 params; -v and -l are counters; -w N */
798 getopt32(argv, "np:s:uvw:" IF_NC_SERVER("lk")
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000799 IF_NC_EXTRA("i:o:z"),
Denis Vlasenko1d426652008-03-17 09:09:09 +0000800 &str_p, &str_s, &o_wait
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100801 IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l));
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000802 argv += optind;
803#if ENABLE_NC_EXTRA
804 if (option_mask32 & OPT_i) /* line-interval time */
805 o_interval = xatou_range(str_i, 1, 0xffff);
806#endif
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100807#if ENABLE_NC_SERVER
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000808 //if (option_mask32 & OPT_l) /* listen mode */
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100809 if (option_mask32 & OPT_k) /* persistent server mode */
810 cnt_l = 2;
811#endif
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000812 //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */
813 //if (option_mask32 & OPT_o) /* hexdump log */
814 if (option_mask32 & OPT_p) { /* local source port */
815 o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0);
816 if (!o_lport)
817 bb_error_msg_and_die("bad local port '%s'", str_p);
818 }
819 //if (option_mask32 & OPT_r) /* randomize various things */
820 //if (option_mask32 & OPT_u) /* use UDP */
821 //if (option_mask32 & OPT_v) /* verbose */
Denis Vlasenko1d426652008-03-17 09:09:09 +0000822 //if (option_mask32 & OPT_w) /* wait time */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000823 //if (option_mask32 & OPT_z) /* little or no data xfer */
824
Denis Vlasenko19507f02007-04-06 10:41:05 +0000825 /* We manage our fd's so that they are never 0,1,2 */
826 /*bb_sanitize_stdio(); - not needed */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000827
Denis Vlasenko5c51a7c2007-06-05 20:08:11 +0000828 if (argv[0]) {
829 themaddr = xhost2sockaddr(argv[0],
830 argv[1]
831 ? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0)
832 : 0);
833 }
834
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000835 /* create & bind network socket */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000836 x = (o_udpmode ? SOCK_DGRAM : SOCK_STREAM);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000837 if (option_mask32 & OPT_s) { /* local address */
Denis Vlasenko19507f02007-04-06 10:41:05 +0000838 /* if o_lport is still 0, then we will use random port */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000839 ouraddr = xhost2sockaddr(str_s, o_lport);
Denis Vlasenko4cf1d082008-03-12 23:13:50 +0000840#ifdef BLOAT
841 /* prevent spurious "UDP listen needs !0 port" */
842 o_lport = get_nport(ouraddr);
843 o_lport = ntohs(o_lport);
844#endif
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000845 x = xsocket(ouraddr->u.sa.sa_family, x, 0);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000846 } else {
Denis Vlasenko5c51a7c2007-06-05 20:08:11 +0000847 /* We try IPv6, then IPv4, unless addr family is
848 * implicitly set by way of remote addr/port spec */
849 x = xsocket_type(&ouraddr,
Denis Vlasenko4e6d5112008-03-12 22:14:34 +0000850 (themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC),
Denis Vlasenko5c51a7c2007-06-05 20:08:11 +0000851 x);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000852 if (o_lport)
Denys Vlasenkoca183112011-04-07 17:52:20 +0200853 set_nport(&ouraddr->u.sa, htons(o_lport));
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000854 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000855 xmove_fd(x, netfd);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000856 setsockopt_reuseaddr(netfd);
857 if (o_udpmode)
858 socket_want_pktinfo(netfd);
Denis Vlasenkof6b46852009-04-25 13:16:53 +0000859 if (!ENABLE_FEATURE_UNIX_LOCAL
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100860 || cnt_l != 0 /* listen */
Denis Vlasenkof6b46852009-04-25 13:16:53 +0000861 || ouraddr->u.sa.sa_family != AF_UNIX
862 ) {
863 xbind(netfd, &ouraddr->u.sa, ouraddr->len);
864 }
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000865#if 0
866 setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf);
867 setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
868#endif
869
Denis Vlasenko4cf1d082008-03-12 23:13:50 +0000870#ifdef BLOAT
Denis Vlasenko19507f02007-04-06 10:41:05 +0000871 if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) {
872 /* apparently UDP can listen ON "port 0",
873 but that's not useful */
874 if (!o_lport)
875 bb_error_msg_and_die("UDP listen needs nonzero -p port");
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000876 }
Denis Vlasenko4cf1d082008-03-12 23:13:50 +0000877#endif
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000878
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000879 FD_SET(STDIN_FILENO, &ding1); /* stdin *is* initially open */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000880 if (proggie) {
881 close(0); /* won't need stdin */
882 option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000883 }
Denis Vlasenko19507f02007-04-06 10:41:05 +0000884#if ENABLE_NC_EXTRA
885 if (o_ofile)
886 xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
887#endif
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000888
Denys Vlasenkode6f1482013-02-28 12:20:06 +0100889 if (cnt_l != 0) {
890 dolisten((cnt_l - 1), proggie);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000891 /* dolisten does its own connect reporting */
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000892 x = readwrite(); /* it even works with UDP! */
893 } else {
894 /* Outbound connects. Now we're more picky about args... */
895 if (!themaddr)
Denys Vlasenkob103fb12010-09-07 18:41:56 +0200896 bb_show_usage();
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000897
898 remend = *themaddr;
899 if (o_verbose)
Denis Vlasenko7cff01e2008-02-02 16:23:43 +0000900 themdotted = xmalloc_sockaddr2dotted(&themaddr->u.sa);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000901
902 x = connect_w_timeout(netfd);
903 if (o_zero && x == 0 && o_udpmode) /* if UDP scanning... */
904 x = udptest();
905 if (x == 0) { /* Yow, are we OPEN YET?! */
906 if (o_verbose)
Denis Vlasenko19507f02007-04-06 10:41:05 +0000907 fprintf(stderr, "%s (%s) open\n", argv[0], themdotted);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000908 if (proggie) /* exec is valid for outbound, too */
909 doexec(proggie);
910 if (!o_zero)
911 x = readwrite();
912 } else { /* connect or udptest wasn't successful */
913 x = 1; /* exit status */
914 /* if we're scanning at a "one -v" verbosity level, don't print refusals.
915 Give it another -v if you want to see everything. */
916 if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED))
Denis Vlasenko19507f02007-04-06 10:41:05 +0000917 bb_perror_msg("%s (%s)", argv[0], themdotted);
Denis Vlasenko29fe7262007-04-05 20:26:28 +0000918 }
919 }
920 if (o_verbose > 1) /* normally we don't care */
921 fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
922 return x;
923}