In many cases, this aqllows to drop use of opt_complementary. Approximately -400 bytes: function old new delta getopt32 1423 1502 +79 opt_string 17 18 +1 OPT_STR 24 25 +1 uniq_main 416 406 -10 timeout_main 279 269 -10 sulogin_main 270 260 -10 readprofile_main 1825 1815 -10 ps_main 543 533 -10 pidof_main 245 235 -10 pgrep_main 611 601 -10 od_main 2600 2590 -10 mkfs_minix_main 2684 2674 -10 mkfs_ext2_main 2603 2593 -10 microcom_main 712 702 -10 makemime_main 315 305 -10 ionice_main 282 272 -10 inetd_main 2074 2064 -10 ifplugd_main 1144 1134 -10 halt_main 353 343 -10 getopt_main 636 626 -10 fdisk_main 2854 2844 -10 env_main 206 196 -10 dmesg_main 319 309 -10 conspy_main 1214 1204 -10 awk_main 981 971 -10 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/22 up/down: 81/-220) Total: -139 bytes text data bss dec hex filename 919373 906 14060 934339 e41c3 busybox_old 918969 906 14060 933935 e402f busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
		
			
				
	
	
		
			923 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			923 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Based on netcat 1.10 RELEASE 960320 written by hobbit@avian.org.
 | 
						|
 * Released into public domain by the author.
 | 
						|
 *
 | 
						|
 * Copyright (C) 2007 Denys Vlasenko.
 | 
						|
 *
 | 
						|
 * Licensed under GPLv2, see file LICENSE in this source tree.
 | 
						|
 */
 | 
						|
 | 
						|
/* Author's comments from nc 1.10:
 | 
						|
 * =====================
 | 
						|
 * Netcat is entirely my own creation, although plenty of other code was used as
 | 
						|
 * examples.  It is freely given away to the Internet community in the hope that
 | 
						|
 * it will be useful, with no restrictions except giving credit where it is due.
 | 
						|
 * No GPLs, Berkeley copyrights or any of that nonsense.  The author assumes NO
 | 
						|
 * responsibility for how anyone uses it.  If netcat makes you rich somehow and
 | 
						|
 * you're feeling generous, mail me a check.  If you are affiliated in any way
 | 
						|
 * with Microsoft Network, get a life.  Always ski in control.  Comments,
 | 
						|
 * questions, and patches to hobbit@avian.org.
 | 
						|
 * ...
 | 
						|
 * Netcat and the associated package is a product of Avian Research, and is freely
 | 
						|
 * available in full source form with no restrictions save an obligation to give
 | 
						|
 * credit where due.
 | 
						|
 * ...
 | 
						|
 * A damn useful little "backend" utility begun 950915 or thereabouts,
 | 
						|
 * as *Hobbit*'s first real stab at some sockets programming.  Something that
 | 
						|
 * should have and indeed may have existed ten years ago, but never became a
 | 
						|
 * standard Unix utility.  IMHO, "nc" could take its place right next to cat,
 | 
						|
 * cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things.
 | 
						|
 * =====================
 | 
						|
 *
 | 
						|
 * Much of author's comments are still retained in the code.
 | 
						|
 *
 | 
						|
 * Functionality removed (rationale):
 | 
						|
 * - miltiple-port ranges, randomized port scanning (use nmap)
 | 
						|
 * - telnet support (use telnet)
 | 
						|
 * - source routing
 | 
						|
 * - multiple DNS checks
 | 
						|
 * Functionalty which is different from nc 1.10:
 | 
						|
 * - PROG in '-e PROG' can have ARGS (and options).
 | 
						|
 *   Because of this -e option must be last.
 | 
						|
//TODO: remove -e incompatibility?
 | 
						|
 * - we don't redirect stderr to the network socket for the -e PROG.
 | 
						|
 *   (PROG can do it itself if needed, but sometimes it is NOT wanted!)
 | 
						|
 * - numeric addresses are printed in (), not [] (IPv6 looks better),
 | 
						|
 *   port numbers are inside (): (1.2.3.4:5678)
 | 
						|
 * - network read errors are reported on verbose levels > 1
 | 
						|
 *   (nc 1.10 treats them as EOF)
 | 
						|
 * - TCP connects from wrong ip/ports (if peer ip:port is specified
 | 
						|
 *   on the command line, but accept() says that it came from different addr)
 | 
						|
 *   are closed, but we don't exit - we continue to listen/accept.
 | 
						|
 * Since bbox 1.22:
 | 
						|
 * - nc exits when _both_ stdin and network are closed.
 | 
						|
 *   This makes these two commands:
 | 
						|
 *    echo "Yes" | nc 127.0.0.1 1234
 | 
						|
 *    echo "no" | nc -lp 1234
 | 
						|
 *   exchange their data _and exit_ instead of being stuck.
 | 
						|
 */
 | 
						|
 | 
						|
/* done in nc.c: #include "libbb.h" */
 | 
						|
 | 
						|
//usage:#if ENABLE_NC_110_COMPAT
 | 
						|
//usage:
 | 
						|
//usage:#define nc_trivial_usage
 | 
						|
//usage:       "[OPTIONS] HOST PORT  - connect"
 | 
						|
//usage:	IF_NC_SERVER("\n"
 | 
						|
//usage:       "nc [OPTIONS] -l -p PORT [HOST] [PORT]  - listen"
 | 
						|
//usage:	)
 | 
						|
//usage:#define nc_full_usage "\n\n"
 | 
						|
//usage:       "	-e PROG	Run PROG after connect (must be last)"
 | 
						|
//usage:	IF_NC_SERVER(
 | 
						|
//usage:     "\n	-l	Listen mode, for inbound connects"
 | 
						|
//usage:     "\n	-lk	With -e, provides persistent server"
 | 
						|
/* -ll does the same as -lk, but its our extension, while -k is BSD'd,
 | 
						|
 * presumably more widely known. Therefore we advertise it, not -ll.
 | 
						|
 * I would like to drop -ll support, but our "small" nc supports it,
 | 
						|
 * and Rob uses it.
 | 
						|
 */
 | 
						|
//usage:	)
 | 
						|
//usage:     "\n	-p PORT	Local port"
 | 
						|
//usage:     "\n	-s ADDR	Local address"
 | 
						|
//usage:     "\n	-w SEC	Timeout for connects and final net reads"
 | 
						|
//usage:	IF_NC_EXTRA(
 | 
						|
//usage:     "\n	-i SEC	Delay interval for lines sent" /* ", ports scanned" */
 | 
						|
//usage:	)
 | 
						|
//usage:     "\n	-n	Don't do DNS resolution"
 | 
						|
//usage:     "\n	-u	UDP mode"
 | 
						|
//usage:     "\n	-v	Verbose"
 | 
						|
//usage:	IF_NC_EXTRA(
 | 
						|
//usage:     "\n	-o FILE	Hex dump traffic"
 | 
						|
//usage:     "\n	-z	Zero-I/O mode (scanning)"
 | 
						|
//usage:	)
 | 
						|
//usage:#endif
 | 
						|
 | 
						|
/*   "\n	-r		Randomize local and remote ports" */
 | 
						|
/*   "\n	-g gateway	Source-routing hop point[s], up to 8" */
 | 
						|
/*   "\n	-G num		Source-routing pointer: 4, 8, 12, ..." */
 | 
						|
/*   "\nport numbers can be individual or ranges: lo-hi [inclusive]" */
 | 
						|
 | 
						|
/* -e PROG can take ARGS too: "nc ... -e ls -l", but we don't document it
 | 
						|
 * in help text: nc 1.10 does not allow that. We don't want to entice
 | 
						|
 * users to use this incompatibility */
 | 
						|
 | 
						|
enum {
 | 
						|
	SLEAZE_PORT = 31337,               /* for UDP-scan RTT trick, change if ya want */
 | 
						|
	BIGSIZ = 8192,                     /* big buffers */
 | 
						|
 | 
						|
	netfd = 3,
 | 
						|
	ofd = 4,
 | 
						|
};
 | 
						|
 | 
						|
struct globals {
 | 
						|
	/* global cmd flags: */
 | 
						|
	unsigned o_verbose;
 | 
						|
	unsigned o_wait;
 | 
						|
#if ENABLE_NC_EXTRA
 | 
						|
	unsigned o_interval;
 | 
						|
#endif
 | 
						|
 | 
						|
	/*int netfd;*/
 | 
						|
	/*int ofd;*/                     /* hexdump output fd */
 | 
						|
#if ENABLE_LFS
 | 
						|
#define SENT_N_RECV_M "sent %llu, rcvd %llu\n"
 | 
						|
	unsigned long long wrote_out;          /* total stdout bytes */
 | 
						|
	unsigned long long wrote_net;          /* total net bytes */
 | 
						|
#else
 | 
						|
#define SENT_N_RECV_M "sent %u, rcvd %u\n"
 | 
						|
	unsigned wrote_out;          /* total stdout bytes */
 | 
						|
	unsigned wrote_net;          /* total net bytes */
 | 
						|
#endif
 | 
						|
	char *proggie0saved;
 | 
						|
	/* ouraddr is never NULL and goes through three states as we progress:
 | 
						|
	 1 - local address before bind (IP/port possibly zero)
 | 
						|
	 2 - local address after bind (port is nonzero)
 | 
						|
	 3 - local address after connect??/recv/accept (IP and port are nonzero) */
 | 
						|
	struct len_and_sockaddr *ouraddr;
 | 
						|
	/* themaddr is NULL if no peer hostname[:port] specified on command line */
 | 
						|
	struct len_and_sockaddr *themaddr;
 | 
						|
	/* remend is set after connect/recv/accept to the actual ip:port of peer */
 | 
						|
	struct len_and_sockaddr remend;
 | 
						|
 | 
						|
	jmp_buf jbuf;                /* timer crud */
 | 
						|
 | 
						|
	char bigbuf_in[BIGSIZ];      /* data buffers */
 | 
						|
	char bigbuf_net[BIGSIZ];
 | 
						|
};
 | 
						|
 | 
						|
#define G (*ptr_to_globals)
 | 
						|
#define wrote_out  (G.wrote_out )
 | 
						|
#define wrote_net  (G.wrote_net )
 | 
						|
#define ouraddr    (G.ouraddr   )
 | 
						|
#define themaddr   (G.themaddr  )
 | 
						|
#define remend     (G.remend    )
 | 
						|
#define jbuf       (G.jbuf      )
 | 
						|
#define bigbuf_in  (G.bigbuf_in )
 | 
						|
#define bigbuf_net (G.bigbuf_net)
 | 
						|
#define o_verbose  (G.o_verbose )
 | 
						|
#define o_wait     (G.o_wait    )
 | 
						|
#if ENABLE_NC_EXTRA
 | 
						|
#define o_interval (G.o_interval)
 | 
						|
#else
 | 
						|
#define o_interval 0
 | 
						|
#endif
 | 
						|
#define INIT_G() do { \
 | 
						|
	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 | 
						|
} while (0)
 | 
						|
 | 
						|
 | 
						|
/* Must match getopt32 call! */
 | 
						|
enum {
 | 
						|
	OPT_n = (1 << 0),
 | 
						|
	OPT_p = (1 << 1),
 | 
						|
	OPT_s = (1 << 2),
 | 
						|
	OPT_u = (1 << 3),
 | 
						|
	OPT_v = (1 << 4),
 | 
						|
	OPT_w = (1 << 5),
 | 
						|
	OPT_l = (1 << 6) * ENABLE_NC_SERVER,
 | 
						|
	OPT_k = (1 << 7) * ENABLE_NC_SERVER,
 | 
						|
	OPT_i = (1 << (6+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
 | 
						|
	OPT_o = (1 << (7+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
 | 
						|
	OPT_z = (1 << (8+2*ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
 | 
						|
};
 | 
						|
 | 
						|
#define o_nflag   (option_mask32 & OPT_n)
 | 
						|
#define o_udpmode (option_mask32 & OPT_u)
 | 
						|
#if ENABLE_NC_EXTRA
 | 
						|
#define o_ofile   (option_mask32 & OPT_o)
 | 
						|
#define o_zero    (option_mask32 & OPT_z)
 | 
						|
#else
 | 
						|
#define o_ofile   0
 | 
						|
#define o_zero    0
 | 
						|
#endif
 | 
						|
 | 
						|
/* Debug: squirt whatever message and sleep a bit so we can see it go by. */
 | 
						|
/* Beware: writes to stdOUT... */
 | 
						|
#if 0
 | 
						|
#define Debug(...) do { printf(__VA_ARGS__); printf("\n"); fflush_all(); sleep(1); } while (0)
 | 
						|
#else
 | 
						|
#define Debug(...) do { } while (0)
 | 
						|
#endif
 | 
						|
 | 
						|
#define holler_error(...)  do { if (o_verbose) bb_error_msg(__VA_ARGS__); } while (0)
 | 
						|
#define holler_perror(...) do { if (o_verbose) bb_perror_msg(__VA_ARGS__); } while (0)
 | 
						|
 | 
						|
/* catch: no-brainer interrupt handler */
 | 
						|
static void catch(int sig)
 | 
						|
{
 | 
						|
	if (o_verbose > 1)                /* normally we don't care */
 | 
						|
		fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
 | 
						|
	fprintf(stderr, "punt!\n");
 | 
						|
	kill_myself_with_sig(sig);
 | 
						|
}
 | 
						|
 | 
						|
/* unarm  */
 | 
						|
static void unarm(void)
 | 
						|
{
 | 
						|
	signal(SIGALRM, SIG_IGN);
 | 
						|
	alarm(0);
 | 
						|
}
 | 
						|
 | 
						|
/* timeout and other signal handling cruft */
 | 
						|
static void tmtravel(int sig UNUSED_PARAM)
 | 
						|
{
 | 
						|
	unarm();
 | 
						|
	longjmp(jbuf, 1);
 | 
						|
}
 | 
						|
 | 
						|
/* arm: set the timer.  */
 | 
						|
static void arm(unsigned secs)
 | 
						|
{
 | 
						|
	signal(SIGALRM, tmtravel);
 | 
						|
	alarm(secs);
 | 
						|
}
 | 
						|
 | 
						|
/* findline:
 | 
						|
 find the next newline in a buffer; return inclusive size of that "line",
 | 
						|
 or the entire buffer size, so the caller knows how much to then write().
 | 
						|
 Not distinguishing \n vs \r\n for the nonce; it just works as is... */
 | 
						|
static unsigned findline(char *buf, unsigned siz)
 | 
						|
{
 | 
						|
	char * p;
 | 
						|
	int x;
 | 
						|
	if (!buf)                        /* various sanity checks... */
 | 
						|
		return 0;
 | 
						|
	if (siz > BIGSIZ)
 | 
						|
		return 0;
 | 
						|
	x = siz;
 | 
						|
	for (p = buf; x > 0; x--) {
 | 
						|
		if (*p == '\n') {
 | 
						|
			x = (int) (p - buf);
 | 
						|
			x++;                        /* 'sokay if it points just past the end! */
 | 
						|
Debug("findline returning %d", x);
 | 
						|
			return x;
 | 
						|
		}
 | 
						|
		p++;
 | 
						|
	} /* for */
 | 
						|
Debug("findline returning whole thing: %d", siz);
 | 
						|
	return siz;
 | 
						|
} /* findline */
 | 
						|
 | 
						|
/* doexec:
 | 
						|
 fiddle all the file descriptors around, and hand off to another prog.  Sort
 | 
						|
 of like a one-off "poor man's inetd".  This is the only section of code
 | 
						|
 that would be security-critical, which is why it's ifdefed out by default.
 | 
						|
 Use at your own hairy risk; if you leave shells lying around behind open
 | 
						|
 listening ports you deserve to lose!! */
 | 
						|
static int doexec(char **proggie) NORETURN;
 | 
						|
static int doexec(char **proggie)
 | 
						|
{
 | 
						|
	if (G.proggie0saved)
 | 
						|
		proggie[0] = G.proggie0saved;
 | 
						|
	xmove_fd(netfd, 0);
 | 
						|
	dup2(0, 1);
 | 
						|
	/* dup2(0, 2); - do we *really* want this? NO!
 | 
						|
	 * exec'ed prog can do it yourself, if needed */
 | 
						|
	BB_EXECVP_or_die(proggie);
 | 
						|
}
 | 
						|
 | 
						|
/* connect_w_timeout:
 | 
						|
 return an fd for one of
 | 
						|
 an open outbound TCP connection, a UDP stub-socket thingie, or
 | 
						|
 an unconnected TCP or UDP socket to listen on.
 | 
						|
 Examines various global o_blah flags to figure out what to do.
 | 
						|
 lad can be NULL, then socket is not bound to any local ip[:port] */
 | 
						|
static int connect_w_timeout(int fd)
 | 
						|
{
 | 
						|
	int rr;
 | 
						|
 | 
						|
	/* wrap connect inside a timer, and hit it */
 | 
						|
	arm(o_wait);
 | 
						|
	if (setjmp(jbuf) == 0) {
 | 
						|
		rr = connect(fd, &themaddr->u.sa, themaddr->len);
 | 
						|
		unarm();
 | 
						|
	} else { /* setjmp: connect failed... */
 | 
						|
		rr = -1;
 | 
						|
		errno = ETIMEDOUT; /* fake it */
 | 
						|
	}
 | 
						|
	return rr;
 | 
						|
}
 | 
						|
 | 
						|
/* dolisten:
 | 
						|
 listens for
 | 
						|
 incoming and returns an open connection *from* someplace.  If we were
 | 
						|
 given host/port args, any connections from elsewhere are rejected.  This
 | 
						|
 in conjunction with local-address binding should limit things nicely... */
 | 
						|
static void dolisten(int is_persistent, char **proggie)
 | 
						|
{
 | 
						|
	int rr;
 | 
						|
 | 
						|
	if (!o_udpmode)
 | 
						|
		xlisten(netfd, 1); /* TCP: gotta listen() before we can get */
 | 
						|
 | 
						|
	/* Various things that follow temporarily trash bigbuf_net, which might contain
 | 
						|
	 a copy of any recvfrom()ed packet, but we'll read() another copy later. */
 | 
						|
 | 
						|
	/* I can't believe I have to do all this to get my own goddamn bound address
 | 
						|
	 and port number.  It should just get filled in during bind() or something.
 | 
						|
	 All this is only useful if we didn't say -p for listening, since if we
 | 
						|
	 said -p we *know* what port we're listening on.  At any rate we won't bother
 | 
						|
	 with it all unless we wanted to see it, although listening quietly on a
 | 
						|
	 random unknown port is probably not very useful without "netstat". */
 | 
						|
	if (o_verbose) {
 | 
						|
		char *addr;
 | 
						|
		getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
 | 
						|
		//if (rr < 0)
 | 
						|
		//	bb_perror_msg_and_die("getsockname after bind");
 | 
						|
		addr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
 | 
						|
		fprintf(stderr, "listening on %s ...\n", addr);
 | 
						|
		free(addr);
 | 
						|
	}
 | 
						|
 | 
						|
	if (o_udpmode) {
 | 
						|
		/* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
 | 
						|
		 party's particulars all at once, listen() and accept() don't apply.
 | 
						|
		 At least in the BSD universe, however, recvfrom/PEEK is enough to tell
 | 
						|
		 us something came in, and we can set things up so straight read/write
 | 
						|
		 actually does work after all.  Yow.  YMMV on strange platforms!  */
 | 
						|
 | 
						|
		/* I'm not completely clear on how this works -- BSD seems to make UDP
 | 
						|
		 just magically work in a connect()ed context, but we'll undoubtedly run
 | 
						|
		 into systems this deal doesn't work on.  For now, we apparently have to
 | 
						|
		 issue a connect() on our just-tickled socket so we can write() back.
 | 
						|
		 Again, why the fuck doesn't it just get filled in and taken care of?!
 | 
						|
		 This hack is anything but optimal.  Basically, if you want your listener
 | 
						|
		 to also be able to send data back, you need this connect() line, which
 | 
						|
		 also has the side effect that now anything from a different source or even a
 | 
						|
		 different port on the other end won't show up and will cause ICMP errors.
 | 
						|
		 I guess that's what they meant by "connect".
 | 
						|
		 Let's try to remember what the "U" is *really* for, eh? */
 | 
						|
 | 
						|
		/* If peer address is specified, connect to it */
 | 
						|
		remend.len = LSA_SIZEOF_SA;
 | 
						|
		if (themaddr) {
 | 
						|
			remend = *themaddr;
 | 
						|
			xconnect(netfd, &themaddr->u.sa, themaddr->len);
 | 
						|
		}
 | 
						|
		/* peek first packet and remember peer addr */
 | 
						|
		arm(o_wait);                /* might as well timeout this, too */
 | 
						|
		if (setjmp(jbuf) == 0) {       /* do timeout for initial connect */
 | 
						|
			/* (*ouraddr) is prefilled with "default" address */
 | 
						|
			/* and here we block... */
 | 
						|
			rr = recv_from_to(netfd, NULL, 0, MSG_PEEK, /*was bigbuf_net, BIGSIZ*/
 | 
						|
				&remend.u.sa, &ouraddr->u.sa, ouraddr->len);
 | 
						|
			if (rr < 0)
 | 
						|
				bb_perror_msg_and_die("recvfrom");
 | 
						|
			unarm();
 | 
						|
		} else
 | 
						|
			bb_error_msg_and_die("timeout");
 | 
						|
/* Now we learned *to which IP* peer has connected, and we want to anchor
 | 
						|
our socket on it, so that our outbound packets will have correct local IP.
 | 
						|
Unfortunately, bind() on already bound socket will fail now (EINVAL):
 | 
						|
	xbind(netfd, &ouraddr->u.sa, ouraddr->len);
 | 
						|
Need to read the packet, save data, close this socket and
 | 
						|
create new one, and bind() it. TODO */
 | 
						|
		if (!themaddr)
 | 
						|
			xconnect(netfd, &remend.u.sa, ouraddr->len);
 | 
						|
	} else {
 | 
						|
		/* TCP */
 | 
						|
 another:
 | 
						|
		arm(o_wait); /* wrap this in a timer, too; 0 = forever */
 | 
						|
		if (setjmp(jbuf) == 0) {
 | 
						|
 again:
 | 
						|
			remend.len = LSA_SIZEOF_SA;
 | 
						|
			rr = accept(netfd, &remend.u.sa, &remend.len);
 | 
						|
			if (rr < 0)
 | 
						|
				bb_perror_msg_and_die("accept");
 | 
						|
			if (themaddr) {
 | 
						|
				int sv_port, port, r;
 | 
						|
 | 
						|
				sv_port = get_nport(&remend.u.sa); /* save */
 | 
						|
				port = get_nport(&themaddr->u.sa);
 | 
						|
				if (port == 0) {
 | 
						|
					/* "nc -nl -p LPORT RHOST" (w/o RPORT!):
 | 
						|
					 * we should accept any remote port */
 | 
						|
					set_nport(&remend.u.sa, 0); /* blot out remote port# */
 | 
						|
				}
 | 
						|
				r = memcmp(&remend.u.sa, &themaddr->u.sa, remend.len);
 | 
						|
				set_nport(&remend.u.sa, sv_port); /* restore */
 | 
						|
				if (r != 0) {
 | 
						|
					/* nc 1.10 bails out instead, and its error message
 | 
						|
					 * is not suppressed by o_verbose */
 | 
						|
					if (o_verbose) {
 | 
						|
						char *remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
 | 
						|
						bb_error_msg("connect from wrong ip/port %s ignored", remaddr);
 | 
						|
						free(remaddr);
 | 
						|
					}
 | 
						|
					close(rr);
 | 
						|
					goto again;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			unarm();
 | 
						|
		} else
 | 
						|
			bb_error_msg_and_die("timeout");
 | 
						|
 | 
						|
		if (is_persistent && proggie) {
 | 
						|
			/* -l -k -e PROG */
 | 
						|
			signal(SIGCHLD, SIG_IGN); /* no zombies please */
 | 
						|
			if (xvfork() != 0) {
 | 
						|
				/* parent: go back and accept more connections */
 | 
						|
				close(rr);
 | 
						|
				goto another;
 | 
						|
			}
 | 
						|
			/* child */
 | 
						|
			signal(SIGCHLD, SIG_DFL);
 | 
						|
		}
 | 
						|
 | 
						|
		xmove_fd(rr, netfd); /* dump the old socket, here's our new one */
 | 
						|
		/* find out what address the connection was *to* on our end, in case we're
 | 
						|
		 doing a listen-on-any on a multihomed machine.  This allows one to
 | 
						|
		 offer different services via different alias addresses, such as the
 | 
						|
		 "virtual web site" hack. */
 | 
						|
		getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
 | 
						|
		//if (rr < 0)
 | 
						|
		//	bb_perror_msg_and_die("getsockname after accept");
 | 
						|
	}
 | 
						|
 | 
						|
	if (o_verbose) {
 | 
						|
		char *lcladdr, *remaddr, *remhostname;
 | 
						|
 | 
						|
#if ENABLE_NC_EXTRA && defined(IP_OPTIONS)
 | 
						|
	/* If we can, look for any IP options.  Useful for testing the receiving end of
 | 
						|
	 such things, and is a good exercise in dealing with it.  We do this before
 | 
						|
	 the connect message, to ensure that the connect msg is uniformly the LAST
 | 
						|
	 thing to emerge after all the intervening crud.  Doesn't work for UDP on
 | 
						|
	 any machines I've tested, but feel free to surprise me. */
 | 
						|
		char optbuf[40];
 | 
						|
		socklen_t x = sizeof(optbuf);
 | 
						|
 | 
						|
		rr = getsockopt(netfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
 | 
						|
		if (rr >= 0 && x) {    /* we've got options, lessee em... */
 | 
						|
			*bin2hex(bigbuf_net, optbuf, x) = '\0';
 | 
						|
			fprintf(stderr, "IP options: %s\n", bigbuf_net);
 | 
						|
		}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* now check out who it is.  We don't care about mismatched DNS names here,
 | 
						|
	 but any ADDR and PORT we specified had better fucking well match the caller.
 | 
						|
	 Converting from addr to inet_ntoa and back again is a bit of a kludge, but
 | 
						|
	 gethostpoop wants a string and there's much gnarlier code out there already,
 | 
						|
	 so I don't feel bad.
 | 
						|
	 The *real* question is why BFD sockets wasn't designed to allow listens for
 | 
						|
	 connections *from* specific hosts/ports, instead of requiring the caller to
 | 
						|
	 accept the connection and then reject undesireable ones by closing.
 | 
						|
	 In other words, we need a TCP MSG_PEEK. */
 | 
						|
	/* bbox: removed most of it */
 | 
						|
		lcladdr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
 | 
						|
		remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
 | 
						|
		remhostname = o_nflag ? remaddr : xmalloc_sockaddr2host(&remend.u.sa);
 | 
						|
		fprintf(stderr, "connect to %s from %s (%s)\n",
 | 
						|
				lcladdr, remhostname, remaddr);
 | 
						|
		free(lcladdr);
 | 
						|
		free(remaddr);
 | 
						|
		if (!o_nflag)
 | 
						|
			free(remhostname);
 | 
						|
	}
 | 
						|
 | 
						|
	if (proggie)
 | 
						|
		doexec(proggie);
 | 
						|
}
 | 
						|
 | 
						|
/* udptest:
 | 
						|
 fire a couple of packets at a UDP target port, just to see if it's really
 | 
						|
 there.  On BSD kernels, ICMP host/port-unreachable errors get delivered to
 | 
						|
 our socket as ECONNREFUSED write errors.  On SV kernels, we lose; we'll have
 | 
						|
 to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
 | 
						|
 backend.  Guess where one could swipe the appropriate code from...
 | 
						|
 | 
						|
 Use the time delay between writes if given, otherwise use the "tcp ping"
 | 
						|
 trick for getting the RTT.  [I got that idea from pluvius, and warped it.]
 | 
						|
 Return either the original fd, or clean up and return -1. */
 | 
						|
#if ENABLE_NC_EXTRA
 | 
						|
static int udptest(void)
 | 
						|
{
 | 
						|
	int rr;
 | 
						|
 | 
						|
	rr = write(netfd, bigbuf_in, 1);
 | 
						|
	if (rr != 1)
 | 
						|
		bb_perror_msg("udptest first write");
 | 
						|
 | 
						|
	if (o_wait)
 | 
						|
		sleep(o_wait); // can be interrupted! while (t) nanosleep(&t)?
 | 
						|
	else {
 | 
						|
	/* use the tcp-ping trick: try connecting to a normally refused port, which
 | 
						|
	 causes us to block for the time that SYN gets there and RST gets back.
 | 
						|
	 Not completely reliable, but it *does* mostly work. */
 | 
						|
	/* Set a temporary connect timeout, so packet filtration doesnt cause
 | 
						|
	 us to hang forever, and hit it */
 | 
						|
		o_wait = 5;                     /* enough that we'll notice?? */
 | 
						|
		rr = xsocket(ouraddr->u.sa.sa_family, SOCK_STREAM, 0);
 | 
						|
		set_nport(&themaddr->u.sa, htons(SLEAZE_PORT));
 | 
						|
		connect_w_timeout(rr);
 | 
						|
		/* don't need to restore themaddr's port, it's not used anymore */
 | 
						|
		close(rr);
 | 
						|
		o_wait = 0; /* restore */
 | 
						|
	}
 | 
						|
 | 
						|
	rr = write(netfd, bigbuf_in, 1);
 | 
						|
	return (rr != 1); /* if rr == 1, return 0 (success) */
 | 
						|
}
 | 
						|
#else
 | 
						|
int udptest(void);
 | 
						|
#endif
 | 
						|
 | 
						|
/* oprint:
 | 
						|
 Hexdump bytes shoveled either way to a running logfile, in the format:
 | 
						|
 D offset       -  - - - --- 16 bytes --- - - -  -     # .... ascii .....
 | 
						|
 where "which" sets the direction indicator, D:
 | 
						|
 0 -- sent to network, or ">"
 | 
						|
 1 -- rcvd and printed to stdout, or "<"
 | 
						|
 and "buf" and "n" are data-block and length.  If the current block generates
 | 
						|
 a partial line, so be it; we *want* that lockstep indication of who sent
 | 
						|
 what when.  Adapted from dgaudet's original example -- but must be ripping
 | 
						|
 *fast*, since we don't want to be too disk-bound... */
 | 
						|
#if ENABLE_NC_EXTRA
 | 
						|
static void oprint(int direction, unsigned char *p, unsigned bc)
 | 
						|
{
 | 
						|
	unsigned obc;           /* current "global" offset */
 | 
						|
	unsigned x;
 | 
						|
	unsigned char *op;      /* out hexdump ptr */
 | 
						|
	unsigned char *ap;      /* out asc-dump ptr */
 | 
						|
	unsigned char stage[100];
 | 
						|
 | 
						|
	if (bc == 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	obc = wrote_net; /* use the globals! */
 | 
						|
	if (direction == '<')
 | 
						|
		obc = wrote_out;
 | 
						|
	stage[0] = direction;
 | 
						|
	stage[59] = '#'; /* preload separator */
 | 
						|
	stage[60] = ' ';
 | 
						|
 | 
						|
	do {    /* for chunk-o-data ... */
 | 
						|
		x = 16;
 | 
						|
		if (bc < 16) {
 | 
						|
			/* memset(&stage[bc*3 + 11], ' ', 16*3 - bc*3); */
 | 
						|
			memset(&stage[11], ' ', 16*3);
 | 
						|
			x = bc;
 | 
						|
		}
 | 
						|
		sprintf((char *)&stage[1], " %8.8x ", obc);  /* xxx: still slow? */
 | 
						|
		bc -= x;          /* fix current count */
 | 
						|
		obc += x;         /* fix current offset */
 | 
						|
		op = &stage[11];  /* where hex starts */
 | 
						|
		ap = &stage[61];  /* where ascii starts */
 | 
						|
 | 
						|
		do {  /* for line of dump, however long ... */
 | 
						|
			*op++ = 0x20 | bb_hexdigits_upcase[*p >> 4];
 | 
						|
			*op++ = 0x20 | bb_hexdigits_upcase[*p & 0x0f];
 | 
						|
			*op++ = ' ';
 | 
						|
			if ((*p > 31) && (*p < 127))
 | 
						|
				*ap = *p;   /* printing */
 | 
						|
			else
 | 
						|
				*ap = '.';  /* nonprinting, loose def */
 | 
						|
			ap++;
 | 
						|
			p++;
 | 
						|
		} while (--x);
 | 
						|
		*ap++ = '\n';  /* finish the line */
 | 
						|
		xwrite(ofd, stage, ap - stage);
 | 
						|
	} while (bc);
 | 
						|
}
 | 
						|
#else
 | 
						|
void oprint(int direction, unsigned char *p, unsigned bc);
 | 
						|
#endif
 | 
						|
 | 
						|
/* readwrite:
 | 
						|
 handle stdin/stdout/network I/O.  Bwahaha!! -- the select loop from hell.
 | 
						|
 In this instance, return what might become our exit status. */
 | 
						|
static int readwrite(void)
 | 
						|
{
 | 
						|
	int rr;
 | 
						|
	char *zp = zp; /* gcc */  /* stdin buf ptr */
 | 
						|
	char *np = np;            /* net-in buf ptr */
 | 
						|
	unsigned rzleft;
 | 
						|
	unsigned rnleft;
 | 
						|
	unsigned netretry;              /* net-read retry counter */
 | 
						|
	unsigned fds_open;
 | 
						|
 | 
						|
	/* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
 | 
						|
	 either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
 | 
						|
	fd_set ding1;                   /* for select loop */
 | 
						|
	fd_set ding2;
 | 
						|
	FD_ZERO(&ding1);
 | 
						|
	FD_SET(netfd, &ding1);
 | 
						|
	FD_SET(STDIN_FILENO, &ding1);
 | 
						|
	fds_open = 2;
 | 
						|
 | 
						|
	netretry = 2;
 | 
						|
	rzleft = rnleft = 0;
 | 
						|
	if (o_interval)
 | 
						|
		sleep(o_interval);                /* pause *before* sending stuff, too */
 | 
						|
 | 
						|
	/* and now the big ol' select shoveling loop ... */
 | 
						|
	/* nc 1.10 has "while (FD_ISSET(netfd)" here */
 | 
						|
	while (fds_open) {
 | 
						|
		unsigned wretry = 8200;               /* net-write sanity counter */
 | 
						|
 | 
						|
		ding2 = ding1;                        /* FD_COPY ain't portable... */
 | 
						|
	/* some systems, notably linux, crap into their select timers on return, so
 | 
						|
	 we create a expendable copy and give *that* to select.  */
 | 
						|
		if (o_wait) {
 | 
						|
			struct timeval tmp_timer;
 | 
						|
			tmp_timer.tv_sec = o_wait;
 | 
						|
			tmp_timer.tv_usec = 0;
 | 
						|
		/* highest possible fd is netfd (3) */
 | 
						|
			rr = select(netfd+1, &ding2, NULL, NULL, &tmp_timer);
 | 
						|
		} else
 | 
						|
			rr = select(netfd+1, &ding2, NULL, NULL, NULL);
 | 
						|
		if (rr < 0 && errno != EINTR) {                /* might have gotten ^Zed, etc */
 | 
						|
			holler_perror("select");
 | 
						|
			close(netfd);
 | 
						|
			return 1;
 | 
						|
		}
 | 
						|
	/* if we have a timeout AND stdin is closed AND we haven't heard anything
 | 
						|
	 from the net during that time, assume it's dead and close it too. */
 | 
						|
		if (rr == 0) {
 | 
						|
			if (!FD_ISSET(STDIN_FILENO, &ding1)) {
 | 
						|
				netretry--;                        /* we actually try a coupla times. */
 | 
						|
				if (!netretry) {
 | 
						|
					if (o_verbose > 1)         /* normally we don't care */
 | 
						|
						fprintf(stderr, "net timeout\n");
 | 
						|
					/*close(netfd); - redundant, exit will do it */
 | 
						|
					return 0;                  /* not an error! */
 | 
						|
				}
 | 
						|
			}
 | 
						|
		} /* select timeout */
 | 
						|
	/* xxx: should we check the exception fds too?  The read fds seem to give
 | 
						|
	 us the right info, and none of the examples I found bothered. */
 | 
						|
 | 
						|
	/* Ding!!  Something arrived, go check all the incoming hoppers, net first */
 | 
						|
		if (FD_ISSET(netfd, &ding2)) {                /* net: ding! */
 | 
						|
			rr = read(netfd, bigbuf_net, BIGSIZ);
 | 
						|
			if (rr <= 0) {
 | 
						|
				if (rr < 0 && o_verbose > 1) {
 | 
						|
					/* nc 1.10 doesn't do this */
 | 
						|
					bb_perror_msg("net read");
 | 
						|
				}
 | 
						|
				FD_CLR(netfd, &ding1);                /* net closed */
 | 
						|
				fds_open--;
 | 
						|
				rzleft = 0;                        /* can't write anymore: broken pipe */
 | 
						|
			} else {
 | 
						|
				rnleft = rr;
 | 
						|
				np = bigbuf_net;
 | 
						|
			}
 | 
						|
Debug("got %d from the net, errno %d", rr, errno);
 | 
						|
		} /* net:ding */
 | 
						|
 | 
						|
	/* if we're in "slowly" mode there's probably still stuff in the stdin
 | 
						|
	 buffer, so don't read unless we really need MORE INPUT!  MORE INPUT! */
 | 
						|
		if (rzleft)
 | 
						|
			goto shovel;
 | 
						|
 | 
						|
	/* okay, suck more stdin */
 | 
						|
		if (FD_ISSET(STDIN_FILENO, &ding2)) {                /* stdin: ding! */
 | 
						|
			rr = read(STDIN_FILENO, bigbuf_in, BIGSIZ);
 | 
						|
	/* Considered making reads here smaller for UDP mode, but 8192-byte
 | 
						|
	 mobygrams are kinda fun and exercise the reassembler. */
 | 
						|
			if (rr <= 0) {                        /* at end, or fukt, or ... */
 | 
						|
				FD_CLR(STDIN_FILENO, &ding1); /* disable stdin */
 | 
						|
				/*close(STDIN_FILENO); - not really necessary */
 | 
						|
				/* Let peer know we have no more data */
 | 
						|
				/* nc 1.10 doesn't do this: */
 | 
						|
				shutdown(netfd, SHUT_WR);
 | 
						|
				fds_open--;
 | 
						|
			} else {
 | 
						|
				rzleft = rr;
 | 
						|
				zp = bigbuf_in;
 | 
						|
			}
 | 
						|
		} /* stdin:ding */
 | 
						|
 shovel:
 | 
						|
	/* now that we've dingdonged all our thingdings, send off the results.
 | 
						|
	 Geez, why does this look an awful lot like the big loop in "rsh"? ...
 | 
						|
	 not sure if the order of this matters, but write net -> stdout first. */
 | 
						|
 | 
						|
		if (rnleft) {
 | 
						|
			rr = write(STDOUT_FILENO, np, rnleft);
 | 
						|
			if (rr > 0) {
 | 
						|
				if (o_ofile) /* log the stdout */
 | 
						|
					oprint('<', (unsigned char *)np, rr);
 | 
						|
				np += rr;
 | 
						|
				rnleft -= rr;
 | 
						|
				wrote_out += rr; /* global count */
 | 
						|
			}
 | 
						|
Debug("wrote %d to stdout, errno %d", rr, errno);
 | 
						|
		} /* rnleft */
 | 
						|
		if (rzleft) {
 | 
						|
			if (o_interval)                        /* in "slowly" mode ?? */
 | 
						|
				rr = findline(zp, rzleft);
 | 
						|
			else
 | 
						|
				rr = rzleft;
 | 
						|
			rr = write(netfd, zp, rr);        /* one line, or the whole buffer */
 | 
						|
			if (rr > 0) {
 | 
						|
				if (o_ofile) /* log what got sent */
 | 
						|
					oprint('>', (unsigned char *)zp, rr);
 | 
						|
				zp += rr;
 | 
						|
				rzleft -= rr;
 | 
						|
				wrote_net += rr; /* global count */
 | 
						|
			}
 | 
						|
Debug("wrote %d to net, errno %d", rr, errno);
 | 
						|
		} /* rzleft */
 | 
						|
		if (o_interval) {                        /* cycle between slow lines, or ... */
 | 
						|
			sleep(o_interval);
 | 
						|
			continue;                        /* ...with hairy select loop... */
 | 
						|
		}
 | 
						|
		if (rzleft || rnleft) {                  /* shovel that shit till they ain't */
 | 
						|
			wretry--;                        /* none left, and get another load */
 | 
						|
	/* net write retries sometimes happen on UDP connections */
 | 
						|
			if (!wretry) {                   /* is something hung? */
 | 
						|
				holler_error("too many output retries");
 | 
						|
				return 1;
 | 
						|
			}
 | 
						|
			goto shovel;
 | 
						|
		}
 | 
						|
	} /* while (fds_open) */
 | 
						|
 | 
						|
	/* XXX: maybe want a more graceful shutdown() here, or screw around with
 | 
						|
	 linger times??  I suspect that I don't need to since I'm always doing
 | 
						|
	 blocking reads and writes and my own manual "last ditch" efforts to read
 | 
						|
	 the net again after a timeout.  I haven't seen any screwups yet, but it's
 | 
						|
	 not like my test network is particularly busy... */
 | 
						|
	close(netfd);
 | 
						|
	return 0;
 | 
						|
} /* readwrite */
 | 
						|
 | 
						|
/* main: now we pull it all together... */
 | 
						|
int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 | 
						|
int nc_main(int argc UNUSED_PARAM, char **argv)
 | 
						|
{
 | 
						|
	char *str_p, *str_s;
 | 
						|
	IF_NC_EXTRA(char *str_i, *str_o;)
 | 
						|
	char *themdotted = themdotted; /* for compiler */
 | 
						|
	char **proggie;
 | 
						|
	int x;
 | 
						|
	unsigned cnt_l = 0;
 | 
						|
	unsigned o_lport = 0;
 | 
						|
 | 
						|
	INIT_G();
 | 
						|
 | 
						|
	/* catch a signal or two for cleanup */
 | 
						|
	bb_signals(0
 | 
						|
		+ (1 << SIGINT)
 | 
						|
		+ (1 << SIGQUIT)
 | 
						|
		+ (1 << SIGTERM)
 | 
						|
		, catch);
 | 
						|
	/* and suppress others... */
 | 
						|
	bb_signals(0
 | 
						|
#ifdef SIGURG
 | 
						|
		+ (1 << SIGURG)
 | 
						|
#endif
 | 
						|
		+ (1 << SIGPIPE) /* important! */
 | 
						|
		, SIG_IGN);
 | 
						|
 | 
						|
	proggie = argv;
 | 
						|
	while (*++proggie) {
 | 
						|
		if (strcmp(*proggie, "-e") == 0) {
 | 
						|
			*proggie = NULL;
 | 
						|
			proggie++;
 | 
						|
			goto e_found;
 | 
						|
		}
 | 
						|
		/* -<other_opts>e PROG [ARGS] ? */
 | 
						|
		/* (aboriginal linux uses this form) */
 | 
						|
		if (proggie[0][0] == '-') {
 | 
						|
			char *optpos = *proggie + 1;
 | 
						|
			/* Skip all valid opts w/o params */
 | 
						|
			optpos = optpos + strspn(optpos, "nuv"IF_NC_SERVER("lk")IF_NC_EXTRA("z"));
 | 
						|
			if (*optpos == 'e' && !optpos[1]) {
 | 
						|
				*optpos = '\0';
 | 
						|
				proggie++;
 | 
						|
				G.proggie0saved = *proggie;
 | 
						|
				*proggie = NULL; /* terminate argv for getopt32 */
 | 
						|
				goto e_found;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	proggie = NULL;
 | 
						|
 e_found:
 | 
						|
 | 
						|
	// -g -G -t -r deleted, unimplemented -a deleted too
 | 
						|
	opt_complementary = "?2:vv:ll"; /* max 2 params; -v and -l are counters; -w N */
 | 
						|
	getopt32(argv, "np:s:uvw:+" IF_NC_SERVER("lk")
 | 
						|
			IF_NC_EXTRA("i:o:z"),
 | 
						|
			&str_p, &str_s, &o_wait
 | 
						|
			IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l));
 | 
						|
	argv += optind;
 | 
						|
#if ENABLE_NC_EXTRA
 | 
						|
	if (option_mask32 & OPT_i) /* line-interval time */
 | 
						|
		o_interval = xatou_range(str_i, 1, 0xffff);
 | 
						|
#endif
 | 
						|
#if ENABLE_NC_SERVER
 | 
						|
	//if (option_mask32 & OPT_l) /* listen mode */
 | 
						|
	if (option_mask32 & OPT_k) /* persistent server mode */
 | 
						|
		cnt_l = 2;
 | 
						|
#endif
 | 
						|
	//if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */
 | 
						|
	//if (option_mask32 & OPT_o) /* hexdump log */
 | 
						|
	if (option_mask32 & OPT_p) { /* local source port */
 | 
						|
		o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0);
 | 
						|
		if (!o_lport)
 | 
						|
			bb_error_msg_and_die("bad local port '%s'", str_p);
 | 
						|
	}
 | 
						|
	//if (option_mask32 & OPT_r) /* randomize various things */
 | 
						|
	//if (option_mask32 & OPT_u) /* use UDP */
 | 
						|
	//if (option_mask32 & OPT_v) /* verbose */
 | 
						|
	//if (option_mask32 & OPT_w) /* wait time */
 | 
						|
	//if (option_mask32 & OPT_z) /* little or no data xfer */
 | 
						|
 | 
						|
	/* We manage our fd's so that they are never 0,1,2 */
 | 
						|
	/*bb_sanitize_stdio(); - not needed */
 | 
						|
 | 
						|
	if (argv[0]) {
 | 
						|
		themaddr = xhost2sockaddr(argv[0],
 | 
						|
			argv[1]
 | 
						|
			? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0)
 | 
						|
			: 0);
 | 
						|
	}
 | 
						|
 | 
						|
	/* create & bind network socket */
 | 
						|
	x = (o_udpmode ? SOCK_DGRAM : SOCK_STREAM);
 | 
						|
	if (option_mask32 & OPT_s) { /* local address */
 | 
						|
		/* if o_lport is still 0, then we will use random port */
 | 
						|
		ouraddr = xhost2sockaddr(str_s, o_lport);
 | 
						|
#ifdef BLOAT
 | 
						|
		/* prevent spurious "UDP listen needs !0 port" */
 | 
						|
		o_lport = get_nport(ouraddr);
 | 
						|
		o_lport = ntohs(o_lport);
 | 
						|
#endif
 | 
						|
		x = xsocket(ouraddr->u.sa.sa_family, x, 0);
 | 
						|
	} else {
 | 
						|
		/* We try IPv6, then IPv4, unless addr family is
 | 
						|
		 * implicitly set by way of remote addr/port spec */
 | 
						|
		x = xsocket_type(&ouraddr,
 | 
						|
				(themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC),
 | 
						|
				x);
 | 
						|
		if (o_lport)
 | 
						|
			set_nport(&ouraddr->u.sa, htons(o_lport));
 | 
						|
	}
 | 
						|
	xmove_fd(x, netfd);
 | 
						|
	setsockopt_reuseaddr(netfd);
 | 
						|
	if (o_udpmode)
 | 
						|
		socket_want_pktinfo(netfd);
 | 
						|
	if (!ENABLE_FEATURE_UNIX_LOCAL
 | 
						|
	 || cnt_l != 0 /* listen */
 | 
						|
	 || ouraddr->u.sa.sa_family != AF_UNIX
 | 
						|
	) {
 | 
						|
		xbind(netfd, &ouraddr->u.sa, ouraddr->len);
 | 
						|
	}
 | 
						|
#if 0
 | 
						|
	setsockopt_SOL_SOCKET_int(netfd, SO_RCVBUF, o_rcvbuf);
 | 
						|
	setsockopt_SOL_SOCKET_int(netfd, SO_SNDBUF, o_sndbuf);
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef BLOAT
 | 
						|
	if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) {
 | 
						|
		/* apparently UDP can listen ON "port 0",
 | 
						|
		 but that's not useful */
 | 
						|
		if (!o_lport)
 | 
						|
			bb_error_msg_and_die("UDP listen needs nonzero -p port");
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	if (proggie) {
 | 
						|
		close(STDIN_FILENO); /* won't need stdin */
 | 
						|
		option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */
 | 
						|
	}
 | 
						|
#if ENABLE_NC_EXTRA
 | 
						|
	if (o_ofile)
 | 
						|
		xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
 | 
						|
#endif
 | 
						|
 | 
						|
	if (cnt_l != 0) {
 | 
						|
		dolisten((cnt_l - 1), proggie);
 | 
						|
		/* dolisten does its own connect reporting */
 | 
						|
		x = readwrite(); /* it even works with UDP! */
 | 
						|
	} else {
 | 
						|
		/* Outbound connects.  Now we're more picky about args... */
 | 
						|
		if (!themaddr)
 | 
						|
			bb_show_usage();
 | 
						|
 | 
						|
		remend = *themaddr;
 | 
						|
		if (o_verbose)
 | 
						|
			themdotted = xmalloc_sockaddr2dotted(&themaddr->u.sa);
 | 
						|
 | 
						|
		x = connect_w_timeout(netfd);
 | 
						|
		if (o_zero && x == 0 && o_udpmode)        /* if UDP scanning... */
 | 
						|
			x = udptest();
 | 
						|
		if (x == 0) {                        /* Yow, are we OPEN YET?! */
 | 
						|
			if (o_verbose)
 | 
						|
				fprintf(stderr, "%s (%s) open\n", argv[0], themdotted);
 | 
						|
			if (proggie)                        /* exec is valid for outbound, too */
 | 
						|
				doexec(proggie);
 | 
						|
			if (!o_zero)
 | 
						|
				x = readwrite();
 | 
						|
		} else { /* connect or udptest wasn't successful */
 | 
						|
			x = 1;                                /* exit status */
 | 
						|
			/* if we're scanning at a "one -v" verbosity level, don't print refusals.
 | 
						|
			 Give it another -v if you want to see everything. */
 | 
						|
			if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED))
 | 
						|
				bb_perror_msg("%s (%s)", argv[0], themdotted);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (o_verbose > 1)                /* normally we don't care */
 | 
						|
		fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
 | 
						|
	return x;
 | 
						|
}
 |