whois: make it actually work
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 6 Jul 2016 13:45:41 +0000 (15:45 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 6 Jul 2016 13:45:41 +0000 (15:45 +0200)
It was doing way too simplistic work of just querying the server,
no redirects, no query massaging. This required user to know a lot about whois,
and enter at least three queries for each host to get meaningful information.

function                                             old     new   delta
whois_main                                           209     646    +437

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/whois.c

index bf330334a2b7f1f69bb1a9d7ce5b53a60121c4d1..5ef83672da7c99e32229b263af00e36b54ae826b 100644 (file)
 
 #include "libbb.h"
 
-static void pipe_out(int fd)
+static char *query(const char *host, int port, const char *domain)
 {
+       int fd;
        FILE *fp;
-       char buf[1024];
+       bool success;
+       char *redir = NULL;
+       const char *pfx = "";
+       char linebuf[1024];
+       char *buf = NULL;
+       unsigned bufpos = 0;
 
+ again:
+       printf("[Querying %s:%d '%s%s']\n", host, port, pfx, domain);
+       fd = create_and_connect_stream_or_die(host, port);
+       success = 0;
+       fdprintf(fd, "%s%s\r\n", pfx, domain);
        fp = xfdopen_for_read(fd);
-       while (fgets(buf, sizeof(buf), fp)) {
-               char *p = strpbrk(buf, "\r\n");
-               if (p)
-                       *p = '\0';
-               puts(buf);
-       }
 
+       while (fgets(linebuf, sizeof(linebuf), fp)) {
+               unsigned len = strcspn(linebuf, "\r\n");
+               linebuf[len++] = '\n';
+
+               buf = xrealloc(buf, bufpos + len + 1);
+               memcpy(buf + bufpos, linebuf, len);
+               bufpos += len;
+
+               if (!redir || !success) {
+                       trim(linebuf);
+                       str_tolower(linebuf);
+                       if (!success) {
+                               success = is_prefixed_with(linebuf, "domain:")
+                                      || is_prefixed_with(linebuf, "domain name:");
+                       }
+                       else if (!redir) {
+                               char *p = is_prefixed_with(linebuf, "whois server:");
+                               if (!p)
+                                       p = is_prefixed_with(linebuf, "whois:");
+                               if (p)
+                                       redir = xstrdup(skip_whitespace(p));
+                       }
+               }
+       }
        fclose(fp); /* closes fd too */
+       if (!success && !pfx[0]) {
+               /*
+                * Looking at jwhois.conf, some whois servers use
+                * "domain = DOMAIN", "DOMAIN ID <DOMAIN>"
+                * and "domain=DOMAIN_WITHOUT_LAST_COMPONENT"
+                * formats, but those are rare.
+                * (There are a few even more contrived ones.)
+                * We are trying only "domain DOMAIN", the typical one.
+                */
+               pfx = "domain ";
+               bufpos = 0;
+               goto again;
+       }
+
+       /* Success */
+       if (redir && strcmp(redir, host) == 0) {
+               /* Redirect to self does not count */
+               free(redir);
+               redir = NULL;
+       }
+       if (!redir) {
+               /* Output saved text */
+               printf("[%s]\n", host);
+               buf[bufpos] = '\0';
+               fputs(buf, stdout);
+       }
+       free(buf);
+       return redir;
 }
 
+static void recursive_query(const char *host, int port, const char *domain)
+{
+       char *free_me = NULL;
+       char *redir;
+ again:
+       redir = query(host, port, domain);
+       free(free_me);
+       if (redir) {
+               printf("[Redirected to %s]\n", redir);
+               host = free_me = redir;
+               port = 43;
+               goto again;
+       }
+}
+
+/* One of "big" whois implementations has these options:
+ *
+ * $ whois --help
+ * jwhois version 4.0, Copyright (C) 1999-2007  Free Software Foundation, Inc.
+ * -v, --verbose              verbose debug output
+ * -c FILE, --config=FILE     use FILE as configuration file
+ * -h HOST, --host=HOST       explicitly query HOST
+ * -n, --no-redirect          disable content redirection
+ * -s, --no-whoisservers      disable whois-servers.net service support
+ * -a, --raw                  disable reformatting of the query
+ * -i, --display-redirections display all redirects instead of hiding them
+ * -p PORT, --port=PORT       use port number PORT (in conjunction with HOST)
+ * -r, --rwhois               force an rwhois query to be made
+ * --rwhois-display=DISPLAY   sets the display option in rwhois queries
+ * --rwhois-limit=LIMIT       sets the maximum number of matches to return
+ *
+ * Example of its output:
+ * $ whois cnn.com
+ * [Querying whois.verisign-grs.com]
+ * [Redirected to whois.corporatedomains.com]
+ * [Querying whois.corporatedomains.com]
+ * [whois.corporatedomains.com]
+ * ...text of the reply...
+ *
+ * With -i, reply from each server is printed, after all redirects are done:
+ * [Querying whois.verisign-grs.com]
+ * [Redirected to whois.corporatedomains.com]
+ * [Querying whois.corporatedomains.com]
+ * [whois.verisign-grs.com]
+ * ...text of the reply...
+ * [whois.corporatedomains.com]
+ * ...text of the reply...
+ *
+ * With -a, no "DOMAIN" -> "domain DOMAIN" transformation is attempted.
+
+ * With -n, the first reply is shown, redirects are not followed:
+ * [Querying whois.verisign-grs.com]
+ * [whois.verisign-grs.com]
+ * ...text of the reply...
+ */
+
 int whois_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int whois_main(int argc UNUSED_PARAM, char **argv)
 {
        int port = 43;
-       const char *host = "whois-servers.net";
+       const char *host = "whois.iana.org";
 
        opt_complementary = "-1:p+";
        getopt32(argv, "h:p:", &host, &port);
-
        argv += optind;
+
        do {
-               int fd = create_and_connect_stream_or_die(host, port);
-               fdprintf(fd, "%s\r\n", *argv);
-               pipe_out(fd);
+               recursive_query(host, port, *argv);
        }
        while (*++argv);