>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 ma...@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:
_______________________________________________
freebsd-bugs@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"

Reply via email to