diff --git a/networking/whois.c b/networking/whois.c index bf330334a..5ef83672d 100644 --- a/networking/whois.c +++ b/networking/whois.c @@ -28,36 +28,147 @@ #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 " + * 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; } - fclose(fp); /* closes fd too */ + /* 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);