svn commit: r283083 - stable/10/usr.bin/whois

Xin LI delphij at FreeBSD.org
Mon May 18 21:27:47 UTC 2015


Author: delphij
Date: Mon May 18 21:27:46 2015
New Revision: 283083
URL: https://svnweb.freebsd.org/changeset/base/283083

Log:
  MFC r281959,282885 (fanf, partial),282888 (fanf):
  
  Try alternate addresses more agressively.
  
  PR:             158125
  Submitted by:   Mark Andrews <marka isc org> (with changes from me)
  
  whois: code cleanup
  
  Use pedantically correct types.
  
  whois: do not clobber command-line flags when tweaking O_NONBLOCK
  
  This can make whois fail to follow referrals when it should.
  The bug was introduced in r281959.

Modified:
  stable/10/usr.bin/whois/whois.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/usr.bin/whois/whois.c
==============================================================================
--- stable/10/usr.bin/whois/whois.c	Mon May 18 21:18:44 2015	(r283082)
+++ stable/10/usr.bin/whois/whois.c	Mon May 18 21:27:46 2015	(r283083)
@@ -1,4 +1,4 @@
-/*
+/*-
  * Copyright (c) 1980, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/poll.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <ctype.h>
@@ -55,6 +56,8 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #include <sysexits.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #define	ABUSEHOST	"whois.abuse.net"
 #define	NICHOST		"whois.crsnic.net"
@@ -280,21 +283,137 @@ whois(const char *query, const char *hos
 	FILE *sfi, *sfo;
 	struct addrinfo *hostres, *res;
 	char *buf, *host, *nhost, *p;
-	int i, s;
-	size_t c, len;
+	int s = -1, f;
+	nfds_t i, j;
+	size_t c, len, count;
+	struct pollfd *fds;
+	int timeout = 180;
 
-	s = -1;
 	hostres = gethostinfo(hostname, 1);
-	for (res = hostres; res; res = res->ai_next) {
-		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+	for (res = hostres, count = 0; res; res = res->ai_next)
+		count++;
+
+	fds = calloc(count, sizeof(*fds));
+	if (fds == NULL)
+		err(EX_OSERR, "calloc()");
+
+	/*
+	 * Traverse the result list elements and make non-block
+	 * connection attempts.
+	 */
+	count = i = 0;
+	for (res = hostres; res != NULL; res = res->ai_next) {
+		s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK,
+		    res->ai_protocol);
 		if (s < 0)
 			continue;
-		if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
-			break;
-		close(s);
+		if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
+			if (errno == EINPROGRESS) {
+				/* Add the socket to poll list */
+				fds[i].fd = s;
+				fds[i].events = POLLERR | POLLHUP |
+						POLLIN | POLLOUT;
+				count++;
+				i++;
+			} else {
+				close(s);
+				s = -1;
+
+				/*
+				 * Poll only if we have something to poll,
+				 * otherwise just go ahead and try next
+				 * address
+				 */
+				if (count == 0)
+					continue;
+			}
+		} else
+			goto done;
+
+		/*
+		 * If we are at the last address, poll until a connection is
+		 * established or we failed all connection attempts.
+		 */
+		if (res->ai_next == NULL)
+			timeout = INFTIM;
+
+		/*
+		 * Poll the watched descriptors for successful connections:
+		 * if we still have more untried resolved addresses, poll only
+		 * once; otherwise, poll until all descriptors have errors,
+		 * which will be considered as ETIMEDOUT later.
+		 */
+		do {
+			int n;
+
+			n = poll(fds, i, timeout);
+			if (n == 0) {
+				/*
+				 * No event reported in time.  Try with a
+				 * smaller timeout (but cap at 2-3ms)
+				 * after a new host have been added.
+				 */
+				if (timeout >= 3)
+					timeout <<= 1;
+
+				break;
+			} else if (n < 0) {
+				/*
+				 * errno here can only be EINTR which we would want
+				 * to clean up and bail out.
+				 */
+				s = -1;
+				goto done;
+			}
+
+			/*
+			 * Check for the event(s) we have seen.
+			 */
+			for (j = 0; j < i; j++) {
+				if (fds[j].fd == -1 || fds[j].events == 0 ||
+				    fds[j].revents == 0)
+					continue;
+				if (fds[j].revents & ~(POLLIN | POLLOUT)) {
+					close(s);
+					fds[j].fd = -1;
+					fds[j].events = 0;
+					count--;
+					continue;
+				} else if (fds[j].revents & (POLLIN | POLLOUT)) {
+					/* Connect succeeded. */
+					s = fds[j].fd;
+
+					goto done;
+				}
+
+			}
+		} while (timeout == INFTIM && count != 0);
 	}
+
+	/* All attempts were failed */
+	s = -1;
+	if (count == 0)
+		errno = ETIMEDOUT;
+
+done:
+	/* Close all watched fds except the succeeded one */
+	for (j = 0; j < i; j++)
+		if (fds[j].fd != s && fds[j].fd != -1)
+			close(fds[j].fd);
+
+	if (s != -1) {
+                /* Restore default blocking behavior.  */
+                if ((f = fcntl(s, F_GETFL)) != -1) {
+                        f &= ~O_NONBLOCK;
+                        if (fcntl(s, F_SETFL, f) == -1)
+                                err(EX_OSERR, "fcntl()");
+                } else
+			err(EX_OSERR, "fcntl()");
+        }
+
+	free(fds);
 	freeaddrinfo(hostres);
-	if (res == NULL)
+	if (s == -1)
 		err(EX_OSERR, "connect()");
 
 	sfi = fdopen(s, "r");


More information about the svn-src-all mailing list