xref: /freebsd/usr.bin/whois/whois.c (revision 0b8224d1cc9dc6c9778ba04a75b2c8d47e5d7481)
13cddd28eSXin LI /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
49b50d902SRodney W. Grimes  * Copyright (c) 1980, 1993
59b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
69b50d902SRodney W. Grimes  *
79b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
89b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
99b50d902SRodney W. Grimes  * are met:
109b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
119b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
129b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
139b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
149b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
169b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
179b50d902SRodney W. Grimes  *    without specific prior written permission.
189b50d902SRodney W. Grimes  *
199b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299b50d902SRodney W. Grimes  * SUCH DAMAGE.
309b50d902SRodney W. Grimes  */
319b50d902SRodney W. Grimes 
329b50d902SRodney W. Grimes #include <sys/types.h>
339b50d902SRodney W. Grimes #include <sys/socket.h>
343cddd28eSXin LI #include <sys/poll.h>
359b50d902SRodney W. Grimes #include <netinet/in.h>
36a60c62a3SPeter Wemm #include <arpa/inet.h>
37861c52f1SPoul-Henning Kamp #include <ctype.h>
38741d304eSPhilippe Charnier #include <err.h>
399b50d902SRodney W. Grimes #include <netdb.h>
40197bca2cSMike Barcroft #include <stdarg.h>
419b50d902SRodney W. Grimes #include <stdio.h>
422053e9a3SAndrey A. Chernov #include <stdlib.h>
43741d304eSPhilippe Charnier #include <string.h>
44a00fe97dSGarrett Wollman #include <sysexits.h>
45741d304eSPhilippe Charnier #include <unistd.h>
463cddd28eSXin LI #include <fcntl.h>
473cddd28eSXin LI #include <errno.h>
489b50d902SRodney W. Grimes 
49fb6dad91SBruce M Simpson #define	ABUSEHOST	"whois.abuse.net"
50a00fe97dSGarrett Wollman #define	ANICHOST	"whois.arin.net"
51f537d420STony Finch #define	DENICHOST	"whois.denic.de"
52f537d420STony Finch #define	DKNICHOST	"whois.dk-hostmaster.dk"
53dfef6b15STony Finch #define	FNICHOST	"whois.afrinic.net"
54dfef6b15STony Finch #define	GNICHOST	"whois.nic.gov"
55dfef6b15STony Finch #define	IANAHOST	"whois.iana.org"
566f4d88dfSTony Finch #define	INICHOST	"whois.internic.net"
57876b0a88SCeri Davies #define	KNICHOST	"whois.krnic.net"
58dfef6b15STony Finch #define	LNICHOST	"whois.lacnic.net"
592053e9a3SAndrey A. Chernov #define	MNICHOST	"whois.ra.net"
6060214b7fSTony Finch #define	PDBHOST		"whois.peeringdb.com"
61dfef6b15STony Finch #define	PNICHOST	"whois.apnic.net"
622053e9a3SAndrey A. Chernov #define	QNICHOST_TAIL	".whois-servers.net"
63dfef6b15STony Finch #define	RNICHOST	"whois.ripe.net"
6452517c0aSTony Finch #define	VNICHOST	"whois.verisign-grs.com"
65dfef6b15STony Finch 
66eade81c9SMike Barcroft #define	DEFAULT_PORT	"whois"
67dfef6b15STony Finch 
682053e9a3SAndrey A. Chernov #define WHOIS_RECURSE	0x01
6942ab40e5SMike Barcroft #define WHOIS_QUICK	0x02
7052517c0aSTony Finch #define WHOIS_SPAM_ME	0x04
7142ab40e5SMike Barcroft 
726f4d88dfSTony Finch #define CHOPSPAM	">>> Last update of WHOIS database:"
736f4d88dfSTony Finch 
7442ab40e5SMike Barcroft #define ishost(h) (isalnum((unsigned char)h) || h == '.' || h == '-')
752053e9a3SAndrey A. Chernov 
766f4d88dfSTony Finch #define SCAN(p, end, check)					\
776f4d88dfSTony Finch 	while ((p) < (end))					\
786f4d88dfSTony Finch 		if (check) ++(p);				\
796f4d88dfSTony Finch 		else break
806f4d88dfSTony Finch 
81c343f061STony Finch static struct {
82c343f061STony Finch 	const char *suffix, *server;
83c343f061STony Finch } whoiswhere[] = {
84c343f061STony Finch 	/* Various handles */
85c343f061STony Finch 	{ "-ARIN", ANICHOST },
86c343f061STony Finch 	{ "-NICAT", "at" QNICHOST_TAIL },
87c343f061STony Finch 	{ "-NORID", "no" QNICHOST_TAIL },
88c343f061STony Finch 	{ "-RIPE", RNICHOST },
89c343f061STony Finch 	/* Nominet's whois server doesn't return referrals to JANET */
90c343f061STony Finch 	{ ".ac.uk", "ac.uk" QNICHOST_TAIL },
91751af43eSTony Finch 	{ ".gov.uk", "ac.uk" QNICHOST_TAIL },
926f4d88dfSTony Finch 	{ "", IANAHOST }, /* default */
936f4d88dfSTony Finch 	{ NULL, NULL } /* safety belt */
94c343f061STony Finch };
95c343f061STony Finch 
9652517c0aSTony Finch #define WHOIS_REFERRAL(s) { s, sizeof(s) - 1 }
9752517c0aSTony Finch static struct {
9852517c0aSTony Finch 	const char *prefix;
9952517c0aSTony Finch 	size_t len;
10052517c0aSTony Finch } whois_referral[] = {
1016f4d88dfSTony Finch 	WHOIS_REFERRAL("whois:"), /* IANA */
10252517c0aSTony Finch 	WHOIS_REFERRAL("Whois Server:"),
1036f4d88dfSTony Finch 	WHOIS_REFERRAL("Registrar WHOIS Server:"), /* corporatedomains.com */
1046f4d88dfSTony Finch 	WHOIS_REFERRAL("ReferralServer:  whois://"), /* ARIN */
105aa949e8aSMark Johnston 	WHOIS_REFERRAL("ReferralServer:  rwhois://"), /* ARIN */
10642e88fe6STony Finch 	WHOIS_REFERRAL("descr:          region. Please query"), /* AfriNIC */
10752517c0aSTony Finch 	{ NULL, 0 }
10852517c0aSTony Finch };
10952517c0aSTony Finch 
1103997df0cSTony Finch /*
1113997df0cSTony Finch  * We have a list of patterns for RIRs that assert ignorance rather than
1123997df0cSTony Finch  * providing referrals. If that happens, we guess that ARIN will be more
1133997df0cSTony Finch  * helpful. But, before following a referral to an RIR, we check if we have
1143997df0cSTony Finch  * asked that RIR already, and if so we make another guess.
1153997df0cSTony Finch  */
11642e88fe6STony Finch static const char *actually_arin[] = {
11742e88fe6STony Finch 	"netname:        ERX-NETBLOCK\n", /* APNIC */
11842e88fe6STony Finch 	"netname:        NON-RIPE-NCC-MANAGED-ADDRESS-BLOCK\n",
11942e88fe6STony Finch 	NULL
12042e88fe6STony Finch };
12142e88fe6STony Finch 
1223997df0cSTony Finch static struct {
1233997df0cSTony Finch 	int loop;
1243997df0cSTony Finch 	const char *host;
1253997df0cSTony Finch } try_rir[] = {
1263997df0cSTony Finch 	{ 0, ANICHOST },
1273997df0cSTony Finch 	{ 0, RNICHOST },
1283997df0cSTony Finch 	{ 0, PNICHOST },
1293997df0cSTony Finch 	{ 0, FNICHOST },
1303997df0cSTony Finch 	{ 0, LNICHOST },
1313997df0cSTony Finch 	{ 0, NULL }
1323997df0cSTony Finch };
1333997df0cSTony Finch 
1343997df0cSTony Finch static void
reset_rir(void)1353997df0cSTony Finch reset_rir(void) {
1363997df0cSTony Finch 	int i;
1373997df0cSTony Finch 
1383997df0cSTony Finch 	for (i = 0; try_rir[i].host != NULL; i++)
1393997df0cSTony Finch 		try_rir[i].loop = 0;
1403997df0cSTony Finch }
1413997df0cSTony Finch 
142e5ec9055SEd Schouten static const char *port = DEFAULT_PORT;
14340682beaSDima Dorfman 
1446f4d88dfSTony Finch static const char *choose_server(char *);
145aa949e8aSMark Johnston static struct addrinfo *gethostinfo(const char *, const char *, int);
14601d71924SKris Kennaway static void s_asprintf(char **ret, const char *format, ...) __printflike(2, 3);
147*cccdaf50SAlfonso Gregory static void usage(void) __dead2;
148aa949e8aSMark Johnston static void whois(const char *, const char *, const char *, int);
149741d304eSPhilippe Charnier 
150741d304eSPhilippe Charnier int
main(int argc,char * argv[])1519b07a066SDag-Erling Smørgrav main(int argc, char *argv[])
1529b50d902SRodney W. Grimes {
153eade81c9SMike Barcroft 	const char *country, *host;
1546f4d88dfSTony Finch 	int ch, flags;
1559b50d902SRodney W. Grimes 
156d9fd44ddSPaul Traina #ifdef	SOCKS
157d9fd44ddSPaul Traina 	SOCKSinit(argv[0]);
158d9fd44ddSPaul Traina #endif
159d9fd44ddSPaul Traina 
1606f4d88dfSTony Finch 	country = host = NULL;
1616f4d88dfSTony Finch 	flags = 0;
16252517c0aSTony Finch 	while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:PQrRS")) != -1) {
1639b07a066SDag-Erling Smørgrav 		switch (ch) {
164a00fe97dSGarrett Wollman 		case 'a':
165a00fe97dSGarrett Wollman 			host = ANICHOST;
166a00fe97dSGarrett Wollman 			break;
167eade81c9SMike Barcroft 		case 'A':
168eade81c9SMike Barcroft 			host = PNICHOST;
169eade81c9SMike Barcroft 			break;
170fb6dad91SBruce M Simpson 		case 'b':
171fb6dad91SBruce M Simpson 			host = ABUSEHOST;
172fb6dad91SBruce M Simpson 			break;
173eade81c9SMike Barcroft 		case 'c':
174eade81c9SMike Barcroft 			country = optarg;
175eade81c9SMike Barcroft 			break;
17623639a23SJohn Hay 		case 'f':
17723639a23SJohn Hay 			host = FNICHOST;
17823639a23SJohn Hay 			break;
1792712c396SGarrett Wollman 		case 'g':
1802712c396SGarrett Wollman 			host = GNICHOST;
1812712c396SGarrett Wollman 			break;
1829b50d902SRodney W. Grimes 		case 'h':
1839b50d902SRodney W. Grimes 			host = optarg;
1849b50d902SRodney W. Grimes 			break;
185036cc89fSAndrey A. Chernov 		case 'i':
186036cc89fSAndrey A. Chernov 			host = INICHOST;
187036cc89fSAndrey A. Chernov 			break;
1887138e3feSBruce M Simpson 		case 'I':
1897138e3feSBruce M Simpson 			host = IANAHOST;
1907138e3feSBruce M Simpson 			break;
191876b0a88SCeri Davies 		case 'k':
192876b0a88SCeri Davies 			host = KNICHOST;
193876b0a88SCeri Davies 			break;
194a5d96e6dSMike Barcroft 		case 'l':
195a5d96e6dSMike Barcroft 			host = LNICHOST;
196a5d96e6dSMike Barcroft 			break;
1972053e9a3SAndrey A. Chernov 		case 'm':
1982053e9a3SAndrey A. Chernov 			host = MNICHOST;
1992053e9a3SAndrey A. Chernov 			break;
200a00fe97dSGarrett Wollman 		case 'p':
201eade81c9SMike Barcroft 			port = optarg;
202a00fe97dSGarrett Wollman 			break;
20360214b7fSTony Finch 		case 'P':
20460214b7fSTony Finch 			host = PDBHOST;
20560214b7fSTony Finch 			break;
2062053e9a3SAndrey A. Chernov 		case 'Q':
2072053e9a3SAndrey A. Chernov 			flags |= WHOIS_QUICK;
2082053e9a3SAndrey A. Chernov 			break;
209a00fe97dSGarrett Wollman 		case 'r':
210a00fe97dSGarrett Wollman 			host = RNICHOST;
211a00fe97dSGarrett Wollman 			break;
21252517c0aSTony Finch 		case 'R':
21352517c0aSTony Finch 			flags |= WHOIS_RECURSE;
21452517c0aSTony Finch 			break;
21552517c0aSTony Finch 		case 'S':
21652517c0aSTony Finch 			flags |= WHOIS_SPAM_ME;
21752517c0aSTony Finch 			break;
2189b50d902SRodney W. Grimes 		case '?':
2199b50d902SRodney W. Grimes 		default:
2209b50d902SRodney W. Grimes 			usage();
2219b07a066SDag-Erling Smørgrav 			/* NOTREACHED */
2229b50d902SRodney W. Grimes 		}
2234fb37726SJosef Karthauser 	}
2249b50d902SRodney W. Grimes 	argc -= optind;
2259b50d902SRodney W. Grimes 	argv += optind;
2269b50d902SRodney W. Grimes 
227eade81c9SMike Barcroft 	if (!argc || (country != NULL && host != NULL))
2289b50d902SRodney W. Grimes 		usage();
2299b50d902SRodney W. Grimes 
2302053e9a3SAndrey A. Chernov 	/*
2316f4d88dfSTony Finch 	 * If no host or country is specified, rely on referrals from IANA.
2322053e9a3SAndrey A. Chernov 	 */
233eade81c9SMike Barcroft 	if (host == NULL && country == NULL) {
2340279f129STony Finch 		if ((host = getenv("WHOIS_SERVER")) == NULL &&
2350279f129STony Finch 		    (host = getenv("RA_SERVER")) == NULL) {
2369b07a066SDag-Erling Smørgrav 			if (!(flags & WHOIS_QUICK))
23742ab40e5SMike Barcroft 				flags |= WHOIS_RECURSE;
2382053e9a3SAndrey A. Chernov 		}
239f35f40c5SHajimu UMEMOTO 	}
240f971bd06SMike Barcroft 	while (argc-- > 0) {
241eade81c9SMike Barcroft 		if (country != NULL) {
2426f4d88dfSTony Finch 			char *qnichost;
243eade81c9SMike Barcroft 			s_asprintf(&qnichost, "%s%s", country, QNICHOST_TAIL);
244aa949e8aSMark Johnston 			whois(*argv, qnichost, port, flags);
2459b07a066SDag-Erling Smørgrav 			free(qnichost);
2466f4d88dfSTony Finch 		} else
2476f4d88dfSTony Finch 			whois(*argv, host != NULL ? host :
248aa949e8aSMark Johnston 			      choose_server(*argv), port, flags);
2493997df0cSTony Finch 		reset_rir();
250f971bd06SMike Barcroft 		argv++;
2512053e9a3SAndrey A. Chernov 	}
2522053e9a3SAndrey A. Chernov 	exit(0);
2532053e9a3SAndrey A. Chernov }
2542053e9a3SAndrey A. Chernov 
2556f4d88dfSTony Finch static const char *
choose_server(char * domain)2566c56da4aSMike Barcroft choose_server(char *domain)
2576c56da4aSMike Barcroft {
2586f4d88dfSTony Finch 	size_t len = strlen(domain);
259c343f061STony Finch 	int i;
2606c56da4aSMike Barcroft 
261c343f061STony Finch 	for (i = 0; whoiswhere[i].suffix != NULL; i++) {
262c343f061STony Finch 		size_t suffix_len = strlen(whoiswhere[i].suffix);
2636f4d88dfSTony Finch 		if (len > suffix_len &&
2646f4d88dfSTony Finch 		    strcasecmp(domain + len - suffix_len,
2656f4d88dfSTony Finch 			       whoiswhere[i].suffix) == 0)
2666f4d88dfSTony Finch 			return (whoiswhere[i].server);
26705854331SEivind Eklund 	}
2686f4d88dfSTony Finch 	errx(EX_SOFTWARE, "no default whois server");
2691326bf51STony Finch }
2706c56da4aSMike Barcroft 
271197bca2cSMike Barcroft static struct addrinfo *
gethostinfo(const char * host,const char * hport,int exit_on_noname)272aa949e8aSMark Johnston gethostinfo(const char *host, const char *hport, int exit_on_noname)
273197bca2cSMike Barcroft {
274197bca2cSMike Barcroft 	struct addrinfo hints, *res;
275197bca2cSMike Barcroft 	int error;
276197bca2cSMike Barcroft 
277197bca2cSMike Barcroft 	memset(&hints, 0, sizeof(hints));
27852517c0aSTony Finch 	hints.ai_flags = AI_CANONNAME;
279197bca2cSMike Barcroft 	hints.ai_family = AF_UNSPEC;
280197bca2cSMike Barcroft 	hints.ai_socktype = SOCK_STREAM;
2811326bf51STony Finch 	res = NULL;
282aa949e8aSMark Johnston 	error = getaddrinfo(host, hport, &hints, &res);
2831326bf51STony Finch 	if (error && (exit_on_noname || error != EAI_NONAME))
2841326bf51STony Finch 		err(EX_NOHOST, "%s: %s", host, gai_strerror(error));
285197bca2cSMike Barcroft 	return (res);
286197bca2cSMike Barcroft }
287197bca2cSMike Barcroft 
288197bca2cSMike Barcroft /*
289197bca2cSMike Barcroft  * Wrapper for asprintf(3) that exits on error.
290197bca2cSMike Barcroft  */
291197bca2cSMike Barcroft static void
s_asprintf(char ** ret,const char * format,...)292197bca2cSMike Barcroft s_asprintf(char **ret, const char *format, ...)
293197bca2cSMike Barcroft {
294197bca2cSMike Barcroft 	va_list ap;
295197bca2cSMike Barcroft 
296197bca2cSMike Barcroft 	va_start(ap, format);
297197bca2cSMike Barcroft 	if (vasprintf(ret, format, ap) == -1) {
298197bca2cSMike Barcroft 		va_end(ap);
299197bca2cSMike Barcroft 		err(EX_OSERR, "vasprintf()");
300197bca2cSMike Barcroft 	}
301197bca2cSMike Barcroft 	va_end(ap);
302197bca2cSMike Barcroft }
303197bca2cSMike Barcroft 
30468c47fcfSConrad Meyer static int
connect_to_any_host(struct addrinfo * hostres)30568c47fcfSConrad Meyer connect_to_any_host(struct addrinfo *hostres)
3062053e9a3SAndrey A. Chernov {
30768c47fcfSConrad Meyer 	struct addrinfo *res;
308298383b0STony Finch 	nfds_t i, j;
30968c47fcfSConrad Meyer 	size_t count;
3103cddd28eSXin LI 	struct pollfd *fds;
31168c47fcfSConrad Meyer 	int timeout = 180, s = -1;
3122053e9a3SAndrey A. Chernov 
3133cddd28eSXin LI 	for (res = hostres, count = 0; res; res = res->ai_next)
3143cddd28eSXin LI 		count++;
3153cddd28eSXin LI 	fds = calloc(count, sizeof(*fds));
3163cddd28eSXin LI 	if (fds == NULL)
3173cddd28eSXin LI 		err(EX_OSERR, "calloc()");
3183cddd28eSXin LI 
3193cddd28eSXin LI 	/*
3203cddd28eSXin LI 	 * Traverse the result list elements and make non-block
3213cddd28eSXin LI 	 * connection attempts.
3223cddd28eSXin LI 	 */
3233cddd28eSXin LI 	count = i = 0;
3243cddd28eSXin LI 	for (res = hostres; res != NULL; res = res->ai_next) {
3253cddd28eSXin LI 		s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK,
3263cddd28eSXin LI 		    res->ai_protocol);
3279b07a066SDag-Erling Smørgrav 		if (s < 0)
3282c5958aaSHajimu UMEMOTO 			continue;
3293cddd28eSXin LI 		if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
3303cddd28eSXin LI 			if (errno == EINPROGRESS) {
3313cddd28eSXin LI 				/* Add the socket to poll list */
3323cddd28eSXin LI 				fds[i].fd = s;
3333cddd28eSXin LI 				fds[i].events = POLLERR | POLLHUP |
3343cddd28eSXin LI 						POLLIN | POLLOUT;
335c24375a7SConrad Meyer 				/*
336c24375a7SConrad Meyer 				 * From here until a socket connects, the
337c24375a7SConrad Meyer 				 * socket fd is owned by the fds[] poll array.
338c24375a7SConrad Meyer 				 */
339c24375a7SConrad Meyer 				s = -1;
3403cddd28eSXin LI 				count++;
3413cddd28eSXin LI 				i++;
3423cddd28eSXin LI 			} else {
3432c5958aaSHajimu UMEMOTO 				close(s);
3443cddd28eSXin LI 				s = -1;
3453cddd28eSXin LI 
3463cddd28eSXin LI 				/*
3473cddd28eSXin LI 				 * Poll only if we have something to poll,
3483cddd28eSXin LI 				 * otherwise just go ahead and try next
3493cddd28eSXin LI 				 * address
3503cddd28eSXin LI 				 */
3513cddd28eSXin LI 				if (count == 0)
3523cddd28eSXin LI 					continue;
3532c5958aaSHajimu UMEMOTO 			}
3543cddd28eSXin LI 		} else
3553cddd28eSXin LI 			goto done;
3563cddd28eSXin LI 
3573cddd28eSXin LI 		/*
3583cddd28eSXin LI 		 * If we are at the last address, poll until a connection is
3593cddd28eSXin LI 		 * established or we failed all connection attempts.
3603cddd28eSXin LI 		 */
3613cddd28eSXin LI 		if (res->ai_next == NULL)
3623cddd28eSXin LI 			timeout = INFTIM;
3633cddd28eSXin LI 
3643cddd28eSXin LI 		/*
3653cddd28eSXin LI 		 * Poll the watched descriptors for successful connections:
3663cddd28eSXin LI 		 * if we still have more untried resolved addresses, poll only
3673cddd28eSXin LI 		 * once; otherwise, poll until all descriptors have errors,
3683cddd28eSXin LI 		 * which will be considered as ETIMEDOUT later.
3693cddd28eSXin LI 		 */
3703cddd28eSXin LI 		do {
3713cddd28eSXin LI 			int n;
3723cddd28eSXin LI 
3733cddd28eSXin LI 			n = poll(fds, i, timeout);
3743cddd28eSXin LI 			if (n == 0) {
3753cddd28eSXin LI 				/*
3763cddd28eSXin LI 				 * No event reported in time.  Try with a
3773cddd28eSXin LI 				 * smaller timeout (but cap at 2-3ms)
3783cddd28eSXin LI 				 * after a new host have been added.
3793cddd28eSXin LI 				 */
3803cddd28eSXin LI 				if (timeout >= 3)
381c24375a7SConrad Meyer 					timeout >>= 1;
3823cddd28eSXin LI 
3833cddd28eSXin LI 				break;
3843cddd28eSXin LI 			} else if (n < 0) {
3853cddd28eSXin LI 				/*
3866f4d88dfSTony Finch 				 * errno here can only be EINTR which we would
3876f4d88dfSTony Finch 				 * want to clean up and bail out.
3883cddd28eSXin LI 				 */
3893cddd28eSXin LI 				s = -1;
3903cddd28eSXin LI 				goto done;
3913cddd28eSXin LI 			}
3923cddd28eSXin LI 
3933cddd28eSXin LI 			/*
3943cddd28eSXin LI 			 * Check for the event(s) we have seen.
3953cddd28eSXin LI 			 */
3963cddd28eSXin LI 			for (j = 0; j < i; j++) {
3973cddd28eSXin LI 				if (fds[j].fd == -1 || fds[j].events == 0 ||
3983cddd28eSXin LI 				    fds[j].revents == 0)
3993cddd28eSXin LI 					continue;
4003cddd28eSXin LI 				if (fds[j].revents & ~(POLLIN | POLLOUT)) {
401c24375a7SConrad Meyer 					close(fds[j].fd);
4023cddd28eSXin LI 					fds[j].fd = -1;
4033cddd28eSXin LI 					fds[j].events = 0;
4043cddd28eSXin LI 					count--;
4053cddd28eSXin LI 					continue;
4063cddd28eSXin LI 				} else if (fds[j].revents & (POLLIN | POLLOUT)) {
4073cddd28eSXin LI 					/* Connect succeeded. */
4083cddd28eSXin LI 					s = fds[j].fd;
409c24375a7SConrad Meyer 					fds[j].fd = -1;
4103cddd28eSXin LI 
4113cddd28eSXin LI 					goto done;
4123cddd28eSXin LI 				}
4133cddd28eSXin LI 
4143cddd28eSXin LI 			}
4153cddd28eSXin LI 		} while (timeout == INFTIM && count != 0);
4163cddd28eSXin LI 	}
4173cddd28eSXin LI 
4183cddd28eSXin LI 	/* All attempts were failed */
4193cddd28eSXin LI 	s = -1;
4203cddd28eSXin LI 	if (count == 0)
4213cddd28eSXin LI 		errno = ETIMEDOUT;
4226f4d88dfSTony Finch 
42368c47fcfSConrad Meyer done:
4243cddd28eSXin LI 	/* Close all watched fds except the succeeded one */
4253cddd28eSXin LI 	for (j = 0; j < i; j++)
426c24375a7SConrad Meyer 		if (fds[j].fd != -1)
4273cddd28eSXin LI 			close(fds[j].fd);
4286f4d88dfSTony Finch 	free(fds);
42968c47fcfSConrad Meyer 	return (s);
43068c47fcfSConrad Meyer }
43168c47fcfSConrad Meyer 
43268c47fcfSConrad Meyer static void
whois(const char * query,const char * hostname,const char * hostport,int flags)433aa949e8aSMark Johnston whois(const char *query, const char *hostname, const char *hostport, int flags)
43468c47fcfSConrad Meyer {
43568c47fcfSConrad Meyer 	FILE *fp;
43668c47fcfSConrad Meyer 	struct addrinfo *hostres;
437aa949e8aSMark Johnston 	char *buf, *host, *nhost, *nport, *p;
4383997df0cSTony Finch 	int comment, s, f;
43968c47fcfSConrad Meyer 	size_t len, i;
44068c47fcfSConrad Meyer 
441aa949e8aSMark Johnston 	hostres = gethostinfo(hostname, hostport, 1);
44268c47fcfSConrad Meyer 	s = connect_to_any_host(hostres);
44368c47fcfSConrad Meyer 	if (s == -1)
44468c47fcfSConrad Meyer 		err(EX_OSERR, "connect()");
4453cddd28eSXin LI 
4463cddd28eSXin LI 	/* Restore default blocking behavior.  */
4476f4d88dfSTony Finch 	if ((f = fcntl(s, F_GETFL)) == -1)
4486f4d88dfSTony Finch 		err(EX_OSERR, "fcntl()");
44935859f3aSTony Finch 	f &= ~O_NONBLOCK;
45035859f3aSTony Finch 	if (fcntl(s, F_SETFL, f) == -1)
4513cddd28eSXin LI 		err(EX_OSERR, "fcntl()");
452a00fe97dSGarrett Wollman 
453372ab06eSKevin Lo 	fp = fdopen(s, "r+");
454372ab06eSKevin Lo 	if (fp == NULL)
4559b07a066SDag-Erling Smørgrav 		err(EX_OSERR, "fdopen()");
4566f4d88dfSTony Finch 
4576f4d88dfSTony Finch 	if (!(flags & WHOIS_SPAM_ME) &&
458f537d420STony Finch 	    (strcasecmp(hostname, DENICHOST) == 0 ||
459f537d420STony Finch 	     strcasecmp(hostname, "de" QNICHOST_TAIL) == 0)) {
460f537d420STony Finch 		const char *q;
461f537d420STony Finch 		int idn = 0;
462f537d420STony Finch 		for (q = query; *q != '\0'; q++)
463f537d420STony Finch 			if (!isascii(*q))
464f537d420STony Finch 				idn = 1;
465f537d420STony Finch 		fprintf(fp, "-T dn%s %s\r\n", idn ? "" : ",ace", query);
466f537d420STony Finch 	} else if (!(flags & WHOIS_SPAM_ME) &&
467f537d420STony Finch 		   (strcasecmp(hostname, DKNICHOST) == 0 ||
468f537d420STony Finch 		    strcasecmp(hostname, "dk" QNICHOST_TAIL) == 0))
469372ab06eSKevin Lo 		fprintf(fp, "--show-handles %s\r\n", query);
4706f4d88dfSTony Finch 	else if ((flags & WHOIS_SPAM_ME) ||
4716f4d88dfSTony Finch 		 strchr(query, ' ') != NULL)
472372ab06eSKevin Lo 		fprintf(fp, "%s\r\n", query);
4738efb5aa1STony Finch 	else if (strcasecmp(hostname, ANICHOST) == 0) {
4748efb5aa1STony Finch 		if (strncasecmp(query, "AS", 2) == 0 &&
4758efb5aa1STony Finch 		    strspn(query+2, "0123456789") == strlen(query+2))
4768efb5aa1STony Finch 			fprintf(fp, "+ a %s\r\n", query+2);
4778efb5aa1STony Finch 		else
4786f4d88dfSTony Finch 			fprintf(fp, "+ %s\r\n", query);
4798efb5aa1STony Finch 	} else if (strcasecmp(hostres->ai_canonname, VNICHOST) == 0)
4806f4d88dfSTony Finch 		fprintf(fp, "domain %s\r\n", query);
4816f4d88dfSTony Finch 	else
4826f4d88dfSTony Finch 		fprintf(fp, "%s\r\n", query);
483372ab06eSKevin Lo 	fflush(fp);
4846f4d88dfSTony Finch 
4853997df0cSTony Finch 	comment = 0;
4863997df0cSTony Finch 	if (!(flags & WHOIS_SPAM_ME) &&
4873997df0cSTony Finch 	    (strcasecmp(hostname, ANICHOST) == 0 ||
4883997df0cSTony Finch 	     strcasecmp(hostname, RNICHOST) == 0)) {
4893997df0cSTony Finch 		comment = 2;
4903997df0cSTony Finch 	}
4913997df0cSTony Finch 
4922053e9a3SAndrey A. Chernov 	nhost = NULL;
493372ab06eSKevin Lo 	while ((buf = fgetln(fp, &len)) != NULL) {
4946f4d88dfSTony Finch 		/* Nominet */
4956f4d88dfSTony Finch 		if (!(flags & WHOIS_SPAM_ME) &&
4966f4d88dfSTony Finch 		    len == 5 && strncmp(buf, "-- \r\n", 5) == 0)
4976f4d88dfSTony Finch 			break;
4983997df0cSTony Finch 		/* RIRs */
4993997df0cSTony Finch 		if (comment == 1 && buf[0] == '#')
5003997df0cSTony Finch 			break;
5013997df0cSTony Finch 		else if (comment == 2) {
5023997df0cSTony Finch 			if (strchr("#%\r\n", buf[0]) != NULL)
5033997df0cSTony Finch 				continue;
5043997df0cSTony Finch 			else
5053997df0cSTony Finch 				comment = 1;
5063997df0cSTony Finch 		}
5076f4d88dfSTony Finch 
5086f4d88dfSTony Finch 		printf("%.*s", (int)len, buf);
5099b07a066SDag-Erling Smørgrav 
51040682beaSDima Dorfman 		if ((flags & WHOIS_RECURSE) && nhost == NULL) {
51152517c0aSTony Finch 			for (i = 0; whois_referral[i].prefix != NULL; i++) {
5126f4d88dfSTony Finch 				p = buf;
5136f4d88dfSTony Finch 				SCAN(p, buf+len, *p == ' ');
5146f4d88dfSTony Finch 				if (strncasecmp(p, whois_referral[i].prefix,
51552517c0aSTony Finch 					           whois_referral[i].len) != 0)
51652517c0aSTony Finch 					continue;
5176f4d88dfSTony Finch 				p += whois_referral[i].len;
5186f4d88dfSTony Finch 				SCAN(p, buf+len, *p == ' ');
5196f4d88dfSTony Finch 				host = p;
5206f4d88dfSTony Finch 				SCAN(p, buf+len, ishost(*p));
521aa949e8aSMark Johnston 				if (p > host) {
522aa949e8aSMark Johnston 					char *pstr;
523aa949e8aSMark Johnston 
52442ab40e5SMike Barcroft 					s_asprintf(&nhost, "%.*s",
52552517c0aSTony Finch 						   (int)(p - host), host);
526aa949e8aSMark Johnston 
527aa949e8aSMark Johnston 					if (*p != ':') {
528aa949e8aSMark Johnston 						s_asprintf(&nport, "%s", port);
529aa949e8aSMark Johnston 						break;
530aa949e8aSMark Johnston 					}
531aa949e8aSMark Johnston 
532aa949e8aSMark Johnston 					pstr = ++p;
533aa949e8aSMark Johnston 					SCAN(p, buf+len, isdigit(*p));
534aa949e8aSMark Johnston 					if (p > pstr && (p - pstr) < 6) {
535aa949e8aSMark Johnston 						s_asprintf(&nport, "%.*s",
536aa949e8aSMark Johnston 						    (int)(p - pstr), pstr);
537aa949e8aSMark Johnston 						break;
538aa949e8aSMark Johnston 					}
539aa949e8aSMark Johnston 
540aa949e8aSMark Johnston 					/* Invalid port; don't recurse */
541aa949e8aSMark Johnston 					free(nhost);
542aa949e8aSMark Johnston 					nhost = NULL;
543aa949e8aSMark Johnston 				}
54496db1251SOllivier Robert 				break;
54596db1251SOllivier Robert 			}
54642e88fe6STony Finch 			for (i = 0; actually_arin[i] != NULL; i++) {
54742e88fe6STony Finch 				if (strncmp(buf, actually_arin[i], len) == 0) {
54842e88fe6STony Finch 					s_asprintf(&nhost, "%s", ANICHOST);
549aa949e8aSMark Johnston 					s_asprintf(&nport, "%s", port);
55042e88fe6STony Finch 					break;
55142e88fe6STony Finch 				}
55242e88fe6STony Finch 			}
55396db1251SOllivier Robert 		}
5546f4d88dfSTony Finch 		/* Verisign etc. */
5556f4d88dfSTony Finch 		if (!(flags & WHOIS_SPAM_ME) &&
5566f4d88dfSTony Finch 		    len >= sizeof(CHOPSPAM)-1 &&
5576f4d88dfSTony Finch 		    (strncasecmp(buf, CHOPSPAM, sizeof(CHOPSPAM)-1) == 0 ||
5586f4d88dfSTony Finch 		     strncasecmp(buf, CHOPSPAM+4, sizeof(CHOPSPAM)-5) == 0)) {
5596f4d88dfSTony Finch 			printf("\n");
5606f4d88dfSTony Finch 			break;
5616f4d88dfSTony Finch 		}
5622053e9a3SAndrey A. Chernov 	}
563372ab06eSKevin Lo 	fclose(fp);
5646f4d88dfSTony Finch 	freeaddrinfo(hostres);
5653997df0cSTony Finch 
5663997df0cSTony Finch 	f = 0;
5673997df0cSTony Finch 	for (i = 0; try_rir[i].host != NULL; i++) {
5683997df0cSTony Finch 		/* Remember visits to RIRs */
5693997df0cSTony Finch 		if (try_rir[i].loop == 0 &&
5703997df0cSTony Finch 		    strcasecmp(try_rir[i].host, hostname) == 0)
5713997df0cSTony Finch 			try_rir[i].loop = 1;
5723997df0cSTony Finch 		/* Do we need to find an alternative RIR? */
5733997df0cSTony Finch 		if (try_rir[i].loop != 0 && nhost != NULL &&
5743997df0cSTony Finch 		    strcasecmp(try_rir[i].host, nhost) == 0) {
5753997df0cSTony Finch 			free(nhost);
5763997df0cSTony Finch 			nhost = NULL;
577aa949e8aSMark Johnston 			free(nport);
578aa949e8aSMark Johnston 			nport = NULL;
5793997df0cSTony Finch 			f = 1;
5803997df0cSTony Finch 		}
5813997df0cSTony Finch 	}
5823997df0cSTony Finch 	if (f) {
5833997df0cSTony Finch 		/* Find a replacement RIR */
5843997df0cSTony Finch 		for (i = 0; try_rir[i].host != NULL; i++) {
5853997df0cSTony Finch 			if (try_rir[i].loop == 0) {
586aa949e8aSMark Johnston 				s_asprintf(&nhost, "%s", try_rir[i].host);
587aa949e8aSMark Johnston 				s_asprintf(&nport, "%s", port);
5883997df0cSTony Finch 				break;
5893997df0cSTony Finch 			}
5903997df0cSTony Finch 		}
5913997df0cSTony Finch 	}
5929b07a066SDag-Erling Smørgrav 	if (nhost != NULL) {
5933997df0cSTony Finch 		/* Ignore self-referrals */
5943997df0cSTony Finch 		if (strcasecmp(hostname, nhost) != 0) {
5953997df0cSTony Finch 			printf("# %s\n\n", nhost);
596aa949e8aSMark Johnston 			whois(query, nhost, nport, flags);
5973997df0cSTony Finch 		}
5989b07a066SDag-Erling Smørgrav 		free(nhost);
599aa949e8aSMark Johnston 		free(nport);
6002053e9a3SAndrey A. Chernov 	}
6019b50d902SRodney W. Grimes }
6029b50d902SRodney W. Grimes 
603741d304eSPhilippe Charnier static void
usage(void)6049b07a066SDag-Erling Smørgrav usage(void)
6059b50d902SRodney W. Grimes {
6069b07a066SDag-Erling Smørgrav 	fprintf(stderr,
607de80c945STony Finch 	    "usage: whois [-aAbfgiIklmPQrRS] [-c country-code | -h hostname] "
608eade81c9SMike Barcroft 	    "[-p port] name ...\n");
609a00fe97dSGarrett Wollman 	exit(EX_USAGE);
6109b50d902SRodney W. Grimes }
611