Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 23 Jan 2016 00:28:18 +0000 (UTC)
From:      Tony Finch <fanf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r294611 - head/usr.bin/whois
Message-ID:  <201601230028.u0N0SIMi082849@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: fanf
Date: Sat Jan 23 00:28:18 2016
New Revision: 294611
URL: https://svnweb.freebsd.org/changeset/base/294611

Log:
  A lot of the cleverness in whois is no longer needed!
  
  The IANA whois server has the right referral information for domain
  names, IP addresses, and AS numbers, so whois does not need to be
  able to choose servers itself (except for a few cases where referrals
  do not work). We can delete a chunk of code, which is always fun.
  
  This change improves the referral handling to be less sensitive to
  all the various formats, and to allow multi-hop referral chains,
  such as IANA -> registry -> registrar.
  
  ARIN queries have the "+" flag added if no flags are present, so we
  get full details if the query matches multiple objects. The Verisign
  anti-spam logic is also now suppressed if the user provided a non-
  trivial query string.
  
  Uninformative rubric is now trimmed by default. The -S option
  turns off trimming, and disables query fettling.
  
  The -i option is back to its traditional pre-1999 hostname, since
  whois.internic.net is more useful than whois.networksolutions.com.
  Note that the old fallback/default server whois.crsnic.net is an
  alias for whois.internic.net.
  
  The manual is more informative about query syntax.

Modified:
  head/usr.bin/whois/whois.1
  head/usr.bin/whois/whois.c

Modified: head/usr.bin/whois/whois.1
==============================================================================
--- head/usr.bin/whois/whois.1	Fri Jan 22 23:33:34 2016	(r294610)
+++ head/usr.bin/whois/whois.1	Sat Jan 23 00:28:18 2016	(r294611)
@@ -28,7 +28,7 @@
 .\"     From: @(#)whois.1	8.1 (Berkeley) 6/6/93
 .\" $FreeBSD$
 .\"
-.Dd January 22, 2016
+.Dd January 23, 2016
 .Dt WHOIS 1
 .Os
 .Sh NAME
@@ -49,31 +49,22 @@ Network Information Centers
 .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 or AS number is specified,
-the whois server will default to
-the American Registry for Internet Numbers
-.Pq Tn ARIN .
-.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 .
+starts by querying the Internet Assigned Numbers Authority (IANA) whois server,
+and follows referrals to whois servers
+that have more specific details about the query
+.Ar name .
+The IANA whois server knows about
+IP address and AS numbers
+as well as domain names.
+.Pp
+There are a few special cases where referrals do not work, so
+.Nm
+goes directly to the appropriate server.
+These include point-of-contact handles for ARIN,
+.Pa nic.at ,
+NORID, and RIPE,
+and domain names under
+.Pa ac.uk .
 .Pp
 The options are as follows:
 .Bl -tag -width indent
@@ -85,17 +76,16 @@ It contains network numbers used in thos
 .Tn APNIC , AfriNIC , LACNIC ,
 nor by
 .Tn RIPE .
-.Pp
-(Hint: All point of contact handles in the
-.Tn ARIN
-whois database end with
-.Qq Li -ARIN . )
+The query syntax is documented at
+.Pa https://www.arin.net/resources/whoisrws/whois_api.html#nicname
 .It Fl A
 Use the Asia/Pacific Network Information Center
 .Pq Tn APNIC
 database.
 It contains network numbers used in East Asia, Australia,
 New Zealand, and the Pacific islands.
+Get query syntax documentation using
+.Ic whois -A help
 .It Fl b
 Use the Network Abuse Clearinghouse database.
 It contains addresses to which network abuse should be reported,
@@ -111,6 +101,8 @@ Use the African Network Information Cent
 database.
 It contains network numbers used in Africa and the islands of the
 western Indian Ocean.
+Get query syntax documentation using
+.Ic whois -f help
 .It Fl g
 Use the US non-military federal government database, which contains points of
 contact for subdomains of
@@ -119,14 +111,28 @@ contact for subdomains of
 Use the specified host instead of the default.
 Either a host name or an IP address may be specified.
 .It Fl i
-Use the obsolete Network Solutions Registry for Internet Numbers
-.Pq Pa whois.networksolutions.com
+Use the traditional Network Information Center (InterNIC)
+.Pq Pa whois.internic.net
 database.
+This now contains only registrations for domain names under
+.Pa .COM ,
+.Pa .NET ,
+.Pa .EDU .
+You can specify the type of object to search for like
+.Ic whois -i ' Ns Ar type Ar name Ns Ic '
+where
+.Ar type
+can be
+.Nm domain , nameserver , registrar .
+The
+.Ar name
+can contain
+.Li *
+wildcards.
 .It Fl I
 Use the Internet Assigned Numbers Authority
 .Pq Tn IANA
 database.
-It contains network information for top-level domains.
 .It Fl k
 Use the National Internet Development Agency of Korea's
 .Pq Tn KRNIC
@@ -160,7 +166,7 @@ Do a quick lookup;
 .Nm
 will not attempt to follow referrals to other whois servers.
 This is the default if a server is explicitly specified
-using one of the other options.
+using one of the other options or in an environment variable.
 See also the
 .Fl R
 option.
@@ -170,6 +176,8 @@ Use the R\(aaeseaux IP Europ\(aaeens
 database.
 It contains network numbers and domain contact information
 for Europe.
+Get query syntax documentation using
+.Ic whois -r help
 .It Fl R
 Do a recursive lookup;
 .Nm
@@ -179,19 +187,16 @@ See also the
 .Fl Q
 option.
 .It Fl S
-By default, if the whois server is
-.Pa whois.verisign-grs.com
-(or a CNAME alias pointing at that name)
-then
+By default
 .Nm
-will query for
-.Dl domain Ar name
-The
+adjusts simple queries (without spaces) to produce more useful output
+from certain whois servers,
+and it suppresses some uninformative output.
+With the
 .Fl S
-option suppresses this behaviour,
-allowing you to make a loose-matching query,
-or query for host objects using the syntax
-.Dl nameserver Ar name
+option,
+.Nm
+sends the query and prints the output verbatim.
 .El
 .Pp
 The operands specified to
@@ -212,22 +217,11 @@ The secondary default whois server.
 If this is unset,
 .Nm
 will use
-.Pa whois.crsnic.net .
+.Pa whois.iana.org .
 .El
 .Sh EXIT STATUS
 .Ex -std
 .Sh EXAMPLES
-Most types of data, such as domain names and
-.Tn IP
-addresses, can be used as arguments to
-.Nm
-without any options, and
-.Nm
-will choose the correct whois server to query.
-Some exceptions, where
-.Nm
-will not be able to handle data correctly, are detailed below.
-.Pp
 To obtain contact information about an
 administrator located in the Russian
 .Tn TLD

Modified: head/usr.bin/whois/whois.c
==============================================================================
--- head/usr.bin/whois/whois.c	Fri Jan 22 23:33:34 2016	(r294610)
+++ head/usr.bin/whois/whois.c	Sat Jan 23 00:28:18 2016	(r294611)
@@ -61,31 +61,35 @@ __FBSDID("$FreeBSD$");
 
 #define	ABUSEHOST	"whois.abuse.net"
 #define	ANICHOST	"whois.arin.net"
-#define	BNICHOST	"whois.registro.br"
+#define	DENICHOST	"de" QNICHOST_TAIL
 #define	FNICHOST	"whois.afrinic.net"
-#define	GERMNICHOST	"de" QNICHOST_TAIL
 #define	GNICHOST	"whois.nic.gov"
 #define	IANAHOST	"whois.iana.org"
-#define	INICHOST	"whois.networksolutions.com"
+#define	INICHOST	"whois.internic.net"
 #define	KNICHOST	"whois.krnic.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	RNICHOST	"whois.ripe.net"
 #define	VNICHOST	"whois.verisign-grs.com"
 
 #define	DEFAULT_PORT	"whois"
 
-#define WHOIS_RECURSE		0x01
-#define WHOIS_QUICK		0x02
-#define WHOIS_SPAM_ME		0x04
+#define WHOIS_RECURSE	0x01
+#define WHOIS_QUICK	0x02
+#define WHOIS_SPAM_ME	0x04
+
+#define CHOPSPAM	">>> Last update of WHOIS database:"
 
 #define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-')
 
+#define SCAN(p, end, check)					\
+	while ((p) < (end))					\
+		if (check) ++(p);				\
+		else break
+
 static struct {
 	const char *suffix, *server;
 } whoiswhere[] = {
@@ -96,7 +100,8 @@ static struct {
 	{ "-RIPE", RNICHOST },
 	/* Nominet's whois server doesn't return referrals to JANET */
 	{ ".ac.uk", "ac.uk" QNICHOST_TAIL },
-	{ NULL, NULL }
+	{ "", IANAHOST }, /* default */
+	{ NULL, NULL } /* safety belt */
 };
 
 #define WHOIS_REFERRAL(s) { s, sizeof(s) - 1 }
@@ -104,18 +109,16 @@ static struct {
 	const char *prefix;
 	size_t len;
 } whois_referral[] = {
-	WHOIS_REFERRAL("Whois Server: "),
-	WHOIS_REFERRAL("WHOIS Server: "),
-	WHOIS_REFERRAL("   Whois Server: "),
-	WHOIS_REFERRAL("refer:        "),
-	WHOIS_REFERRAL("Registrant Street1:Whois Server:"),
-	WHOIS_REFERRAL("ReferralServer:  whois://"),
+	WHOIS_REFERRAL("whois:"), /* IANA */
+	WHOIS_REFERRAL("Whois Server:"),
+	WHOIS_REFERRAL("Registrar WHOIS Server:"), /* corporatedomains.com */
+	WHOIS_REFERRAL("ReferralServer:  whois://"), /* ARIN */
 	{ NULL, 0 }
 };
 
 static const char *port = DEFAULT_PORT;
 
-static char *choose_server(char *);
+static const char *choose_server(char *);
 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);
@@ -125,15 +128,14 @@ int
 main(int argc, char *argv[])
 {
 	const char *country, *host;
-	char *qnichost;
-	int ch, flags, use_qnichost;
+	int ch, flags;
 
 #ifdef	SOCKS
 	SOCKSinit(argv[0]);
 #endif
 
-	country = host = qnichost = NULL;
-	flags = use_qnichost = 0;
+	country = host = NULL;
+	flags = 0;
 	while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQrRS")) != -1) {
 		switch (ch) {
 		case 'a':
@@ -203,103 +205,43 @@ main(int argc, char *argv[])
 		usage();
 
 	/*
-	 * If no host or country is specified, try to determine the top
-	 * level domain from the query, or fall back to NICHOST.
+	 * If no host or country is specified, rely on referrals from IANA.
 	 */
 	if (host == NULL && country == NULL) {
 		if ((host = getenv("WHOIS_SERVER")) == NULL &&
 		    (host = getenv("RA_SERVER")) == NULL) {
-			use_qnichost = 1;
-			host = NICHOST;
 			if (!(flags & WHOIS_QUICK))
 				flags |= WHOIS_RECURSE;
 		}
 	}
 	while (argc-- > 0) {
 		if (country != NULL) {
+			char *qnichost;
 			s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL);
 			whois(*argv, qnichost, flags);
-		} else if (use_qnichost)
-			if ((qnichost = choose_server(*argv)) != NULL)
-				whois(*argv, qnichost, flags);
-		if (qnichost == NULL)
-			whois(*argv, host, flags);
-		free(qnichost);
-		qnichost = NULL;
+			free(qnichost);
+		} else
+			whois(*argv, host != NULL ? host :
+			      choose_server(*argv), flags);
 		argv++;
 	}
 	exit(0);
 }
 
-/*
- * This function will remove any trailing periods from domain, after which it
- * 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 *
+static const char *
 choose_server(char *domain)
 {
-	char *pos, *retval;
+	size_t len = strlen(domain);
 	int i;
-	struct addrinfo *res;
 
-	if (strchr(domain, ':')) {
-		s_asprintf(&retval, "%s", ANICHOST);
-		return (retval);
-	}
-	if (strncasecmp(domain, "AS", 2) == 0) {
-		size_t len = strspn(domain + 2, "0123456789");
-		if (domain[len + 2] == '\0') {
-			s_asprintf(&retval, "%s", ANICHOST);
-			return (retval);
-		}
-	}
-	for (pos = strchr(domain, '\0'); pos > domain && pos[-1] == '.';)
-		*--pos = '\0';
-	if (*domain == '\0')
-		errx(EX_USAGE, "can't search for a null string");
 	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)) {
-		s_asprintf(&retval, "%s", ANICHOST);
-		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;
-		}
+		if (len > suffix_len &&
+		    strcasecmp(domain + len - suffix_len,
+			       whoiswhere[i].suffix) == 0)
+			return (whoiswhere[i].server);
 	}
+	errx(EX_SOFTWARE, "no default whois server");
 }
 
 static struct addrinfo *
@@ -341,7 +283,7 @@ whois(const char *query, const char *hos
 	FILE *fp;
 	struct addrinfo *hostres, *res;
 	char *buf, *host, *nhost, *p;
-	int s = -1, f, antispam;
+	int s = -1, f;
 	nfds_t i, j;
 	size_t len, count;
 	struct pollfd *fds;
@@ -350,10 +292,6 @@ whois(const char *query, const char *hos
 	hostres = gethostinfo(hostname, 1);
 	for (res = hostres, count = 0; res; res = res->ai_next)
 		count++;
-
-	antispam = (flags & WHOIS_SPAM_ME) == 0 &&
-	    strcmp(hostres->ai_canonname, VNICHOST) == 0;
-
 	fds = calloc(count, sizeof(*fds));
 	if (fds == NULL)
 		err(EX_OSERR, "calloc()");
@@ -420,8 +358,8 @@ whois(const char *query, const char *hos
 				break;
 			} else if (n < 0) {
 				/*
-				 * errno here can only be EINTR which we would want
-				 * to clean up and bail out.
+				 * errno here can only be EINTR which we would
+				 * want to clean up and bail out.
 				 */
 				s = -1;
 				goto done;
@@ -455,66 +393,84 @@ whois(const char *query, const char *hos
 	s = -1;
 	if (count == 0)
 		errno = ETIMEDOUT;
-
 done:
+	if (s == -1)
+		err(EX_OSERR, "connect()");
+
 	/* 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 (s == -1)
-		err(EX_OSERR, "connect()");
+
+	/* Restore default blocking behavior.  */
+	if ((f = fcntl(s, F_GETFL)) == -1)
+		err(EX_OSERR, "fcntl()");
+	f &= ~O_NONBLOCK;
+	if (fcntl(s, F_SETFL, f) == -1)
+		err(EX_OSERR, "fcntl()");
 
 	fp = fdopen(s, "r+");
 	if (fp == NULL)
 		err(EX_OSERR, "fdopen()");
-	if (strcmp(hostname, GERMNICHOST) == 0) {
+
+	if (!(flags & WHOIS_SPAM_ME) &&
+	    strcmp(hostname, DENICHOST) == 0)
 		fprintf(fp, "-T dn,ace -C ISO-8859-1 %s\r\n", query);
-	} else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) {
+	else if (!(flags & WHOIS_SPAM_ME) &&
+		 strcmp(hostname, "dk" QNICHOST_TAIL) == 0)
 		fprintf(fp, "--show-handles %s\r\n", query);
-	} else if (antispam) {
+	else if ((flags & WHOIS_SPAM_ME) ||
+		 strchr(query, ' ') != NULL)
+		fprintf(fp, "%s\r\n", query);
+	else if (strcmp(hostname, ANICHOST) == 0)
+		fprintf(fp, "+ %s\r\n", query);
+	else if (strcmp(hostres->ai_canonname, VNICHOST) == 0)
 		fprintf(fp, "domain %s\r\n", query);
-	} else {
+	else
 		fprintf(fp, "%s\r\n", query);
-	}
 	fflush(fp);
+
 	nhost = NULL;
 	while ((buf = fgetln(fp, &len)) != NULL) {
-		while (len > 0 && isspace((unsigned char)buf[len - 1]))
-			buf[--len] = '\0';
-		printf("%.*s\n", (int)len, buf);
+		/* Nominet */
+		if (!(flags & WHOIS_SPAM_ME) &&
+		    len == 5 && strncmp(buf, "-- \r\n", 5) == 0)
+			break;
+
+		printf("%.*s", (int)len, buf);
 
 		if ((flags & WHOIS_RECURSE) && nhost == NULL) {
 			for (i = 0; whois_referral[i].prefix != NULL; i++) {
-				if (strncmp(buf,
-					    whois_referral[i].prefix,
-					    whois_referral[i].len) != 0)
+				p = buf;
+				SCAN(p, buf+len, *p == ' ');
+				if (strncasecmp(p, whois_referral[i].prefix,
+					           whois_referral[i].len) != 0)
 					continue;
-				host = buf + whois_referral[i].len;
-				for (p = host; p < buf + len; p++)
-					if (!ishost(*p))
-						break;
-				s_asprintf(&nhost, "%.*s",
-				    (int)(p - host), host);
+				p += whois_referral[i].len;
+				SCAN(p, buf+len, *p == ' ');
+				host = p;
+				SCAN(p, buf+len, ishost(*p));
+				/* avoid loops */
+				if (strncmp(hostname, host, p - host) != 0)
+					s_asprintf(&nhost, "%.*s",
+						   (int)(p - host), host);
 				break;
 			}
 		}
+		/* Verisign etc. */
+		if (!(flags & WHOIS_SPAM_ME) &&
+		    len >= sizeof(CHOPSPAM)-1 &&
+		    (strncasecmp(buf, CHOPSPAM, sizeof(CHOPSPAM)-1) == 0 ||
+		     strncasecmp(buf, CHOPSPAM+4, sizeof(CHOPSPAM)-5) == 0)) {
+			printf("\n");
+			break;
+		}
 	}
 	fclose(fp);
+	freeaddrinfo(hostres);
 	if (nhost != NULL) {
-		whois(query, nhost, 0);
+		whois(query, nhost, flags);
 		free(nhost);
 	}
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201601230028.u0N0SIMi082849>