Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 7 May 2015 22:54:32 +0000 (UTC)
From:      Xin LI <delphij@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r282613 - head/contrib/netcat
Message-ID:  <201505072254.t47MsWpO030187@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Thu May  7 22:54:31 2015
New Revision: 282613
URL: https://svnweb.freebsd.org/changeset/base/282613

Log:
  MFV r282611: netcat from OpenBSD 5.7.
  
  MFC after:	2 weeks

Modified:
  head/contrib/netcat/netcat.c
Directory Properties:
  head/contrib/netcat/   (props changed)

Modified: head/contrib/netcat/netcat.c
==============================================================================
--- head/contrib/netcat/netcat.c	Thu May  7 22:48:13 2015	(r282612)
+++ head/contrib/netcat/netcat.c	Thu May  7 22:54:31 2015	(r282613)
@@ -1,4 +1,4 @@
-/* $OpenBSD: netcat.c,v 1.122 2014/07/20 01:38:40 guenther Exp $ */
+/* $OpenBSD: netcat.c,v 1.127 2015/02/14 22:40:22 jca Exp $ */
 /*
  * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
  *
@@ -42,7 +42,6 @@
 #include <sys/un.h>
 
 #include <netinet/in.h>
-#include <netinet/in_systm.h>
 #ifdef IPSEC
 #include <netipsec/ipsec.h>
 #endif
@@ -73,6 +72,12 @@
 #define PORT_MAX_LEN	6
 #define UNIX_DG_TMP_SOCKET_SIZE	19
 
+#define POLL_STDIN 0
+#define POLL_NETOUT 1
+#define POLL_NETIN 2
+#define POLL_STDOUT 3
+#define BUFSIZE 16384
+
 /* Command Line Options */
 int	dflag;					/* detached, no stdin */
 int	Fflag;					/* fdpass sock to stdout */
@@ -117,10 +122,12 @@ int	udptest(int);
 int	unix_bind(char *);
 int	unix_connect(char *);
 int	unix_listen(char *);
-void	set_common_sockopts(int);
+void	set_common_sockopts(int, int);
 int	map_tos(char *, int *);
 void	report_connect(const struct sockaddr *, socklen_t);
 void	usage(int);
+ssize_t drainbuf(int, unsigned char *, size_t *);
+ssize_t fillbuf(int, unsigned char *, size_t *);
 
 #ifdef IPSEC
 void	add_ipsec_policy(int, char *);
@@ -436,7 +443,7 @@ main(int argc, char *argv[])
 				    &len);
 				if (connfd == -1) {
 					/* For now, all errnos are fatal */
-   					err(1, "accept");
+					err(1, "accept");
 				}
 				if (vflag)
 					report_connect((struct sockaddr *)&cliaddr, len);
@@ -663,7 +670,7 @@ remote_connect(const char *host, const c
 			freeaddrinfo(ares);
 		}
 
-		set_common_sockopts(s);
+		set_common_sockopts(s, res0->ai_family);
 
 		if (timeout_connect(s, res0->ai_addr, res0->ai_addrlen) == 0)
 			break;
@@ -767,6 +774,8 @@ local_listen(char *host, char *port, str
 				err(1, "disable TCP options");
 		}
 
+		set_common_sockopts(s, res0->ai_family);
+
 		if (bind(s, (struct sockaddr *)res0->ai_addr,
 		    res0->ai_addrlen) == 0)
 			break;
@@ -790,68 +799,224 @@ local_listen(char *host, char *port, str
  * Loop that polls on the network file descriptor and stdin.
  */
 void
-readwrite(int nfd)
+readwrite(int net_fd)
 {
-	struct pollfd pfd[2];
-	unsigned char buf[16 * 1024];
-	int n, wfd = fileno(stdin);
-	int lfd = fileno(stdout);
-	int plen;
-
-	plen = sizeof(buf);
-
-	/* Setup Network FD */
-	pfd[0].fd = nfd;
-	pfd[0].events = POLLIN;
-
-	/* Set up STDIN FD. */
-	pfd[1].fd = wfd;
-	pfd[1].events = POLLIN;
+	struct pollfd pfd[4];
+	int stdin_fd = STDIN_FILENO;
+	int stdout_fd = STDOUT_FILENO;
+	unsigned char netinbuf[BUFSIZE];
+	size_t netinbufpos = 0;
+	unsigned char stdinbuf[BUFSIZE];
+	size_t stdinbufpos = 0;
+	int n, num_fds;
+	ssize_t ret;
+
+	/* don't read from stdin if requested */
+	if (dflag)
+		stdin_fd = -1;
+
+	/* stdin */
+	pfd[POLL_STDIN].fd = stdin_fd;
+	pfd[POLL_STDIN].events = POLLIN;
+
+	/* network out */
+	pfd[POLL_NETOUT].fd = net_fd;
+	pfd[POLL_NETOUT].events = 0;
+
+	/* network in */
+	pfd[POLL_NETIN].fd = net_fd;
+	pfd[POLL_NETIN].events = POLLIN;
+
+	/* stdout */
+	pfd[POLL_STDOUT].fd = stdout_fd;
+	pfd[POLL_STDOUT].events = 0;
+
+	while (1) {
+		/* both inputs are gone, buffers are empty, we are done */
+		if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1
+		    && stdinbufpos == 0 && netinbufpos == 0) {
+			close(net_fd);
+			return;
+		}
+		/* both outputs are gone, we can't continue */
+		if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) {
+			close(net_fd);
+			return;
+		}
+		/* listen and net in gone, queues empty, done */
+		if (lflag && pfd[POLL_NETIN].fd == -1
+		    && stdinbufpos == 0 && netinbufpos == 0) {
+			close(net_fd);
+			return;
+		}
 
-	while (pfd[0].fd != -1) {
+		/* help says -i is for "wait between lines sent". We read and
+		 * write arbitrary amounts of data, and we don't want to start
+		 * scanning for newlines, so this is as good as it gets */
 		if (iflag)
 			sleep(iflag);
 
-		if ((n = poll(pfd, 2 - dflag, timeout)) < 0) {
-			int saved_errno = errno;
-			close(nfd);
-			errc(1, saved_errno, "Polling Error");
+		/* poll */
+		num_fds = poll(pfd, 4, timeout);
+
+		/* treat poll errors */
+		if (num_fds == -1) {
+			close(net_fd);
+			err(1, "polling error");
 		}
 
-		if (n == 0)
+		/* timeout happened */
+		if (num_fds == 0)
 			return;
 
-		if (pfd[0].revents & POLLIN) {
-			if ((n = read(nfd, buf, plen)) < 0)
-				return;
-			else if (n == 0) {
-				shutdown(nfd, SHUT_RD);
-				pfd[0].fd = -1;
-				pfd[0].events = 0;
-			} else {
-				if (tflag)
-					atelnet(nfd, buf, n);
-				if (atomicio(vwrite, lfd, buf, n) != n)
-					return;
+		/* treat socket error conditions */
+		for (n = 0; n < 4; n++) {
+			if (pfd[n].revents & (POLLERR|POLLNVAL)) {
+				pfd[n].fd = -1;
 			}
 		}
+		/* reading is possible after HUP */
+		if (pfd[POLL_STDIN].events & POLLIN &&
+		    pfd[POLL_STDIN].revents & POLLHUP &&
+		    ! (pfd[POLL_STDIN].revents & POLLIN))
+				pfd[POLL_STDIN].fd = -1;
+
+		if (pfd[POLL_NETIN].events & POLLIN &&
+		    pfd[POLL_NETIN].revents & POLLHUP &&
+		    ! (pfd[POLL_NETIN].revents & POLLIN))
+				pfd[POLL_NETIN].fd = -1;
+
+		if (pfd[POLL_NETOUT].revents & POLLHUP) {
+			if (Nflag)
+				shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
+			pfd[POLL_NETOUT].fd = -1;
+		}
+		/* if HUP, stop watching stdout */
+		if (pfd[POLL_STDOUT].revents & POLLHUP)
+			pfd[POLL_STDOUT].fd = -1;
+		/* if no net out, stop watching stdin */
+		if (pfd[POLL_NETOUT].fd == -1)
+			pfd[POLL_STDIN].fd = -1;
+		/* if no stdout, stop watching net in */
+		if (pfd[POLL_STDOUT].fd == -1) {
+			if (pfd[POLL_NETIN].fd != -1)
+				shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
+			pfd[POLL_NETIN].fd = -1;
+		}
 
-		if (!dflag && pfd[1].revents & POLLIN) {
-			if ((n = read(wfd, buf, plen)) < 0)
-				return;
-			else if (n == 0) {
-				if (Nflag)
-					shutdown(nfd, SHUT_WR);
-				pfd[1].fd = -1;
-				pfd[1].events = 0;
-			} else {
-				if (atomicio(vwrite, nfd, buf, n) != n)
-					return;
+		/* try to read from stdin */
+		if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) {
+			ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf,
+			    &stdinbufpos);
+			/* error or eof on stdin - remove from pfd */
+			if (ret == 0 || ret == -1)
+				pfd[POLL_STDIN].fd = -1;
+			/* read something - poll net out */
+			if (stdinbufpos > 0)
+				pfd[POLL_NETOUT].events = POLLOUT;
+			/* filled buffer - remove self from polling */
+			if (stdinbufpos == BUFSIZE)
+				pfd[POLL_STDIN].events = 0;
+		}
+		/* try to write to network */
+		if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) {
+			ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf,
+			    &stdinbufpos);
+			if (ret == -1)
+				pfd[POLL_NETOUT].fd = -1;
+			/* buffer empty - remove self from polling */
+			if (stdinbufpos == 0)
+				pfd[POLL_NETOUT].events = 0;
+			/* buffer no longer full - poll stdin again */
+			if (stdinbufpos < BUFSIZE)
+				pfd[POLL_STDIN].events = POLLIN;
+		}
+		/* try to read from network */
+		if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) {
+			ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf,
+			    &netinbufpos);
+			if (ret == -1)
+				pfd[POLL_NETIN].fd = -1;
+			/* eof on net in - remove from pfd */
+			if (ret == 0) {
+				shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
+				pfd[POLL_NETIN].fd = -1;
 			}
+			/* read something - poll stdout */
+			if (netinbufpos > 0)
+				pfd[POLL_STDOUT].events = POLLOUT;
+			/* filled buffer - remove self from polling */
+			if (netinbufpos == BUFSIZE)
+				pfd[POLL_NETIN].events = 0;
+			/* handle telnet */
+			if (tflag)
+				atelnet(pfd[POLL_NETIN].fd, netinbuf,
+				    netinbufpos);
+		}
+		/* try to write to stdout */
+		if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) {
+			ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf,
+			    &netinbufpos);
+			if (ret == -1)
+				pfd[POLL_STDOUT].fd = -1;
+			/* buffer empty - remove self from polling */
+			if (netinbufpos == 0)
+				pfd[POLL_STDOUT].events = 0;
+			/* buffer no longer full - poll net in again */
+			if (netinbufpos < BUFSIZE)
+				pfd[POLL_NETIN].events = POLLIN;
+		}
+
+		/* stdin gone and queue empty? */
+		if (pfd[POLL_STDIN].fd == -1 && stdinbufpos == 0) {
+			if (pfd[POLL_NETOUT].fd != -1 && Nflag)
+				shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
+			pfd[POLL_NETOUT].fd = -1;
+		}
+		/* net in gone and queue empty? */
+		if (pfd[POLL_NETIN].fd == -1 && netinbufpos == 0) {
+			pfd[POLL_STDOUT].fd = -1;
 		}
 	}
 }
 
+ssize_t
+drainbuf(int fd, unsigned char *buf, size_t *bufpos)
+{
+	ssize_t n;
+	ssize_t adjust;
+
+	n = write(fd, buf, *bufpos);
+	/* don't treat EAGAIN, EINTR as error */
+	if (n == -1 && (errno == EAGAIN || errno == EINTR))
+		n = -2;
+	if (n <= 0)
+		return n;
+	/* adjust buffer */
+	adjust = *bufpos - n;
+	if (adjust > 0)
+		memmove(buf, buf + n, adjust);
+	*bufpos -= n;
+	return n;
+}
+
+
+ssize_t
+fillbuf(int fd, unsigned char *buf, size_t *bufpos)
+{
+	size_t num = BUFSIZE - *bufpos;
+	ssize_t n;
+
+	n = read(fd, buf + *bufpos, num);
+	/* don't treat EAGAIN, EINTR as error */
+	if (n == -1 && (errno == EAGAIN || errno == EINTR))
+		n = -2;
+	if (n <= 0)
+		return n;
+	*bufpos += n;
+	return n;
+}
+
 /*
  * fdpass()
  * Pass the connected file descriptor to stdout and exit.
@@ -1025,7 +1190,7 @@ udptest(int s)
 }
 
 void
-set_common_sockopts(int s)
+set_common_sockopts(int s, int af)
 {
 	int x = 1;
 
@@ -1040,8 +1205,17 @@ set_common_sockopts(int s)
 			err(1, NULL);
 	}
 	if (Tflag != -1) {
-		if (setsockopt(s, IPPROTO_IP, IP_TOS,
-		    &Tflag, sizeof(Tflag)) == -1)
+		int proto, option;
+
+		if (af == AF_INET6) {
+			proto = IPPROTO_IPV6;
+			option = IPV6_TCLASS;
+		} else {
+			proto = IPPROTO_IP;
+			option = IP_TOS;
+		}
+
+		if (setsockopt(s, proto, option, &Tflag, sizeof(Tflag)) == -1)
 			err(1, "set IP ToS");
 	}
 	if (Iflag) {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201505072254.t47MsWpO030187>