Index: head/usr.bin/whois/whois.1 =================================================================== --- head/usr.bin/whois/whois.1 (revision 294574) +++ head/usr.bin/whois/whois.1 (revision 294575) @@ -1,257 +1,273 @@ .\" Copyright (c) 1985, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" From: @(#)whois.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" .Dd May 14, 2015 .Dt WHOIS 1 .Os .Sh NAME .Nm whois .Nd "Internet domain name and network number directory service" .Sh SYNOPSIS .Nm .Op Fl aAbfgiIklmPQr .Op Fl c Ar country-code | Fl h Ar host .Op Fl p Ar port .Ar name ... .Sh DESCRIPTION The .Nm 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 +If an IP address or AS number 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 Use the American Registry for Internet Numbers .Pq Tn ARIN database. It contains network numbers used in those parts of the world covered neither by .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 . ) .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. .It Fl b Use the Network Abuse Clearinghouse database. It contains addresses to which network abuse should be reported, indexed by domain name. .It Fl c Ar country-code This is the equivalent of using the .Fl h option with an argument of .Qq Ar country-code Ns Li .whois-servers.net . .It Fl f Use the African Network Information Centre .Pq Tn AfriNIC database. It contains network numbers used in Africa and the islands of the western Indian Ocean. .It Fl g 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. 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 database. .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 database. It contains network numbers and domain contact information for Korea. .It Fl l Use the Latin American and Caribbean IP address Regional Registry .Pq Tn LACNIC database. It contains network numbers used in much of Latin America and the Caribbean. .It Fl m Use the Route Arbiter Database .Pq Tn RADB database. It contains route policy specifications for a large number of operators' networks. .It Fl p Ar port Connect to the whois server on .Ar port . 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 +Do a quick lookup; .Nm -will not attempt to lookup the name in the authoritative whois -server (if one is listed). -This option has no effect when combined with any other options. +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. +See also the +.Fl R +option. .It Fl r Use the R\(aaeseaux IP Europ\(aaeens .Pq Tn RIPE database. It contains network numbers and domain contact information for Europe. +.It Fl R +Do a recursive lookup; +.Nm +will attempt to follow referrals to other whois servers. +This is the default if no server is explicitly specified. +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 +.Nm +will query for +.Dl domain Ar name +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 .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 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 domain .Qq Li RU , use the .Fl c option as shown in the following example, where .Ar CONTACT-ID is substituted with the actual contact identifier. .Pp .Dl "whois -c RU CONTACT-ID" .Pp (Note: This example is specific to the .Tn TLD .Qq Li RU , but other .Tn TLDs can be queried by using a similar syntax.) .Pp The following example demonstrates how to query a whois server using a non-standard port, where .Dq Li query-data is the query to be sent to .Dq Li whois.example.com on port .Dq Li rwhois (written numerically as 4321). .Pp .Dl "whois -h whois.example.com -p rwhois query-data" .Sh SEE ALSO .Rs .%A Ken Harrenstien .%A Vic White .%T NICNAME/WHOIS .%D 1 March 1982 .%O RFC 812 .Re .Sh HISTORY The .Nm command appeared in .Bx 4.3 . Index: head/usr.bin/whois/whois.c =================================================================== --- head/usr.bin/whois/whois.c (revision 294574) +++ head/usr.bin/whois/whois.c (revision 294575) @@ -1,521 +1,529 @@ /*- * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)whois.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ABUSEHOST "whois.abuse.net" #define ANICHOST "whois.arin.net" #define BNICHOST "whois.registro.br" #define FNICHOST "whois.afrinic.net" -#define GERMNICHOST "de.whois-servers.net" +#define GERMNICHOST "de" QNICHOST_TAIL #define GNICHOST "whois.nic.gov" #define IANAHOST "whois.iana.org" #define INICHOST "whois.networksolutions.com" #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_SERVER_ID "Whois Server: " -#define WHOIS_ORG_SERVER_ID "Registrant Street1:Whois Server:" - #define WHOIS_RECURSE 0x01 #define WHOIS_QUICK 0x02 +#define WHOIS_SPAM_ME 0x04 #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 }; +#define WHOIS_REFERRAL(s) { s, sizeof(s) - 1 } +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://"), + { NULL, 0 } +}; + static const char *port = DEFAULT_PORT; static 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); static void whois(const char *, const char *, int); int main(int argc, char *argv[]) { const char *country, *host; char *qnichost; int ch, flags, use_qnichost; #ifdef SOCKS SOCKSinit(argv[0]); #endif country = host = qnichost = NULL; flags = use_qnichost = 0; - while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQr")) != -1) { + while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQrRS")) != -1) { switch (ch) { case 'a': host = ANICHOST; break; case 'A': host = PNICHOST; break; case 'b': host = ABUSEHOST; break; case 'c': country = optarg; break; case 'f': host = FNICHOST; break; case 'g': host = GNICHOST; break; case 'h': host = optarg; break; case 'i': host = INICHOST; break; case 'I': host = IANAHOST; break; case 'k': host = KNICHOST; break; case 'l': host = LNICHOST; break; case 'm': host = MNICHOST; break; case 'p': port = optarg; break; case 'P': host = PDBHOST; break; case 'Q': flags |= WHOIS_QUICK; break; case 'r': host = RNICHOST; break; + case 'R': + flags |= WHOIS_RECURSE; + break; + case 'S': + flags |= WHOIS_SPAM_ME; + break; case '?': default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (!argc || (country != NULL && host != NULL)) usage(); /* * 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("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) { 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; 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 * choose_server(char *domain) { char *pos, *retval; 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; } } } static struct addrinfo * gethostinfo(char const *host, int exit_on_noname) { struct addrinfo hints, *res; int error; memset(&hints, 0, sizeof(hints)); - hints.ai_flags = 0; + hints.ai_flags = AI_CANONNAME; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; res = NULL; error = getaddrinfo(host, port, &hints, &res); if (error && (exit_on_noname || error != EAI_NONAME)) err(EX_NOHOST, "%s: %s", host, gai_strerror(error)); return (res); } /* * Wrapper for asprintf(3) that exits on error. */ static void s_asprintf(char **ret, const char *format, ...) { va_list ap; va_start(ap, format); if (vasprintf(ret, format, ap) == -1) { va_end(ap); err(EX_OSERR, "vasprintf()"); } va_end(ap); } static void whois(const char *query, const char *hostname, int flags) { FILE *fp; struct addrinfo *hostres, *res; char *buf, *host, *nhost, *p; - int s = -1, f; + int s = -1, f, antispam; nfds_t i, j; - size_t c, len, count; + size_t len, count; struct pollfd *fds; int timeout = 180; 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()"); /* * 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) { 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 (s == -1) err(EX_OSERR, "connect()"); fp = fdopen(s, "r+"); if (fp == NULL) err(EX_OSERR, "fdopen()"); if (strcmp(hostname, GERMNICHOST) == 0) { fprintf(fp, "-T dn,ace -C ISO-8859-1 %s\r\n", query); } else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) { fprintf(fp, "--show-handles %s\r\n", query); + } else if (antispam) { + fprintf(fp, "domain %s\r\n", query); } 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); if ((flags & WHOIS_RECURSE) && nhost == NULL) { - host = strnstr(buf, WHOIS_SERVER_ID, len); - if (host != NULL) { - host += sizeof(WHOIS_SERVER_ID) - 1; - for (p = host; p < buf + len; p++) { - if (!ishost(*p)) { - *p = '\0'; + for (i = 0; whois_referral[i].prefix != NULL; i++) { + if (strncmp(buf, + 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)(buf + len - host), host); - } else if ((host = - strnstr(buf, WHOIS_ORG_SERVER_ID, len)) != NULL) { - host += sizeof(WHOIS_ORG_SERVER_ID) - 1; - for (p = host; p < buf + len; p++) { - if (!ishost(*p)) { - *p = '\0'; - break; - } - } - s_asprintf(&nhost, "%.*s", - (int)(buf + len - host), host); - } else if (strcmp(hostname, ANICHOST) == 0) { - for (c = 0; c <= len; c++) - buf[c] = tolower((unsigned char)buf[c]); - for (i = 0; ip_whois[i] != NULL; i++) { - if (strnstr(buf, ip_whois[i], len) != - NULL) { - s_asprintf(&nhost, "%s", - ip_whois[i]); - break; - } - } + (int)(p - host), host); + break; } } } fclose(fp); if (nhost != NULL) { whois(query, nhost, 0); free(nhost); } } static void usage(void) { fprintf(stderr, "usage: whois [-aAbfgiIklmPQr] [-c country-code | -h hostname] " "[-p port] name ...\n"); exit(EX_USAGE); }