"Fossies" - the Fresh Open Source Software Archive

Member "whois/whois.c" (16 Feb 2021, 39287 Bytes) of package /linux/privat/whois_5.5.9.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "whois.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 5.5.6_vs_5.5.8.

    1 /*
    2  * Copyright (C) 1999-2021 Marco d'Itri <md@linux.it>.
    3  *
    4  * This program is free software; you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation; either version 2 of the License, or
    7  * (at your option) any later version.
    8  */
    9 
   10 /* for AI_IDN */
   11 #define _GNU_SOURCE
   12 
   13 /* System library */
   14 #include <stdio.h>
   15 #include <stdlib.h>
   16 #include <stdarg.h>
   17 #include <unistd.h>
   18 #include "config.h"
   19 #include <string.h>
   20 #include <ctype.h>
   21 #include <sys/time.h>
   22 #include <sys/types.h>
   23 #include <sys/socket.h>
   24 #include <netinet/in.h>
   25 #include <netdb.h>
   26 #include <errno.h>
   27 #include <fcntl.h>
   28 #include <signal.h>
   29 #ifdef HAVE_GETOPT_LONG
   30 #include <getopt.h>
   31 #endif
   32 #ifdef HAVE_REGEXEC
   33 #include <regex.h>
   34 #endif
   35 #ifdef HAVE_LIBIDN2
   36 #include <idn2.h>
   37 #elif defined HAVE_LIBIDN
   38 #include <idna.h>
   39 #endif
   40 
   41 /* Application-specific */
   42 #include "version.h"
   43 #include "data.h"
   44 #include "whois.h"
   45 #include "utils.h"
   46 
   47 #ifdef HAVE_ICONV
   48 #include "simple_recode.h"
   49 #else
   50 #define recode_fputs(a, b) fputs(a, b)
   51 #endif
   52 
   53 /* hack */
   54 #define malloc(s) NOFAIL(malloc(s))
   55 #define realloc(p, s) NOFAIL(realloc(p, s))
   56 #ifdef strdup
   57 #undef strdup
   58 #define strdup(s) NOFAIL(__strdup(s))
   59 #else
   60 #define strdup(s) NOFAIL(strdup(s))
   61 #endif
   62 
   63 /* Global variables */
   64 int sockfd, verb = 0;
   65 
   66 #ifdef ALWAYS_HIDE_DISCL
   67 int hide_discl = HIDE_NOT_STARTED;
   68 #else
   69 int hide_discl = HIDE_DISABLED;
   70 #endif
   71 
   72 const char *client_tag = IDSTRING;
   73 
   74 #ifndef HAVE_GETOPT_LONG
   75 extern char *optarg;
   76 extern int optind;
   77 #endif
   78 
   79 int main(int argc, char *argv[])
   80 {
   81 #ifdef HAVE_GETOPT_LONG
   82     const struct option longopts[] = {
   83     /* program flags */
   84     {"version",     no_argument,        NULL, 1  },
   85     {"verbose",     no_argument,        NULL, 2  },
   86     {"help",        no_argument,        NULL, 3  },
   87     {"server",      required_argument,  NULL, 'h'},
   88     {"host",        required_argument,  NULL, 'h'},
   89     {"port",        required_argument,  NULL, 'p'},
   90     /* long RIPE flags */
   91     {"exact",       required_argument,  NULL, 'x'},
   92     {"all-more",        required_argument,  NULL, 'M'},
   93     {"one-more",        required_argument,  NULL, 'm'},
   94     {"all-less",        required_argument,  NULL, 'L'},
   95     {"one-less",        required_argument,  NULL, 'l'},
   96     {"reverse-domain",  required_argument,  NULL, 'd'},
   97     {"irt",         required_argument,  NULL, 'c'},
   98     {"abuse-contact",   no_argument,        NULL, 'b'},
   99     {"brief",       no_argument,        NULL, 'F'},
  100     {"primary-keys",    no_argument,        NULL, 'K'},
  101     {"persistent-connection", no_argument,      NULL, 'k'},
  102     {"no-referenced",   no_argument,        NULL, 'r'},
  103     {"no-filtering",    no_argument,        NULL, 'B'},
  104     {"no-grouping",     no_argument,        NULL, 'G'},
  105     {"select-types",    required_argument,  NULL, 'T'},
  106     {"all-sources",     no_argument,        NULL, 'a'},
  107     {"sources",     required_argument,  NULL, 's'},
  108     {"types",       no_argument,        NULL, 12 }, /* -q */
  109     {"ripe-version",    no_argument,        NULL, 12 }, /* -q */
  110     {"list-sources",    no_argument,        NULL, 12 }, /* -q */
  111     {"template",        required_argument,  NULL, 't'},
  112     {"ripe-verbose",    required_argument,  NULL, 'v'},
  113     /* long RIPE flags with no short equivalent */
  114     {"list-versions",   no_argument,        NULL, 10 },
  115     {"diff-versions",   required_argument,  NULL, 11 },
  116     {"show-version",    required_argument,  NULL, 11 },
  117     {"resource",        no_argument,        NULL, 10 },
  118     {"show-personal",   no_argument,        NULL, 10 },
  119     {"no-personal",     no_argument,        NULL, 10 },
  120     {"show-tag-info",   no_argument,        NULL, 10 },
  121     {"no-tag-info",     no_argument,        NULL, 10 },
  122     {"filter-tag-include",  required_argument,  NULL, 11 },
  123     {"filter-tag-exclude",  required_argument,  NULL, 11 },
  124     {NULL,          0,          NULL, 0  }
  125     };
  126     int longindex;
  127 #endif
  128 
  129     int ch, nopar = 0;
  130     size_t fstringlen = 64;
  131     const char *server = NULL, *port = NULL;
  132     char *qstring, *fstring;
  133     int ret;
  134 
  135 #ifdef ENABLE_NLS
  136     setlocale(LC_ALL, "");
  137     bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
  138     textdomain(NLS_CAT_NAME);
  139 #endif
  140 
  141     fstring = malloc(fstringlen + 1);
  142     *fstring = '\0';
  143 
  144     /* interface for American Fuzzy Lop */
  145     if (AFL_MODE) {
  146     FILE *fp = fdopen(0, "r");
  147     char *buf = NULL;
  148     size_t len = 0;
  149 
  150     /* read one line from stdin */
  151     if (getline(&buf, &len, fp) < 0)
  152         err_sys("getline");
  153     fflush(fp);
  154     /* and use it as command line arguments */
  155     argv = merge_args(buf, argv, &argc);
  156     }
  157 
  158     /* prepend options from environment */
  159     argv = merge_args(getenv("WHOIS_OPTIONS"), argv, &argc);
  160 
  161     while ((ch = GETOPT_LONGISH(argc, argv,
  162         "abBcdFg:Gh:Hi:IKlLmMp:q:rRs:t:T:v:V:x",
  163         longopts, &longindex)) > 0) {
  164     /* RIPE flags */
  165     if (strchr(ripeflags, ch)) {
  166         if (strlen(fstring) + 3 > fstringlen) {
  167         fstringlen += 3;
  168         fstring = realloc(fstring, fstringlen + 1);
  169         }
  170         sprintf(fstring + strlen(fstring), "-%c ", ch);
  171         continue;
  172     }
  173     if (strchr(ripeflagsp, ch)) {
  174         int flaglen = 3 + strlen(optarg) + 1;
  175         if (strlen(fstring) + flaglen > fstringlen) {
  176         fstringlen += flaglen;
  177         fstring = realloc(fstring, fstringlen + 1);
  178         }
  179         sprintf(fstring + strlen(fstring), "-%c %s ", ch, optarg);
  180         if (ch == 't' || ch == 'v' || ch == 'q')
  181         nopar = 1;
  182         continue;
  183     }
  184     switch (ch) {
  185 #ifdef HAVE_GETOPT_LONG
  186     /* long RIPE flags with no short equivalent */
  187     case 12:
  188         nopar = 1;
  189         /* fall through */
  190     case 10:
  191         {
  192         int flaglen = 2 + strlen(longopts[longindex].name) + 1;
  193         if (strlen(fstring) + flaglen > fstringlen) {
  194             fstringlen += flaglen;
  195             fstring = realloc(fstring, fstringlen + 1);
  196         }
  197         sprintf(fstring + strlen(fstring), "--%s ",
  198             longopts[longindex].name);
  199         }
  200         break;
  201     case 11:
  202         {
  203         int flaglen = 2 + strlen(longopts[longindex].name) + 1
  204             + strlen(optarg) + 1;
  205         if (strlen(fstring) + flaglen > fstringlen) {
  206             fstringlen += flaglen;
  207             fstring = realloc(fstring, fstringlen + 1);
  208         }
  209         sprintf(fstring + strlen(fstring), "--%s %s ",
  210             longopts[longindex].name, optarg);
  211         }
  212         break;
  213 #endif
  214     /* program flags */
  215     case 'h':
  216         server = strdup(optarg);
  217         break;
  218     case 'V':
  219         client_tag = optarg;
  220         break;
  221     case 'H':
  222         hide_discl = HIDE_NOT_STARTED;  /* enable disclaimers hiding */
  223         break;
  224     case 'I':
  225         server = strdup("\x0E");
  226         break;
  227     case 'p':
  228         port = strdup(optarg);
  229         break;
  230     case 3:
  231         usage(EXIT_SUCCESS);
  232     case 2:
  233         verb = 1;
  234         break;
  235     case 1:
  236         fprintf(stdout, _("Version %s.\n\nReport bugs to %s.\n"),
  237             VERSION, "<md+whois@linux.it>");
  238         exit(EXIT_SUCCESS);
  239     default:
  240         usage(EXIT_FAILURE);
  241     }
  242     }
  243     argc -= optind;
  244     argv += optind;
  245 
  246     if (argc == 0 && !nopar)    /* there is no parameter */
  247     usage(EXIT_FAILURE);
  248 
  249     /* On some systems realloc only works on non-NULL buffers */
  250     /* I wish I could remember which ones they are... */
  251     qstring = malloc(64);
  252     *qstring = '\0';
  253 
  254     /* parse other parameters, if any */
  255     if (!nopar) {
  256     int qstringlen = 0;
  257 
  258     while (1) {
  259         qstringlen += strlen(*argv) + 1;
  260         qstring = realloc(qstring, qstringlen + 1);
  261         strcat(qstring, *argv++);
  262         if (argc == 1)
  263         break;
  264         strcat(qstring, " ");
  265         argc--;
  266     }
  267     }
  268 
  269     signal(SIGTERM, sighandler);
  270     signal(SIGINT, sighandler);
  271     signal(SIGALRM, alarm_handler);
  272 
  273     if (getenv("WHOIS_HIDE"))
  274     hide_discl = HIDE_NOT_STARTED;
  275 
  276     /* -v or -t or long flags have been used */
  277     if (!server && (!*qstring || *fstring))
  278     server = strdup("whois.ripe.net");
  279 
  280     if (*qstring) {
  281     char *tmp = normalize_domain(qstring);
  282     free(qstring);
  283     qstring = tmp;
  284     }
  285 
  286 #ifdef CONFIG_FILE
  287     if (!server)
  288     server = match_config_file(qstring);
  289 #endif
  290 
  291     if (!server)
  292     server = guess_server(qstring);
  293 
  294     ret = handle_query(server, port, qstring, fstring);
  295 
  296     exit(ret);
  297 }
  298 
  299 /*
  300  * Server may be a server name from the command line, a server name got
  301  * from guess_server or an encoded command/message from guess_server.
  302  * This function has multiple memory leaks.
  303  */
  304 int handle_query(const char *hserver, const char *hport,
  305     const char *query, const char *flags)
  306 {
  307     char *server = NULL, *port = NULL;
  308     char *p, *query_string;
  309 
  310     if (hport) {
  311     server = strdup(hserver);
  312     port = strdup(hport);
  313     } else if (hserver[0] < ' ')
  314     server = strdup(hserver);
  315     else
  316     split_server_port(hserver, &server, &port);
  317 
  318     retry:
  319     switch (server[0]) {
  320     case 0:
  321         if (!(server = getenv("WHOIS_SERVER")))
  322         server = strdup(DEFAULTSERVER);
  323         break;
  324     case 1:
  325         puts(_("This TLD has no whois server, but you can access the "
  326             "whois database at"));
  327         puts(server + 1);
  328         return 1;
  329     case 3:
  330         puts(_("This TLD has no whois server."));
  331         return 1;
  332     case 5:
  333         puts(_("No whois server is known for this kind of object."));
  334         return 1;
  335     case 6:
  336         puts(_("Unknown AS number or IP network. Please upgrade this program."));
  337         return 1;
  338     case 4:
  339         if (verb)
  340         printf(_("Using server %s.\n"), server + 1);
  341         sockfd = openconn(server + 1, NULL);
  342         free(server);
  343         server = query_crsnic(sockfd, query);
  344         break;
  345     case 8:
  346         if (verb)
  347         printf(_("Using server %s.\n"), server + 1);
  348         sockfd = openconn(server + 1, NULL);
  349         free(server);
  350         server = query_afilias(sockfd, query);
  351         break;
  352     case 0x0A:
  353         p = convert_6to4(query);
  354         printf(_("\nQuerying for the IPv4 endpoint %s of a 6to4 IPv6 address.\n\n"), p);
  355         free(server);
  356         server = guess_server(p);
  357         query = p;
  358         goto retry;
  359     case 0x0B:
  360         p = convert_teredo(query);
  361         printf(_("\nQuerying for the IPv4 endpoint %s of a Teredo IPv6 address.\n\n"), p);
  362         free(server);
  363         server = guess_server(p);
  364         query = p;
  365         goto retry;
  366     case 0x0C:
  367         p = convert_inaddr(query);
  368         free(server);
  369         server = guess_server(p);
  370         free(p);
  371         goto retry;
  372     case 0x0D:
  373         p = convert_in6arpa(query);
  374         free(server);
  375         server = guess_server(p);
  376         free(p);
  377         goto retry;
  378     case 0x0E:
  379         if (verb)
  380         printf(_("Using server %s.\n"), "whois.iana.org");
  381         sockfd = openconn("whois.iana.org", NULL);
  382         free(server);
  383         server = query_iana(sockfd, query);
  384         break;
  385     default:
  386         break;
  387     }
  388 
  389     if (!server)
  390     return 1;
  391 
  392     if (*server == '\0')
  393     return 0;
  394 
  395     query_string = queryformat(server, flags, query);
  396     if (verb) {
  397     printf(_("Using server %s.\n"), server);
  398     printf(_("Query string: \"%s\"\n\n"), query_string);
  399     }
  400 
  401     sockfd = openconn(server, port);
  402 
  403     server = do_query(sockfd, query_string);
  404     free(query_string);
  405 
  406     /* recursion is fun */
  407     if (server && !strchr(query, ' ')) {
  408     printf(_("\n\nFound a referral to %s.\n\n"), server);
  409     handle_query(server, NULL, query, flags);
  410     }
  411 
  412     return 0;
  413 }
  414 
  415 #ifdef CONFIG_FILE
  416 const char *match_config_file(const char *s)
  417 {
  418     FILE *fp;
  419     char buf[512];
  420     static const char delim[] = " \t";
  421 
  422     if ((fp = fopen(CONFIG_FILE, "r")) == NULL) {
  423     if (errno != ENOENT)
  424         err_sys("Cannot open " CONFIG_FILE);
  425     return NULL;
  426     }
  427 
  428     while (fgets(buf, sizeof(buf), fp) != NULL) {
  429     char *p;
  430     const char *pattern, *server;
  431 #ifdef HAVE_REGEXEC
  432     int i;
  433     regex_t re;
  434 #endif
  435 
  436     if ((p = strpbrk(buf, "\r\n")))
  437         *p = '\0';
  438 
  439     p = buf;
  440     while (*p == ' ' || *p == '\t') /* eat leading blanks */
  441         p++;
  442     if (!*p)
  443         continue;       /* skip empty lines */
  444     if (*p == '#')
  445         continue;       /* skip comments */
  446 
  447     pattern = strtok(p, delim);
  448     server = strtok(NULL, delim);
  449     if (!pattern || !server)
  450         err_quit(_("Cannot parse this line: %s"), p);
  451     p = strtok(NULL, delim);
  452     if (p)
  453         err_quit(_("Cannot parse this line: %s"), p);
  454 
  455 #ifdef HAVE_REGEXEC
  456     i = regcomp(&re, pattern, REG_EXTENDED | REG_ICASE | REG_NOSUB);
  457     if (i != 0) {
  458         char m[1024];
  459         regerror(i, &re, m, sizeof(m));
  460         err_quit("Invalid regular expression '%s': %s", pattern, m);
  461     }
  462 
  463     i = regexec(&re, s, 0, NULL, 0);
  464     if (i == 0) {
  465         regfree(&re);
  466         fclose(fp);
  467         return strdup(server);
  468     }
  469     if (i != REG_NOMATCH) {
  470         char m[1024];
  471         regerror(i, &re, m, sizeof(m));
  472         err_quit("regexec: %s",  m);
  473     }
  474     regfree(&re);
  475 #else
  476     if (endstrcaseeq(s, pattern)) {
  477         fclose(fp);
  478         return strdup(server);
  479     }
  480 #endif
  481     }
  482     fclose(fp);
  483     return NULL;
  484 }
  485 #endif
  486 
  487 /* Parses an user-supplied string and tries to guess the right whois server.
  488  * Returns a dinamically allocated buffer.
  489  */
  490 char *guess_server(const char *s)
  491 {
  492     unsigned long ip, as32;
  493     unsigned int i;
  494     const char *colon, *tld;
  495 
  496     /* IPv6 address */
  497     if ((colon = strchr(s, ':'))) {
  498     unsigned long v6prefix, v6net;
  499 
  500     /* RPSL hierarchical objects */
  501     if (strncaseeq(s, "as", 2)) {
  502         if (isasciidigit(s[2]))
  503         return strdup(whereas(atol(s + 2)));
  504         else
  505         return strdup("");
  506     }
  507 
  508     v6prefix = strtol(s, NULL, 16);
  509 
  510     if (v6prefix == 0)
  511         return strdup("\x05");      /* unknown */
  512 
  513     v6net = (v6prefix << 16) + strtol(colon + 1, NULL, 16);/* second u16 */
  514 
  515     for (i = 0; ip6_assign[i].serv; i++) {
  516         if ((v6net & (~0UL << (32 - ip6_assign[i].masklen)))
  517             == ip6_assign[i].net)
  518         return strdup(ip6_assign[i].serv);
  519     }
  520 
  521     return strdup("\x06");          /* unknown allocation */
  522     }
  523 
  524     /* email address */
  525     if (strchr(s, '@'))
  526     return strdup("\x05");
  527 
  528     if (!strpbrk(s, ".")) {
  529     /* if it is a TLD or a new gTLD then ask IANA */
  530     for (i = 0; tld_serv[i]; i += 2)
  531         if (strcaseeq(s, tld_serv[i]))
  532         return strdup("whois.iana.org");
  533 
  534     for (i = 0; new_gtlds[i]; i++)
  535         if (strcaseeq(s, new_gtlds[i]))
  536         return strdup("whois.iana.org");
  537     }
  538 
  539     /* no dot and no hyphen means it's a NSI NIC handle or ASN (?) */
  540     if (!strpbrk(s, ".-")) {
  541     if (strncaseeq(s, "as", 2) &&       /* it's an AS */
  542         (isasciidigit(s[2]) || s[2] == ' '))
  543         return strdup(whereas(atol(s + 2)));
  544     if (*s == '!')  /* NSI NIC handle */
  545         return strdup("whois.networksolutions.com");
  546     else
  547         return strdup("\x05"); /* probably a unknown kind of nic handle */
  548     }
  549 
  550     /* ASN32? */
  551     if (strncaseeq(s, "as", 2) && s[2] && (as32 = asn32_to_long(s + 2)) != 0)
  552     return strdup(whereas32(as32));
  553 
  554     /* smells like an IP? */
  555     if ((ip = myinet_aton(s))) {
  556     for (i = 0; ip_assign[i].serv; i++)
  557         if ((ip & ip_assign[i].mask) == ip_assign[i].net)
  558         return strdup(ip_assign[i].serv);
  559     return strdup("\x05");      /* not in the unicast IPv4 space */
  560     }
  561 
  562     /* check the TLDs list */
  563     for (i = 0; tld_serv[i]; i += 2)
  564     if (in_domain(s, tld_serv[i]))
  565         return strdup(tld_serv[i + 1]);
  566 
  567     /* use the default server name for "new" gTLDs */
  568     if ((tld = is_new_gtld(s))) {
  569     char *server = malloc(strlen("whois.nic.") + strlen(tld) + 1);
  570     strcpy(server, "whois.nic.");
  571     strcat(server, tld);
  572     return server;
  573     }
  574 
  575     /* no dot but hyphen */
  576     if (!strchr(s, '.')) {
  577     /* search for strings at the start of the word */
  578     for (i = 0; nic_handles[i]; i += 2)
  579         if (strncaseeq(s, nic_handles[i], strlen(nic_handles[i])))
  580         return strdup(nic_handles[i + 1]);
  581 
  582     /* search for strings at the end of the word */
  583     for (i = 0; nic_handles_post[i]; i += 2)
  584         if (endstrcaseeq(s, nic_handles_post[i]))
  585         return strdup(nic_handles_post[i + 1]);
  586 
  587     /* it's probably a network name */
  588     return strdup("");
  589     }
  590 
  591     /* has dot and maybe a hyphen and it's not in tld_serv[], WTF is it? */
  592     /* either a TLD or a NIC handle we don't know about yet */
  593     return strdup("\x05");
  594 }
  595 
  596 const char *whereas32(const unsigned long asn)
  597 {
  598     int i;
  599 
  600     for (i = 0; as32_assign[i].serv; i++)
  601     if (asn >= as32_assign[i].first && asn <= as32_assign[i].last)
  602         return as32_assign[i].serv;
  603     return "\x06";
  604 }
  605 
  606 const char *whereas(const unsigned long asn)
  607 {
  608     int i;
  609 
  610     if (asn > 65535)
  611     return whereas32(asn);
  612 
  613     for (i = 0; as_assign[i].serv; i++)
  614     if (asn >= as_assign[i].first && asn <= as_assign[i].last)
  615         return as_assign[i].serv;
  616     return "\x06";
  617 }
  618 
  619 /*
  620  * Construct the query string.
  621  * Determines the server character set as a side effect.
  622  * Returns a malloc'ed string which needs to be freed by the caller.
  623  */
  624 char *queryformat(const char *server, const char *flags, const char *query)
  625 {
  626     char *buf;
  627     int i, isripe = 0;
  628 
  629     /* 64 bytes reserved for server-specific flags added later */
  630     buf = malloc(strlen(flags) + strlen(query) + strlen(client_tag) + 64);
  631     *buf = '\0';
  632 
  633     for (i = 0; ripe_servers[i]; i++)
  634     if (streq(server, ripe_servers[i])) {
  635         sprintf(buf + strlen(buf), "-V %s ", client_tag);
  636         isripe = 1;
  637         break;
  638     }
  639 
  640     if (*flags) {
  641     if (!isripe)
  642         puts(_("Warning: RIPE flags used with a traditional server."));
  643     strcat(buf, flags);
  644     }
  645 
  646 #ifdef HAVE_ICONV
  647     simple_recode_iconv_close();
  648     for (i = 0; servers_charset[i].name; i++)
  649     if (streq(server, servers_charset[i].name)) {
  650         simple_recode_input_charset = servers_charset[i].charset;
  651         if (servers_charset[i].options) {
  652         strcat(buf, servers_charset[i].options);
  653         strcat(buf, " ");
  654         }
  655         break;
  656     }
  657 
  658     /* Use UTF-8 by default for "new" gTLDs */
  659     if (!simple_recode_input_charset &&     /* was not in the database */
  660         !strchr(query, ' ') &&      /* and has no parameters */
  661         is_new_gtld(query))         /* and is a "new" gTLD: */
  662     simple_recode_input_charset = "utf-8";  /* then try UTF-8 */
  663 #endif
  664 
  665 #if defined HAVE_LIBIDN || defined HAVE_LIBIDN2
  666 # define DENIC_PARAM_ACE ",ace"
  667 #else
  668 # define DENIC_PARAM_ACE ""
  669 #endif
  670 #ifdef HAVE_ICONV
  671 # define DENIC_PARAM_CHARSET ""
  672 #else
  673 # define DENIC_PARAM_CHARSET " -C US-ASCII"
  674 #endif
  675 
  676     /* add useful default flags if there are no flags or multiple arguments */
  677     if (isripe) { }
  678     else if (strchr(query, ' ') || *flags) { }
  679     else if (streq(server, "whois.denic.de") && in_domain(query, "de"))
  680     strcat(buf, "-T dn" DENIC_PARAM_ACE DENIC_PARAM_CHARSET " ");
  681     else if (streq(server, "whois.dk-hostmaster.dk") && in_domain(query, "dk"))
  682     strcat(buf, "--show-handles ");
  683 
  684     /* mangle and add the query string */
  685     if (!isripe && streq(server, "whois.nic.ad.jp") &&
  686         strncaseeq(query, "AS", 2) && isasciidigit(query[2])) {
  687     strcat(buf, "AS ");
  688     strcat(buf, query + 2);
  689     }
  690     else if (!isripe && streq(server, "whois.arin.net") &&
  691         !strrchr(query, ' ')) {
  692     if (strncaseeq(query, "AS", 2) && isasciidigit(query[2])) {
  693         strcat(buf, "a ");
  694         strcat(buf, query + 2);
  695     } else if (myinet_aton(query) || strchr(query, ':')) {
  696         if (strchr(query, '/'))
  697         strcat(buf, "r + = ");
  698         else
  699         strcat(buf, "n + ");
  700         strcat(buf, query);
  701     } else
  702         strcat(buf, query);
  703     }
  704     else
  705     strcat(buf, query);
  706 
  707     /* ask for english text */
  708     if (!isripe && (streq(server, "whois.nic.ad.jp") ||
  709         streq(server, "whois.jprs.jp")) && japanese_locale())
  710     strcat(buf, "/e");
  711 
  712     return buf;
  713 }
  714 
  715 /* the first parameter contains the state of this simple state machine:
  716  * HIDE_DISABLED: hidden text finished
  717  * HIDE_NOT_STARTED: hidden text not seen yet
  718  * >= 0: currently hiding message hide_strings[*hiding]
  719  */
  720 int hide_line(int *hiding, const char *const line)
  721 {
  722     int i;
  723 
  724     if (*hiding == HIDE_TO_THE_END) {
  725     return 1;
  726     } else if (*hiding == HIDE_DISABLED) {
  727     return 0;
  728     } else if (*hiding == HIDE_NOT_STARTED) {   /* looking for smtng to hide */
  729     for (i = 0; hide_strings[i] != NULL; i += 2) {
  730         if (strneq(line, hide_strings[i], strlen(hide_strings[i]))) {
  731         if (hide_strings[i + 1] == NULL)
  732             *hiding = HIDE_TO_THE_END;  /* all the remaining output */
  733         else
  734             *hiding = i;        /* start hiding */
  735         return 1;           /* and hide this line */
  736         }
  737     }
  738     return 0;               /* don't hide this line */
  739     } else if (*hiding > HIDE_NOT_STARTED) {    /* hiding something */
  740     if (*hide_strings[*hiding + 1] == '\0') { /*look for a blank line?*/
  741         if (*line == '\n' || *line == '\r' || *line == '\0') {
  742         *hiding = HIDE_NOT_STARTED; /* stop hiding */
  743         return 0;       /* but do not hide the blank line */
  744         }
  745     } else {                /*look for a matching string*/
  746         if (strneq(line, hide_strings[*hiding + 1],
  747             strlen(hide_strings[*hiding + 1]))) {
  748         *hiding = HIDE_NOT_STARTED; /* stop hiding */
  749         return 1;           /* but hide the last line */
  750         }
  751     }
  752     return 1;               /* we are hiding, so do it */
  753     } else
  754     return 0;
  755 }
  756 
  757 /* returns a string which should be freed by the caller, or NULL */
  758 char *do_query(const int sock, const char *query)
  759 {
  760     char *temp, *p, buf[2000];
  761     FILE *fi;
  762     int hide = hide_discl;
  763     char *referral_server = NULL;
  764 
  765     temp = malloc(strlen(query) + 2 + 1);
  766     strcpy(temp, query);
  767     strcat(temp, "\r\n");
  768 
  769     fi = fdopen(sock, "r");
  770     if (write(sock, temp, strlen(temp)) < 0)
  771     err_sys("write");
  772     free(temp);
  773 
  774     while (fgets(buf, sizeof(buf), fi)) {
  775     /* 6bone-style referral:
  776      * % referto: whois -h whois.arin.net -p 43 as 1
  777      */
  778     if (!referral_server && strneq(buf, "% referto:", 10)) {
  779         char nh[256], np[16], nq[1024];
  780 
  781         if (sscanf(buf, REFERTO_FORMAT, nh, np, nq) == 3) {
  782         /* XXX we are ignoring the new query string */
  783         referral_server = malloc(strlen(nh) + 1 + strlen(np) + 1);
  784         sprintf(referral_server, "%s:%s", nh, np);
  785         }
  786     }
  787 
  788     /* ARIN referrals:
  789      * ReferralServer: rwhois://rwhois.fuse.net:4321/
  790      * ReferralServer: whois://whois.ripe.net
  791      */
  792     if (!referral_server && strneq(buf, "ReferralServer:", 15)) {
  793         if ((p = strstr(buf, "rwhois://")))
  794         referral_server = strdup(p + 9);
  795         else if ((p = strstr(buf, "whois://")))
  796         referral_server = strdup(p + 8);
  797         if (referral_server && (p = strpbrk(referral_server, "/\r\n")))
  798         *p = '\0';
  799     }
  800 
  801     if (hide_line(&hide, buf))
  802         continue;
  803 
  804     if ((p = strpbrk(buf, "\r\n")))
  805         *p = '\0';
  806     recode_fputs(buf, stdout);
  807     fputc('\n', stdout);
  808     }
  809 
  810     if (ferror(fi))
  811     err_sys("fgets");
  812     fclose(fi);
  813 
  814     if (hide > HIDE_NOT_STARTED && hide != HIDE_TO_THE_END)
  815     err_quit(_("Catastrophic error: disclaimer text has been changed.\n"
  816            "Please upgrade this program.\n"));
  817 
  818     return referral_server;
  819 }
  820 
  821 char *query_crsnic(const int sock, const char *query)
  822 {
  823     char *temp, *p, buf[2000];
  824     FILE *fi;
  825     int hide = hide_discl;
  826     char *referral_server = NULL;
  827     int state = 0;
  828     int dotscount = 0;
  829 
  830     temp = malloc(strlen("domain ") + strlen(query) + 2 + 1);
  831     *temp = '\0';
  832 
  833     /* if this has more than one dot then it is a name server */
  834     for (p = (char *) query; *p != '\0'; p++)
  835     if (*p == '.')
  836         dotscount++;
  837 
  838     if (dotscount == 1 && !strpbrk(query, "=~ "))
  839     strcpy(temp, "domain ");
  840     strcat(temp, query);
  841     strcat(temp, "\r\n");
  842 
  843     fi = fdopen(sock, "r");
  844     if (write(sock, temp, strlen(temp)) < 0)
  845     err_sys("write");
  846     free(temp);
  847 
  848     while (fgets(buf, sizeof(buf), fi)) {
  849     /* If there are multiple matches only the server of the first record
  850        is queried */
  851     if (state == 0 && strneq(buf, "   Domain Name:", 15))
  852         state = 1;
  853     if (state == 0 && strneq(buf, "   Server Name:", 15)) {
  854         referral_server = strdup("");
  855         state = 2;
  856     }
  857     if (state == 1 && strneq(buf, "   Registrar WHOIS Server:", 26)) {
  858         for (p = buf; *p != ':'; p++);  /* skip until the colon */
  859         for (p++; *p == ' '; p++);      /* skip the spaces */
  860         referral_server = strdup(p);
  861         if ((p = strpbrk(referral_server, "\r\n ")))
  862         *p = '\0';
  863         state = 2;
  864     }
  865 
  866     /* the output must not be hidden or no data will be shown for
  867        host records and not-existing domains */
  868     if (hide_line(&hide, buf))
  869         continue;
  870 
  871     if ((p = strpbrk(buf, "\r\n")))
  872         *p = '\0';
  873     recode_fputs(buf, stdout);
  874     fputc('\n', stdout);
  875     }
  876 
  877     if (ferror(fi))
  878     err_sys("fgets");
  879     fclose(fi);
  880 
  881     return referral_server;
  882 }
  883 
  884 char *query_afilias(const int sock, const char *query)
  885 {
  886     char *temp, *p, buf[2000];
  887     FILE *fi;
  888     int hide = hide_discl;
  889     char *referral_server = NULL;
  890     int state = 0;
  891 
  892     temp = malloc(strlen(query) + 2 + 1);
  893     strcpy(temp, query);
  894     strcat(temp, "\r\n");
  895 
  896     fi = fdopen(sock, "r");
  897     if (write(sock, temp, strlen(temp)) < 0)
  898     err_sys("write");
  899     free(temp);
  900 
  901     while (fgets(buf, sizeof(buf), fi)) {
  902     /* If multiple attributes are returned then use the first result.
  903        This is not supposed to happen. */
  904     if (state == 0 && strneq(buf, "Domain Name:", 12))
  905         state = 1;
  906     if (state == 1 && strneq(buf, "Registrar WHOIS Server:", 23)) {
  907         for (p = buf; *p != ':'; p++);  /* skip until colon */
  908         for (p++; *p == ' '; p++);      /* skip colon and spaces */
  909         referral_server = strdup(p);
  910         if ((p = strpbrk(referral_server, "\r\n ")))
  911         *p = '\0';
  912         state = 2;
  913     }
  914 
  915     /* the output must not be hidden or no data will be shown for
  916        host records and not-existing domains */
  917     if (hide_line(&hide, buf))
  918         continue;
  919 
  920     if ((p = strpbrk(buf, "\r\n")))
  921         *p = '\0';
  922     recode_fputs(buf, stdout);
  923     fputc('\n', stdout);
  924     }
  925 
  926     if (ferror(fi))
  927     err_sys("fgets");
  928     fclose(fi);
  929 
  930     if (hide > HIDE_NOT_STARTED && hide != HIDE_TO_THE_END)
  931     err_quit(_("Catastrophic error: disclaimer text has been changed.\n"
  932            "Please upgrade this program.\n"));
  933 
  934     return referral_server;
  935 }
  936 
  937 char *query_iana(const int sock, const char *query)
  938 {
  939     char *temp, *p, buf[2000];
  940     FILE *fi;
  941     char *referral_server = NULL;
  942     int state = 0;
  943 
  944     temp = malloc(strlen(query) + 2 + 1);
  945     strcpy(temp, query);
  946     strcat(temp, "\r\n");
  947 
  948     fi = fdopen(sock, "r");
  949     if (write(sock, temp, strlen(temp)) < 0)
  950     err_sys("write");
  951     free(temp);
  952 
  953     while (fgets(buf, sizeof(buf), fi)) {
  954     /* If multiple attributes are returned then use the first result.
  955        This is not supposed to happen. */
  956     if (state == 0 && strneq(buf, "refer:", 6)) {
  957         for (p = buf; *p != ':'; p++);  /* skip until colon */
  958         for (p++; *p == ' '; p++);      /* skip colon and spaces */
  959         referral_server = strdup(p);
  960         if ((p = strpbrk(referral_server, "\r\n ")))
  961         *p = '\0';
  962         state = 2;
  963     }
  964 
  965     if ((p = strpbrk(buf, "\r\n")))
  966         *p = '\0';
  967     recode_fputs(buf, stdout);
  968     fputc('\n', stdout);
  969     }
  970 
  971     if (ferror(fi))
  972     err_sys("fgets");
  973     fclose(fi);
  974 
  975     return referral_server;
  976 }
  977 
  978 int openconn(const char *server, const char *port)
  979 {
  980     int fd = -1;
  981     int timeout = 10;
  982 #ifdef HAVE_GETADDRINFO
  983     int err;
  984     struct addrinfo hints, *res, *ai;
  985 #else
  986     struct hostent *hostinfo;
  987     struct servent *servinfo;
  988     struct sockaddr_in saddr;
  989 #endif
  990 
  991     /*
  992      * When using American Fuzzy Lop get the data from it using stdin
  993      * instead of connecting to the actual whois server.
  994      */
  995     if (AFL_MODE)
  996     return dup(0);
  997 
  998     alarm(60);
  999 
 1000 #ifdef HAVE_GETADDRINFO
 1001     memset(&hints, 0, sizeof(struct addrinfo));
 1002     hints.ai_family = AF_UNSPEC;
 1003     hints.ai_socktype = SOCK_STREAM;
 1004     hints.ai_flags = AI_ADDRCONFIG;
 1005     hints.ai_flags |= AI_IDN;
 1006 
 1007     if ((err = getaddrinfo(server, port ? port : "nicname", &hints, &res))
 1008         != 0) {
 1009     if (err == EAI_SYSTEM)
 1010         err_sys("getaddrinfo(%s)", server);
 1011     else
 1012         err_quit("getaddrinfo(%s): %s", server, gai_strerror(err));
 1013     }
 1014 
 1015     for (ai = res; ai; ai = ai->ai_next) {
 1016     /* no timeout for the last address. is this a good idea? */
 1017     if (!ai->ai_next)
 1018         timeout = 0;
 1019     if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
 1020         continue;       /* ignore */
 1021     if (connect_with_timeout(fd, (struct sockaddr *)ai->ai_addr,
 1022             ai->ai_addrlen, timeout) == 0)
 1023         break;      /* success */
 1024     close(fd);
 1025     }
 1026     freeaddrinfo(res);
 1027 
 1028     if (!ai)
 1029     err_sys("connect");
 1030 #else
 1031     if ((hostinfo = gethostbyname(server)) == NULL)
 1032     err_quit(_("Host %s not found."), server);
 1033     if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
 1034     err_sys("socket");
 1035     memset(&saddr, 0, sizeof(saddr));
 1036     saddr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
 1037     saddr.sin_family = AF_INET;
 1038     if (!port) {
 1039     saddr.sin_port = htons(43);
 1040     } else if ((saddr.sin_port = htons(atoi(port))) == 0) {
 1041     if ((servinfo = getservbyname(port, "tcp")) == NULL)
 1042         err_quit(_("%s/tcp: unknown service"), port);
 1043     saddr.sin_port = servinfo->s_port;
 1044     }
 1045     if (connect_with_timeout(fd, (struct sockaddr *)&saddr, sizeof(saddr),
 1046         timeout) < 0)
 1047     err_sys("connect");
 1048 #endif
 1049 
 1050     return fd;
 1051 }
 1052 
 1053 int connect_with_timeout(int fd, const struct sockaddr *addr,
 1054     socklen_t addrlen, int timeout)
 1055 {
 1056     int savedflags, rc, connect_errno, opt;
 1057     unsigned int len;
 1058     fd_set fd_w;
 1059     struct timeval tv;
 1060 
 1061     if (timeout <= 0)
 1062     return connect(fd, addr, addrlen);
 1063 
 1064     if ((savedflags = fcntl(fd, F_GETFL, 0)) < 0)
 1065     return -1;
 1066 
 1067     /* set the socket non-blocking, so connect(2) will return immediately */
 1068     if (fcntl(fd, F_SETFL, savedflags | O_NONBLOCK) < 0)
 1069     return -1;
 1070 
 1071     rc = connect(fd, addr, addrlen);
 1072 
 1073     /* set the socket to block again */
 1074     connect_errno = errno;
 1075     if (fcntl(fd, F_SETFL, savedflags) < 0)
 1076     return -1;
 1077     errno = connect_errno;
 1078 
 1079     if (rc == 0 || errno != EINPROGRESS)
 1080     return rc;
 1081 
 1082     FD_ZERO(&fd_w);
 1083     FD_SET(fd, &fd_w);
 1084     tv.tv_sec = timeout;
 1085     tv.tv_usec = 0;
 1086 
 1087     /* loop until an error or the timeout has expired */
 1088     do {
 1089     rc = select(fd + 1, NULL, &fd_w, NULL, &tv);
 1090     } while (rc == -1 && errno == EINTR);
 1091 
 1092     if (rc == 0) {      /* timed out */
 1093     errno = ETIMEDOUT;
 1094     return -1;
 1095     }
 1096 
 1097     if (rc < 0 || rc > 1)   /* select failed */
 1098     return rc;
 1099 
 1100     /* rc == 1: success. check for errors */
 1101     len = sizeof(opt);
 1102     if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &len) < 0)
 1103     return -1;
 1104 
 1105     /* and report them */
 1106     if (opt != 0) {
 1107     errno = opt;
 1108     return -1;
 1109     }
 1110 
 1111     return 0;
 1112 }
 1113 
 1114 void NORETURN alarm_handler(int signum)
 1115 {
 1116     close(sockfd);
 1117     err_quit(_("Timeout."));
 1118 }
 1119 
 1120 void NORETURN sighandler(int signum)
 1121 {
 1122     close(sockfd);
 1123     err_quit(_("Interrupted by signal %d..."), signum);
 1124 }
 1125 
 1126 int japanese_locale(void)
 1127 {
 1128     char *lang;
 1129 
 1130     lang = getenv("LC_MESSAGE");
 1131     if (lang) {
 1132     if (strneq(lang, "ja", 2))
 1133         return 0;
 1134     return 1;
 1135     }
 1136 
 1137     lang = getenv("LANG");
 1138     if (lang && strneq(lang, "ja", 2))
 1139     return 0;
 1140     return 1;
 1141 }
 1142 
 1143 /* check if dom ends with tld */
 1144 int endstrcaseeq(const char *dom, const char *tld)
 1145 {
 1146     size_t dom_len, tld_len;
 1147     const char *p = NULL;
 1148 
 1149     if ((dom_len = strlen(dom)) == 0)
 1150     return 0;
 1151 
 1152     if ((tld_len = strlen(tld)) == 0)
 1153     return 0;
 1154 
 1155     /* dom cannot be shorter than what we are looking for */
 1156     if (tld_len > dom_len)
 1157     return 0;
 1158 
 1159     p = dom + dom_len - tld_len;
 1160 
 1161     return strcaseeq(p, tld);
 1162 }
 1163 
 1164 /* check if dom is a subdomain of tld */
 1165 int in_domain(const char *dom, const char *tld)
 1166 {
 1167     size_t dom_len, tld_len;
 1168     const char *p = NULL;
 1169 
 1170     if ((dom_len = strlen(dom)) == 0)
 1171     return 0;
 1172 
 1173     if ((tld_len = strlen(tld)) == 0)
 1174     return 0;
 1175 
 1176     /* dom cannot be shorter than what we are looking for */
 1177     /* -1 to ignore dom containing just a dot and tld */
 1178     if (tld_len >= dom_len - 1)
 1179     return 0;
 1180 
 1181     p = dom + dom_len - tld_len;
 1182 
 1183     /* fail if the character before tld is not a dot */
 1184     if (*(p - 1) != '.')
 1185     return 0;
 1186 
 1187     return strcaseeq(p, tld);
 1188 }
 1189 
 1190 const char *is_new_gtld(const char *s)
 1191 {
 1192     int i;
 1193 
 1194     for (i = 0; new_gtlds[i]; i++)
 1195     if (in_domain(s, new_gtlds[i]))
 1196         return new_gtlds[i];
 1197 
 1198     return NULL;
 1199 }
 1200 
 1201 /*
 1202  * Attempt to normalize a query by removing trailing dots and whitespace,
 1203  * then convert the domain to punycode.
 1204  * The function assumes that the domain is the last token of the query.
 1205  * Returns a malloc'ed string which needs to be freed by the caller.
 1206  */
 1207 char *normalize_domain(const char *dom)
 1208 {
 1209     char *p, *ret;
 1210 #if defined HAVE_LIBIDN || defined HAVE_LIBIDN2
 1211     char *domain_start = NULL;
 1212 #endif
 1213 
 1214     ret = strdup(dom);
 1215     /* start from the last character */
 1216     p = ret + strlen(ret) - 1;
 1217     /* and then eat trailing dots and blanks */
 1218     while (p > ret) {
 1219     if (!(*p == '.' || *p == ' ' || *p == '\t'))
 1220         break;
 1221     *p = '\0';
 1222     p--;
 1223     }
 1224 
 1225 #if defined HAVE_LIBIDN || defined HAVE_LIBIDN2
 1226     /* find the start of the last word if there are spaces in the query */
 1227     for (p = ret; *p; p++)
 1228     if (*p == ' ')
 1229         domain_start = p + 1;
 1230 
 1231     if (domain_start) {
 1232     char *q, *r;
 1233     int prefix_len;
 1234 
 1235 #ifdef HAVE_LIBIDN2
 1236     if (idn2_lookup_ul(domain_start, &q, IDN2_NONTRANSITIONAL) != IDN2_OK)
 1237         return ret;
 1238 #else
 1239     if (idna_to_ascii_lz(domain_start, &q, 0) != IDNA_SUCCESS)
 1240         return ret;
 1241 #endif
 1242 
 1243     /* reassemble the original query in a new buffer */
 1244     prefix_len = domain_start - ret;
 1245     r = malloc(prefix_len + strlen(q) + 1);
 1246     strncpy(r, ret, prefix_len);
 1247     r[prefix_len] = '\0';
 1248     strcat(r, q);
 1249 
 1250     free(q);
 1251     free(ret);
 1252     return r;
 1253     } else {
 1254     char *q;
 1255 
 1256 #ifdef HAVE_LIBIDN2
 1257     if (idn2_lookup_ul(ret, &q, IDN2_NONTRANSITIONAL) != IDN2_OK)
 1258         return ret;
 1259 #else
 1260     if (idna_to_ascii_lz(ret, &q, 0) != IDNA_SUCCESS)
 1261         return ret;
 1262 #endif
 1263 
 1264     free(ret);
 1265     return q;
 1266     }
 1267 #else
 1268     return ret;
 1269 #endif
 1270 }
 1271 
 1272 /* server and port have to be freed by the caller */
 1273 void split_server_port(const char *const input,
 1274     char **server, char **port)
 1275 {
 1276     char *p;
 1277 
 1278     if (*input == '[' && (p = strchr(input, ']'))) {    /* IPv6 */
 1279     char *s;
 1280     int len = p - input - 1;
 1281 
 1282     *server = s = malloc(len + 1);
 1283     memcpy(s, input + 1, len);
 1284     *(s + len) = '\0';
 1285 
 1286     p = strchr(p, ':');
 1287     if (p && *(p + 1) != '\0')
 1288         *port = strdup(p + 1);          /* IPv6 + port */
 1289     } else if ((p = strchr(input, ':')) &&      /* IPv6, no port */
 1290         strchr(p + 1, ':')) {           /*   and no brackets */
 1291     *server = strdup(input);
 1292     } else if ((p = strchr(input, ':'))) {      /* IPv4 + port */
 1293     char *s;
 1294     int len = p - input;
 1295 
 1296     *server = s = malloc(len + 1);
 1297     memcpy(s, input, len);
 1298     *(s + len) = '\0';
 1299 
 1300     p++;
 1301     if (*p != '\0')
 1302         *port = strdup(p);
 1303     } else {                        /* IPv4, no port */
 1304     *server = strdup(input);
 1305     }
 1306 
 1307     /* change the server name to lower case */
 1308     for (p = (char *) *server; *p; p++)
 1309     *p = tolower(*p);
 1310 }
 1311 
 1312 char *convert_6to4(const char *s)
 1313 {
 1314     char *new;
 1315     int items;
 1316     unsigned int a, b;
 1317     char c;
 1318 
 1319     items = sscanf(s, "2002:%x:%x%c", &a, &b, &c);
 1320 
 1321     if (items <= 0 || items == 2 || (items == 3 && c != ':'))
 1322     return strdup("0.0.0.0");
 1323 
 1324     if (items == 1) {
 1325     items = sscanf(s, "2002:%x:%c", &a, &c);
 1326     if (items != 2 || c != ':')
 1327         return strdup("0.0.0.0");
 1328     b = 0;
 1329     }
 1330 
 1331     new = malloc(sizeof("255.255.255.255"));
 1332     sprintf(new, "%u.%u.%u.%u", a >> 8, a & 0xff, b >> 8, b & 0xff);
 1333 
 1334     return new;
 1335 }
 1336 
 1337 char *convert_teredo(const char *s)
 1338 {
 1339     char *new;
 1340     unsigned int a, b;
 1341 
 1342     if (sscanf(s, "2001:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%x:%x", &a, &b) != 2)
 1343     return strdup("0.0.0.0");
 1344 
 1345     a ^= 0xffff;
 1346     b ^= 0xffff;
 1347     new = malloc(sizeof("255.255.255.255"));
 1348     sprintf(new, "%u.%u.%u.%u", a >> 8, a & 0xff, b >> 8, b & 0xff);
 1349 
 1350     return new;
 1351 }
 1352 
 1353 char *convert_inaddr(const char *s)
 1354 {
 1355     char *new;
 1356     char *endptr;
 1357     long int a, b = 0, c = 0;
 1358 
 1359     errno = 0;
 1360 
 1361     a = strtol(s, &endptr, 10);
 1362     if (errno || a < 0 || a > 255 || *endptr != '.')
 1363     return strdup("0.0.0.0");
 1364 
 1365     if (in_domain(endptr + 1, "in-addr.arpa")) {
 1366     b = strtol(endptr + 1, &endptr, 10);            /* 1.2. */
 1367     if (errno || b < 0 || b > 255 || *endptr != '.')
 1368         return strdup("0.0.0.0");
 1369 
 1370     if (in_domain(endptr + 1, "in-addr.arpa")) {
 1371         c = strtol(endptr + 1, &endptr, 10);        /* 1.2.3. */
 1372         if (errno || c < 0 || c > 255 || *endptr != '.')
 1373         return strdup("0.0.0.0");
 1374 
 1375         if (in_domain(endptr + 1, "in-addr.arpa"))
 1376         return strdup("0.0.0.0");
 1377     } else {
 1378         c = b; b = a; a = 0;
 1379     }
 1380     } else {
 1381     c = a; a = 0;
 1382     }
 1383 
 1384     new = malloc(sizeof("255.255.255.255"));
 1385     sprintf(new, "%ld.%ld.%ld.0", c, b, a);
 1386     return new;
 1387 }
 1388 
 1389 char *convert_in6arpa(const char *s)
 1390 {
 1391     char *ip, *p;
 1392     int character = 0;
 1393     int digits = 1;
 1394 
 1395     ip = malloc(40);
 1396 
 1397     p = strstr(s, ".ip6.arpa");
 1398     if (!p || p == s) {
 1399     ip[character] = '\0';
 1400     return ip;
 1401     }
 1402 
 1403     /* start from the first character before ".ip6.arpa" */
 1404     p--;
 1405 
 1406     while (1) {
 1407     /* check that this is a valid digit for an IPv6 address */
 1408     if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') ||
 1409           (*p >= 'A' && *p <= 'F'))) {
 1410         ip[character] = '\0';
 1411         return ip;
 1412     }
 1413 
 1414     /* copy the digit to the IP address */
 1415     ip[character++] = *p;
 1416 
 1417     /* stop if we have reached the beginning of the string */
 1418     if (p == s)
 1419         break;
 1420 
 1421     /* stop if we have parsed a complete address */
 1422     if (character == 39)
 1423         break;
 1424 
 1425     /* add the colon separator every four digits */
 1426     if ((digits++ % 4) == 0)
 1427         ip[character++] = ':';
 1428 
 1429     /* go to the precedent character and abort if it is not a dot */
 1430     p--;
 1431     if (*p != '.') {
 1432         ip[character] = '\0';
 1433         return ip;
 1434     }
 1435 
 1436     /* abort if the string starts with the dot */
 1437     if (p == s) {
 1438         ip[character] = '\0';
 1439         return ip;
 1440     }
 1441 
 1442     /* go to the precedent character and continue */
 1443     p--;
 1444     }
 1445 
 1446     /* terminate the string */
 1447     ip[character] = '\0';
 1448     return ip;
 1449 }
 1450 
 1451 unsigned long myinet_aton(const char *s)
 1452 {
 1453     unsigned long a, b, c, d;
 1454     int elements;
 1455     char junk;
 1456 
 1457     if (!s)
 1458     return 0;
 1459     elements = sscanf(s, "%lu.%lu.%lu.%lu%c", &a, &b, &c, &d, &junk);
 1460     if (!(elements == 4 || (elements == 5 && junk == '/')))
 1461     return 0;
 1462     if (a > 255 || b > 255 || c > 255 || d > 255)
 1463     return 0;
 1464     return (a << 24) + (b << 16) + (c << 8) + d;
 1465 }
 1466 
 1467 unsigned long asn32_to_long(const char *s)
 1468 {
 1469     unsigned long a, b;
 1470     char junk;
 1471 
 1472     if (!s)
 1473     return 0;
 1474     if (sscanf(s, "%lu.%lu%c", &a, &b, &junk) != 2)
 1475     return 0;
 1476     if (a > 65535 || b > 65535)
 1477     return 0;
 1478     return (a << 16) + b;
 1479 }
 1480 
 1481 int isasciidigit(const char c)
 1482 {
 1483     return (c >= '0' && c <= '9') ? 1 : 0;
 1484 }
 1485 
 1486 /* http://www.ripe.net/ripe/docs/databaseref-manual.html */
 1487 
 1488 void NORETURN usage(int error)
 1489 {
 1490     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1491 "Usage: whois [OPTION]... OBJECT...\n\n"
 1492 "-h HOST, --host HOST   connect to server HOST\n"
 1493 "-p PORT, --port PORT   connect to PORT\n"
 1494 "-I                     query whois.iana.org and follow its referral\n"
 1495 "-H                     hide legal disclaimers\n"
 1496     ));
 1497     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1498 "      --verbose        explain what is being done\n"
 1499 "      --help           display this help and exit\n"
 1500 "      --version        output version information and exit\n"
 1501 "\n"
 1502     ));
 1503     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1504 "These flags are supported by whois.ripe.net and some RIPE-like servers:\n"
 1505 "-l                     find the one level less specific match\n"
 1506 "-L                     find all levels less specific matches\n"
 1507 "-m                     find all one level more specific matches\n"
 1508 "-M                     find all levels of more specific matches\n"
 1509     ));
 1510     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1511 "-c                     find the smallest match containing a mnt-irt attribute\n"
 1512 "-x                     exact match\n"
 1513 "-b                     return brief IP address ranges with abuse contact\n"
 1514     ));
 1515     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1516 "-B                     turn off object filtering (show email addresses)\n"
 1517 "-G                     turn off grouping of associated objects\n"
 1518 "-d                     return DNS reverse delegation objects too\n"
 1519     ));
 1520     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1521 "-i ATTR[,ATTR]...      do an inverse look-up for specified ATTRibutes\n"
 1522 "-T TYPE[,TYPE]...      only look for objects of TYPE\n"
 1523 "-K                     only primary keys are returned\n"
 1524 "-r                     turn off recursive look-ups for contact information\n"
 1525     ));
 1526     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1527 "-R                     force to show local copy of the domain object even\n"
 1528 "                       if it contains referral\n"
 1529 "-a                     also search all the mirrored databases\n"
 1530 "-s SOURCE[,SOURCE]...  search the database mirrored from SOURCE\n"
 1531 "-g SOURCE:FIRST-LAST   find updates from SOURCE from serial FIRST to LAST\n"
 1532     ));
 1533     fprintf((EXIT_SUCCESS == error) ? stdout : stderr, _(
 1534 "-t TYPE                request template for object of TYPE\n"
 1535 "-v TYPE                request verbose template for object of TYPE\n"
 1536 "-q [version|sources|types]  query specified server info\n"
 1537 ));
 1538     exit(error);
 1539 }
 1540