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) {
182268154Sume		if ((host = getenv("RA_SERVER")) == NULL) {
183268154Sume			use_qnichost = 1;
184268154Sume			host = NICHOST;
185268154Sume			if (!(flags & WHOIS_QUICK))
186268154Sume				flags |= WHOIS_RECURSE;
187268154Sume		}
18853291Sache	}
18990131Smike	while (argc-- > 0) {
19081165Smike		if (country != NULL) {
19181165Smike			s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL);
19290131Smike			whois(*argv, qnichost, flags);
19385067Smike		} else if (use_qnichost)
19480050Smike			if ((qnichost = choose_server(*argv)) != NULL)
19590131Smike				whois(*argv, qnichost, flags);
19680050Smike		if (qnichost == NULL)
19790131Smike			whois(*argv, host, flags);
19878581Sdes		free(qnichost);
19978581Sdes		qnichost = NULL;
20090131Smike		argv++;
20153291Sache	}
20253291Sache	exit(0);
20353291Sache}
20453291Sache
20579835Smike/*
20679835Smike * This function will remove any trailing periods from domain, after which it
20779835Smike * returns a pointer to newly allocated memory containing the whois server to
20879835Smike * be queried, or a NULL if the correct server couldn't be determined.  The
20979835Smike * caller must remember to free(3) the allocated memory.
21079835Smike */
21179835Smikestatic char *
21279835Smikechoose_server(char *domain)
21379835Smike{
21479835Smike	char *pos, *retval;
21579835Smike
216202281Sedwin	if (strchr(domain, ':')) {
217202281Sedwin		s_asprintf(&retval, "%s", ANICHOST);
218202281Sedwin		return (retval);
219202281Sedwin	}
22079835Smike	for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.';)
22179835Smike		*pos = '\0';
22279835Smike	if (*domain == '\0')
22379835Smike		errx(EX_USAGE, "can't search for a null string");
224112617Seivind	if (strlen(domain) > sizeof("-NORID")-1 &&
225112617Seivind	    strcasecmp(domain + strlen(domain) - sizeof("-NORID") + 1,
226112617Seivind		"-NORID") == 0) {
227112617Seivind		s_asprintf(&retval, "%s", NORIDHOST);
228112617Seivind		return (retval);
229112617Seivind	}
23079835Smike	while (pos > domain && *pos != '.')
23179835Smike		--pos;
23280155Smike	if (pos <= domain)
23380155Smike		return (NULL);
23480050Smike	if (isdigit((unsigned char)*++pos))
23580050Smike		s_asprintf(&retval, "%s", ANICHOST);
236117050Sache	else
23780050Smike		s_asprintf(&retval, "%s%s", pos, QNICHOST_TAIL);
23879835Smike	return (retval);
23979835Smike}
24079835Smike
24185067Smikestatic struct addrinfo *
24280050Smikegethostinfo(char const *host, int exit_on_error)
24380050Smike{
24480050Smike	struct addrinfo hints, *res;
24580050Smike	int error;
24680050Smike
24780050Smike	memset(&hints, 0, sizeof(hints));
24880050Smike	hints.ai_flags = 0;
24980050Smike	hints.ai_family = AF_UNSPEC;
25080050Smike	hints.ai_socktype = SOCK_STREAM;
25181165Smike	error = getaddrinfo(host, port, &hints, &res);
25280050Smike	if (error) {
25380050Smike		warnx("%s: %s", host, gai_strerror(error));
25480050Smike		if (exit_on_error)
25580050Smike			exit(EX_NOHOST);
25680050Smike		return (NULL);
25780050Smike	}
25880050Smike	return (res);
25985067Smike}
26080050Smike
26180050Smike/*
26280050Smike * Wrapper for asprintf(3) that exits on error.
26380050Smike */
26453291Sachestatic void
26580050Smikes_asprintf(char **ret, const char *format, ...)
26680050Smike{
26780050Smike	va_list ap;
26880050Smike
26980050Smike	va_start(ap, format);
27080050Smike	if (vasprintf(ret, format, ap) == -1) {
27180050Smike		va_end(ap);
27280050Smike		err(EX_OSERR, "vasprintf()");
27380050Smike	}
27480050Smike	va_end(ap);
27580050Smike}
27680050Smike
27780050Smikestatic void
27890131Smikewhois(const char *query, const char *hostname, int flags)
27953291Sache{
28053291Sache	FILE *sfi, *sfo;
28190131Smike	struct addrinfo *hostres, *res;
28284852Smike	char *buf, *host, *nhost, *p;
28384852Smike	int i, s;
284103530Smike	size_t c, len;
28553291Sache
286146752Scharnier	s = -1;
28790131Smike	hostres = gethostinfo(hostname, 1);
28890131Smike	for (res = hostres; res; res = res->ai_next) {
28977585Sume		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
29078581Sdes		if (s < 0)
29177585Sume			continue;
29278581Sdes		if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
29377585Sume			break;
29477585Sume		close(s);
29554227Sjoe	}
29690131Smike	freeaddrinfo(hostres);
29778581Sdes	if (res == NULL)
29878581Sdes		err(EX_OSERR, "connect()");
29933626Swollman
3001590Srgrimes	sfi = fdopen(s, "r");
3011590Srgrimes	sfo = fdopen(s, "w");
30278581Sdes	if (sfi == NULL || sfo == NULL)
30378581Sdes		err(EX_OSERR, "fdopen()");
304134294Smbr	if (strcmp(hostname, GERMNICHOST) == 0) {
305134294Smbr		fprintf(sfo, "-T dn,ace -C US-ASCII %s\r\n", query);
306166103Sphk	} else if (strcmp(hostname, "dk" QNICHOST_TAIL) == 0) {
307166103Sphk		fprintf(sfo, "--show-handles %s\r\n", query);
308134294Smbr	} else {
309134294Smbr		fprintf(sfo, "%s\r\n", query);
310134294Smbr	}
31178581Sdes	fflush(sfo);
31253291Sache	nhost = NULL;
31378581Sdes	while ((buf = fgetln(sfi, &len)) != NULL) {
31484852Smike		while (len > 0 && isspace((unsigned char)buf[len - 1]))
31578581Sdes			buf[--len] = '\0';
31684852Smike		printf("%.*s\n", (int)len, buf);
31753291Sache
31878900Sdd		if ((flags & WHOIS_RECURSE) && nhost == NULL) {
31984852Smike			host = strnstr(buf, WHOIS_SERVER_ID, len);
32084852Smike			if (host != NULL) {
32184852Smike				host += sizeof(WHOIS_SERVER_ID) - 1;
32284852Smike				for (p = host; p < buf + len; p++) {
32384852Smike					if (!ishost(*p)) {
32484852Smike						*p = '\0';
32584852Smike						break;
32684852Smike					}
32778900Sdd				}
32884852Smike				s_asprintf(&nhost, "%.*s",
32984852Smike				     (int)(buf + len - host), host);
330111430Smike			} else if ((host =
331111430Smike			    strnstr(buf, WHOIS_ORG_SERVER_ID, len)) != NULL) {
332111430Smike				host += sizeof(WHOIS_ORG_SERVER_ID) - 1;
333110159Sroberto				for (p = host; p < buf + len; p++) {
334110159Sroberto					if (!ishost(*p)) {
335110159Sroberto						*p = '\0';
336110159Sroberto						break;
337110159Sroberto					}
338110159Sroberto				}
339110159Sroberto				s_asprintf(&nhost, "%.*s",
340111430Smike				    (int)(buf + len - host), host);
341111430Smike			} else if (strcmp(hostname, ANICHOST) == 0) {
342103530Smike				for (c = 0; c <= len; c++)
343168721Sache					buf[c] = tolower((unsigned char)buf[c]);
34478900Sdd				for (i = 0; ip_whois[i] != NULL; i++) {
34584852Smike					if (strnstr(buf, ip_whois[i], len) !=
34684852Smike					    NULL) {
34784852Smike						s_asprintf(&nhost, "%s",
34884852Smike						    ip_whois[i]);
34984852Smike						break;
35084852Smike					}
35178900Sdd				}
35253291Sache			}
35353291Sache		}
35453291Sache	}
35578581Sdes	if (nhost != NULL) {
35690131Smike		whois(query, nhost, 0);
35778581Sdes		free(nhost);
35853291Sache	}
3591590Srgrimes}
3601590Srgrimes
36128792Scharnierstatic void
36278581Sdesusage(void)
3631590Srgrimes{
36478581Sdes	fprintf(stderr,
365202280Sedwin	    "usage: whois [-aAbfgiIklmQrR6] [-c country-code | -h hostname] "
36681165Smike	    "[-p port] name ...\n");
36733626Swollman	exit(EX_USAGE);
3681590Srgrimes}
369