bin/158125: whois takes too long to move to next address. [patch]

Mark Andrews marka at isc.org
Tue Jun 21 14:20:10 UTC 2011


>Number:         158125
>Category:       bin
>Synopsis:       whois takes too long to move to next address. [patch]
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jun 21 14:20:09 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     Mark Andrews
>Release:        FreeBSD 8.2-STABLE i386
>Organization:
ISC
>Environment:
System: FreeBSD sex.dv.isc.org 8.2-STABLE FreeBSD 8.2-STABLE #10: Sat Feb 26 18:02:12 EST 2011 marka at sex.dv.isc.org:/usr/obj/usr/src/sys/DEBUG i386


>Description:

	whois has a simple connect loop which has doesn't fallback to
	alternate address on connect failures in a timely manner.
	
>How-To-Repeat:

	block connections to whois.ripe.net over IPv6 then try to
	lookup information in ripe's whois database from a dual
	stack server.

>Fix:

	Attempt to connect to alternate addresses if the connect
	doesn't succeed in 100ms.  Take the first connection to
	succeed and close the others.  Reduce the wait between
	connection attempts by 2 on each connection attempt.

--- /usr/src/usr.bin/whois/whois.c	2010-01-21 21:16:21.000000000 +1100
+++ whois.c	2011-06-22 00:01:47.000000000 +1000
@@ -48,6 +48,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/poll.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <ctype.h>
@@ -59,6 +60,8 @@
 #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"
@@ -282,21 +285,96 @@
 	FILE *sfi, *sfo;
 	struct addrinfo *hostres, *res;
 	char *buf, *host, *nhost, *p;
-	int i, s;
+	int i, j, n, s, count;
 	size_t c, len;
+	struct pollfd *fds;
+	int timeout = 100;
 
 	s = -1;
 	hostres = gethostinfo(hostname, 1);
-	for (res = hostres; res; res = res->ai_next) {
+	for (res = hostres, count = 0; res; res = res->ai_next)
+		count++;
+
+	fds = calloc(count, sizeof(*fds));
+	if (fds == NULL)
+		err(EX_OSERR, "calloc()");
+	
+	for (res = hostres, i = 0, count = 0; res; res = res->ai_next) {
 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-		if (s < 0)
+		if (s < 0) {
+			if (res->ai_next != NULL)
+				continue;
+		} else if ((flags = fcntl(s, F_GETFL)) == -1) {
+			close(s);
+		} else if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
+			close(s);
+		} else if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
+			if (errno != EINPROGRESS) {
+				close(s);
+			} else {
+				fds[i].fd = s;
+				fds[i].events = POLLERR | POLLHUP |
+						POLLIN | POLLOUT;
+				count++;
+				i++;
+			}
+		} else
+			goto done;
+
+		if (count == 0)
 			continue;
-		if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
-			break;
-		close(s);
+			
+		do {
+			if (res->ai_next == NULL)
+				timeout = -1;
+			n = poll(fds, i, timeout);
+			if (n == 0) {
+				timeout >> 1;
+				break;
+			}
+			if (n < 0 ) {
+				if (errno == EAGAIN || errno == EINTR)
+                                        continue;
+                                s = -1;
+                                goto done;
+			}
+			for (j = 0; j < i; j++) {
+                                if (fds[j].fd == -1 || fds[j].events == 0 ||
+                                    fds[j].revents == 0)
+                                        continue;
+                                s = fds[j].fd;
+                                if (fds[j].revents & POLLHUP) {
+                                        close(s);
+                                        fds[j].fd = -1;
+                                        fds[j].events = 0;
+                                        count--;
+                                        continue;
+                                }
+                                /* Connect succeeded. */
+                                goto done;
+                        }
+		} while (timeout == -1 && count != 0);
 	}
+	s = -1;
+
+ done:
+	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 behaviour.  */
+                if ((flags = fcntl(s, F_GETFL)) != -1) {
+                        flags &= ~O_NONBLOCK;
+                        if (fcntl(s, F_SETFL, flags) == -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");

>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list