summaryrefslogtreecommitdiffstats
path: root/usr.bin/whois
diff options
context:
space:
mode:
authorsjg <sjg@FreeBSD.org>2015-05-27 01:19:58 +0000
committersjg <sjg@FreeBSD.org>2015-05-27 01:19:58 +0000
commit65145fa4c81da358fcbc3b650156dab705dfa34e (patch)
tree55c065b6730aaac2afb6c29933ee6ec5fa4c4249 /usr.bin/whois
parent60ff4eb0dff94a04d75d0d52a3957aaaf5f8c693 (diff)
parente6b664c390af88d4a87208bc042ce503da664c3b (diff)
downloadFreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.zip
FreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.tar.gz
Merge sync of head
Diffstat (limited to 'usr.bin/whois')
-rw-r--r--usr.bin/whois/whois.1130
-rw-r--r--usr.bin/whois/whois.c265
2 files changed, 269 insertions, 126 deletions
diff --git a/usr.bin/whois/whois.1 b/usr.bin/whois/whois.1
index 0f0f177..a9ed50a 100644
--- a/usr.bin/whois/whois.1
+++ b/usr.bin/whois/whois.1
@@ -28,7 +28,7 @@
.\" From: @(#)whois.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$
.\"
-.Dd October 2, 2009
+.Dd May 14, 2015
.Dt WHOIS 1
.Os
.Sh NAME
@@ -36,7 +36,7 @@
.Nd "Internet domain name and network number directory service"
.Sh SYNOPSIS
.Nm
-.Op Fl aAbfgiIklmQrR
+.Op Fl aAbfgiIklmPQr
.Op Fl c Ar country-code | Fl h Ar host
.Op Fl p Ar port
.Ar name ...
@@ -47,6 +47,42 @@ utility looks up records in the databases maintained by several
Network Information Centers
.Pq Tn NICs .
.Pp
+By default
+.Nm
+automatically discovers the name of a whois server to use
+from the top-level domain
+.Pq Tn TLD
+of the supplied (single) argument.
+It tries
+.Qq Va TLD Ns Li .whois-servers.net
+and
+.Qq Li whois.nic. Ns Va TLD
+and if neither host exists it falls back to its default server.
+.Pp
+If an IP address is specified, the whois server will default to
+the American Registry for Internet Numbers
+.Pq Tn ARIN .
+If a query to
+.Tn ARIN
+references
+.Tn APNIC , AfriNIC , LACNIC ,
+or
+.Tn RIPE ,
+that server will be queried also, provided that the
+.Fl Q
+option is not specified.
+.Pp
+If
+.Nm
+cannot automatically discover a server,
+it will fall back to
+the host specified in the
+.Ev WHOIS_SERVER
+or
+.Ev RA_SERVER
+environment variables, or if those are not set, it will use
+.Pa whois.crsnic.net .
+.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl a
@@ -88,66 +124,12 @@ Use the US non-military federal government database, which contains points of
contact for subdomains of
.Pa .GOV .
.It Fl h Ar host
-Use the specified host instead of the default variant.
+Use the specified host instead of the default.
Either a host name or an IP address may be specified.
-.Pp
-By default
-.Nm
-constructs the name of a whois server to use from the top-level domain
-.Pq Tn TLD
-of the supplied (single) argument, and appending
-.Qq Li .whois-servers.net .
-This effectively allows a suitable whois server to be selected
-automatically for a large number of
-.Tn TLDs .
-.Pp
-In the event that an IP
-address is specified, the whois server will default to the American
-Registry for Internet Numbers
-.Pq Tn ARIN .
-If a query to
-.Tn ARIN
-references
-.Tn APNIC , AfriNIC , LACNIC ,
-or
-.Tn RIPE ,
-that server will be queried also, provided that the
-.Fl Q
-option is not specified.
-.Pp
-If the query is not a domain name or IP address,
-.Nm
-will fall back to
-.Pa whois.crsnic.net .
.It Fl i
-Use the Network Solutions Registry for Internet Numbers
+Use the obsolete Network Solutions Registry for Internet Numbers
.Pq Pa whois.networksolutions.com
database.
-It contains network numbers and domain contact information for most of
-.Pa .COM , .NET , .ORG
-and
-.Pa .EDU
-domains.
-.Pp
-.Sy NOTE !
-The registration of these domains is now done by a number of
-independent and competing registrars and this database holds no information
-on the domains registered by organizations other than Network Solutions, Inc.
-Also, note that the
-.Tn InterNIC
-database
-.Pq Pa whois.internic.net
-is no longer handled by Network Solutions, Inc.
-For details, see
-.Pa http://www.internic.net/ .
-.Pp
-(Hint: Contact information, identified by the term
-.Em handle ,
-can be looked up by prefixing
-.Qq Li "handle "
-to the
-.Tn NIC
-handle in the query.)
.It Fl I
Use the Internet Assigned Numbers Authority
.Pq Tn IANA
@@ -177,6 +159,10 @@ Connect to the whois server on
If this option is not specified,
.Nm
defaults to port 43.
+.It Fl P
+Use the PeeringDB database of AS numbers.
+It contains details about presence at internet peering points
+for many network operators.
.It Fl Q
Do a quick lookup.
This means that
@@ -190,24 +176,28 @@ Use the R\(aaeseaux IP Europ\(aaeens
database.
It contains network numbers and domain contact information
for Europe.
-.It Fl R
-Use the Russia Network Information Center
-.Pq Tn RIPN
-database.
-It contains network numbers and domain contact information
-for subdomains of
-.Pa .RU .
-This option is deprecated; use the
-.Fl c
-option with an argument of
-.Qq Li RU
-instead.
.El
.Pp
The operands specified to
.Nm
are treated independently and may be used
as queries on different whois servers.
+.Sh ENVIRONMENT
+.Bl -tag
+.It Ev WHOIS_SERVER
+The primary default whois server.
+If this is unset,
+.Nm
+uses the
+.Ev RA_SERVER
+environment variable.
+.It Ev RA_SERVER
+The secondary default whois server.
+If this is unset,
+.Nm
+will use
+.Pa whois.crsnic.net .
+.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
diff --git a/usr.bin/whois/whois.c b/usr.bin/whois/whois.c
index 3f6f93a..6ad8826 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,24 +56,29 @@ __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"
-#define INICHOST "whois.networksolutions.com"
-#define GNICHOST "whois.nic.gov"
#define ANICHOST "whois.arin.net"
-#define LNICHOST "whois.lacnic.net"
+#define BNICHOST "whois.registro.br"
+#define FNICHOST "whois.afrinic.net"
+#define GERMNICHOST "de.whois-servers.net"
+#define GNICHOST "whois.nic.gov"
+#define IANAHOST "whois.iana.org"
+#define INICHOST "whois.networksolutions.com"
#define KNICHOST "whois.krnic.net"
-#define RNICHOST "whois.ripe.net"
-#define PNICHOST "whois.apnic.net"
+#define LNICHOST "whois.lacnic.net"
#define MNICHOST "whois.ra.net"
+#define NICHOST "whois.crsnic.net"
+#define PDBHOST "whois.peeringdb.com"
+#define PNICHOST "whois.apnic.net"
+#define QNICHOST_HEAD "whois.nic."
#define QNICHOST_TAIL ".whois-servers.net"
-#define BNICHOST "whois.registro.br"
-#define NORIDHOST "whois.norid.no"
-#define IANAHOST "whois.iana.org"
-#define GERMNICHOST "de.whois-servers.net"
-#define FNICHOST "whois.afrinic.net"
+#define RNICHOST "whois.ripe.net"
+
#define DEFAULT_PORT "whois"
+
#define WHOIS_SERVER_ID "Whois Server: "
#define WHOIS_ORG_SERVER_ID "Registrant Street1:Whois Server:"
@@ -81,12 +87,25 @@ __FBSDID("$FreeBSD$");
#define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-')
+static struct {
+ const char *suffix, *server;
+} whoiswhere[] = {
+ /* Various handles */
+ { "-ARIN", ANICHOST },
+ { "-NICAT", "at" QNICHOST_TAIL },
+ { "-NORID", "no" QNICHOST_TAIL },
+ { "-RIPE", RNICHOST },
+ /* Nominet's whois server doesn't return referrals to JANET */
+ { ".ac.uk", "ac.uk" QNICHOST_TAIL },
+ { NULL, NULL }
+};
+
static const char *ip_whois[] = { LNICHOST, RNICHOST, PNICHOST, BNICHOST,
FNICHOST, NULL };
static const char *port = DEFAULT_PORT;
static char *choose_server(char *);
-static struct addrinfo *gethostinfo(char const *host, int exit_on_error);
+static struct addrinfo *gethostinfo(char const *host, int exitnoname);
static void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3);
static void usage(void);
static void whois(const char *, const char *, int);
@@ -104,7 +123,7 @@ main(int argc, char *argv[])
country = host = qnichost = NULL;
flags = use_qnichost = 0;
- while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:QrR6")) != -1) {
+ while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQr")) != -1) {
switch (ch) {
case 'a':
host = ANICHOST;
@@ -145,21 +164,15 @@ main(int argc, char *argv[])
case 'p':
port = optarg;
break;
+ case 'P':
+ host = PDBHOST;
+ break;
case 'Q':
flags |= WHOIS_QUICK;
break;
case 'r':
host = RNICHOST;
break;
- case 'R':
- warnx("-R is deprecated; use '-c ru' instead");
- country = "ru";
- break;
- /* Remove in FreeBSD 10 */
- case '6':
- errx(EX_USAGE,
- "-6 is deprecated; use -[aAflr] instead");
- break;
case '?':
default:
usage();
@@ -173,13 +186,12 @@ main(int argc, char *argv[])
usage();
/*
- * If no host or country is specified determine the top level domain
- * from the query. If the TLD is a number, query ARIN. Otherwise, use
- * TLD.whois-server.net. If the domain does not contain '.', fall
- * back to NICHOST.
+ * If no host or country is specified, try to determine the top
+ * level domain from the query, or fall back to NICHOST.
*/
if (host == NULL && country == NULL) {
- if ((host = getenv("RA_SERVER")) == NULL) {
+ if ((host = getenv("WHOIS_SERVER")) == NULL &&
+ (host = getenv("RA_SERVER")) == NULL) {
use_qnichost = 1;
host = NICHOST;
if (!(flags & WHOIS_QUICK))
@@ -207,39 +219,67 @@ main(int argc, char *argv[])
* returns a pointer to newly allocated memory containing the whois server to
* be queried, or a NULL if the correct server couldn't be determined. The
* caller must remember to free(3) the allocated memory.
+ *
+ * If the domain is an IPv6 address or has a known suffix, that determines
+ * the server, else if the TLD is a number, query ARIN, else try a couple of
+ * formulaic server names. Fail if the domain does not contain '.'.
*/
static char *
choose_server(char *domain)
{
char *pos, *retval;
+ int i;
+ struct addrinfo *res;
if (strchr(domain, ':')) {
s_asprintf(&retval, "%s", ANICHOST);
return (retval);
}
- for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.';)
- *pos = '\0';
+ for (pos = strchr(domain, '\0'); pos > domain && pos[-1] == '.';)
+ *--pos = '\0';
if (*domain == '\0')
errx(EX_USAGE, "can't search for a null string");
- if (strlen(domain) > sizeof("-NORID")-1 &&
- strcasecmp(domain + strlen(domain) - sizeof("-NORID") + 1,
- "-NORID") == 0) {
- s_asprintf(&retval, "%s", NORIDHOST);
- return (retval);
+ for (i = 0; whoiswhere[i].suffix != NULL; i++) {
+ size_t suffix_len = strlen(whoiswhere[i].suffix);
+ if (domain + suffix_len < pos &&
+ strcasecmp(pos - suffix_len, whoiswhere[i].suffix) == 0) {
+ s_asprintf(&retval, "%s", whoiswhere[i].server);
+ return (retval);
+ }
}
while (pos > domain && *pos != '.')
--pos;
if (pos <= domain)
return (NULL);
- if (isdigit((unsigned char)*++pos))
+ if (isdigit((unsigned char)*++pos)) {
s_asprintf(&retval, "%s", ANICHOST);
- else
- s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL);
- return (retval);
+ return (retval);
+ }
+ /* Try possible alternative whois server name formulae. */
+ for (i = 0; ; ++i) {
+ switch (i) {
+ case 0:
+ s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL);
+ break;
+ case 1:
+ s_asprintf(&retval, "%s%s", QNICHOST_HEAD, pos);
+ break;
+ default:
+ return (NULL);
+ }
+ res = gethostinfo(retval, 0);
+ if (res) {
+ freeaddrinfo(res);
+ return (retval);
+ } else {
+ free(retval);
+ continue;
+ }
+ }
}
static struct addrinfo *
-gethostinfo(char const *host, int exit_on_error)
+gethostinfo(char const *host, int exit_on_noname)
{
struct addrinfo hints, *res;
int error;
@@ -248,13 +288,10 @@ gethostinfo(char const *host, int exit_on_error)
hints.ai_flags = 0;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
+ res = NULL;
error = getaddrinfo(host, port, &hints, &res);
- if (error) {
- warnx("%s: %s", host, gai_strerror(error));
- if (exit_on_error)
- exit(EX_NOHOST);
- return (NULL);
- }
+ if (error && (exit_on_noname || error != EAI_NONAME))
+ err(EX_NOHOST, "%s: %s", host, gai_strerror(error));
return (res);
}
@@ -280,21 +317,137 @@ whois(const char *query, const char *hostname, int flags)
FILE *fp;
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()");
fp = fdopen(s, "r+");
@@ -362,7 +515,7 @@ static void
usage(void)
{
fprintf(stderr,
- "usage: whois [-aAbfgiIklmQrR6] [-c country-code | -h hostname] "
+ "usage: whois [-aAbfgiIklmPQr] [-c country-code | -h hostname] "
"[-p port] name ...\n");
exit(EX_USAGE);
}
OpenPOWER on IntegriCloud