1283083Sdelphij/*- 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> 47283083Sdelphij#include <sys/poll.h> 481590Srgrimes#include <netinet/in.h> 4936913Speter#include <arpa/inet.h> 5077368Sphk#include <ctype.h> 5128792Scharnier#include <err.h> 521590Srgrimes#include <netdb.h> 5380050Smike#include <stdarg.h> 541590Srgrimes#include <stdio.h> 5553291Sache#include <stdlib.h> 5628792Scharnier#include <string.h> 5733626Swollman#include <sysexits.h> 5828792Scharnier#include <unistd.h> 59283083Sdelphij#include <fcntl.h> 60283083Sdelphij#include <errno.h> 611590Srgrimes 62130479Sbms#define ABUSEHOST "whois.abuse.net" 6353291Sache#define NICHOST "whois.crsnic.net" 6454088Sache#define INICHOST "whois.networksolutions.com" 6543506Swollman#define GNICHOST "whois.nic.gov" 6633626Swollman#define ANICHOST "whois.arin.net" 67106735Smike#define LNICHOST "whois.lacnic.net" 68138681Sceri#define KNICHOST "whois.krnic.net" 6933626Swollman#define RNICHOST "whois.ripe.net" 7033626Swollman#define PNICHOST "whois.apnic.net" 7153291Sache#define MNICHOST "whois.ra.net" 7253291Sache#define QNICHOST_TAIL ".whois-servers.net" 7387536Smike#define BNICHOST "whois.registro.br" 74112617Seivind#define NORIDHOST "whois.norid.no" 75130466Sbms#define IANAHOST "whois.iana.org" 76134294Smbr#define GERMNICHOST "de.whois-servers.net" 77154710Sjhay#define FNICHOST "whois.afrinic.net" 7881165Smike#define DEFAULT_PORT "whois" 7978581Sdes#define WHOIS_SERVER_ID "Whois Server: " 80110159Sroberto#define WHOIS_ORG_SERVER_ID "Registrant Street1:Whois Server:" 811590Srgrimes 8253291Sache#define WHOIS_RECURSE 0x01 8384852Smike#define WHOIS_QUICK 0x02 8453291Sache 8584852Smike#define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-') 8684852Smike 87227246Sedstatic const char *ip_whois[] = { LNICHOST, RNICHOST, PNICHOST, BNICHOST, 88227246Sed FNICHOST, NULL }; 89227246Sedstatic const char *port = DEFAULT_PORT; 9078900Sdd 9179835Smikestatic char *choose_server(char *); 9280050Smikestatic struct addrinfo *gethostinfo(char const *host, int exit_on_error); 9390163Skrisstatic void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3); 9478581Sdesstatic void usage(void); 9590131Smikestatic void whois(const char *, const char *, int); 9628792Scharnier 9728792Scharnierint 9878581Sdesmain(int argc, char *argv[]) 991590Srgrimes{ 10081165Smike const char *country, *host; 10153291Sache char *qnichost; 10280050Smike int ch, flags, use_qnichost; 1031590Srgrimes 10415359Spst#ifdef SOCKS 10515359Spst SOCKSinit(argv[0]); 10615359Spst#endif 10715359Spst 10881165Smike country = host = qnichost = NULL; 10981165Smike flags = use_qnichost = 0; 110202280Sedwin while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:QrR6")) != -1) { 11178581Sdes switch (ch) { 11233626Swollman case 'a': 11333626Swollman host = ANICHOST; 11433626Swollman break; 11581165Smike case 'A': 11681165Smike host = PNICHOST; 11781165Smike break; 118130479Sbms case 'b': 119130479Sbms host = ABUSEHOST; 120130479Sbms break; 12181165Smike case 'c': 12281165Smike country = optarg; 12385067Smike break; 124154710Sjhay case 'f': 125154710Sjhay host = FNICHOST; 126154710Sjhay break; 12743506Swollman case 'g': 12843506Swollman host = GNICHOST; 12943506Swollman break; 1301590Srgrimes case 'h': 1311590Srgrimes host = optarg; 1321590Srgrimes break; 13353048Sache case 'i': 13453048Sache host = INICHOST; 13553048Sache break; 136130466Sbms case 'I': 137130466Sbms host = IANAHOST; 138130466Sbms break; 139138681Sceri case 'k': 140138681Sceri host = KNICHOST; 141138681Sceri break; 142106735Smike case 'l': 143106735Smike host = LNICHOST; 144106735Smike break; 14553291Sache case 'm': 14653291Sache host = MNICHOST; 14753291Sache break; 14833626Swollman case 'p': 14981165Smike port = optarg; 15033626Swollman break; 15153291Sache case 'Q': 15253291Sache flags |= WHOIS_QUICK; 15353291Sache break; 15433626Swollman case 'r': 15533626Swollman host = RNICHOST; 15633626Swollman break; 15743520Sache case 'R': 15881165Smike warnx("-R is deprecated; use '-c ru' instead"); 15981165Smike country = "ru"; 16043520Sache break; 161197725Sdougb /* Remove in FreeBSD 10 */ 16254172Sjoe case '6': 163197725Sdougb errx(EX_USAGE, 164197725Sdougb "-6 is deprecated; use -[aAflr] instead"); 16554172Sjoe break; 1661590Srgrimes case '?': 1671590Srgrimes default: 1681590Srgrimes usage(); 16978581Sdes /* NOTREACHED */ 1701590Srgrimes } 17154227Sjoe } 1721590Srgrimes argc -= optind; 1731590Srgrimes argv += optind; 1741590Srgrimes 17581165Smike if (!argc || (country != NULL && host != NULL)) 1761590Srgrimes usage(); 1771590Srgrimes 17853291Sache /* 17981165Smike * If no host or country is specified determine the top level domain 18081165Smike * from the query. If the TLD is a number, query ARIN. Otherwise, use 18178581Sdes * TLD.whois-server.net. If the domain does not contain '.', fall 18278581Sdes * back to NICHOST. 18353291Sache */ 18481165Smike if (host == NULL && country == NULL) { 185268154Sume if ((host = getenv("RA_SERVER")) == NULL) { 186268154Sume use_qnichost = 1; 187268154Sume host = NICHOST; 188268154Sume if (!(flags & WHOIS_QUICK)) 189268154Sume flags |= WHOIS_RECURSE; 190268154Sume } 19153291Sache } 19290131Smike while (argc-- > 0) { 19381165Smike if (country != NULL) { 19481165Smike s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL); 19590131Smike whois(*argv, qnichost, flags); 19685067Smike } else if (use_qnichost) 19780050Smike if ((qnichost = choose_server(*argv)) != NULL) 19890131Smike whois(*argv, qnichost, flags); 19980050Smike if (qnichost == NULL) 20090131Smike whois(*argv, host, flags); 20178581Sdes free(qnichost); 20278581Sdes qnichost = NULL; 20390131Smike argv++; 20453291Sache } 20553291Sache exit(0); 20653291Sache} 20753291Sache 20879835Smike/* 20979835Smike * This function will remove any trailing periods from domain, after which it 21079835Smike * returns a pointer to newly allocated memory containing the whois server to 21179835Smike * be queried, or a NULL if the correct server couldn't be determined. The 21279835Smike * caller must remember to free(3) the allocated memory. 21379835Smike */ 21479835Smikestatic char * 21579835Smikechoose_server(char *domain) 21679835Smike{ 21779835Smike char *pos, *retval; 21879835Smike 219202281Sedwin if (strchr(domain, ':')) { 220202281Sedwin s_asprintf(&retval, "%s", ANICHOST); 221202281Sedwin return (retval); 222202281Sedwin } 22379835Smike for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.';) 22479835Smike *pos = '\0'; 22579835Smike if (*domain == '\0') 22679835Smike errx(EX_USAGE, "can't search for a null string"); 227112617Seivind if (strlen(domain) > sizeof("-NORID")-1 && 228112617Seivind strcasecmp(domain + strlen(domain) - sizeof("-NORID") + 1, 229112617Seivind "-NORID") == 0) { 230112617Seivind s_asprintf(&retval, "%s", NORIDHOST); 231112617Seivind return (retval); 232112617Seivind } 23379835Smike while (pos > domain && *pos != '.') 23479835Smike --pos; 23580155Smike if (pos <= domain) 23680155Smike return (NULL); 23780050Smike if (isdigit((unsigned char)*++pos)) 23880050Smike s_asprintf(&retval, "%s", ANICHOST); 239117050Sache else 24080050Smike s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL); 24179835Smike return (retval); 24279835Smike} 24379835Smike 24485067Smikestatic struct addrinfo * 24580050Smikegethostinfo(char const *host, int exit_on_error) 24680050Smike{ 24780050Smike struct addrinfo hints, *res; 24880050Smike int error; 24980050Smike 25080050Smike memset(&hints, 0, sizeof(hints)); 25180050Smike hints.ai_flags = 0; 25280050Smike hints.ai_family = AF_UNSPEC; 25380050Smike hints.ai_socktype = SOCK_STREAM; 25481165Smike error = getaddrinfo(host, port, &hints, &res); 25580050Smike if (error) { 25680050Smike warnx("%s: %s", host, gai_strerror(error)); 25780050Smike if (exit_on_error) 25880050Smike exit(EX_NOHOST); 25980050Smike return (NULL); 26080050Smike } 26180050Smike return (res); 26285067Smike} 26380050Smike 26480050Smike/* 26580050Smike * Wrapper for asprintf(3) that exits on error. 26680050Smike */ 26753291Sachestatic void 26880050Smikes_asprintf(char **ret, const char *format, ...) 26980050Smike{ 27080050Smike va_list ap; 27180050Smike 27280050Smike va_start(ap, format); 27380050Smike if (vasprintf(ret, format, ap) == -1) { 27480050Smike va_end(ap); 27580050Smike err(EX_OSERR, "vasprintf()"); 27680050Smike } 27780050Smike va_end(ap); 27880050Smike} 27980050Smike 28080050Smikestatic void 28190131Smikewhois(const char *query, const char *hostname, int flags) 28253291Sache{ 28353291Sache FILE *sfi, *sfo; 28490131Smike struct addrinfo *hostres, *res; 28584852Smike char *buf, *host, *nhost, *p; 286283083Sdelphij int s = -1, f; 287283083Sdelphij nfds_t i, j; 288283083Sdelphij size_t c, len, count; 289283083Sdelphij struct pollfd *fds; 290283083Sdelphij int timeout = 180; 29153291Sache 29290131Smike hostres = gethostinfo(hostname, 1); 293283083Sdelphij for (res = hostres, count = 0; res; res = res->ai_next) 294283083Sdelphij count++; 295283083Sdelphij 296283083Sdelphij fds = calloc(count, sizeof(*fds)); 297283083Sdelphij if (fds == NULL) 298283083Sdelphij err(EX_OSERR, "calloc()"); 299283083Sdelphij 300283083Sdelphij /* 301283083Sdelphij * Traverse the result list elements and make non-block 302283083Sdelphij * connection attempts. 303283083Sdelphij */ 304283083Sdelphij count = i = 0; 305283083Sdelphij for (res = hostres; res != NULL; res = res->ai_next) { 306283083Sdelphij s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK, 307283083Sdelphij res->ai_protocol); 30878581Sdes if (s < 0) 30977585Sume continue; 310283083Sdelphij if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 311283083Sdelphij if (errno == EINPROGRESS) { 312283083Sdelphij /* Add the socket to poll list */ 313283083Sdelphij fds[i].fd = s; 314283083Sdelphij fds[i].events = POLLERR | POLLHUP | 315283083Sdelphij POLLIN | POLLOUT; 316283083Sdelphij count++; 317283083Sdelphij i++; 318283083Sdelphij } else { 319283083Sdelphij close(s); 320283083Sdelphij s = -1; 321283083Sdelphij 322283083Sdelphij /* 323283083Sdelphij * Poll only if we have something to poll, 324283083Sdelphij * otherwise just go ahead and try next 325283083Sdelphij * address 326283083Sdelphij */ 327283083Sdelphij if (count == 0) 328283083Sdelphij continue; 329283083Sdelphij } 330283083Sdelphij } else 331283083Sdelphij goto done; 332283083Sdelphij 333283083Sdelphij /* 334283083Sdelphij * If we are at the last address, poll until a connection is 335283083Sdelphij * established or we failed all connection attempts. 336283083Sdelphij */ 337283083Sdelphij if (res->ai_next == NULL) 338283083Sdelphij timeout = INFTIM; 339283083Sdelphij 340283083Sdelphij /* 341283083Sdelphij * Poll the watched descriptors for successful connections: 342283083Sdelphij * if we still have more untried resolved addresses, poll only 343283083Sdelphij * once; otherwise, poll until all descriptors have errors, 344283083Sdelphij * which will be considered as ETIMEDOUT later. 345283083Sdelphij */ 346283083Sdelphij do { 347283083Sdelphij int n; 348283083Sdelphij 349283083Sdelphij n = poll(fds, i, timeout); 350283083Sdelphij if (n == 0) { 351283083Sdelphij /* 352283083Sdelphij * No event reported in time. Try with a 353283083Sdelphij * smaller timeout (but cap at 2-3ms) 354283083Sdelphij * after a new host have been added. 355283083Sdelphij */ 356283083Sdelphij if (timeout >= 3) 357283083Sdelphij timeout <<= 1; 358283083Sdelphij 359283083Sdelphij break; 360283083Sdelphij } else if (n < 0) { 361283083Sdelphij /* 362283083Sdelphij * errno here can only be EINTR which we would want 363283083Sdelphij * to clean up and bail out. 364283083Sdelphij */ 365283083Sdelphij s = -1; 366283083Sdelphij goto done; 367283083Sdelphij } 368283083Sdelphij 369283083Sdelphij /* 370283083Sdelphij * Check for the event(s) we have seen. 371283083Sdelphij */ 372283083Sdelphij for (j = 0; j < i; j++) { 373283083Sdelphij if (fds[j].fd == -1 || fds[j].events == 0 || 374283083Sdelphij fds[j].revents == 0) 375283083Sdelphij continue; 376283083Sdelphij if (fds[j].revents & ~(POLLIN | POLLOUT)) { 377283083Sdelphij close(s); 378283083Sdelphij fds[j].fd = -1; 379283083Sdelphij fds[j].events = 0; 380283083Sdelphij count--; 381283083Sdelphij continue; 382283083Sdelphij } else if (fds[j].revents & (POLLIN | POLLOUT)) { 383283083Sdelphij /* Connect succeeded. */ 384283083Sdelphij s = fds[j].fd; 385283083Sdelphij 386283083Sdelphij goto done; 387283083Sdelphij } 388283083Sdelphij 389283083Sdelphij } 390283083Sdelphij } while (timeout == INFTIM && count != 0); 39154227Sjoe } 392283083Sdelphij 393283083Sdelphij /* All attempts were failed */ 394283083Sdelphij s = -1; 395283083Sdelphij if (count == 0) 396283083Sdelphij errno = ETIMEDOUT; 397283083Sdelphij 398283083Sdelphijdone: 399283083Sdelphij /* Close all watched fds except the succeeded one */ 400283083Sdelphij for (j = 0; j < i; j++) 401283083Sdelphij if (fds[j].fd != s && fds[j].fd != -1) 402283083Sdelphij close(fds[j].fd); 403283083Sdelphij 404283083Sdelphij if (s != -1) { 405283083Sdelphij /* Restore default blocking behavior. */ 406283083Sdelphij if ((f = fcntl(s, F_GETFL)) != -1) { 407283083Sdelphij f &= ~O_NONBLOCK; 408283083Sdelphij if (fcntl(s, F_SETFL, f) == -1) 409283083Sdelphij err(EX_OSERR, "fcntl()"); 410283083Sdelphij } else 411283083Sdelphij err(EX_OSERR, "fcntl()"); 412283083Sdelphij } 413283083Sdelphij 414283083Sdelphij free(fds); 41590131Smike freeaddrinfo(hostres); 416283083Sdelphij if (s == -1) 41778581Sdes err(EX_OSERR, "connect()"); 41833626Swollman 4191590Srgrimes sfi = fdopen(s, "r"); 4201590Srgrimes sfo = fdopen(s, "w"); 42178581Sdes if (sfi == NULL || sfo == NULL) 42278581Sdes err(EX_OSERR, "fdopen()"); 423134294Smbr if (strcmp(hostname, GERMNICHOST) == 0) { 424134294Smbr fprintf(sfo, "-T dn,ace -C US-ASCII %s\r\n", query); 425166103Sphk } else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) { 426166103Sphk fprintf(sfo, "--show-handles %s\r\n", query); 427134294Smbr } else { 428134294Smbr fprintf(sfo, "%s\r\n", query); 429134294Smbr } 43078581Sdes fflush(sfo); 43153291Sache nhost = NULL; 43278581Sdes while ((buf = fgetln(sfi, &len)) != NULL) { 43384852Smike while (len > 0 && isspace((unsigned char)buf[len - 1])) 43478581Sdes buf[--len] = '\0'; 43584852Smike printf("%.*s\n", (int)len, buf); 43653291Sache 43778900Sdd if ((flags & WHOIS_RECURSE) && nhost == NULL) { 43884852Smike host = strnstr(buf, WHOIS_SERVER_ID, len); 43984852Smike if (host != NULL) { 44084852Smike host += sizeof(WHOIS_SERVER_ID) - 1; 44184852Smike for (p = host; p < buf + len; p++) { 44284852Smike if (!ishost(*p)) { 44384852Smike *p = '\0'; 44484852Smike break; 44584852Smike } 44678900Sdd } 44784852Smike s_asprintf(&nhost, "%.*s", 44884852Smike (int)(buf + len - host), host); 449111430Smike } else if ((host = 450111430Smike strnstr(buf, WHOIS_ORG_SERVER_ID, len)) != NULL) { 451111430Smike host += sizeof(WHOIS_ORG_SERVER_ID) - 1; 452110159Sroberto for (p = host; p < buf + len; p++) { 453110159Sroberto if (!ishost(*p)) { 454110159Sroberto *p = '\0'; 455110159Sroberto break; 456110159Sroberto } 457110159Sroberto } 458110159Sroberto s_asprintf(&nhost, "%.*s", 459111430Smike (int)(buf + len - host), host); 460111430Smike } else if (strcmp(hostname, ANICHOST) == 0) { 461103530Smike for (c = 0; c <= len; c++) 462168721Sache buf[c] = tolower((unsigned char)buf[c]); 46378900Sdd for (i = 0; ip_whois[i] != NULL; i++) { 46484852Smike if (strnstr(buf, ip_whois[i], len) != 46584852Smike NULL) { 46684852Smike s_asprintf(&nhost, "%s", 46784852Smike ip_whois[i]); 46884852Smike break; 46984852Smike } 47078900Sdd } 47153291Sache } 47253291Sache } 47353291Sache } 47478581Sdes if (nhost != NULL) { 47590131Smike whois(query, nhost, 0); 47678581Sdes free(nhost); 47753291Sache } 4781590Srgrimes} 4791590Srgrimes 48028792Scharnierstatic void 48178581Sdesusage(void) 4821590Srgrimes{ 48378581Sdes fprintf(stderr, 484202280Sedwin "usage: whois [-aAbfgiIklmQrR6] [-c country-code | -h hostname] " 48581165Smike "[-p port] name ...\n"); 48633626Swollman exit(EX_USAGE); 4871590Srgrimes} 488