summaryrefslogtreecommitdiff
path: root/whois.c
diff options
context:
space:
mode:
Diffstat (limited to 'whois.c')
-rw-r--r--whois.c525
1 files changed, 416 insertions, 109 deletions
diff --git a/whois.c b/whois.c
index e1fb9e3..608153b 100644
--- a/whois.c
+++ b/whois.c
@@ -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"