diff options
Diffstat (limited to 'whois.c')
-rw-r--r-- | whois.c | 525 |
1 files changed, 416 insertions, 109 deletions
@@ -1,5 +1,5 @@ /* - * Copyright 1999-2010 by Marco d'Itri <md@linux.it>. + * Copyright (C) 1999-2019 Marco d'Itri <md@linux.it>. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ #include "config.h" #include <string.h> #include <ctype.h> +#include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> @@ -31,12 +32,11 @@ #ifdef HAVE_REGEXEC #include <regex.h> #endif -#ifdef HAVE_LIBIDN +#ifdef HAVE_LIBIDN2 +#include <idn2.h> +#elif defined HAVE_LIBIDN #include <idna.h> #endif -#ifdef HAVE_INET_PTON -#include <arpa/inet.h> -#endif /* Application-specific */ #include "version.h" @@ -71,24 +71,63 @@ int hide_discl = HIDE_DISABLED; const char *client_tag = IDSTRING; -#ifdef HAVE_GETOPT_LONG -static const struct option longopts[] = { - {"version", no_argument, NULL, 1 }, - {"verbose", no_argument, NULL, 2 }, - {"help", no_argument, NULL, 3 }, - {"server", required_argument, NULL, 'h'}, - {"host", required_argument, NULL, 'h'}, - {"port", required_argument, NULL, 'p'}, - {NULL, 0, NULL, 0 } -}; -#else +#ifndef HAVE_GETOPT_LONG extern char *optarg; extern int optind; #endif int main(int argc, char *argv[]) { - int ch, nopar = 0, fstringlen = 64; +#ifdef HAVE_GETOPT_LONG + const struct option longopts[] = { + /* program flags */ + {"version", no_argument, NULL, 1 }, + {"verbose", no_argument, NULL, 2 }, + {"help", no_argument, NULL, 3 }, + {"server", required_argument, NULL, 'h'}, + {"host", required_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + /* long RIPE flags */ + {"exact", required_argument, NULL, 'x'}, + {"all-more", required_argument, NULL, 'M'}, + {"one-more", required_argument, NULL, 'm'}, + {"all-less", required_argument, NULL, 'L'}, + {"one-less", required_argument, NULL, 'l'}, + {"reverse-domain", required_argument, NULL, 'd'}, + {"irt", required_argument, NULL, 'c'}, + {"abuse-contact", no_argument, NULL, 'b'}, + {"brief", no_argument, NULL, 'F'}, + {"primary-keys", no_argument, NULL, 'K'}, + {"persistent-connection", no_argument, NULL, 'k'}, + {"no-referenced", no_argument, NULL, 'r'}, + {"no-filtering", no_argument, NULL, 'B'}, + {"no-grouping", no_argument, NULL, 'G'}, + {"select-types", required_argument, NULL, 'T'}, + {"all-sources", no_argument, NULL, 'a'}, + {"sources", required_argument, NULL, 's'}, + {"types", no_argument, NULL, 12 }, /* -q */ + {"ripe-version", no_argument, NULL, 12 }, /* -q */ + {"list-sources", no_argument, NULL, 12 }, /* -q */ + {"template", required_argument, NULL, 't'}, + {"ripe-verbose", required_argument, NULL, 'v'}, + /* long RIPE flags with no short equivalent */ + {"list-versions", no_argument, NULL, 10 }, + {"diff-versions", required_argument, NULL, 11 }, + {"show-version", required_argument, NULL, 11 }, + {"resource", no_argument, NULL, 10 }, + {"show-personal", no_argument, NULL, 10 }, + {"no-personal", no_argument, NULL, 10 }, + {"show-tag-info", no_argument, NULL, 10 }, + {"no-tag-info", no_argument, NULL, 10 }, + {"filter-tag-include", required_argument, NULL, 11 }, + {"filter-tag-exclude", required_argument, NULL, 11 }, + {NULL, 0, NULL, 0 } + }; + int longindex; +#endif + + int ch, nopar = 0; + size_t fstringlen = 64; const char *server = NULL, *port = NULL; char *qstring, *fstring; int ret; @@ -102,11 +141,26 @@ int main(int argc, char *argv[]) fstring = malloc(fstringlen + 1); *fstring = '\0'; + /* interface for American Fuzzy Lop */ + if (AFL_MODE) { + FILE *fp = fdopen(0, "r"); + char *buf = NULL; + size_t len = 0; + + /* read one line from stdin */ + if (getline(&buf, &len, fp) < 0) + err_sys("getline"); + fflush(fp); + /* and use it as command line arguments */ + argv = merge_args(buf, argv, &argc); + } + /* prepend options from environment */ argv = merge_args(getenv("WHOIS_OPTIONS"), argv, &argc); while ((ch = GETOPT_LONGISH(argc, argv, - "abBcdFg:Gh:Hi:KlLmMp:q:rRs:t:T:v:V:x", longopts, 0)) > 0) { + "abBcdFg:Gh:Hi:IKlLmMp:q:rRs:t:T:v:V:x", + longopts, &longindex)) > 0) { /* RIPE flags */ if (strchr(ripeflags, ch)) { if (strlen(fstring) + 3 > fstringlen) { @@ -127,8 +181,37 @@ int main(int argc, char *argv[]) nopar = 1; continue; } - /* program flags */ switch (ch) { +#ifdef HAVE_GETOPT_LONG + /* long RIPE flags with no short equivalent */ + case 12: + nopar = 1; + /* fall through */ + case 10: + { + int flaglen = 2 + strlen(longopts[longindex].name) + 1; + if (strlen(fstring) + flaglen > fstringlen) { + fstringlen += flaglen; + fstring = realloc(fstring, fstringlen + 1); + } + sprintf(fstring + strlen(fstring), "--%s ", + longopts[longindex].name); + } + break; + case 11: + { + int flaglen = 2 + strlen(longopts[longindex].name) + 1 + + strlen(optarg) + 1; + if (strlen(fstring) + flaglen > fstringlen) { + fstringlen += flaglen; + fstring = realloc(fstring, fstringlen + 1); + } + sprintf(fstring + strlen(fstring), "--%s %s ", + longopts[longindex].name, optarg); + } + break; +#endif + /* program flags */ case 'h': server = strdup(optarg); break; @@ -138,6 +221,9 @@ int main(int argc, char *argv[]) case 'H': hide_discl = HIDE_NOT_STARTED; /* enable disclaimers hiding */ break; + case 'I': + server = strdup("\x0E"); + break; case 'p': port = strdup(optarg); break; @@ -187,8 +273,8 @@ int main(int argc, char *argv[]) if (getenv("WHOIS_HIDE")) hide_discl = HIDE_NOT_STARTED; - /* -v or -t has been used */ - if (!server && !*qstring) + /* -v or -t or long flags have been used */ + if (!server && (!*qstring || *fstring)) server = strdup("whois.ripe.net"); if (*qstring) { @@ -198,11 +284,8 @@ int main(int argc, char *argv[]) } #ifdef CONFIG_FILE - if (!server) { + if (!server) server = match_config_file(qstring); - if (verb && server) - printf(_("Using server %s.\n"), server); - } #endif if (!server) @@ -286,6 +369,19 @@ int handle_query(const char *hserver, const char *hport, server = guess_server(p); free(p); goto retry; + case 0x0D: + p = convert_in6arpa(query); + free(server); + server = guess_server(p); + free(p); + goto retry; + case 0x0E: + if (verb) + printf(_("Using server %s.\n"), "whois.iana.org"); + sockfd = openconn("whois.iana.org", NULL); + free(server); + server = query_iana(sockfd, query); + break; default: break; } @@ -293,6 +389,9 @@ int handle_query(const char *hserver, const char *hport, if (!server) return 1; + if (*server == '\0') + return 0; + query_string = queryformat(server, flags, query); if (verb) { printf(_("Using server %s.\n"), server); @@ -374,7 +473,7 @@ const char *match_config_file(const char *s) } regfree(&re); #else - if (domcmp(s, pattern)) { + if (endstrcaseeq(s, pattern)) { fclose(fp); return strdup(server); } @@ -392,7 +491,7 @@ char *guess_server(const char *s) { unsigned long ip, as32; unsigned int i; - const char *colon; + const char *colon, *tld; /* IPv6 address */ if ((colon = strchr(s, ':'))) { @@ -426,6 +525,17 @@ char *guess_server(const char *s) if (strchr(s, '@')) return strdup("\x05"); + if (!strpbrk(s, ".")) { + /* if it is a TLD or a new gTLD then ask IANA */ + for (i = 0; tld_serv[i]; i += 2) + if (strcaseeq(s, tld_serv[i])) + return strdup("whois.iana.org"); + + for (i = 0; new_gtlds[i]; i++) + if (strcaseeq(s, new_gtlds[i])) + return strdup("whois.iana.org"); + } + /* no dot and no hyphen means it's a NSI NIC handle or ASN (?) */ if (!strpbrk(s, ".-")) { if (strncaseeq(s, "as", 2) && /* it's an AS */ @@ -442,12 +552,7 @@ char *guess_server(const char *s) return strdup(whereas32(as32)); /* smells like an IP? */ -#ifdef HAVE_INET_PTON - if (inet_pton(AF_INET, s, &ip) > 0) { - ip = ntohl(ip); -#else if ((ip = myinet_aton(s))) { -#endif for (i = 0; ip_assign[i].serv; i++) if ((ip & ip_assign[i].mask) == ip_assign[i].net) return strdup(ip_assign[i].serv); @@ -456,15 +561,29 @@ char *guess_server(const char *s) /* check the TLDs list */ for (i = 0; tld_serv[i]; i += 2) - if (domcmp(s, tld_serv[i])) + if (in_domain(s, tld_serv[i])) return strdup(tld_serv[i + 1]); + /* use the default server name for "new" gTLDs */ + if ((tld = is_new_gtld(s))) { + char *server = malloc(strlen("whois.nic.") + strlen(tld) + 1); + strcpy(server, "whois.nic."); + strcat(server, tld); + return server; + } + /* no dot but hyphen */ if (!strchr(s, '.')) { /* search for strings at the start of the word */ for (i = 0; nic_handles[i]; i += 2) if (strncaseeq(s, nic_handles[i], strlen(nic_handles[i]))) return strdup(nic_handles[i + 1]); + + /* search for strings at the end of the word */ + for (i = 0; nic_handles_post[i]; i += 2) + if (endstrcaseeq(s, nic_handles_post[i])) + return strdup(nic_handles_post[i + 1]); + /* it's probably a network name */ return strdup(""); } @@ -535,9 +654,15 @@ char *queryformat(const char *server, const char *flags, const char *query) } break; } + + /* Use UTF-8 by default for "new" gTLDs */ + if (!simple_recode_input_charset && /* was not in the database */ + !strchr(query, ' ') && /* and has no parameters */ + is_new_gtld(query)) /* and is a "new" gTLD: */ + simple_recode_input_charset = "utf-8"; /* then try UTF-8 */ #endif -#ifdef HAVE_LIBIDN +#if defined HAVE_LIBIDN || defined HAVE_LIBIDN2 # define DENIC_PARAM_ACE ",ace" #else # define DENIC_PARAM_ACE "" @@ -551,9 +676,9 @@ char *queryformat(const char *server, const char *flags, const char *query) /* add useful default flags if there are no flags or multiple arguments */ if (isripe) { } else if (strchr(query, ' ') || *flags) { } - else if (streq(server, "whois.denic.de") && domcmp(query, ".de")) + else if (streq(server, "whois.denic.de") && in_domain(query, "de")) strcat(buf, "-T dn" DENIC_PARAM_ACE DENIC_PARAM_CHARSET " "); - else if (streq(server, "whois.dk-hostmaster.dk") && domcmp(query, ".dk")) + else if (streq(server, "whois.dk-hostmaster.dk") && in_domain(query, "dk")) strcat(buf, "--show-handles "); /* mangle and add the query string */ @@ -593,12 +718,17 @@ int hide_line(int *hiding, const char *const line) { int i; - if (*hiding == HIDE_DISABLED) { + if (*hiding == HIDE_TO_THE_END) { + return 1; + } else if (*hiding == HIDE_DISABLED) { return 0; } else if (*hiding == HIDE_NOT_STARTED) { /* looking for smtng to hide */ for (i = 0; hide_strings[i] != NULL; i += 2) { if (strneq(line, hide_strings[i], strlen(hide_strings[i]))) { - *hiding = i; /* start hiding */ + if (hide_strings[i + 1] == NULL) + *hiding = HIDE_TO_THE_END; /* all the remaining output */ + else + *hiding = i; /* start hiding */ return 1; /* and hide this line */ } } @@ -606,13 +736,13 @@ int hide_line(int *hiding, const char *const line) } else if (*hiding > HIDE_NOT_STARTED) { /* hiding something */ if (*hide_strings[*hiding + 1] == '\0') { /*look for a blank line?*/ if (*line == '\n' || *line == '\r' || *line == '\0') { - *hiding = HIDE_DISABLED; /* stop hiding */ + *hiding = HIDE_NOT_STARTED; /* stop hiding */ return 0; /* but do not hide the blank line */ } } else { /*look for a matching string*/ if (strneq(line, hide_strings[*hiding + 1], strlen(hide_strings[*hiding + 1]))) { - *hiding = HIDE_DISABLED; /* stop hiding */ + *hiding = HIDE_NOT_STARTED; /* stop hiding */ return 1; /* but hide the last line */ } } @@ -678,7 +808,7 @@ char *do_query(const int sock, const char *query) err_sys("fgets"); fclose(fi); - if (hide > HIDE_NOT_STARTED) + if (hide > HIDE_NOT_STARTED && hide != HIDE_TO_THE_END) err_quit(_("Catastrophic error: disclaimer text has been changed.\n" "Please upgrade this program.\n")); @@ -692,10 +822,19 @@ char *query_crsnic(const int sock, const char *query) int hide = hide_discl; char *referral_server = NULL; int state = 0; + int dotscount = 0; + + temp = malloc(strlen("domain ") + strlen(query) + 2 + 1); + *temp = '\0'; - temp = malloc(strlen(query) + 1 + 2 + 1); - *temp = '='; - strcpy(temp + 1, query); + /* if this has more than one dot then it is a name server */ + for (p = (char *) query; *p != '\0'; p++) + if (*p == '.') + dotscount++; + + if (dotscount == 1 && !strpbrk(query, "=~ ")) + strcpy(temp, "domain "); + strcat(temp, query); strcat(temp, "\r\n"); fi = fdopen(sock, "r"); @@ -708,9 +847,13 @@ char *query_crsnic(const int sock, const char *query) is queried */ if (state == 0 && strneq(buf, " Domain Name:", 15)) state = 1; - if (state == 1 && strneq(buf, " Whois Server:", 16)) { - for (p = buf; *p != ':'; p++); /* skip until colon */ - for (p++; *p == ' '; p++); /* skip colon and spaces */ + if (state == 0 && strneq(buf, " Server Name:", 15)) { + referral_server = strdup(""); + state = 2; + } + if (state == 1 && strneq(buf, " Registrar WHOIS Server:", 26)) { + for (p = buf; *p != ':'; p++); /* skip until the colon */ + for (p++; *p == ' '; p++); /* skip the spaces */ referral_server = strdup(p); if ((p = strpbrk(referral_server, "\r\n "))) *p = '\0'; @@ -753,16 +896,21 @@ char *query_afilias(const int sock, const char *query) free(temp); while (fgets(buf, sizeof(buf), fi)) { + /* If multiple attributes are returned then use the first result. + This is not supposed to happen. */ if (state == 0 && strneq(buf, "Domain Name:", 12)) state = 1; - if (state == 1 && strneq(buf, "Whois Server:", 13)) { + if (state == 1 && strneq(buf, "Registrar WHOIS Server:", 23)) { for (p = buf; *p != ':'; p++); /* skip until colon */ for (p++; *p == ' '; p++); /* skip colon and spaces */ referral_server = strdup(p); if ((p = strpbrk(referral_server, "\r\n "))) *p = '\0'; + state = 2; } + /* the output must not be hidden or no data will be shown for + host records and not-existing domains */ if (hide_line(&hide, buf)) continue; @@ -776,13 +924,54 @@ char *query_afilias(const int sock, const char *query) err_sys("fgets"); fclose(fi); - if (hide > HIDE_NOT_STARTED) + if (hide > HIDE_NOT_STARTED && hide != HIDE_TO_THE_END) err_quit(_("Catastrophic error: disclaimer text has been changed.\n" "Please upgrade this program.\n")); return referral_server; } +char *query_iana(const int sock, const char *query) +{ + char *temp, *p, buf[2000]; + FILE *fi; + char *referral_server = NULL; + int state = 0; + + temp = malloc(strlen(query) + 2 + 1); + strcpy(temp, query); + strcat(temp, "\r\n"); + + fi = fdopen(sock, "r"); + if (write(sock, temp, strlen(temp)) < 0) + err_sys("write"); + free(temp); + + while (fgets(buf, sizeof(buf), fi)) { + /* If multiple attributes are returned then use the first result. + This is not supposed to happen. */ + if (state == 0 && strneq(buf, "refer:", 6)) { + for (p = buf; *p != ':'; p++); /* skip until colon */ + for (p++; *p == ' '; p++); /* skip colon and spaces */ + referral_server = strdup(p); + if ((p = strpbrk(referral_server, "\r\n "))) + *p = '\0'; + state = 2; + } + + if ((p = strpbrk(buf, "\r\n"))) + *p = '\0'; + recode_fputs(buf, stdout); + fputc('\n', stdout); + } + + if (ferror(fi)) + err_sys("fgets"); + fclose(fi); + + return referral_server; +} + int openconn(const char *server, const char *port) { int fd = -1; @@ -796,6 +985,13 @@ int openconn(const char *server, const char *port) struct sockaddr_in saddr; #endif + /* + * When using American Fuzzy Lop get the data from it using stdin + * instead of connecting to the actual whois server. + */ + if (AFL_MODE) + return dup(0); + alarm(60); #ifdef HAVE_GETADDRINFO @@ -803,9 +999,7 @@ int openconn(const char *server, const char *port) hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; -#ifdef HAVE_LIBIDN hints.ai_flags |= AI_IDN; -#endif if ((err = getaddrinfo(server, port ? port : "nicname", &hints, &res)) != 0) { @@ -862,7 +1056,7 @@ int connect_with_timeout(int fd, const struct sockaddr *addr, struct timeval tv; if (timeout <= 0) - return (connect(fd, addr, addrlen)); + return connect(fd, addr, addrlen); if ((savedflags = fcntl(fd, F_GETFL, 0)) < 0) return -1; @@ -914,19 +1108,20 @@ int connect_with_timeout(int fd, const struct sockaddr *addr, return 0; } -void alarm_handler(int signum) +void NORETURN alarm_handler(int signum) { close(sockfd); err_quit(_("Timeout.")); } -void sighandler(int signum) +void NORETURN sighandler(int signum) { close(sockfd); err_quit(_("Interrupted by signal %d..."), signum); } -int japanese_locale(void) { +int japanese_locale(void) +{ char *lang; lang = getenv("LC_MESSAGE"); @@ -943,40 +1138,88 @@ int japanese_locale(void) { } /* check if dom ends with tld */ -int domcmp(const char *dom, const char *tld) +int endstrcaseeq(const char *dom, const char *tld) { - const char *p, *q; + size_t dom_len, tld_len; + const char *p = NULL; - for (p = dom; *p; p++); p--; /* move to the last char */ - for (q = tld; *q; q++); q--; - while (p >= dom && q >= tld && tolower(*p) == *q) { /* compare backwards */ - if (q == tld) /* start of the second word? */ - return 1; - p--; q--; - } - return 0; + if ((dom_len = strlen(dom)) == 0) + return 0; + + if ((tld_len = strlen(tld)) == 0) + return 0; + + /* dom cannot be shorter than what we are looking for */ + if (tld_len > dom_len) + return 0; + + p = dom + dom_len - tld_len; + + return strcaseeq(p, tld); +} + +/* check if dom is a subdomain of tld */ +int in_domain(const char *dom, const char *tld) +{ + size_t dom_len, tld_len; + const char *p = NULL; + + if ((dom_len = strlen(dom)) == 0) + return 0; + + if ((tld_len = strlen(tld)) == 0) + return 0; + + /* dom cannot be shorter than what we are looking for */ + /* -1 to ignore dom containing just a dot and tld */ + if (tld_len >= dom_len - 1) + return 0; + + p = dom + dom_len - tld_len; + + /* fail if the character before tld is not a dot */ + if (*(p - 1) != '.') + return 0; + + return strcaseeq(p, tld); +} + +const char *is_new_gtld(const char *s) +{ + int i; + + for (i = 0; new_gtlds[i]; i++) + if (in_domain(s, new_gtlds[i])) + return new_gtlds[i]; + + return NULL; } /* * Attempt to normalize a query by removing trailing dots and whitespace, * then convert the domain to punycode. - * The function assumes that the domain is the last token of they query. + * The function assumes that the domain is the last token of the query. * Returns a malloc'ed string which needs to be freed by the caller. */ char *normalize_domain(const char *dom) { char *p, *ret; -#ifdef HAVE_LIBIDN +#if defined HAVE_LIBIDN || defined HAVE_LIBIDN2 char *domain_start = NULL; #endif ret = strdup(dom); - /* eat trailing dots and blanks */ - p = ret + strlen(ret); - for (; *p == '.' || *p == ' ' || *p == '\t' || p == ret; p--) + /* start from the last character */ + p = ret + strlen(ret) - 1; + /* and then eat trailing dots and blanks */ + while (p > ret) { + if (!(*p == '.' || *p == ' ' || *p == '\t')) + break; *p = '\0'; + p--; + } -#ifdef HAVE_LIBIDN +#if defined HAVE_LIBIDN || defined HAVE_LIBIDN2 /* find the start of the last word if there are spaces in the query */ for (p = ret; *p; p++) if (*p == ' ') @@ -986,8 +1229,13 @@ char *normalize_domain(const char *dom) char *q, *r; int prefix_len; +#ifdef HAVE_LIBIDN2 + if (idn2_lookup_ul(domain_start, &q, IDN2_NONTRANSITIONAL) != IDN2_OK) + return ret; +#else if (idna_to_ascii_lz(domain_start, &q, 0) != IDNA_SUCCESS) return ret; +#endif /* reassemble the original query in a new buffer */ prefix_len = domain_start - ret; @@ -1002,8 +1250,13 @@ char *normalize_domain(const char *dom) } else { char *q; +#ifdef HAVE_LIBIDN2 + if (idn2_lookup_ul(ret, &q, IDN2_NONTRANSITIONAL) != IDN2_OK) + return ret; +#else if (idna_to_ascii_lz(ret, &q, 0) != IDNA_SUCCESS) return ret; +#endif free(ret); return q; @@ -1015,7 +1268,8 @@ char *normalize_domain(const char *dom) /* server and port have to be freed by the caller */ void split_server_port(const char *const input, - char **server, char **port) { + char **server, char **port) +{ char *p; if (*input == '[' && (p = strchr(input, ']'))) { /* IPv6 */ @@ -1048,25 +1302,13 @@ void split_server_port(const char *const input, } /* change the server name to lower case */ - for (p = (char *) *server; *p && *p != '\0'; p++) + for (p = (char *) *server; *p; p++) *p = tolower(*p); } char *convert_6to4(const char *s) { char *new; - -#ifdef HAVE_INET_PTON - struct in6_addr ipaddr; - unsigned char *ip; - - if (inet_pton(AF_INET6, s, &ipaddr) <= 0) - return strdup("0.0.0.0"); - - ip = (unsigned char *)&ipaddr; - new = malloc(sizeof("255.255.255.255")); - sprintf(new, "%d.%d.%d.%d", *(ip + 2), *(ip + 3), *(ip + 4), *(ip + 5)); -#else int items; unsigned int a, b; char c; @@ -1084,8 +1326,7 @@ char *convert_6to4(const char *s) } new = malloc(sizeof("255.255.255.255")); - sprintf(new, "%d.%d.%d.%d", a >> 8, a & 0xff, b >> 8, b & 0xff); -#endif + sprintf(new, "%u.%u.%u.%u", a >> 8, a & 0xff, b >> 8, b & 0xff); return new; } @@ -1093,19 +1334,6 @@ char *convert_6to4(const char *s) char *convert_teredo(const char *s) { char *new; - -#ifdef HAVE_INET_PTON - struct in6_addr ipaddr; - unsigned char *ip; - - if (inet_pton(AF_INET6, s, &ipaddr) <= 0) - return strdup("0.0.0.0"); - - ip = (unsigned char *)&ipaddr; - new = malloc(sizeof("255.255.255.255")); - sprintf(new, "%d.%d.%d.%d", *(ip + 12) ^ 0xff, *(ip + 13) ^ 0xff, - *(ip + 14) ^ 0xff, *(ip + 15) ^ 0xff); -#else unsigned int a, b; if (sscanf(s, "2001:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%x:%x", &a, &b) != 2) @@ -1114,8 +1342,7 @@ char *convert_teredo(const char *s) a ^= 0xffff; b ^= 0xffff; new = malloc(sizeof("255.255.255.255")); - sprintf(new, "%d.%d.%d.%d", a >> 8, a & 0xff, b >> 8, b & 0xff); -#endif + sprintf(new, "%u.%u.%u.%u", a >> 8, a & 0xff, b >> 8, b & 0xff); return new; } @@ -1132,19 +1359,23 @@ char *convert_inaddr(const char *s) if (errno || a < 0 || a > 255 || *endptr != '.') return strdup("0.0.0.0"); - if (domcmp(endptr + 1, ".in-addr.arpa")) { + if (in_domain(endptr + 1, "in-addr.arpa")) { b = strtol(endptr + 1, &endptr, 10); /* 1.2. */ if (errno || b < 0 || b > 255 || *endptr != '.') return strdup("0.0.0.0"); - if (domcmp(endptr + 1, ".in-addr.arpa")) { + if (in_domain(endptr + 1, "in-addr.arpa")) { c = strtol(endptr + 1, &endptr, 10); /* 1.2.3. */ if (errno || c < 0 || c > 255 || *endptr != '.') return strdup("0.0.0.0"); - if (domcmp(endptr + 1, ".in-addr.arpa")) + if (in_domain(endptr + 1, "in-addr.arpa")) return strdup("0.0.0.0"); + } else { + c = b; b = a; a = 0; } + } else { + c = a; a = 0; } new = malloc(sizeof("255.255.255.255")); @@ -1152,7 +1383,68 @@ char *convert_inaddr(const char *s) return new; } -#ifndef HAVE_INET_PTON +char *convert_in6arpa(const char *s) +{ + char *ip, *p; + int character = 0; + int digits = 1; + + ip = malloc(40); + + p = strstr(s, ".ip6.arpa"); + if (!p || p == s) { + ip[character] = '\0'; + return ip; + } + + /* start from the first character before ".ip6.arpa" */ + p--; + + while (1) { + /* check that this is a valid digit for an IPv6 address */ + if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || + (*p >= 'A' && *p <= 'F'))) { + ip[character] = '\0'; + return ip; + } + + /* copy the digit to the IP address */ + ip[character++] = *p; + + /* stop if we have reached the beginning of the string */ + if (p == s) + break; + + /* stop if we have parsed a complete address */ + if (character == 39) + break; + + /* add the colon separator every four digits */ + if ((digits++ % 4) == 0) + ip[character++] = ':'; + + /* go to the precedent character and abort if it is not a dot */ + p--; + if (*p != '.') { + ip[character] = '\0'; + return ip; + } + + /* abort if the string starts with the dot */ + if (p == s) { + ip[character] = '\0'; + return ip; + } + + /* go to the precedent character and continue */ + p--; + } + + /* terminate the string */ + ip[character] = '\0'; + return ip; +} + unsigned long myinet_aton(const char *s) { unsigned long a, b, c, d; @@ -1168,7 +1460,6 @@ unsigned long myinet_aton(const char *s) return 0; return (a << 24) + (b << 16) + (c << 8) + d; } -#endif unsigned long asn32_to_long(const char *s) { @@ -1184,43 +1475,59 @@ unsigned long asn32_to_long(const char *s) return (a << 16) + b; } -int isasciidigit(const char c) { +int isasciidigit(const char c) +{ return (c >= '0' && c <= '9') ? 1 : 0; } /* http://www.ripe.net/ripe/docs/databaseref-manual.html */ -void usage(int error) +void NORETURN usage(int error) { fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( "Usage: whois [OPTION]... OBJECT...\n\n" "-h HOST, --host HOST connect to server HOST\n" "-p PORT, --port PORT connect to PORT\n" +"-I query whois.iana.org and follow its referral\n" "-H hide legal disclaimers\n" + )); + fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( " --verbose explain what is being done\n" " --help display this help and exit\n" " --version output version information and exit\n" "\n" + )); + fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( "These flags are supported by whois.ripe.net and some RIPE-like servers:\n" "-l find the one level less specific match\n" "-L find all levels less specific matches\n" "-m find all one level more specific matches\n" "-M find all levels of more specific matches\n" + )); + fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( "-c find the smallest match containing a mnt-irt attribute\n" "-x exact match\n" "-b return brief IP address ranges with abuse contact\n" + )); + fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( "-B turn off object filtering (show email addresses)\n" "-G turn off grouping of associated objects\n" "-d return DNS reverse delegation objects too\n" + )); + fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( "-i ATTR[,ATTR]... do an inverse look-up for specified ATTRibutes\n" "-T TYPE[,TYPE]... only look for objects of TYPE\n" "-K only primary keys are returned\n" "-r turn off recursive look-ups for contact information\n" + )); + fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( "-R force to show local copy of the domain object even\n" " if it contains referral\n" "-a also search all the mirrored databases\n" "-s SOURCE[,SOURCE]... search the database mirrored from SOURCE\n" "-g SOURCE:FIRST-LAST find updates from SOURCE from serial FIRST to LAST\n" + )); + fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _( "-t TYPE request template for object of TYPE\n" "-v TYPE request verbose template for object of TYPE\n" "-q [version|sources|types] query specified server info\n" |