diff options
author | delphij <delphij@FreeBSD.org> | 2015-05-18 21:27:46 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2015-05-18 21:27:46 +0000 |
commit | 8786fdbe9dcbc192740f45e0c056c504a1ba9ccd (patch) | |
tree | 6005c89e9407dd35086b7eab887fd1d4de8a40bf /usr.bin | |
parent | 3b84fa4ac901f1d654117f36d5f0ec549861e24b (diff) | |
download | FreeBSD-src-8786fdbe9dcbc192740f45e0c056c504a1ba9ccd.zip FreeBSD-src-8786fdbe9dcbc192740f45e0c056c504a1ba9ccd.tar.gz |
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.
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/whois/whois.c | 139 |
1 files changed, 129 insertions, 10 deletions
diff --git a/usr.bin/whois/whois.c b/usr.bin/whois/whois.c index 112e089..58f50dc 100644 --- a/usr.bin/whois/whois.c +++ b/usr.bin/whois/whois.c @@ -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 *hostname, int flags) 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"); |