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