mirror of
https://github.com/rfc1036/whois.git
synced 2026-01-03 06:15:17 +00:00
1552 lines
39 KiB
C
1552 lines
39 KiB
C
/*
|
|
* Copyright (C) 1999-2022 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
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
/* for AI_IDN */
|
|
#define _GNU_SOURCE
|
|
|
|
/* System library */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#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>
|
|
#include <netdb.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#ifdef HAVE_GETOPT_LONG
|
|
#include <getopt.h>
|
|
#endif
|
|
#ifdef HAVE_REGEXEC
|
|
#include <regex.h>
|
|
#endif
|
|
#ifdef HAVE_LIBIDN2
|
|
#include <idn2.h>
|
|
#elif defined HAVE_LIBIDN
|
|
#include <idna.h>
|
|
#endif
|
|
|
|
/* Application-specific */
|
|
#include "version.h"
|
|
#include "data.h"
|
|
#include "whois.h"
|
|
#include "utils.h"
|
|
|
|
#ifdef HAVE_ICONV
|
|
#include "simple_recode.h"
|
|
#else
|
|
#define recode_fputs(a, b) fputs(a, b)
|
|
#endif
|
|
|
|
/* hack */
|
|
#define malloc(s) NOFAIL(malloc(s))
|
|
#define realloc(p, s) NOFAIL(realloc(p, s))
|
|
#ifdef strdup
|
|
#undef strdup
|
|
#define strdup(s) NOFAIL(__strdup(s))
|
|
#else
|
|
#define strdup(s) NOFAIL(strdup(s))
|
|
#endif
|
|
|
|
/* Global variables */
|
|
int sockfd, verb = 0, no_recursion = 0;
|
|
|
|
#ifdef ALWAYS_HIDE_DISCL
|
|
int hide_discl = HIDE_NOT_STARTED;
|
|
#else
|
|
int hide_discl = HIDE_DISABLED;
|
|
#endif
|
|
|
|
const char *client_tag = IDSTRING;
|
|
|
|
#ifndef HAVE_GETOPT_LONG
|
|
extern char *optarg;
|
|
extern int optind;
|
|
#endif
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
#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 },
|
|
{"no-recursion", no_argument, NULL, 4 },
|
|
{"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;
|
|
|
|
#ifdef ENABLE_NLS
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
|
|
textdomain(NLS_CAT_NAME);
|
|
#endif
|
|
|
|
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:IKlLmMp:q:rRs:t:T:v:V:x",
|
|
longopts, &longindex)) > 0) {
|
|
/* RIPE flags */
|
|
if (strchr(ripeflags, ch)) {
|
|
if (strlen(fstring) + 3 > fstringlen) {
|
|
fstringlen += 3;
|
|
fstring = realloc(fstring, fstringlen + 1);
|
|
}
|
|
sprintf(fstring + strlen(fstring), "-%c ", ch);
|
|
continue;
|
|
}
|
|
if (strchr(ripeflagsp, ch)) {
|
|
int flaglen = 3 + strlen(optarg) + 1;
|
|
if (strlen(fstring) + flaglen > fstringlen) {
|
|
fstringlen += flaglen;
|
|
fstring = realloc(fstring, fstringlen + 1);
|
|
}
|
|
sprintf(fstring + strlen(fstring), "-%c %s ", ch, optarg);
|
|
if (ch == 't' || ch == 'v' || ch == 'q')
|
|
nopar = 1;
|
|
continue;
|
|
}
|
|
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;
|
|
case 'V':
|
|
client_tag = optarg;
|
|
break;
|
|
case 'H':
|
|
hide_discl = HIDE_NOT_STARTED; /* enable disclaimers hiding */
|
|
break;
|
|
case 'I':
|
|
server = strdup("\x0E");
|
|
break;
|
|
case 'p':
|
|
port = strdup(optarg);
|
|
break;
|
|
case 4:
|
|
no_recursion = 1;
|
|
break;
|
|
case 3:
|
|
usage(EXIT_SUCCESS);
|
|
case 2:
|
|
verb = 1;
|
|
break;
|
|
case 1:
|
|
fprintf(stdout, _("Version %s.\n\nReport bugs to %s.\n"),
|
|
VERSION, "<md+whois@linux.it>");
|
|
exit(EXIT_SUCCESS);
|
|
default:
|
|
usage(EXIT_FAILURE);
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc == 0 && !nopar) /* there is no parameter */
|
|
usage(EXIT_FAILURE);
|
|
|
|
/* On some systems realloc only works on non-NULL buffers */
|
|
/* I wish I could remember which ones they are... */
|
|
qstring = malloc(64);
|
|
*qstring = '\0';
|
|
|
|
/* parse other parameters, if any */
|
|
if (!nopar) {
|
|
int qstringlen = 0;
|
|
|
|
while (1) {
|
|
qstringlen += strlen(*argv) + 1;
|
|
qstring = realloc(qstring, qstringlen + 1);
|
|
strcat(qstring, *argv++);
|
|
if (argc == 1)
|
|
break;
|
|
strcat(qstring, " ");
|
|
argc--;
|
|
}
|
|
}
|
|
|
|
signal(SIGTERM, sighandler);
|
|
signal(SIGINT, sighandler);
|
|
signal(SIGALRM, alarm_handler);
|
|
|
|
if (getenv("WHOIS_HIDE"))
|
|
hide_discl = HIDE_NOT_STARTED;
|
|
|
|
/* -v or -t or long flags have been used */
|
|
if (!server && (!*qstring || *fstring))
|
|
server = strdup("whois.ripe.net");
|
|
|
|
if (*qstring) {
|
|
char *tmp = normalize_domain(qstring);
|
|
free(qstring);
|
|
qstring = tmp;
|
|
}
|
|
|
|
#ifdef CONFIG_FILE
|
|
if (!server)
|
|
server = match_config_file(qstring);
|
|
#endif
|
|
|
|
if (!server)
|
|
server = guess_server(qstring);
|
|
|
|
ret = handle_query(server, port, qstring, fstring);
|
|
|
|
exit(ret);
|
|
}
|
|
|
|
/*
|
|
* Server may be a server name from the command line, a server name got
|
|
* from guess_server or an encoded command/message from guess_server.
|
|
* This function has multiple memory leaks.
|
|
*/
|
|
int handle_query(const char *hserver, const char *hport,
|
|
const char *query, const char *flags)
|
|
{
|
|
char *server = NULL, *port = NULL;
|
|
char *p, *query_string;
|
|
|
|
if (hport) {
|
|
server = strdup(hserver);
|
|
port = strdup(hport);
|
|
} else if (hserver[0] < ' ')
|
|
server = strdup(hserver);
|
|
else
|
|
split_server_port(hserver, &server, &port);
|
|
|
|
retry:
|
|
switch (server[0]) {
|
|
case 0:
|
|
if (!(server = getenv("WHOIS_SERVER")))
|
|
server = strdup(DEFAULTSERVER);
|
|
break;
|
|
case 1:
|
|
puts(_("This TLD has no whois server, but you can access the "
|
|
"whois database at"));
|
|
puts(server + 1);
|
|
return 1;
|
|
case 3:
|
|
puts(_("This TLD has no whois server."));
|
|
return 1;
|
|
case 5:
|
|
puts(_("No whois server is known for this kind of object."));
|
|
return 1;
|
|
case 6:
|
|
puts(_("Unknown AS number or IP network. Please upgrade this program."));
|
|
return 1;
|
|
case 4:
|
|
if (verb)
|
|
printf(_("Using server %s.\n"), server + 1);
|
|
sockfd = openconn(server + 1, NULL);
|
|
free(server);
|
|
server = query_crsnic(sockfd, query);
|
|
if (no_recursion)
|
|
server[0] = '\0';
|
|
break;
|
|
case 8:
|
|
if (verb)
|
|
printf(_("Using server %s.\n"), server + 1);
|
|
sockfd = openconn(server + 1, NULL);
|
|
free(server);
|
|
server = query_afilias(sockfd, query);
|
|
if (no_recursion)
|
|
server[0] = '\0';
|
|
break;
|
|
case 0x0A:
|
|
p = convert_6to4(query);
|
|
printf(_("\nQuerying for the IPv4 endpoint %s of a 6to4 IPv6 address.\n\n"), p);
|
|
free(server);
|
|
server = guess_server(p);
|
|
query = p;
|
|
goto retry;
|
|
case 0x0B:
|
|
p = convert_teredo(query);
|
|
printf(_("\nQuerying for the IPv4 endpoint %s of a Teredo IPv6 address.\n\n"), p);
|
|
free(server);
|
|
server = guess_server(p);
|
|
query = p;
|
|
goto retry;
|
|
case 0x0C:
|
|
p = convert_inaddr(query);
|
|
free(server);
|
|
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;
|
|
}
|
|
|
|
if (!server)
|
|
return 1;
|
|
|
|
if (*server == '\0')
|
|
return 0;
|
|
|
|
query_string = queryformat(server, flags, query);
|
|
if (verb) {
|
|
printf(_("Using server %s.\n"), server);
|
|
printf(_("Query string: \"%s\"\n\n"), query_string);
|
|
}
|
|
|
|
sockfd = openconn(server, port);
|
|
|
|
server = do_query(sockfd, query_string);
|
|
free(query_string);
|
|
|
|
/* recursion is fun */
|
|
if (!no_recursion && server && !strchr(query, ' ')) {
|
|
printf(_("\n\nFound a referral to %s.\n\n"), server);
|
|
handle_query(server, NULL, query, flags);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_FILE
|
|
const char *match_config_file(const char *s)
|
|
{
|
|
FILE *fp;
|
|
char buf[512];
|
|
static const char delim[] = " \t";
|
|
|
|
if ((fp = fopen(CONFIG_FILE, "r")) == NULL) {
|
|
if (errno != ENOENT)
|
|
err_sys("Cannot open " CONFIG_FILE);
|
|
return NULL;
|
|
}
|
|
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
|
char *p;
|
|
const char *pattern, *server;
|
|
#ifdef HAVE_REGEXEC
|
|
int i;
|
|
regex_t re;
|
|
#endif
|
|
|
|
if ((p = strpbrk(buf, "\r\n")))
|
|
*p = '\0';
|
|
|
|
p = buf;
|
|
while (*p == ' ' || *p == '\t') /* eat leading blanks */
|
|
p++;
|
|
if (!*p)
|
|
continue; /* skip empty lines */
|
|
if (*p == '#')
|
|
continue; /* skip comments */
|
|
|
|
pattern = strtok(p, delim);
|
|
server = strtok(NULL, delim);
|
|
if (!pattern || !server)
|
|
err_quit(_("Cannot parse this line: %s"), p);
|
|
p = strtok(NULL, delim);
|
|
if (p)
|
|
err_quit(_("Cannot parse this line: %s"), p);
|
|
|
|
#ifdef HAVE_REGEXEC
|
|
i = regcomp(&re, pattern, REG_EXTENDED | REG_ICASE | REG_NOSUB);
|
|
if (i != 0) {
|
|
char m[1024];
|
|
regerror(i, &re, m, sizeof(m));
|
|
err_quit("Invalid regular expression '%s': %s", pattern, m);
|
|
}
|
|
|
|
i = regexec(&re, s, 0, NULL, 0);
|
|
if (i == 0) {
|
|
regfree(&re);
|
|
fclose(fp);
|
|
return strdup(server);
|
|
}
|
|
if (i != REG_NOMATCH) {
|
|
char m[1024];
|
|
regerror(i, &re, m, sizeof(m));
|
|
err_quit("regexec: %s", m);
|
|
}
|
|
regfree(&re);
|
|
#else
|
|
if (endstrcaseeq(s, pattern)) {
|
|
fclose(fp);
|
|
return strdup(server);
|
|
}
|
|
#endif
|
|
}
|
|
fclose(fp);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/* Parses an user-supplied string and tries to guess the right whois server.
|
|
* Returns a dynamically allocated buffer.
|
|
*/
|
|
char *guess_server(const char *s)
|
|
{
|
|
unsigned long ip, as32;
|
|
unsigned int i;
|
|
const char *colon, *tld;
|
|
|
|
/* IPv6 address */
|
|
if ((colon = strchr(s, ':'))) {
|
|
unsigned long v6prefix, v6net;
|
|
|
|
/* RPSL hierarchical objects */
|
|
if (strncaseeq(s, "as", 2)) {
|
|
if (isasciidigit(s[2]))
|
|
return strdup(whereas(atol(s + 2)));
|
|
else
|
|
return strdup("");
|
|
}
|
|
|
|
v6prefix = strtol(s, NULL, 16);
|
|
|
|
if (v6prefix == 0)
|
|
return strdup("\x05"); /* unknown */
|
|
|
|
v6net = (v6prefix << 16) + strtol(colon + 1, NULL, 16);/* second u16 */
|
|
|
|
for (i = 0; ip6_assign[i].serv; i++) {
|
|
if ((v6net & (~0UL << (32 - ip6_assign[i].masklen)))
|
|
== ip6_assign[i].net)
|
|
return strdup(ip6_assign[i].serv);
|
|
}
|
|
|
|
return strdup("\x06"); /* unknown allocation */
|
|
}
|
|
|
|
/* email address */
|
|
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 */
|
|
(isasciidigit(s[2]) || s[2] == ' '))
|
|
return strdup(whereas(atol(s + 2)));
|
|
if (*s == '!') /* NSI NIC handle */
|
|
return strdup("whois.networksolutions.com");
|
|
else
|
|
return strdup("\x05"); /* probably a unknown kind of nic handle */
|
|
}
|
|
|
|
/* ASN32? */
|
|
if (strncaseeq(s, "as", 2) && s[2] && (as32 = asn32_to_long(s + 2)) != 0)
|
|
return strdup(whereas32(as32));
|
|
|
|
/* smells like an IP? */
|
|
if ((ip = myinet_aton(s))) {
|
|
for (i = 0; ip_assign[i].serv; i++)
|
|
if ((ip & ip_assign[i].mask) == ip_assign[i].net)
|
|
return strdup(ip_assign[i].serv);
|
|
return strdup("\x05"); /* not in the unicast IPv4 space */
|
|
}
|
|
|
|
/* check the TLDs list */
|
|
for (i = 0; tld_serv[i]; i += 2)
|
|
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("");
|
|
}
|
|
|
|
/* has dot and maybe a hyphen and it's not in tld_serv[], WTF is it? */
|
|
/* either a TLD or a NIC handle we don't know about yet */
|
|
return strdup("\x05");
|
|
}
|
|
|
|
const char *whereas32(const unsigned long asn)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; as32_assign[i].serv; i++)
|
|
if (asn >= as32_assign[i].first && asn <= as32_assign[i].last)
|
|
return as32_assign[i].serv;
|
|
return "\x06";
|
|
}
|
|
|
|
const char *whereas(const unsigned long asn)
|
|
{
|
|
int i;
|
|
|
|
if (asn > 65535)
|
|
return whereas32(asn);
|
|
|
|
for (i = 0; as_assign[i].serv; i++)
|
|
if (asn >= as_assign[i].first && asn <= as_assign[i].last)
|
|
return as_assign[i].serv;
|
|
return "\x06";
|
|
}
|
|
|
|
/*
|
|
* Construct the query string.
|
|
* Determines the server character set as a side effect.
|
|
* Returns a malloc'ed string which needs to be freed by the caller.
|
|
*/
|
|
char *queryformat(const char *server, const char *flags, const char *query)
|
|
{
|
|
char *buf;
|
|
int i, isripe = 0;
|
|
|
|
/* 64 bytes reserved for server-specific flags added later */
|
|
buf = malloc(strlen(flags) + strlen(query) + strlen(client_tag) + 64);
|
|
*buf = '\0';
|
|
|
|
for (i = 0; ripe_servers[i]; i++)
|
|
if (streq(server, ripe_servers[i])) {
|
|
sprintf(buf + strlen(buf), "-V %s ", client_tag);
|
|
isripe = 1;
|
|
break;
|
|
}
|
|
|
|
if (*flags) {
|
|
if (!isripe)
|
|
puts(_("Warning: RIPE flags used with a traditional server."));
|
|
strcat(buf, flags);
|
|
}
|
|
|
|
#ifdef HAVE_ICONV
|
|
simple_recode_iconv_close();
|
|
for (i = 0; servers_charset[i].name; i++)
|
|
if (streq(server, servers_charset[i].name)) {
|
|
simple_recode_input_charset = servers_charset[i].charset;
|
|
if (servers_charset[i].options) {
|
|
strcat(buf, servers_charset[i].options);
|
|
strcat(buf, " ");
|
|
}
|
|
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
|
|
|
|
#if defined HAVE_LIBIDN || defined HAVE_LIBIDN2
|
|
# define DENIC_PARAM_ACE ",ace"
|
|
#else
|
|
# define DENIC_PARAM_ACE ""
|
|
#endif
|
|
#ifdef HAVE_ICONV
|
|
# define DENIC_PARAM_CHARSET ""
|
|
#else
|
|
# define DENIC_PARAM_CHARSET " -C US-ASCII"
|
|
#endif
|
|
|
|
/* 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") && in_domain(query, "de"))
|
|
strcat(buf, "-T dn" DENIC_PARAM_ACE DENIC_PARAM_CHARSET " ");
|
|
else if (streq(server, "whois.dk-hostmaster.dk") && in_domain(query, "dk"))
|
|
strcat(buf, "--show-handles ");
|
|
|
|
/* mangle and add the query string */
|
|
if (!isripe && streq(server, "whois.nic.ad.jp") &&
|
|
strncaseeq(query, "AS", 2) && isasciidigit(query[2])) {
|
|
strcat(buf, "AS ");
|
|
strcat(buf, query + 2);
|
|
}
|
|
else if (!isripe && streq(server, "whois.arin.net") &&
|
|
!strrchr(query, ' ')) {
|
|
if (strncaseeq(query, "AS", 2) && isasciidigit(query[2])) {
|
|
strcat(buf, "a ");
|
|
strcat(buf, query + 2);
|
|
} else if (myinet_aton(query) || strchr(query, ':')) {
|
|
if (strchr(query, '/'))
|
|
strcat(buf, "r + = ");
|
|
else
|
|
strcat(buf, "n + ");
|
|
strcat(buf, query);
|
|
} else
|
|
strcat(buf, query);
|
|
}
|
|
else
|
|
strcat(buf, query);
|
|
|
|
/* ask for english text */
|
|
if (!isripe && (streq(server, "whois.nic.ad.jp") ||
|
|
streq(server, "whois.jprs.jp")) && japanese_locale())
|
|
strcat(buf, "/e");
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* the first parameter contains the state of this simple state machine:
|
|
* HIDE_DISABLED: hidden text finished
|
|
* HIDE_NOT_STARTED: hidden text not seen yet
|
|
* >= 0: currently hiding message hide_strings[*hiding]
|
|
*/
|
|
int hide_line(int *hiding, const char *const line)
|
|
{
|
|
int i;
|
|
|
|
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]))) {
|
|
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 */
|
|
}
|
|
}
|
|
return 0; /* don't hide this 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_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_NOT_STARTED; /* stop hiding */
|
|
return 1; /* but hide the last line */
|
|
}
|
|
}
|
|
return 1; /* we are hiding, so do it */
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/* returns a string which should be freed by the caller, or NULL */
|
|
char *do_query(const int sock, const char *query)
|
|
{
|
|
char *temp, *p, buf[2000];
|
|
FILE *fi;
|
|
int hide = hide_discl;
|
|
char *referral_server = NULL;
|
|
|
|
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)) {
|
|
/* 6bone-style referral:
|
|
* % referto: whois -h whois.arin.net -p 43 as 1
|
|
*/
|
|
if (!referral_server && strneq(buf, "% referto:", 10)) {
|
|
char nh[256], np[16], nq[1024];
|
|
|
|
if (sscanf(buf, REFERTO_FORMAT, nh, np, nq) == 3) {
|
|
/* XXX we are ignoring the new query string */
|
|
referral_server = malloc(strlen(nh) + 1 + strlen(np) + 1);
|
|
sprintf(referral_server, "%s:%s", nh, np);
|
|
}
|
|
}
|
|
|
|
/* ARIN referrals:
|
|
* ReferralServer: rwhois://rwhois.fuse.net:4321/
|
|
* ReferralServer: whois://whois.ripe.net
|
|
*/
|
|
if (!referral_server && strneq(buf, "ReferralServer:", 15)) {
|
|
if ((p = strstr(buf, "rwhois://")))
|
|
referral_server = strdup(p + 9);
|
|
else if ((p = strstr(buf, "whois://")))
|
|
referral_server = strdup(p + 8);
|
|
if (referral_server && (p = strpbrk(referral_server, "/\r\n")))
|
|
*p = '\0';
|
|
}
|
|
|
|
if (hide_line(&hide, buf))
|
|
continue;
|
|
|
|
if ((p = strpbrk(buf, "\r\n")))
|
|
*p = '\0';
|
|
recode_fputs(buf, stdout);
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
if (ferror(fi))
|
|
err_sys("fgets");
|
|
fclose(fi);
|
|
|
|
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_crsnic(const int sock, const char *query)
|
|
{
|
|
char *temp, *p, buf[2000];
|
|
FILE *fi;
|
|
int hide = hide_discl;
|
|
char *referral_server = NULL;
|
|
int state = 0;
|
|
int dotscount = 0;
|
|
|
|
temp = malloc(strlen("domain ") + strlen(query) + 2 + 1);
|
|
*temp = '\0';
|
|
|
|
/* 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");
|
|
if (write(sock, temp, strlen(temp)) < 0)
|
|
err_sys("write");
|
|
free(temp);
|
|
|
|
while (fgets(buf, sizeof(buf), fi)) {
|
|
/* If there are multiple matches only the server of the first record
|
|
is queried */
|
|
if (state == 0 && strneq(buf, " Domain Name:", 15))
|
|
state = 1;
|
|
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';
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
char *query_afilias(const int sock, const char *query)
|
|
{
|
|
char *temp, *p, buf[2000];
|
|
FILE *fi;
|
|
int hide = hide_discl;
|
|
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, "Domain Name:", 12))
|
|
state = 1;
|
|
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;
|
|
|
|
if ((p = strpbrk(buf, "\r\n")))
|
|
*p = '\0';
|
|
recode_fputs(buf, stdout);
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
if (ferror(fi))
|
|
err_sys("fgets");
|
|
fclose(fi);
|
|
|
|
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;
|
|
int timeout = 10;
|
|
#ifdef HAVE_GETADDRINFO
|
|
int err;
|
|
struct addrinfo hints, *res, *ai;
|
|
#else
|
|
struct hostent *hostinfo;
|
|
struct servent *servinfo;
|
|
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
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_ADDRCONFIG;
|
|
hints.ai_flags |= AI_IDN;
|
|
|
|
if ((err = getaddrinfo(server, port ? port : "nicname", &hints, &res))
|
|
!= 0) {
|
|
if (err == EAI_SYSTEM)
|
|
err_sys("getaddrinfo(%s)", server);
|
|
else
|
|
err_quit("getaddrinfo(%s): %s", server, gai_strerror(err));
|
|
}
|
|
|
|
for (ai = res; ai; ai = ai->ai_next) {
|
|
/* no timeout for the last address. is this a good idea? */
|
|
if (!ai->ai_next)
|
|
timeout = 0;
|
|
if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
|
|
continue; /* ignore */
|
|
if (connect_with_timeout(fd, (struct sockaddr *)ai->ai_addr,
|
|
ai->ai_addrlen, timeout) == 0)
|
|
break; /* success */
|
|
close(fd);
|
|
}
|
|
freeaddrinfo(res);
|
|
|
|
if (!ai)
|
|
err_sys("connect");
|
|
#else
|
|
if ((hostinfo = gethostbyname(server)) == NULL)
|
|
err_quit(_("Host %s not found."), server);
|
|
if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
|
|
err_sys("socket");
|
|
memset(&saddr, 0, sizeof(saddr));
|
|
saddr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
|
|
saddr.sin_family = AF_INET;
|
|
if (!port) {
|
|
saddr.sin_port = htons(43);
|
|
} else if ((saddr.sin_port = htons(atoi(port))) == 0) {
|
|
if ((servinfo = getservbyname(port, "tcp")) == NULL)
|
|
err_quit(_("%s/tcp: unknown service"), port);
|
|
saddr.sin_port = servinfo->s_port;
|
|
}
|
|
if (connect_with_timeout(fd, (struct sockaddr *)&saddr, sizeof(saddr),
|
|
timeout) < 0)
|
|
err_sys("connect");
|
|
#endif
|
|
|
|
return fd;
|
|
}
|
|
|
|
int connect_with_timeout(int fd, const struct sockaddr *addr,
|
|
socklen_t addrlen, int timeout)
|
|
{
|
|
int savedflags, rc, connect_errno, opt;
|
|
unsigned int len;
|
|
fd_set fd_w;
|
|
struct timeval tv;
|
|
|
|
if (timeout <= 0)
|
|
return connect(fd, addr, addrlen);
|
|
|
|
if ((savedflags = fcntl(fd, F_GETFL, 0)) < 0)
|
|
return -1;
|
|
|
|
/* set the socket non-blocking, so connect(2) will return immediately */
|
|
if (fcntl(fd, F_SETFL, savedflags | O_NONBLOCK) < 0)
|
|
return -1;
|
|
|
|
rc = connect(fd, addr, addrlen);
|
|
|
|
/* set the socket to block again */
|
|
connect_errno = errno;
|
|
if (fcntl(fd, F_SETFL, savedflags) < 0)
|
|
return -1;
|
|
errno = connect_errno;
|
|
|
|
if (rc == 0 || errno != EINPROGRESS)
|
|
return rc;
|
|
|
|
FD_ZERO(&fd_w);
|
|
FD_SET(fd, &fd_w);
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
/* loop until an error or the timeout has expired */
|
|
do {
|
|
rc = select(fd + 1, NULL, &fd_w, NULL, &tv);
|
|
} while (rc == -1 && errno == EINTR);
|
|
|
|
if (rc == 0) { /* timed out */
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
|
|
if (rc < 0 || rc > 1) /* select failed */
|
|
return rc;
|
|
|
|
/* rc == 1: success. check for errors */
|
|
len = sizeof(opt);
|
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &len) < 0)
|
|
return -1;
|
|
|
|
/* and report them */
|
|
if (opt != 0) {
|
|
errno = opt;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void NORETURN alarm_handler(int signum)
|
|
{
|
|
close(sockfd);
|
|
err_quit(_("Timeout."));
|
|
}
|
|
|
|
void NORETURN sighandler(int signum)
|
|
{
|
|
close(sockfd);
|
|
err_quit(_("Interrupted by signal %d..."), signum);
|
|
}
|
|
|
|
int japanese_locale(void)
|
|
{
|
|
char *lang;
|
|
|
|
lang = getenv("LC_MESSAGES");
|
|
if (lang) {
|
|
if (strneq(lang, "ja", 2))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
lang = getenv("LANG");
|
|
if (lang && strneq(lang, "ja", 2))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* check if dom ends with tld */
|
|
int endstrcaseeq(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 */
|
|
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 the query.
|
|
* Returns a malloc'ed string which needs to be freed by the caller.
|
|
*/
|
|
char *normalize_domain(const char *dom)
|
|
{
|
|
char *p, *ret;
|
|
#if defined HAVE_LIBIDN || defined HAVE_LIBIDN2
|
|
char *domain_start = NULL;
|
|
#endif
|
|
|
|
ret = strdup(dom);
|
|
/* 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--;
|
|
}
|
|
|
|
#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 == ' ')
|
|
domain_start = p + 1;
|
|
|
|
if (domain_start) {
|
|
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;
|
|
r = malloc(prefix_len + strlen(q) + 1);
|
|
strncpy(r, ret, prefix_len);
|
|
r[prefix_len] = '\0';
|
|
strcat(r, q);
|
|
|
|
free(q);
|
|
free(ret);
|
|
return r;
|
|
} 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;
|
|
}
|
|
#else
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
/* server and port have to be freed by the caller */
|
|
void split_server_port(const char *const input,
|
|
char **server, char **port)
|
|
{
|
|
char *p;
|
|
|
|
if (*input == '[' && (p = strchr(input, ']'))) { /* IPv6 */
|
|
char *s;
|
|
int len = p - input - 1;
|
|
|
|
*server = s = malloc(len + 1);
|
|
memcpy(s, input + 1, len);
|
|
*(s + len) = '\0';
|
|
|
|
p = strchr(p, ':');
|
|
if (p && *(p + 1) != '\0')
|
|
*port = strdup(p + 1); /* IPv6 + port */
|
|
} else if ((p = strchr(input, ':')) && /* IPv6, no port */
|
|
strchr(p + 1, ':')) { /* and no brackets */
|
|
*server = strdup(input);
|
|
} else if ((p = strchr(input, ':'))) { /* IPv4 + port */
|
|
char *s;
|
|
int len = p - input;
|
|
|
|
*server = s = malloc(len + 1);
|
|
memcpy(s, input, len);
|
|
*(s + len) = '\0';
|
|
|
|
p++;
|
|
if (*p != '\0')
|
|
*port = strdup(p);
|
|
} else { /* IPv4, no port */
|
|
*server = strdup(input);
|
|
}
|
|
|
|
/* change the server name to lower case */
|
|
for (p = (char *) *server; *p; p++)
|
|
*p = tolower(*p);
|
|
}
|
|
|
|
char *convert_6to4(const char *s)
|
|
{
|
|
char *new;
|
|
int items;
|
|
unsigned int a, b;
|
|
char c;
|
|
|
|
items = sscanf(s, "2002:%x:%x%c", &a, &b, &c);
|
|
|
|
if (items <= 0 || items == 2 || (items == 3 && c != ':'))
|
|
return strdup("0.0.0.0");
|
|
|
|
if (items == 1) {
|
|
items = sscanf(s, "2002:%x:%c", &a, &c);
|
|
if (items != 2 || c != ':')
|
|
return strdup("0.0.0.0");
|
|
b = 0;
|
|
}
|
|
|
|
new = malloc(sizeof("255.255.255.255"));
|
|
sprintf(new, "%u.%u.%u.%u", a >> 8, a & 0xff, b >> 8, b & 0xff);
|
|
|
|
return new;
|
|
}
|
|
|
|
char *convert_teredo(const char *s)
|
|
{
|
|
char *new;
|
|
unsigned int a, b;
|
|
|
|
if (sscanf(s, "2001:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%x:%x", &a, &b) != 2)
|
|
return strdup("0.0.0.0");
|
|
|
|
a ^= 0xffff;
|
|
b ^= 0xffff;
|
|
new = malloc(sizeof("255.255.255.255"));
|
|
sprintf(new, "%u.%u.%u.%u", a >> 8, a & 0xff, b >> 8, b & 0xff);
|
|
|
|
return new;
|
|
}
|
|
|
|
char *convert_inaddr(const char *s)
|
|
{
|
|
char *new;
|
|
char *endptr;
|
|
long int a, b = 0, c = 0;
|
|
|
|
errno = 0;
|
|
|
|
a = strtol(s, &endptr, 10);
|
|
if (errno || a < 0 || a > 255 || *endptr != '.')
|
|
return strdup("0.0.0.0");
|
|
|
|
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 (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 (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"));
|
|
sprintf(new, "%ld.%ld.%ld.0", c, b, a);
|
|
return new;
|
|
}
|
|
|
|
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;
|
|
int elements;
|
|
char junk;
|
|
|
|
if (!s)
|
|
return 0;
|
|
elements = sscanf(s, "%lu.%lu.%lu.%lu%c", &a, &b, &c, &d, &junk);
|
|
if (!(elements == 4 || (elements == 5 && junk == '/')))
|
|
return 0;
|
|
if (a > 255 || b > 255 || c > 255 || d > 255)
|
|
return 0;
|
|
return (a << 24) + (b << 16) + (c << 8) + d;
|
|
}
|
|
|
|
unsigned long asn32_to_long(const char *s)
|
|
{
|
|
unsigned long a, b;
|
|
char junk;
|
|
|
|
if (!s)
|
|
return 0;
|
|
if (sscanf(s, "%lu.%lu%c", &a, &b, &junk) != 2)
|
|
return 0;
|
|
if (a > 65535 || b > 65535)
|
|
return 0;
|
|
return (a << 16) + b;
|
|
}
|
|
|
|
int isasciidigit(const char c)
|
|
{
|
|
return (c >= '0' && c <= '9') ? 1 : 0;
|
|
}
|
|
|
|
/* http://www.ripe.net/ripe/docs/databaseref-manual.html */
|
|
|
|
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"
|
|
" --no-recursion disable recursion from registry to registrar servers\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"
|
|
));
|
|
exit(error);
|
|
}
|
|
|