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

Reply via email to