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