11590Srgrimes/* 21590Srgrimes * Copyright (c) 1980, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3191792Smikestatic const char copyright[] = 321590Srgrimes"@(#) Copyright (c) 1980, 1993\n\ 331590Srgrimes The Regents of the University of California. All rights reserved.\n"; 341590Srgrimes#endif /* not lint */ 351590Srgrimes 3691792Smike#if 0 371590Srgrimes#ifndef lint 381590Srgrimesstatic char sccsid[] = "@(#)whois.c 8.1 (Berkeley) 6/6/93"; 3990131Smike#endif /* not lint */ 4028792Scharnier#endif 411590Srgrimes 4290131Smike#include <sys/cdefs.h> 4390131Smike__FBSDID("$FreeBSD$"); 4490131Smike 451590Srgrimes#include <sys/types.h> 461590Srgrimes#include <sys/socket.h> 471590Srgrimes#include <netinet/in.h> 4836913Speter#include <arpa/inet.h> 4977368Sphk#include <ctype.h> 5028792Scharnier#include <err.h> 511590Srgrimes#include <netdb.h> 5280050Smike#include <stdarg.h> 531590Srgrimes#include <stdio.h> 5453291Sache#include <stdlib.h> 5528792Scharnier#include <string.h> 5633626Swollman#include <sysexits.h> 5728792Scharnier#include <unistd.h> 581590Srgrimes 59130479Sbms#define ABUSEHOST "whois.abuse.net" 6053291Sache#define NICHOST "whois.crsnic.net" 6154088Sache#define INICHOST "whois.networksolutions.com" 6243506Swollman#define GNICHOST "whois.nic.gov" 6333626Swollman#define ANICHOST "whois.arin.net" 64106735Smike#define LNICHOST "whois.lacnic.net" 65138681Sceri#define KNICHOST "whois.krnic.net" 6633626Swollman#define RNICHOST "whois.ripe.net" 6733626Swollman#define PNICHOST "whois.apnic.net" 6853291Sache#define MNICHOST "whois.ra.net" 6953291Sache#define QNICHOST_TAIL ".whois-servers.net" 7087536Smike#define BNICHOST "whois.registro.br" 71112617Seivind#define NORIDHOST "whois.norid.no" 72130466Sbms#define IANAHOST "whois.iana.org" 73134294Smbr#define GERMNICHOST "de.whois-servers.net" 74154710Sjhay#define FNICHOST "whois.afrinic.net" 7581165Smike#define DEFAULT_PORT "whois" 7678581Sdes#define WHOIS_SERVER_ID "Whois Server: " 77110159Sroberto#define WHOIS_ORG_SERVER_ID "Registrant Street1:Whois Server:" 781590Srgrimes 7953291Sache#define WHOIS_RECURSE 0x01 8084852Smike#define WHOIS_QUICK 0x02 8153291Sache 8284852Smike#define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-') 8384852Smike 84227246Sedstatic const char *ip_whois[] = { LNICHOST, RNICHOST, PNICHOST, BNICHOST, 85227246Sed FNICHOST, NULL }; 86227246Sedstatic const char *port = DEFAULT_PORT; 8778900Sdd 8879835Smikestatic char *choose_server(char *); 8980050Smikestatic struct addrinfo *gethostinfo(char const *host, int exit_on_error); 9090163Skrisstatic void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3); 9178581Sdesstatic void usage(void); 9290131Smikestatic void whois(const char *, const char *, int); 9328792Scharnier 9428792Scharnierint 9578581Sdesmain(int argc, char *argv[]) 961590Srgrimes{ 9781165Smike const char *country, *host; 9853291Sache char *qnichost; 9980050Smike int ch, flags, use_qnichost; 1001590Srgrimes 10115359Spst#ifdef SOCKS 10215359Spst SOCKSinit(argv[0]); 10315359Spst#endif 10415359Spst 10581165Smike country = host = qnichost = NULL; 10681165Smike flags = use_qnichost = 0; 107202280Sedwin while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:QrR6")) != -1) { 10878581Sdes switch (ch) { 10933626Swollman case 'a': 11033626Swollman host = ANICHOST; 11133626Swollman break; 11281165Smike case 'A': 11381165Smike host = PNICHOST; 11481165Smike break; 115130479Sbms case 'b': 116130479Sbms host = ABUSEHOST; 117130479Sbms break; 11881165Smike case 'c': 11981165Smike country = optarg; 12085067Smike break; 121154710Sjhay case 'f': 122154710Sjhay host = FNICHOST; 123154710Sjhay break; 12443506Swollman case 'g': 12543506Swollman host = GNICHOST; 12643506Swollman break; 1271590Srgrimes case 'h': 1281590Srgrimes host = optarg; 1291590Srgrimes break; 13053048Sache case 'i': 13153048Sache host = INICHOST; 13253048Sache break; 133130466Sbms case 'I': 134130466Sbms host = IANAHOST; 135130466Sbms break; 136138681Sceri case 'k': 137138681Sceri host = KNICHOST; 138138681Sceri break; 139106735Smike case 'l': 140106735Smike host = LNICHOST; 141106735Smike break; 14253291Sache case 'm': 14353291Sache host = MNICHOST; 14453291Sache break; 14533626Swollman case 'p': 14681165Smike port = optarg; 14733626Swollman break; 14853291Sache case 'Q': 14953291Sache flags |= WHOIS_QUICK; 15053291Sache break; 15133626Swollman case 'r': 15233626Swollman host = RNICHOST; 15333626Swollman break; 15443520Sache case 'R': 15581165Smike warnx("-R is deprecated; use '-c ru' instead"); 15681165Smike country = "ru"; 15743520Sache break; 158197725Sdougb /* Remove in FreeBSD 10 */ 15954172Sjoe case '6': 160197725Sdougb errx(EX_USAGE, 161197725Sdougb "-6 is deprecated; use -[aAflr] instead"); 16254172Sjoe break; 1631590Srgrimes case '?': 1641590Srgrimes default: 1651590Srgrimes usage(); 16678581Sdes /* NOTREACHED */ 1671590Srgrimes } 16854227Sjoe } 1691590Srgrimes argc -= optind; 1701590Srgrimes argv += optind; 1711590Srgrimes 17281165Smike if (!argc || (country != NULL && host != NULL)) 1731590Srgrimes usage(); 1741590Srgrimes 17553291Sache /* 17681165Smike * If no host or country is specified determine the top level domain 17781165Smike * from the query. If the TLD is a number, query ARIN. Otherwise, use 17878581Sdes * TLD.whois-server.net. If the domain does not contain '.', fall 17978581Sdes * back to NICHOST. 18053291Sache */ 18181165Smike if (host == NULL && country == NULL) { 18253291Sache use_qnichost = 1; 18353291Sache host = NICHOST; 18478581Sdes if (!(flags & WHOIS_QUICK)) 18584852Smike flags |= WHOIS_RECURSE; 18653291Sache } 18790131Smike while (argc-- > 0) { 18881165Smike if (country != NULL) { 18981165Smike s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); 19090131Smike whois(*argv, qnichost, flags); 19185067Smike } else if (use_qnichost) 19280050Smike if ((qnichost = choose_server(*argv)) != NULL) 19390131Smike whois(*argv, qnichost, flags); 19480050Smike if (qnichost == NULL) 19590131Smike whois(*argv, host, flags); 19678581Sdes free(qnichost); 19778581Sdes qnichost = NULL; 19890131Smike argv++; 19953291Sache } 20053291Sache exit(0); 20153291Sache} 20253291Sache 20379835Smike/* 20479835Smike * This function will remove any trailing periods from domain, after which it 20579835Smike * returns a pointer to newly allocated memory containing the whois server to 20679835Smike * be queried, or a NULL if the correct server couldn't be determined. The 20779835Smike * caller must remember to free(3) the allocated memory. 20879835Smike */ 20979835Smikestatic char * 21079835Smikechoose_server(char *domain) 21179835Smike{ 21279835Smike char *pos, *retval; 21379835Smike 214202281Sedwin if (strchr(domain, ':')) { 215202281Sedwin s_asprintf(&retval, "%s", ANICHOST); 216202281Sedwin return (retval); 217202281Sedwin } 21879835Smike for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.';) 21979835Smike *pos = '\0'; 22079835Smike if (*domain == '\0') 22179835Smike errx(EX_USAGE, "can't search for a null string"); 222112617Seivind if (strlen(domain) > sizeof("-NORID")-1 && 223112617Seivind strcasecmp(domain + strlen(domain) - sizeof("-NORID") + 1, 224112617Seivind "-NORID") == 0) { 225112617Seivind s_asprintf(&retval, "%s", NORIDHOST); 226112617Seivind return (retval); 227112617Seivind } 22879835Smike while (pos > domain && *pos != '.') 22979835Smike --pos; 23080155Smike if (pos <= domain) 23180155Smike return (NULL); 23280050Smike if (isdigit((unsigned char)*++pos)) 23380050Smike s_asprintf(&retval, "%s", ANICHOST); 234117050Sache else 23580050Smike s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL); 23679835Smike return (retval); 23779835Smike} 23879835Smike 23985067Smikestatic struct addrinfo * 24080050Smikegethostinfo(char const *host, int exit_on_error) 24180050Smike{ 24280050Smike struct addrinfo hints, *res; 24380050Smike int error; 24480050Smike 24580050Smike memset(&hints, 0, sizeof(hints)); 24680050Smike hints.ai_flags = 0; 24780050Smike hints.ai_family = AF_UNSPEC; 24880050Smike hints.ai_socktype = SOCK_STREAM; 24981165Smike error = getaddrinfo(host, port, &hints, &res); 25080050Smike if (error) { 25180050Smike warnx("%s: %s", host, gai_strerror(error)); 25280050Smike if (exit_on_error) 25380050Smike exit(EX_NOHOST); 25480050Smike return (NULL); 25580050Smike } 25680050Smike return (res); 25785067Smike} 25880050Smike 25980050Smike/* 26080050Smike * Wrapper for asprintf(3) that exits on error. 26180050Smike */ 26253291Sachestatic void 26380050Smikes_asprintf(char **ret, const char *format, ...) 26480050Smike{ 26580050Smike va_list ap; 26680050Smike 26780050Smike va_start(ap, format); 26880050Smike if (vasprintf(ret, format, ap) == -1) { 26980050Smike va_end(ap); 27080050Smike err(EX_OSERR, "vasprintf()"); 27180050Smike } 27280050Smike va_end(ap); 27380050Smike} 27480050Smike 27580050Smikestatic void 27690131Smikewhois(const char *query, const char *hostname, int flags) 27753291Sache{ 27853291Sache FILE *sfi, *sfo; 27990131Smike struct addrinfo *hostres, *res; 28084852Smike char *buf, *host, *nhost, *p; 28184852Smike int i, s; 282103530Smike size_t c, len; 28353291Sache 284146752Scharnier s = -1; 28590131Smike hostres = gethostinfo(hostname, 1); 28690131Smike for (res = hostres; res; res = res->ai_next) { 28777585Sume s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 28878581Sdes if (s < 0) 28977585Sume continue; 29078581Sdes if (connect(s, res->ai_addr, res->ai_addrlen) == 0) 29177585Sume break; 29277585Sume close(s); 29354227Sjoe } 29490131Smike freeaddrinfo(hostres); 29578581Sdes if (res == NULL) 29678581Sdes err(EX_OSERR, "connect()"); 29733626Swollman 2981590Srgrimes sfi = fdopen(s, "r"); 2991590Srgrimes sfo = fdopen(s, "w"); 30078581Sdes if (sfi == NULL || sfo == NULL) 30178581Sdes err(EX_OSERR, "fdopen()"); 302134294Smbr if (strcmp(hostname, GERMNICHOST) == 0) { 303134294Smbr fprintf(sfo, "-T dn,ace -C US-ASCII %s\r\n", query); 304166103Sphk } else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) { 305166103Sphk fprintf(sfo, "--show-handles %s\r\n", query); 306134294Smbr } else { 307134294Smbr fprintf(sfo, "%s\r\n", query); 308134294Smbr } 30978581Sdes fflush(sfo); 31053291Sache nhost = NULL; 31178581Sdes while ((buf = fgetln(sfi, &len)) != NULL) { 31284852Smike while (len > 0 && isspace((unsigned char)buf[len - 1])) 31378581Sdes buf[--len] = '\0'; 31484852Smike printf("%.*s\n", (int)len, buf); 31553291Sache 31678900Sdd if ((flags & WHOIS_RECURSE) && nhost == NULL) { 31784852Smike host = strnstr(buf, WHOIS_SERVER_ID, len); 31884852Smike if (host != NULL) { 31984852Smike host += sizeof(WHOIS_SERVER_ID) - 1; 32084852Smike for (p = host; p < buf + len; p++) { 32184852Smike if (!ishost(*p)) { 32284852Smike *p = '\0'; 32384852Smike break; 32484852Smike } 32578900Sdd } 32684852Smike s_asprintf(&nhost, "%.*s", 32784852Smike (int)(buf + len - host), host); 328111430Smike } else if ((host = 329111430Smike strnstr(buf, WHOIS_ORG_SERVER_ID, len)) != NULL) { 330111430Smike host += sizeof(WHOIS_ORG_SERVER_ID) - 1; 331110159Sroberto for (p = host; p < buf + len; p++) { 332110159Sroberto if (!ishost(*p)) { 333110159Sroberto *p = '\0'; 334110159Sroberto break; 335110159Sroberto } 336110159Sroberto } 337110159Sroberto s_asprintf(&nhost, "%.*s", 338111430Smike (int)(buf + len - host), host); 339111430Smike } else if (strcmp(hostname, ANICHOST) == 0) { 340103530Smike for (c = 0; c <= len; c++) 341168721Sache buf[c] = tolower((unsigned char)buf[c]); 34278900Sdd for (i = 0; ip_whois[i] != NULL; i++) { 34384852Smike if (strnstr(buf, ip_whois[i], len) != 34484852Smike NULL) { 34584852Smike s_asprintf(&nhost, "%s", 34684852Smike ip_whois[i]); 34784852Smike break; 34884852Smike } 34978900Sdd } 35053291Sache } 35153291Sache } 35253291Sache } 35378581Sdes if (nhost != NULL) { 35490131Smike whois(query, nhost, 0); 35578581Sdes free(nhost); 35653291Sache } 3571590Srgrimes} 3581590Srgrimes 35928792Scharnierstatic void 36078581Sdesusage(void) 3611590Srgrimes{ 36278581Sdes fprintf(stderr, 363202280Sedwin "usage: whois [-aAbfgiIklmQrR6] [-c country-code | -h hostname] " 36481165Smike "[-p port] name ...\n"); 36533626Swollman exit(EX_USAGE); 3661590Srgrimes} 367