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 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; 917c478bd9Sstevel@tonic-gate 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; 98f098c48bSDerek Morr 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 */ 145f098c48bSDerek Morr } 146f098c48bSDerek Morr } 1471dd3983cSYuri Pankov argc -= optind; 1481dd3983cSYuri Pankov argv += optind; 149f098c48bSDerek Morr 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 * 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 * 2191dd3983cSYuri Pankov gethostinfo(char const *host, int exit_on_error) 2201dd3983cSYuri Pankov { 2211dd3983cSYuri Pankov struct addrinfo hints, *res; 2221dd3983cSYuri Pankov int error; 2231dd3983cSYuri Pankov 2241dd3983cSYuri Pankov (void) memset(&hints, 0, sizeof (hints)); 2251dd3983cSYuri Pankov hints.ai_flags = 0; 2261dd3983cSYuri Pankov hints.ai_family = AF_UNSPEC; 2271dd3983cSYuri Pankov 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); 2341dd3983cSYuri Pankov } 2351dd3983cSYuri Pankov return (res); 2361dd3983cSYuri Pankov } 2371dd3983cSYuri Pankov 2381dd3983cSYuri Pankov /* 2391dd3983cSYuri Pankov * Wrapper for asprintf(3) that exits on error. 2401dd3983cSYuri Pankov */ 2411dd3983cSYuri Pankov /* PRINTFLIKE2 */ 2421dd3983cSYuri Pankov static void 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()"); 2511dd3983cSYuri Pankov } 2521dd3983cSYuri Pankov va_end(ap); 2531dd3983cSYuri Pankov } 2541dd3983cSYuri Pankov 2551dd3983cSYuri Pankov static void 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); 2791dd3983cSYuri Pankov } 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 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