1 /* vi: set sw=4 ts=4: */
2 /*
3  * whois - tiny client for the whois directory service
4  *
5  * Copyright (c) 2011 Pere Orga <gotrunks@gmail.com>
6  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7  */
8 /* TODO
9  * Add ipv6 support
10  * Add proxy support
11  */
12 //config:config WHOIS
13 //config:	bool "whois (6.3 kb)"
14 //config:	default y
15 //config:	help
16 //config:	whois is a client for the whois directory service
17 
18 //applet:IF_WHOIS(APPLET(whois, BB_DIR_USR_BIN, BB_SUID_DROP))
19 
20 //kbuild:lib-$(CONFIG_WHOIS) += whois.o
21 
22 //usage:#define whois_trivial_usage
23 //usage:       "[-i] [-h SERVER] [-p PORT] NAME..."
24 //usage:#define whois_full_usage "\n\n"
25 //usage:       "Query WHOIS info about NAME\n"
26 //usage:     "\n	-i	Show redirect results too"
27 //usage:     "\n	-h,-p	Server to query"
28 
29 #include "libbb.h"
30 
31 enum {
32 	OPT_i = (1 << 0),
33 };
34 
query(const char * host,int port,const char * domain)35 static char *query(const char *host, int port, const char *domain)
36 {
37 	int fd;
38 	FILE *fp;
39 	bool success;
40 	char *redir = NULL;
41 	const char *pfx = "";
42 	/* some .io domains reported to have very long strings in whois
43 	 * responses, 1k was not enough:
44 	 */
45 	char linebuf[2 * 1024];
46 	char *buf = NULL;
47 	unsigned bufpos = 0;
48 
49  again:
50 	printf("[Querying %s:%d '%s%s']\n", host, port, pfx, domain);
51 	fd = create_and_connect_stream_or_die(host, port);
52 	fdprintf(fd, "%s%s\r\n", pfx, domain);
53 	fp = xfdopen_for_read(fd);
54 
55 	success = 0;
56 	while (bufpos < 32*1024 /* paranoia */
57 	 && fgets(linebuf, sizeof(linebuf)-1, fp)
58 	) {
59 		unsigned len;
60 
61 		len = strcspn(linebuf, "\r\n");
62 		linebuf[len++] = '\n';
63 		linebuf[len] = '\0';
64 
65 		buf = xrealloc(buf, bufpos + len + 1);
66 		memcpy(buf + bufpos, linebuf, len);
67 		bufpos += len;
68 		buf[bufpos] = '\0';
69 
70 		if (!redir || !success) {
71 			trim(linebuf);
72 			str_tolower(linebuf);
73 			if (!success) {
74 				success = is_prefixed_with(linebuf, "domain:")
75 				       || is_prefixed_with(linebuf, "domain name:");
76 			}
77 			else if (!redir) {
78 				char *p = is_prefixed_with(linebuf, "whois server:");
79 				if (!p)
80 					p = is_prefixed_with(linebuf, "whois:");
81 				if (p)
82 					redir = xstrdup(skip_whitespace(p));
83 			}
84 		}
85 	}
86 	fclose(fp); /* closes fd too */
87 	if (!success && !pfx[0]) {
88 		/*
89 		 * Looking at /etc/jwhois.conf, some whois servers use
90 		 * "domain = DOMAIN", "DOMAIN ID <DOMAIN>"
91 		 * and "domain=DOMAIN_WITHOUT_LAST_COMPONENT"
92 		 * formats, but those are rare.
93 		 * (There are a few even more contrived ones.)
94 		 * We are trying only "domain DOMAIN", the typical one.
95 		 */
96 		pfx = "domain ";
97 		bufpos = 0;
98 		goto again;
99 	}
100 
101 	/* Success */
102 	if (redir && strcmp(redir, host) == 0) {
103 		/* Redirect to self does not count */
104 		free(redir);
105 		redir = NULL;
106 	}
107 	if (!redir || (option_mask32 & OPT_i)) {
108 		/* Output saved text */
109 		printf("[%s]\n%s", host, buf ? buf : "");
110 	}
111 	free(buf);
112 	return redir;
113 }
114 
recursive_query(const char * host,int port,const char * domain)115 static void recursive_query(const char *host, int port, const char *domain)
116 {
117 	char *free_me = NULL;
118 	char *redir;
119  again:
120 	redir = query(host, port, domain);
121 	free(free_me);
122 	if (redir) {
123 		printf("[Redirected to %s]\n", redir);
124 		host = free_me = redir;
125 		port = 43;
126 		goto again;
127 	}
128 }
129 
130 /* One of "big" whois implementations has these options:
131  *
132  * $ whois --help
133  * jwhois version 4.0, Copyright (C) 1999-2007  Free Software Foundation, Inc.
134  * -v, --verbose              verbose debug output
135  * -c FILE, --config=FILE     use FILE as configuration file
136  * -h HOST, --host=HOST       explicitly query HOST
137  * -n, --no-redirect          disable content redirection
138  * -s, --no-whoisservers      disable whois-servers.net service support
139  * -a, --raw                  disable reformatting of the query
140  * -i, --display-redirections display all redirects instead of hiding them
141  * -p PORT, --port=PORT       use port number PORT (in conjunction with HOST)
142  * -r, --rwhois               force an rwhois query to be made
143  * --rwhois-display=DISPLAY   sets the display option in rwhois queries
144  * --rwhois-limit=LIMIT       sets the maximum number of matches to return
145  *
146  * Example of its output:
147  * $ whois cnn.com
148  * [Querying whois.verisign-grs.com]
149  * [Redirected to whois.corporatedomains.com]
150  * [Querying whois.corporatedomains.com]
151  * [whois.corporatedomains.com]
152  * ...text of the reply...
153  *
154  * With -i, reply from each server is printed, after all redirects are done:
155  * [Querying whois.verisign-grs.com]
156  * [Redirected to whois.corporatedomains.com]
157  * [Querying whois.corporatedomains.com]
158  * [whois.verisign-grs.com]
159  * ...text of the reply...
160  * [whois.corporatedomains.com]
161  * ...text of the reply...
162  *
163  * With -a, no "DOMAIN" -> "domain DOMAIN" transformation is attempted.
164 
165  * With -n, the first reply is shown, redirects are not followed:
166  * [Querying whois.verisign-grs.com]
167  * [whois.verisign-grs.com]
168  * ...text of the reply...
169  */
170 
171 int whois_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
whois_main(int argc UNUSED_PARAM,char ** argv)172 int whois_main(int argc UNUSED_PARAM, char **argv)
173 {
174 	int port = 43;
175 	const char *host = "whois.iana.org";
176 
177 	getopt32(argv, "^" "ih:p:+" "\0" "-1", &host, &port);
178 	argv += optind;
179 
180 	do {
181 		recursive_query(host, port, *argv);
182 	}
183 	while (*++argv);
184 
185 	return EXIT_SUCCESS;
186 }
187