17c478bd9Sstevel@tonic-gate /*
21dd3983cSYuri Pankov * Copyright (c) 1980, 1993
31dd3983cSYuri Pankov * The Regents of the University of California. All rights reserved.
47c478bd9Sstevel@tonic-gate *
51dd3983cSYuri Pankov * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
67c478bd9Sstevel@tonic-gate *
7*deeb0f36SJohann 'Myrkraverk' Oskarsson * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.com>
8*deeb0f36SJohann 'Myrkraverk' Oskarsson *
91dd3983cSYuri Pankov * Redistribution and use in source and binary forms, with or without
101dd3983cSYuri Pankov * modification, are permitted provided that the following conditions
111dd3983cSYuri Pankov * are met:
121dd3983cSYuri Pankov * 1. Redistributions of source code must retain the above copyright
131dd3983cSYuri Pankov * notice, this list of conditions and the following disclaimer.
141dd3983cSYuri Pankov * 2. Redistributions in binary form must reproduce the above copyright
151dd3983cSYuri Pankov * notice, this list of conditions and the following disclaimer in the
161dd3983cSYuri Pankov * documentation and/or other materials provided with the distribution.
171dd3983cSYuri Pankov * 4. Neither the name of the University nor the names of its contributors
181dd3983cSYuri Pankov * may be used to endorse or promote products derived from this software
191dd3983cSYuri Pankov * without specific prior written permission.
207c478bd9Sstevel@tonic-gate *
211dd3983cSYuri Pankov * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221dd3983cSYuri Pankov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231dd3983cSYuri Pankov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241dd3983cSYuri Pankov * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251dd3983cSYuri Pankov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261dd3983cSYuri Pankov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271dd3983cSYuri Pankov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281dd3983cSYuri Pankov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291dd3983cSYuri Pankov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301dd3983cSYuri Pankov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311dd3983cSYuri Pankov * SUCH DAMAGE.
327c478bd9Sstevel@tonic-gate */
337c478bd9Sstevel@tonic-gate
347c478bd9Sstevel@tonic-gate #include <sys/types.h>
357c478bd9Sstevel@tonic-gate #include <sys/socket.h>
367c478bd9Sstevel@tonic-gate
377c478bd9Sstevel@tonic-gate #include <netinet/in.h>
381dd3983cSYuri Pankov #include <arpa/inet.h>
391dd3983cSYuri Pankov #include <ctype.h>
401dd3983cSYuri Pankov #include <err.h>
411dd3983cSYuri Pankov #include <limits.h>
427c478bd9Sstevel@tonic-gate #include <netdb.h>
431dd3983cSYuri Pankov #include <stdarg.h>
441dd3983cSYuri Pankov #include <stdio.h>
45f098c48bSDerek Morr #include <stdlib.h>
461dd3983cSYuri Pankov #include <string.h>
471dd3983cSYuri Pankov #include <sysexits.h>
48f098c48bSDerek Morr #include <unistd.h>
497c478bd9Sstevel@tonic-gate
501dd3983cSYuri Pankov #define ABUSEHOST "whois.abuse.net"
511dd3983cSYuri Pankov #define NICHOST "whois.crsnic.net"
521dd3983cSYuri Pankov #define INICHOST "whois.networksolutions.com"
531dd3983cSYuri Pankov #define GNICHOST "whois.nic.gov"
541dd3983cSYuri Pankov #define ANICHOST "whois.arin.net"
551dd3983cSYuri Pankov #define LNICHOST "whois.lacnic.net"
561dd3983cSYuri Pankov #define KNICHOST "whois.krnic.net"
571dd3983cSYuri Pankov #define RNICHOST "whois.ripe.net"
581dd3983cSYuri Pankov #define PNICHOST "whois.apnic.net"
591dd3983cSYuri Pankov #define MNICHOST "whois.ra.net"
601dd3983cSYuri Pankov #define QNICHOST_TAIL ".whois-servers.net"
611dd3983cSYuri Pankov #define BNICHOST "whois.registro.br"
621dd3983cSYuri Pankov #define NORIDHOST "whois.norid.no"
631dd3983cSYuri Pankov #define IANAHOST "whois.iana.org"
641dd3983cSYuri Pankov #define GERMNICHOST "de.whois-servers.net"
651dd3983cSYuri Pankov #define FNICHOST "whois.afrinic.net"
661dd3983cSYuri Pankov #define DEFAULT_PORT "whois"
671dd3983cSYuri Pankov #define WHOIS_SERVER_ID "Whois Server: "
681dd3983cSYuri Pankov #define WHOIS_ORG_SERVER_ID "Registrant Street1:Whois Server:"
691dd3983cSYuri Pankov
701dd3983cSYuri Pankov #define WHOIS_RECURSE 0x01
711dd3983cSYuri Pankov #define WHOIS_QUICK 0x02
721dd3983cSYuri Pankov
731dd3983cSYuri Pankov #define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-')
741dd3983cSYuri Pankov
751dd3983cSYuri Pankov const char *ip_whois[] = { LNICHOST, RNICHOST, PNICHOST, BNICHOST,
761dd3983cSYuri Pankov FNICHOST, NULL };
771dd3983cSYuri Pankov const char *port = DEFAULT_PORT;
781dd3983cSYuri Pankov
791dd3983cSYuri Pankov static char *choose_server(char *);
801dd3983cSYuri Pankov static struct addrinfo *gethostinfo(char const *host, int exit_on_error);
811dd3983cSYuri Pankov static void s_asprintf(char **ret, const char *format, ...);
821dd3983cSYuri Pankov static void usage(void);
831dd3983cSYuri Pankov static void whois(const char *, const char *, int);
847c478bd9Sstevel@tonic-gate
85740638c8Sbw int
main(int argc,char * argv[])86f098c48bSDerek Morr main(int argc, char *argv[])
877c478bd9Sstevel@tonic-gate {
881dd3983cSYuri Pankov const char *country, *host;
891dd3983cSYuri Pankov char *qnichost;
901dd3983cSYuri Pankov int ch, flags, use_qnichost;
911dd3983cSYuri Pankov
921dd3983cSYuri Pankov country = host = qnichost = NULL;
931dd3983cSYuri Pankov flags = use_qnichost = 0;
941dd3983cSYuri Pankov while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:Qr")) != -1) {
951dd3983cSYuri Pankov switch (ch) {
961dd3983cSYuri Pankov case 'a':
971dd3983cSYuri Pankov host = ANICHOST;
981dd3983cSYuri Pankov break;
991dd3983cSYuri Pankov case 'A':
1001dd3983cSYuri Pankov host = PNICHOST;
1011dd3983cSYuri Pankov break;
1021dd3983cSYuri Pankov case 'b':
1031dd3983cSYuri Pankov host = ABUSEHOST;
1041dd3983cSYuri Pankov break;
1051dd3983cSYuri Pankov case 'c':
1061dd3983cSYuri Pankov country = optarg;
1071dd3983cSYuri Pankov break;
1081dd3983cSYuri Pankov case 'f':
1091dd3983cSYuri Pankov host = FNICHOST;
1101dd3983cSYuri Pankov break;
1111dd3983cSYuri Pankov case 'g':
1121dd3983cSYuri Pankov host = GNICHOST;
1131dd3983cSYuri Pankov break;
1141dd3983cSYuri Pankov case 'h':
1151dd3983cSYuri Pankov host = optarg;
1161dd3983cSYuri Pankov break;
1171dd3983cSYuri Pankov case 'i':
1181dd3983cSYuri Pankov host = INICHOST;
1191dd3983cSYuri Pankov break;
1201dd3983cSYuri Pankov case 'I':
1211dd3983cSYuri Pankov host = IANAHOST;
1221dd3983cSYuri Pankov break;
1231dd3983cSYuri Pankov case 'k':
1241dd3983cSYuri Pankov host = KNICHOST;
1251dd3983cSYuri Pankov break;
1261dd3983cSYuri Pankov case 'l':
1271dd3983cSYuri Pankov host = LNICHOST;
1281dd3983cSYuri Pankov break;
1291dd3983cSYuri Pankov case 'm':
1301dd3983cSYuri Pankov host = MNICHOST;
1311dd3983cSYuri Pankov break;
1321dd3983cSYuri Pankov case 'p':
1331dd3983cSYuri Pankov port = optarg;
1341dd3983cSYuri Pankov break;
1351dd3983cSYuri Pankov case 'Q':
1361dd3983cSYuri Pankov flags |= WHOIS_QUICK;
1371dd3983cSYuri Pankov break;
1381dd3983cSYuri Pankov case 'r':
1391dd3983cSYuri Pankov host = RNICHOST;
1401dd3983cSYuri Pankov break;
1411dd3983cSYuri Pankov case '?':
1421dd3983cSYuri Pankov default:
1431dd3983cSYuri Pankov usage();
1441dd3983cSYuri Pankov /* NOTREACHED */
1451dd3983cSYuri Pankov }
1467c478bd9Sstevel@tonic-gate }
1471dd3983cSYuri Pankov argc -= optind;
1481dd3983cSYuri Pankov argv += optind;
1491dd3983cSYuri Pankov
1501dd3983cSYuri Pankov if (!argc || (country != NULL && host != NULL))
1511dd3983cSYuri Pankov usage();
1521dd3983cSYuri Pankov
1531dd3983cSYuri Pankov /*
1541dd3983cSYuri Pankov * If no host or country is specified determine the top level domain
1551dd3983cSYuri Pankov * from the query. If the TLD is a number, query ARIN. Otherwise, use
1561dd3983cSYuri Pankov * TLD.whois-server.net. If the domain does not contain '.', fall
1571dd3983cSYuri Pankov * back to NICHOST.
1581dd3983cSYuri Pankov */
1591dd3983cSYuri Pankov if (host == NULL && country == NULL) {
1601dd3983cSYuri Pankov use_qnichost = 1;
1611dd3983cSYuri Pankov host = NICHOST;
1621dd3983cSYuri Pankov if (!(flags & WHOIS_QUICK))
1631dd3983cSYuri Pankov flags |= WHOIS_RECURSE;
1647c478bd9Sstevel@tonic-gate }
1651dd3983cSYuri Pankov while (argc-- > 0) {
1661dd3983cSYuri Pankov if (country != NULL) {
1671dd3983cSYuri Pankov s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL);
1681dd3983cSYuri Pankov whois(*argv, qnichost, flags);
1691dd3983cSYuri Pankov } else if (use_qnichost)
1701dd3983cSYuri Pankov if ((qnichost = choose_server(*argv)) != NULL)
1711dd3983cSYuri Pankov whois(*argv, qnichost, flags);
1721dd3983cSYuri Pankov if (qnichost == NULL)
1731dd3983cSYuri Pankov whois(*argv, host, flags);
1741dd3983cSYuri Pankov free(qnichost);
1751dd3983cSYuri Pankov qnichost = NULL;
1761dd3983cSYuri Pankov argv++;
1771dd3983cSYuri Pankov }
1781dd3983cSYuri Pankov
1791dd3983cSYuri Pankov return (0);
1801dd3983cSYuri Pankov }
1811dd3983cSYuri Pankov
1821dd3983cSYuri Pankov /*
1831dd3983cSYuri Pankov * This function will remove any trailing periods from domain, after which it
1841dd3983cSYuri Pankov * returns a pointer to newly allocated memory containing the whois server to
1851dd3983cSYuri Pankov * be queried, or a NULL if the correct server couldn't be determined. The
1861dd3983cSYuri Pankov * caller must remember to free(3) the allocated memory.
1871dd3983cSYuri Pankov */
1881dd3983cSYuri Pankov static char *
choose_server(char * domain)1891dd3983cSYuri Pankov choose_server(char *domain)
1901dd3983cSYuri Pankov {
1911dd3983cSYuri Pankov char *pos, *retval;
1921dd3983cSYuri Pankov
1931dd3983cSYuri Pankov if (strchr(domain, ':')) {
1941dd3983cSYuri Pankov s_asprintf(&retval, "%s", ANICHOST);
1951dd3983cSYuri Pankov return (retval);
1961dd3983cSYuri Pankov }
1971dd3983cSYuri Pankov for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.'; )
1981dd3983cSYuri Pankov *pos = '\0';
1991dd3983cSYuri Pankov if (*domain == '\0')
2001dd3983cSYuri Pankov errx(EX_USAGE, "can't search for a null string");
2011dd3983cSYuri Pankov if (strlen(domain) > sizeof ("-NORID")-1 &&
2021dd3983cSYuri Pankov strcasecmp(domain + strlen(domain) - sizeof ("-NORID") + 1,
2031dd3983cSYuri Pankov "-NORID") == 0) {
2041dd3983cSYuri Pankov s_asprintf(&retval, "%s", NORIDHOST);
2051dd3983cSYuri Pankov return (retval);
2061dd3983cSYuri Pankov }
2071dd3983cSYuri Pankov while (pos > domain && *pos != '.')
2081dd3983cSYuri Pankov --pos;
2091dd3983cSYuri Pankov if (pos <= domain)
2101dd3983cSYuri Pankov return (NULL);
2111dd3983cSYuri Pankov if (isdigit((unsigned char)*++pos))
2121dd3983cSYuri Pankov s_asprintf(&retval, "%s", ANICHOST);
2131dd3983cSYuri Pankov else
2141dd3983cSYuri Pankov s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL);
2151dd3983cSYuri Pankov return (retval);
2161dd3983cSYuri Pankov }
2171dd3983cSYuri Pankov
2181dd3983cSYuri Pankov static struct addrinfo *
gethostinfo(char const * host,int exit_on_error)2191dd3983cSYuri Pankov gethostinfo(char const *host, int exit_on_error)
2201dd3983cSYuri Pankov {
2211dd3983cSYuri Pankov struct addrinfo hints, *res;
2221dd3983cSYuri Pankov int error;
223f098c48bSDerek Morr
2241dd3983cSYuri Pankov (void) memset(&hints, 0, sizeof (hints));
2251dd3983cSYuri Pankov hints.ai_flags = 0;
2261dd3983cSYuri Pankov hints.ai_family = AF_UNSPEC;
227f098c48bSDerek Morr hints.ai_socktype = SOCK_STREAM;
2281dd3983cSYuri Pankov error = getaddrinfo(host, port, &hints, &res);
2291dd3983cSYuri Pankov if (error) {
2301dd3983cSYuri Pankov warnx("%s: %s", host, gai_strerror(error));
2311dd3983cSYuri Pankov if (exit_on_error)
2321dd3983cSYuri Pankov exit(EX_NOHOST);
2331dd3983cSYuri Pankov return (NULL);
234f098c48bSDerek Morr }
2351dd3983cSYuri Pankov return (res);
2361dd3983cSYuri Pankov }
237f098c48bSDerek Morr
2381dd3983cSYuri Pankov /*
2391dd3983cSYuri Pankov * Wrapper for asprintf(3) that exits on error.
2401dd3983cSYuri Pankov */
2411dd3983cSYuri Pankov /* PRINTFLIKE2 */
2421dd3983cSYuri Pankov static void
s_asprintf(char ** ret,const char * format,...)2431dd3983cSYuri Pankov s_asprintf(char **ret, const char *format, ...)
2441dd3983cSYuri Pankov {
2451dd3983cSYuri Pankov va_list ap;
2461dd3983cSYuri Pankov
2471dd3983cSYuri Pankov va_start(ap, format);
2481dd3983cSYuri Pankov if (vasprintf(ret, format, ap) == -1) {
2491dd3983cSYuri Pankov va_end(ap);
2501dd3983cSYuri Pankov err(EX_OSERR, "vasprintf()");
2517c478bd9Sstevel@tonic-gate }
2521dd3983cSYuri Pankov va_end(ap);
2531dd3983cSYuri Pankov }
2541dd3983cSYuri Pankov
2551dd3983cSYuri Pankov static void
whois(const char * query,const char * hostname,int flags)2561dd3983cSYuri Pankov whois(const char *query, const char *hostname, int flags)
2571dd3983cSYuri Pankov {
2581dd3983cSYuri Pankov FILE *sfi, *sfo;
2591dd3983cSYuri Pankov struct addrinfo *hostres, *res;
260*deeb0f36SJohann 'Myrkraverk' Oskarsson /*
261*deeb0f36SJohann 'Myrkraverk' Oskarsson * The variables buf and buflen are static so the buffer for
262*deeb0f36SJohann 'Myrkraverk' Oskarsson * getline() is retained across calls.
263*deeb0f36SJohann 'Myrkraverk' Oskarsson */
264*deeb0f36SJohann 'Myrkraverk' Oskarsson static char *buf = NULL;
265*deeb0f36SJohann 'Myrkraverk' Oskarsson static size_t buflen = 0;
266*deeb0f36SJohann 'Myrkraverk' Oskarsson char *host, *nhost, *p;
2671dd3983cSYuri Pankov int i, s;
268*deeb0f36SJohann 'Myrkraverk' Oskarsson ssize_t c, len;
2691dd3983cSYuri Pankov
2701dd3983cSYuri Pankov s = -1;
2711dd3983cSYuri Pankov hostres = gethostinfo(hostname, 1);
2721dd3983cSYuri Pankov for (res = hostres; res; res = res->ai_next) {
2731dd3983cSYuri Pankov s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
2741dd3983cSYuri Pankov if (s < 0)
2751dd3983cSYuri Pankov continue;
2761dd3983cSYuri Pankov if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
2771dd3983cSYuri Pankov break;
2781dd3983cSYuri Pankov (void) close(s);
2797c478bd9Sstevel@tonic-gate }
2801dd3983cSYuri Pankov freeaddrinfo(hostres);
2811dd3983cSYuri Pankov if (res == NULL)
2821dd3983cSYuri Pankov err(EX_OSERR, "connect()");
283f098c48bSDerek Morr
2847c478bd9Sstevel@tonic-gate sfi = fdopen(s, "r");
2857c478bd9Sstevel@tonic-gate sfo = fdopen(s, "w");
2861dd3983cSYuri Pankov if (sfi == NULL || sfo == NULL)
2871dd3983cSYuri Pankov err(EX_OSERR, "fdopen()");
2881dd3983cSYuri Pankov if (strcmp(hostname, GERMNICHOST) == 0) {
2891dd3983cSYuri Pankov (void) fprintf(sfo, "-T dn,ace -C US-ASCII %s\r\n", query);
2901dd3983cSYuri Pankov } else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) {
2911dd3983cSYuri Pankov (void) fprintf(sfo, "--show-handles %s\r\n", query);
2921dd3983cSYuri Pankov } else {
2931dd3983cSYuri Pankov (void) fprintf(sfo, "%s\r\n", query);
2947c478bd9Sstevel@tonic-gate }
29543f842b8SMilan Jurik (void) fflush(sfo);
2961dd3983cSYuri Pankov nhost = NULL;
297*deeb0f36SJohann 'Myrkraverk' Oskarsson while ((len = getline(&buf, &buflen, sfi)) != -1) {
2981dd3983cSYuri Pankov while (len > 0 && isspace((unsigned char)buf[len - 1]))
2991dd3983cSYuri Pankov buf[--len] = '\0';
3001dd3983cSYuri Pankov (void) printf("%.*s\n", (int)len, buf);
3011dd3983cSYuri Pankov
3021dd3983cSYuri Pankov if ((flags & WHOIS_RECURSE) && nhost == NULL) {
3031dd3983cSYuri Pankov host = strnstr(buf, WHOIS_SERVER_ID, len);
3041dd3983cSYuri Pankov if (host != NULL) {
3051dd3983cSYuri Pankov host += sizeof (WHOIS_SERVER_ID) - 1;
3061dd3983cSYuri Pankov for (p = host; p < buf + len; p++) {
3071dd3983cSYuri Pankov if (!ishost(*p)) {
3081dd3983cSYuri Pankov *p = '\0';
3091dd3983cSYuri Pankov break;
3101dd3983cSYuri Pankov }
3111dd3983cSYuri Pankov }
3121dd3983cSYuri Pankov s_asprintf(&nhost, "%.*s",
3131dd3983cSYuri Pankov (int)(buf + len - host), host);
3141dd3983cSYuri Pankov } else if ((host =
3151dd3983cSYuri Pankov strnstr(buf, WHOIS_ORG_SERVER_ID, len)) != NULL) {
3161dd3983cSYuri Pankov host += sizeof (WHOIS_ORG_SERVER_ID) - 1;
3171dd3983cSYuri Pankov for (p = host; p < buf + len; p++) {
3181dd3983cSYuri Pankov if (!ishost(*p)) {
3191dd3983cSYuri Pankov *p = '\0';
3201dd3983cSYuri Pankov break;
3211dd3983cSYuri Pankov }
3221dd3983cSYuri Pankov }
3231dd3983cSYuri Pankov s_asprintf(&nhost, "%.*s",
3241dd3983cSYuri Pankov (int)(buf + len - host), host);
3251dd3983cSYuri Pankov } else if (strcmp(hostname, ANICHOST) == 0) {
3261dd3983cSYuri Pankov for (c = 0; c <= len; c++)
3271dd3983cSYuri Pankov buf[c] = tolower((unsigned char)buf[c]);
3281dd3983cSYuri Pankov for (i = 0; ip_whois[i] != NULL; i++) {
3291dd3983cSYuri Pankov if (strnstr(buf, ip_whois[i], len) !=
3301dd3983cSYuri Pankov NULL) {
3311dd3983cSYuri Pankov s_asprintf(&nhost, "%s",
3321dd3983cSYuri Pankov ip_whois[i]);
3331dd3983cSYuri Pankov break;
3341dd3983cSYuri Pankov }
3351dd3983cSYuri Pankov }
3361dd3983cSYuri Pankov }
3371dd3983cSYuri Pankov }
3381dd3983cSYuri Pankov }
3391dd3983cSYuri Pankov if (nhost != NULL) {
3401dd3983cSYuri Pankov whois(query, nhost, 0);
3411dd3983cSYuri Pankov free(nhost);
3421dd3983cSYuri Pankov }
3431dd3983cSYuri Pankov }
3441dd3983cSYuri Pankov
3451dd3983cSYuri Pankov static void
usage(void)3461dd3983cSYuri Pankov usage(void)
3471dd3983cSYuri Pankov {
3481dd3983cSYuri Pankov (void) fprintf(stderr,
3491dd3983cSYuri Pankov "usage: whois [-aAbfgiIklmQr] [-c country-code | -h hostname] "
3501dd3983cSYuri Pankov "[-p port] name ...\n");
3511dd3983cSYuri Pankov exit(EX_USAGE);
3521dd3983cSYuri Pankov }
353