quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

adig.c (48111B)


      1 /* MIT License
      2  *
      3  * Copyright (c) 1998 Massachusetts Institute of Technology
      4  * Copyright (c) The c-ares project and its contributors
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the next
     14  * paragraph) shall be included in all copies or substantial portions of the
     15  * Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23  * SOFTWARE.
     24  *
     25  * SPDX-License-Identifier: MIT
     26  */
     27 #include "ares_setup.h"
     28 
     29 #ifdef HAVE_NETINET_IN_H
     30 #  include <netinet/in.h>
     31 #endif
     32 #ifdef HAVE_ARPA_INET_H
     33 #  include <arpa/inet.h>
     34 #endif
     35 #ifdef HAVE_NETDB_H
     36 #  include <netdb.h>
     37 #endif
     38 
     39 #include "ares_nameser.h"
     40 
     41 #ifdef HAVE_STRINGS_H
     42 #  include <strings.h>
     43 #endif
     44 
     45 #include "ares.h"
     46 #include "ares_array.h"
     47 #include "ares_buf.h"
     48 #include "ares_dns.h"
     49 #include "ares_getopt.h"
     50 #include "ares_mem.h"
     51 #include "ares_str.h"
     52 
     53 #include "limits.h"
     54 
     55 #ifndef PATH_MAX
     56 #  define PATH_MAX 1024
     57 #endif
     58 
     59 typedef struct {
     60   unsigned short port;
     61   size_t         tries;
     62   size_t         ndots;
     63   ares_bool_t    tcp;
     64   ares_bool_t    ignore_tc;
     65   char          *search;
     66   ares_bool_t    do_search;
     67   ares_bool_t    aa_flag;
     68   ares_bool_t    ad_flag;
     69   ares_bool_t    cd_flag;
     70   ares_bool_t    rd_flag;
     71   /* ares_bool_t do_flag; */
     72   ares_bool_t    edns;
     73   size_t         udp_size;
     74   ares_bool_t    primary;
     75   ares_bool_t    aliases;
     76   ares_bool_t    stayopen;
     77   ares_bool_t    dns0x20;
     78   ares_bool_t    display_class;
     79   ares_bool_t    display_ttl;
     80   ares_bool_t    display_command;
     81   ares_bool_t    display_stats;
     82   ares_bool_t    display_query;
     83   ares_bool_t    display_question;
     84   ares_bool_t    display_answer;
     85   ares_bool_t    display_authority;
     86   ares_bool_t    display_additional;
     87   ares_bool_t    display_comments;
     88 } dns_options_t;
     89 
     90 typedef struct {
     91   dns_options_t       opts;
     92   ares_bool_t         is_help;
     93   ares_bool_t         no_rcfile;
     94   struct ares_options options;
     95   int                 optmask;
     96   ares_dns_class_t    qclass;
     97   ares_dns_rec_type_t qtype;
     98   char               *name;
     99   char               *servers;
    100   char                error[256];
    101 } adig_config_t;
    102 
    103 static adig_config_t global_config;
    104 
    105 
    106 static const char   *helpstr[] = {
    107   "usage: adig [@server] [-c class] [-p port#] [-q name] [-t type] [-x addr]",
    108   "       [name] [type] [class] [queryopt...]",
    109   "",
    110   "@server: server ip address.  May specify multiple in comma delimited "
    111     "format.",
    112   "         may be specified in URI format",
    113   "name:    name of the resource record that is to be looked up",
    114   "type:    what type of query is required.  e.g. - A, AAAA, MX, TXT, etc.  If",
    115   "         no specified, A will be used.",
    116   "class:   Sets the query class, defaults to IN.  May also be HS or CH.",
    117   "",
    118   "FLAGS",
    119   "-c class: Sets the query class, defaults to IN.  May also be HS or CH.",
    120   "-h:       Prints this help.",
    121   "-p port:  Sends query to a port other than 53.  Often recommended to set",
    122   "          the port using @server instead.",
    123   "-q name:  Specifies the domain name to query. Useful to distinguish name",
    124   "          from other arguments",
    125   "-r:       Skip adigrc processing",
    126   "-s:       Server (alias for @server syntax), compatibility with old cmdline",
    127   "-t type:  Indicates resource record type to query. Useful to distinguish",
    128   "          type from other arguments",
    129   "-x addr:  Simplified reverse lookups.  Sets the type to PTR and forms a",
    130   "          valid in-arpa query string",
    131   "",
    132   "QUERY OPTIONS",
    133   "+[no]aaonly:      Sets the aa flag in the query. Default is off.",
    134   "+[no]aaflag:      Alias for +[no]aaonly",
    135   "+[no]additional:  Toggles printing the additional section. On by default.",
    136   "+[no]adflag:      Sets the ad (authentic data) bit in the query. Default is",
    137   "                  off.",
    138   "+[no]aliases:     Whether or not to honor the HOSTALIASES file. Default is",
    139   "                  on.",
    140   "+[no]all:         Toggles all of +[no]cmd, +[no]stats, +[no]question,",
    141   "                  +[no]answer, +[no]authority, +[no]additional, "
    142     "+[no]comments",
    143   "+[no]answer:      Toggles printing the answer. On by default.",
    144   "+[no]authority:   Toggles printing the authority. On by default.",
    145   "+bufsize=#:       UDP EDNS 0 packet size allowed. Defaults to 1232.",
    146   "+[no]cdflag:      Sets the CD (checking disabled) bit in the query. Default",
    147   "                  is off.",
    148   "+[no]class:       Display the class when printing the record. On by "
    149     "default.",
    150   "+[no]cmd:         Toggles printing the command requested. On by default.",
    151   "+[no]comments:    Toggles printing the comments. On by default",
    152   "+[no]defname:     Alias for +[no]search",
    153   "+domain=somename: Sets the search list to a single domain.",
    154   "+[no]dns0x20:     Whether or not to use DNS 0x20 case randomization when",
    155   "                  sending queries.  Default is off.",
    156   "+[no]edns[=#]:    Enable or disable EDNS.  Only allows a value of 0 if",
    157   "                  specified. Default is to enable EDNS.",
    158   "+[no]ignore:      Ignore truncation on UDP, by default retried on TCP.",
    159   "+[no]keepopen:    Whether or not the server connection should be "
    160     "persistent.",
    161   "                  Default is off.",
    162   "+ndots=#:         Sets the number of dots that must appear before being",
    163   "                  considered absolute. Defaults to 1.",
    164   "+[no]primary:     Whether or not to only use a single server if more than "
    165     "one",
    166   "                  server is available.  Defaults to using all servers.",
    167   "+[no]qr:          Toggles printing the request query. Off by default.",
    168   "+[no]question:    Toggles printing the question. On by default.",
    169   "+[no]recurse:     Toggles the RD (Recursion Desired) bit. On by default.",
    170   "+retry=#:         Same as +tries but does not include the initial attempt.",
    171   "+[no]search:      To use or not use the search list. Search list is not "
    172     "used",
    173   "                  by default.",
    174   "+[no]stats:       Toggles printing the statistics. On by default.",
    175   "+[no]tcp:         Whether to use TCP when querying name servers. Default is",
    176   "                  UDP.",
    177   "+tries=#:         Number of query tries. Defaults to 3.",
    178   "+[no]ttlid:       Display the TTL when printing the record. On by default.",
    179   "+[no]vc:          Alias for +[no]tcp",
    180   "",
    181   NULL
    182 };
    183 
    184 static void free_config(void)
    185 {
    186   free(global_config.servers);
    187   free(global_config.name);
    188   free(global_config.opts.search);
    189   memset(&global_config, 0, sizeof(global_config));
    190 }
    191 
    192 static void print_help(void)
    193 {
    194   size_t i;
    195   printf("adig version %s\n\n", ares_version(NULL));
    196   for (i = 0; helpstr[i] != NULL; i++) {
    197     printf("%s\n", helpstr[i]);
    198   }
    199 }
    200 
    201 static void print_flags(ares_dns_flags_t flags)
    202 {
    203   if (flags & ARES_FLAG_QR) {
    204     printf(" qr");
    205   }
    206   if (flags & ARES_FLAG_AA) {
    207     printf(" aa");
    208   }
    209   if (flags & ARES_FLAG_TC) {
    210     printf(" tc");
    211   }
    212   if (flags & ARES_FLAG_RD) {
    213     printf(" rd");
    214   }
    215   if (flags & ARES_FLAG_RA) {
    216     printf(" ra");
    217   }
    218   if (flags & ARES_FLAG_AD) {
    219     printf(" ad");
    220   }
    221   if (flags & ARES_FLAG_CD) {
    222     printf(" cd");
    223   }
    224 }
    225 
    226 static void print_header(const ares_dns_record_t *dnsrec)
    227 {
    228   printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n",
    229          ares_dns_opcode_tostr(ares_dns_record_get_opcode(dnsrec)),
    230          ares_dns_rcode_tostr(ares_dns_record_get_rcode(dnsrec)),
    231          ares_dns_record_get_id(dnsrec));
    232   printf(";; flags:");
    233   print_flags(ares_dns_record_get_flags(dnsrec));
    234   printf("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n\n",
    235          (unsigned int)ares_dns_record_query_cnt(dnsrec),
    236          (unsigned int)ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER),
    237          (unsigned int)ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_AUTHORITY),
    238          (unsigned int)ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ADDITIONAL));
    239 }
    240 
    241 static void print_question(const ares_dns_record_t *dnsrec)
    242 {
    243   size_t i;
    244 
    245   if (global_config.opts.display_comments) {
    246     printf(";; QUESTION SECTION:\n");
    247   }
    248 
    249   for (i = 0; i < ares_dns_record_query_cnt(dnsrec); i++) {
    250     const char         *name;
    251     ares_dns_rec_type_t qtype;
    252     ares_dns_class_t    qclass;
    253     size_t              len;
    254     if (ares_dns_record_query_get(dnsrec, i, &name, &qtype, &qclass) !=
    255         ARES_SUCCESS) {
    256       return;
    257     }
    258     if (name == NULL) {
    259       return;
    260     }
    261     len = strlen(name);
    262     printf(";%s.\t", name);
    263     if (len + 1 < 24) {
    264       printf("\t");
    265     }
    266     if (len + 1 < 16) {
    267       printf("\t");
    268     }
    269 
    270     if (global_config.opts.display_class) {
    271       printf("%s\t", ares_dns_class_tostr(qclass));
    272     }
    273 
    274     printf("%s\n", ares_dns_rec_type_tostr(qtype));
    275   }
    276 
    277   if (global_config.opts.display_comments) {
    278     printf("\n");
    279   }
    280 }
    281 
    282 static void print_opt_none(const unsigned char *val, size_t val_len)
    283 {
    284   (void)val;
    285   if (val_len != 0) {
    286     printf("INVALID!");
    287   }
    288 }
    289 
    290 static void print_opt_addr_list(const unsigned char *val, size_t val_len)
    291 {
    292   size_t i;
    293   if (val_len % 4 != 0) {
    294     printf("INVALID!");
    295     return;
    296   }
    297   for (i = 0; i < val_len; i += 4) {
    298     char buf[256] = "";
    299     ares_inet_ntop(AF_INET, val + i, buf, sizeof(buf));
    300     if (i != 0) {
    301       printf(",");
    302     }
    303     printf("%s", buf);
    304   }
    305 }
    306 
    307 static void print_opt_addr6_list(const unsigned char *val, size_t val_len)
    308 {
    309   size_t i;
    310   if (val_len % 16 != 0) {
    311     printf("INVALID!");
    312     return;
    313   }
    314   for (i = 0; i < val_len; i += 16) {
    315     char buf[256] = "";
    316 
    317     ares_inet_ntop(AF_INET6, val + i, buf, sizeof(buf));
    318     if (i != 0) {
    319       printf(",");
    320     }
    321     printf("%s", buf);
    322   }
    323 }
    324 
    325 static void print_opt_u8_list(const unsigned char *val, size_t val_len)
    326 {
    327   size_t i;
    328 
    329   for (i = 0; i < val_len; i++) {
    330     if (i != 0) {
    331       printf(",");
    332     }
    333     printf("%u", (unsigned int)val[i]);
    334   }
    335 }
    336 
    337 static void print_opt_u16_list(const unsigned char *val, size_t val_len)
    338 {
    339   size_t i;
    340   if (val_len < 2 || val_len % 2 != 0) {
    341     printf("INVALID!");
    342     return;
    343   }
    344   for (i = 0; i < val_len; i += 2) {
    345     unsigned short u16 = 0;
    346     unsigned short c;
    347     /* Jumping over backwards to try to avoid odd compiler warnings */
    348     c    = (unsigned short)val[i];
    349     u16 |= (unsigned short)((c << 8) & 0xFFFF);
    350     c    = (unsigned short)val[i + 1];
    351     u16 |= c;
    352     if (i != 0) {
    353       printf(",");
    354     }
    355     printf("%u", (unsigned int)u16);
    356   }
    357 }
    358 
    359 static void print_opt_u32_list(const unsigned char *val, size_t val_len)
    360 {
    361   size_t i;
    362   if (val_len < 4 || val_len % 4 != 0) {
    363     printf("INVALID!");
    364     return;
    365   }
    366   for (i = 0; i < val_len; i += 4) {
    367     unsigned int u32 = 0;
    368 
    369     u32 |= (unsigned int)(val[i] << 24);
    370     u32 |= (unsigned int)(val[i + 1] << 16);
    371     u32 |= (unsigned int)(val[i + 2] << 8);
    372     u32 |= (unsigned int)(val[i + 3]);
    373     if (i != 0) {
    374       printf(",");
    375     }
    376     printf("%u", u32);
    377   }
    378 }
    379 
    380 static void print_opt_str_list(const unsigned char *val, size_t val_len)
    381 {
    382   size_t cnt = 0;
    383 
    384   printf("\"");
    385   while (val_len) {
    386     long           read_len = 0;
    387     unsigned char *str      = NULL;
    388     ares_status_t  status;
    389 
    390     if (cnt) {
    391       printf(",");
    392     }
    393 
    394     status = (ares_status_t)ares_expand_string(val, val, (int)val_len, &str,
    395                                                &read_len);
    396     if (status != ARES_SUCCESS) {
    397       printf("INVALID");
    398       break;
    399     }
    400     printf("%s", str);
    401     ares_free_string(str);
    402     val_len -= (size_t)read_len;
    403     val     += read_len;
    404     cnt++;
    405   }
    406   printf("\"");
    407 }
    408 
    409 static void print_opt_name(const unsigned char *val, size_t val_len)
    410 {
    411   char *str      = NULL;
    412   long  read_len = 0;
    413 
    414   if (ares_expand_name(val, val, (int)val_len, &str, &read_len) !=
    415       ARES_SUCCESS) {
    416     printf("INVALID!");
    417     return;
    418   }
    419 
    420   printf("%s.", str);
    421   ares_free_string(str);
    422 }
    423 
    424 static void print_opt_bin(const unsigned char *val, size_t val_len)
    425 {
    426   size_t i;
    427 
    428   for (i = 0; i < val_len; i++) {
    429     printf("%02x", (unsigned int)val[i]);
    430   }
    431 }
    432 
    433 static ares_bool_t adig_isprint(int ch)
    434 {
    435   if (ch >= 0x20 && ch <= 0x7E) {
    436     return ARES_TRUE;
    437   }
    438   return ARES_FALSE;
    439 }
    440 
    441 static void print_opt_binp(const unsigned char *val, size_t val_len)
    442 {
    443   size_t i;
    444   printf("\"");
    445   for (i = 0; i < val_len; i++) {
    446     if (adig_isprint(val[i])) {
    447       printf("%c", val[i]);
    448     } else {
    449       printf("\\%03d", val[i]);
    450     }
    451   }
    452   printf("\"");
    453 }
    454 
    455 static void print_opts(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    456 {
    457   size_t i;
    458 
    459   for (i = 0; i < ares_dns_rr_get_opt_cnt(rr, key); i++) {
    460     size_t               val_len = 0;
    461     const unsigned char *val     = NULL;
    462     unsigned short       opt;
    463     const char          *name;
    464 
    465     if (i != 0) {
    466       printf(" ");
    467     }
    468 
    469     opt  = ares_dns_rr_get_opt(rr, key, i, &val, &val_len);
    470     name = ares_dns_opt_get_name(key, opt);
    471     if (name == NULL) {
    472       printf("key%u", (unsigned int)opt);
    473     } else {
    474       printf("%s", name);
    475     }
    476     if (val_len == 0) {
    477       return;
    478     }
    479 
    480     printf("=");
    481 
    482     switch (ares_dns_opt_get_datatype(key, opt)) {
    483       case ARES_OPT_DATATYPE_NONE:
    484         print_opt_none(val, val_len);
    485         break;
    486       case ARES_OPT_DATATYPE_U8_LIST:
    487         print_opt_u8_list(val, val_len);
    488         break;
    489       case ARES_OPT_DATATYPE_INADDR4_LIST:
    490         print_opt_addr_list(val, val_len);
    491         break;
    492       case ARES_OPT_DATATYPE_INADDR6_LIST:
    493         print_opt_addr6_list(val, val_len);
    494         break;
    495       case ARES_OPT_DATATYPE_U16:
    496       case ARES_OPT_DATATYPE_U16_LIST:
    497         print_opt_u16_list(val, val_len);
    498         break;
    499       case ARES_OPT_DATATYPE_U32:
    500       case ARES_OPT_DATATYPE_U32_LIST:
    501         print_opt_u32_list(val, val_len);
    502         break;
    503       case ARES_OPT_DATATYPE_STR_LIST:
    504         print_opt_str_list(val, val_len);
    505         break;
    506       case ARES_OPT_DATATYPE_BIN:
    507         print_opt_bin(val, val_len);
    508         break;
    509       case ARES_OPT_DATATYPE_NAME:
    510         print_opt_name(val, val_len);
    511         break;
    512     }
    513   }
    514 }
    515 
    516 static void print_addr(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    517 {
    518   const struct in_addr *addr     = ares_dns_rr_get_addr(rr, key);
    519   char                  buf[256] = "";
    520 
    521   ares_inet_ntop(AF_INET, addr, buf, sizeof(buf));
    522   printf("%s", buf);
    523 }
    524 
    525 static void print_addr6(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    526 {
    527   const struct ares_in6_addr *addr     = ares_dns_rr_get_addr6(rr, key);
    528   char                        buf[256] = "";
    529 
    530   ares_inet_ntop(AF_INET6, addr, buf, sizeof(buf));
    531   printf("%s", buf);
    532 }
    533 
    534 static void print_u8(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    535 {
    536   unsigned char u8 = ares_dns_rr_get_u8(rr, key);
    537   printf("%u", (unsigned int)u8);
    538 }
    539 
    540 static void print_u16(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    541 {
    542   unsigned short u16 = ares_dns_rr_get_u16(rr, key);
    543   printf("%u", (unsigned int)u16);
    544 }
    545 
    546 static void print_u32(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    547 {
    548   unsigned int u32 = ares_dns_rr_get_u32(rr, key);
    549   printf("%u", u32);
    550 }
    551 
    552 static void print_name(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    553 {
    554   const char *str = ares_dns_rr_get_str(rr, key);
    555   printf("%s.", str);
    556 }
    557 
    558 static void print_str(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    559 {
    560   const char *str = ares_dns_rr_get_str(rr, key);
    561   printf("\"%s\"", str);
    562 }
    563 
    564 static void print_bin(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    565 {
    566   size_t               len  = 0;
    567   const unsigned char *binp = ares_dns_rr_get_bin(rr, key, &len);
    568   print_opt_bin(binp, len);
    569 }
    570 
    571 static void print_binp(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    572 {
    573   size_t               len;
    574   const unsigned char *binp = ares_dns_rr_get_bin(rr, key, &len);
    575 
    576   print_opt_binp(binp, len);
    577 }
    578 
    579 static void print_abinp(const ares_dns_rr_t *rr, ares_dns_rr_key_t key)
    580 {
    581   size_t i;
    582   size_t cnt = ares_dns_rr_get_abin_cnt(rr, key);
    583 
    584   for (i = 0; i < cnt; i++) {
    585     size_t               len;
    586     const unsigned char *binp = ares_dns_rr_get_abin(rr, key, i, &len);
    587     if (i != 0) {
    588       printf(" ");
    589     }
    590     print_opt_binp(binp, len);
    591   }
    592 }
    593 
    594 static void print_rr(const ares_dns_rr_t *rr)
    595 {
    596   const char              *name     = ares_dns_rr_get_name(rr);
    597   size_t                   len      = 0;
    598   size_t                   keys_cnt = 0;
    599   ares_dns_rec_type_t      rtype    = ares_dns_rr_get_type(rr);
    600   const ares_dns_rr_key_t *keys     = ares_dns_rr_get_keys(rtype, &keys_cnt);
    601   size_t                   i;
    602 
    603   if (name == NULL) {
    604     return;
    605   }
    606 
    607   len = strlen(name);
    608 
    609   printf("%s.\t", name);
    610   if (len < 24) {
    611     printf("\t");
    612   }
    613 
    614   if (global_config.opts.display_ttl) {
    615     printf("%u\t", ares_dns_rr_get_ttl(rr));
    616   }
    617 
    618   if (global_config.opts.display_class) {
    619     printf("%s\t", ares_dns_class_tostr(ares_dns_rr_get_class(rr)));
    620   }
    621 
    622   printf("%s\t", ares_dns_rec_type_tostr(rtype));
    623 
    624   /* Output params here */
    625   for (i = 0; i < keys_cnt; i++) {
    626     ares_dns_datatype_t datatype = ares_dns_rr_key_datatype(keys[i]);
    627     if (i != 0) {
    628       printf(" ");
    629     }
    630 
    631     switch (datatype) {
    632       case ARES_DATATYPE_INADDR:
    633         print_addr(rr, keys[i]);
    634         break;
    635       case ARES_DATATYPE_INADDR6:
    636         print_addr6(rr, keys[i]);
    637         break;
    638       case ARES_DATATYPE_U8:
    639         print_u8(rr, keys[i]);
    640         break;
    641       case ARES_DATATYPE_U16:
    642         print_u16(rr, keys[i]);
    643         break;
    644       case ARES_DATATYPE_U32:
    645         print_u32(rr, keys[i]);
    646         break;
    647       case ARES_DATATYPE_NAME:
    648         print_name(rr, keys[i]);
    649         break;
    650       case ARES_DATATYPE_STR:
    651         print_str(rr, keys[i]);
    652         break;
    653       case ARES_DATATYPE_BIN:
    654         print_bin(rr, keys[i]);
    655         break;
    656       case ARES_DATATYPE_BINP:
    657         print_binp(rr, keys[i]);
    658         break;
    659       case ARES_DATATYPE_ABINP:
    660         print_abinp(rr, keys[i]);
    661         break;
    662       case ARES_DATATYPE_OPT:
    663         print_opts(rr, keys[i]);
    664         break;
    665     }
    666   }
    667 
    668   printf("\n");
    669 }
    670 
    671 static const ares_dns_rr_t *has_opt(const ares_dns_record_t *dnsrec,
    672                                     ares_dns_section_t       section)
    673 {
    674   size_t i;
    675   for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, section); i++) {
    676     const ares_dns_rr_t *rr = ares_dns_record_rr_get_const(dnsrec, section, i);
    677     if (ares_dns_rr_get_type(rr) == ARES_REC_TYPE_OPT) {
    678       return rr;
    679     }
    680   }
    681   return NULL;
    682 }
    683 
    684 static void print_section(const ares_dns_record_t *dnsrec,
    685                           ares_dns_section_t       section)
    686 {
    687   size_t i;
    688 
    689   if (ares_dns_record_rr_cnt(dnsrec, section) == 0 ||
    690       (ares_dns_record_rr_cnt(dnsrec, section) == 1 &&
    691        has_opt(dnsrec, section) != NULL)) {
    692     return;
    693   }
    694 
    695   if (global_config.opts.display_comments) {
    696     printf(";; %s SECTION:\n", ares_dns_section_tostr(section));
    697   }
    698   for (i = 0; i < ares_dns_record_rr_cnt(dnsrec, section); i++) {
    699     const ares_dns_rr_t *rr = ares_dns_record_rr_get_const(dnsrec, section, i);
    700     if (ares_dns_rr_get_type(rr) == ARES_REC_TYPE_OPT) {
    701       continue;
    702     }
    703     print_rr(rr);
    704   }
    705   if (global_config.opts.display_comments) {
    706     printf("\n");
    707   }
    708 }
    709 
    710 static void print_opt_psuedosection(const ares_dns_record_t *dnsrec)
    711 {
    712   const ares_dns_rr_t *rr         = has_opt(dnsrec, ARES_SECTION_ADDITIONAL);
    713   const unsigned char *cookie     = NULL;
    714   size_t               cookie_len = 0;
    715 
    716   if (rr == NULL) {
    717     return;
    718   }
    719 
    720   if (!ares_dns_rr_get_opt_byid(rr, ARES_RR_OPT_OPTIONS, ARES_OPT_PARAM_COOKIE,
    721                                 &cookie, &cookie_len)) {
    722     cookie = NULL;
    723   }
    724 
    725   printf(";; OPT PSEUDOSECTION:\n");
    726   printf("; EDNS: version: %u, flags: %u; udp: %u\n",
    727          (unsigned int)ares_dns_rr_get_u8(rr, ARES_RR_OPT_VERSION),
    728          (unsigned int)ares_dns_rr_get_u16(rr, ARES_RR_OPT_FLAGS),
    729          (unsigned int)ares_dns_rr_get_u16(rr, ARES_RR_OPT_UDP_SIZE));
    730 
    731   if (cookie) {
    732     printf("; COOKIE: ");
    733     print_opt_bin(cookie, cookie_len);
    734     printf(" (good)\n");
    735   }
    736 }
    737 
    738 static void print_record(const ares_dns_record_t *dnsrec)
    739 {
    740   if (global_config.opts.display_comments) {
    741     print_header(dnsrec);
    742     print_opt_psuedosection(dnsrec);
    743   }
    744 
    745   if (global_config.opts.display_question) {
    746     print_question(dnsrec);
    747   }
    748 
    749   if (global_config.opts.display_answer) {
    750     print_section(dnsrec, ARES_SECTION_ANSWER);
    751   }
    752 
    753   if (global_config.opts.display_additional) {
    754     print_section(dnsrec, ARES_SECTION_ADDITIONAL);
    755   }
    756 
    757   if (global_config.opts.display_authority) {
    758     print_section(dnsrec, ARES_SECTION_AUTHORITY);
    759   }
    760 
    761   if (global_config.opts.display_stats) {
    762     unsigned char *abuf = NULL;
    763     size_t         alen = 0;
    764     ares_dns_write(dnsrec, &abuf, &alen);
    765     printf(";; MSG SIZE  rcvd: %d\n\n", (int)alen);
    766     ares_free_string(abuf);
    767   }
    768 }
    769 
    770 static void callback(void *arg, ares_status_t status, size_t timeouts,
    771                      const ares_dns_record_t *dnsrec)
    772 {
    773   (void)arg;
    774   (void)timeouts;
    775 
    776   if (global_config.opts.display_comments) {
    777     /* We got a "Server status" */
    778     if (status >= ARES_SUCCESS && status <= ARES_EREFUSED) {
    779       printf(";; Got answer:");
    780     } else {
    781       printf(";;");
    782     }
    783     if (status != ARES_SUCCESS) {
    784       printf(" %s", ares_strerror((int)status));
    785     }
    786     printf("\n");
    787   }
    788 
    789   print_record(dnsrec);
    790 }
    791 
    792 static ares_status_t enqueue_query(ares_channel_t *channel)
    793 {
    794   ares_dns_record_t *dnsrec = NULL;
    795   ares_dns_rr_t     *rr     = NULL;
    796   ares_status_t      status;
    797   unsigned short     flags    = 0;
    798   char              *nametemp = NULL;
    799   const char        *name     = global_config.name;
    800 
    801   if (global_config.opts.aa_flag) {
    802     flags |= ARES_FLAG_AA;
    803   }
    804 
    805   if (global_config.opts.ad_flag) {
    806     flags |= ARES_FLAG_AD;
    807   }
    808 
    809   if (global_config.opts.cd_flag) {
    810     flags |= ARES_FLAG_CD;
    811   }
    812 
    813   if (global_config.opts.rd_flag) {
    814     flags |= ARES_FLAG_RD;
    815   }
    816 
    817   status = ares_dns_record_create(&dnsrec, 0, flags, ARES_OPCODE_QUERY,
    818                                   ARES_RCODE_NOERROR);
    819   if (status != ARES_SUCCESS) {
    820     goto done;
    821   }
    822 
    823   /* If it is a PTR record, convert from ip address into in-arpa form
    824    * automatically */
    825   if (global_config.qtype == ARES_REC_TYPE_PTR) {
    826     struct ares_addr addr;
    827     size_t           len;
    828     addr.family = AF_UNSPEC;
    829 
    830     if (ares_dns_pton(name, &addr, &len) != NULL) {
    831       nametemp = ares_dns_addr_to_ptr(&addr);
    832       name     = nametemp;
    833     }
    834   }
    835 
    836   status = ares_dns_record_query_add(dnsrec, name, global_config.qtype,
    837                                      global_config.qclass);
    838   if (status != ARES_SUCCESS) {
    839     goto done;
    840   }
    841 
    842   if (global_config.opts.edns) {
    843     status = ares_dns_record_rr_add(&rr, dnsrec, ARES_SECTION_ADDITIONAL, "",
    844                                     ARES_REC_TYPE_OPT, ARES_CLASS_IN, 0);
    845     if (status != ARES_SUCCESS) {
    846       goto done;
    847     }
    848     ares_dns_rr_set_u16(rr, ARES_RR_OPT_UDP_SIZE,
    849                         (unsigned short)global_config.opts.udp_size);
    850     ares_dns_rr_set_u8(rr, ARES_RR_OPT_VERSION, 0);
    851   }
    852 
    853   if (global_config.opts.display_query) {
    854     printf(";; Sending:\n");
    855     print_record(dnsrec);
    856   }
    857 
    858   if (global_config.opts.do_search) {
    859     status = ares_search_dnsrec(channel, dnsrec, callback, NULL);
    860   } else {
    861     status = ares_send_dnsrec(channel, dnsrec, callback, NULL, NULL);
    862   }
    863 
    864 done:
    865   ares_free_string(nametemp);
    866   ares_dns_record_destroy(dnsrec);
    867   return status;
    868 }
    869 
    870 static int event_loop(ares_channel_t *channel)
    871 {
    872   while (1) {
    873     fd_set          read_fds;
    874     fd_set          write_fds;
    875     int             nfds;
    876     struct timeval  tv;
    877     struct timeval *tvp;
    878     int             count;
    879 
    880     FD_ZERO(&read_fds);
    881     FD_ZERO(&write_fds);
    882     memset(&tv, 0, sizeof(tv));
    883 
    884     nfds = ares_fds(channel, &read_fds, &write_fds);
    885     if (nfds == 0) {
    886       break;
    887     }
    888     tvp = ares_timeout(channel, NULL, &tv);
    889     if (tvp == NULL) {
    890       break;
    891     }
    892     count = select(nfds, &read_fds, &write_fds, NULL, tvp);
    893     if (count < 0) {
    894 #ifdef USE_WINSOCK
    895       int err = WSAGetLastError();
    896 #else
    897       int err = errno;
    898 #endif
    899       if (err != EAGAIN && err != EINTR) {
    900         fprintf(stderr, "select fail: %d", err);
    901         return 1;
    902       }
    903     }
    904     ares_process(channel, &read_fds, &write_fds);
    905   }
    906   return 0;
    907 }
    908 
    909 typedef enum {
    910   OPT_TYPE_BOOL,
    911   OPT_TYPE_STRING,
    912   OPT_TYPE_SIZE_T,
    913   OPT_TYPE_U16,
    914   OPT_TYPE_FUNC
    915 } opt_type_t;
    916 
    917 /* Callback called with OPT_TYPE_FUNC when processing options.
    918  * \param[in] prefix  prefix character for option
    919  * \param[in] name    name for option
    920  * \param[in] is_true ARES_TRUE unless option was prefixed with 'no'
    921  * \param[in] value   value for option
    922  * \return ARES_TRUE on success, ARES_FALSE on failure.  Should fill in
    923  *         global_config.error on error */
    924 typedef ares_bool_t (*dig_opt_cb_t)(char prefix, const char *name,
    925                                     ares_bool_t is_true, const char *value);
    926 
    927 static ares_bool_t opt_class_cb(char prefix, const char *name,
    928                                 ares_bool_t is_true, const char *value)
    929 {
    930   (void)prefix;
    931   (void)name;
    932   (void)is_true;
    933 
    934   if (!ares_dns_class_fromstr(&global_config.qclass, value)) {
    935     snprintf(global_config.error, sizeof(global_config.error),
    936              "unrecognized class %s", value);
    937     return ARES_FALSE;
    938   }
    939 
    940   return ARES_TRUE;
    941 }
    942 
    943 static ares_bool_t opt_type_cb(char prefix, const char *name,
    944                                ares_bool_t is_true, const char *value)
    945 {
    946   (void)prefix;
    947   (void)name;
    948   (void)is_true;
    949 
    950   if (!ares_dns_rec_type_fromstr(&global_config.qtype, value)) {
    951     snprintf(global_config.error, sizeof(global_config.error),
    952              "unrecognized record type %s", value);
    953     return ARES_FALSE;
    954   }
    955   return ARES_TRUE;
    956 }
    957 
    958 static ares_bool_t opt_ptr_cb(char prefix, const char *name,
    959                               ares_bool_t is_true, const char *value)
    960 {
    961   (void)prefix;
    962   (void)name;
    963   (void)is_true;
    964   global_config.qtype = ARES_REC_TYPE_PTR;
    965   ares_free(global_config.name);
    966   global_config.name = strdup(value);
    967   return ARES_TRUE;
    968 }
    969 
    970 static ares_bool_t opt_all_cb(char prefix, const char *name,
    971                               ares_bool_t is_true, const char *value)
    972 {
    973   (void)prefix;
    974   (void)name;
    975   (void)value;
    976 
    977   global_config.opts.display_command    = is_true;
    978   global_config.opts.display_stats      = is_true;
    979   global_config.opts.display_question   = is_true;
    980   global_config.opts.display_answer     = is_true;
    981   global_config.opts.display_authority  = is_true;
    982   global_config.opts.display_additional = is_true;
    983   global_config.opts.display_comments   = is_true;
    984   return ARES_TRUE;
    985 }
    986 
    987 static ares_bool_t opt_edns_cb(char prefix, const char *name,
    988                                ares_bool_t is_true, const char *value)
    989 {
    990   (void)prefix;
    991   (void)name;
    992 
    993   global_config.opts.edns = is_true;
    994   if (is_true && value != NULL && atoi(value) > 0) {
    995     snprintf(global_config.error, sizeof(global_config.error),
    996              "edns 0 only supported");
    997     return ARES_FALSE;
    998   }
    999   return ARES_TRUE;
   1000 }
   1001 
   1002 static ares_bool_t opt_retry_cb(char prefix, const char *name,
   1003                                 ares_bool_t is_true, const char *value)
   1004 {
   1005   (void)prefix;
   1006   (void)name;
   1007   (void)is_true;
   1008 
   1009   if (!ares_str_isnum(value)) {
   1010     snprintf(global_config.error, sizeof(global_config.error),
   1011              "value not numeric");
   1012     return ARES_FALSE;
   1013   }
   1014 
   1015   global_config.opts.tries = strtoul(value, NULL, 10) + 1;
   1016   return ARES_TRUE;
   1017 }
   1018 
   1019 static ares_bool_t opt_dig_bare_cb(char prefix, const char *name,
   1020                                    ares_bool_t is_true, const char *value)
   1021 {
   1022   (void)prefix;
   1023   (void)name;
   1024   (void)is_true;
   1025 
   1026   /* Handle @servers */
   1027   if (*value == '@') {
   1028     free(global_config.servers);
   1029     global_config.servers = strdup(value + 1);
   1030     return ARES_TRUE;
   1031   }
   1032 
   1033   /* Make sure we don't pass options */
   1034   if (*value == '-' || *value == '+') {
   1035     snprintf(global_config.error, sizeof(global_config.error),
   1036              "unrecognized argument %s", value);
   1037     return ARES_FALSE;
   1038   }
   1039 
   1040   /* See if it is a DNS class */
   1041   if (ares_dns_class_fromstr(&global_config.qclass, value)) {
   1042     return ARES_TRUE;
   1043   }
   1044 
   1045   /* See if it is a DNS record type */
   1046   if (ares_dns_rec_type_fromstr(&global_config.qtype, value)) {
   1047     return ARES_TRUE;
   1048   }
   1049 
   1050   /* See if it is a domain name */
   1051   if (ares_is_hostname(value)) {
   1052     free(global_config.name);
   1053     global_config.name = strdup(value);
   1054     return ARES_TRUE;
   1055   }
   1056 
   1057   snprintf(global_config.error, sizeof(global_config.error),
   1058            "unrecognized argument %s", value);
   1059   return ARES_FALSE;
   1060 }
   1061 
   1062 static const struct {
   1063   /* Prefix for option.  If 0 then this param is a non-option and type must be
   1064    * OPT_TYPE_FUNC where the entire value for the param will be passed */
   1065   char         prefix;
   1066   /* Name of option.  If null, there is none and the value is expected to be
   1067    * immediately after the prefix character */
   1068   const char  *name;
   1069   /* Separator between key and value.  If 0 then uses the next argument as the
   1070    * value, otherwise splits on the separator. BOOL types won't ever use a
   1071    * separator and is ignored.*/
   1072   char         separator;
   1073   /* Type of parameter passed in.  If it is OPT_TYPE_FUNC, then it calls the
   1074    * dig_opt_cb_t callback */
   1075   opt_type_t   type;
   1076   /* Pointer to argument to fill in */
   1077   void        *opt;
   1078   /* Callback if OPT_TYPE_FUNC */
   1079   dig_opt_cb_t cb;
   1080 } dig_options[] = {
   1081   /* -4 (ipv4 only) */
   1082   /* -6 (ipv6 only) */
   1083   /* { '-', "b",          0,   OPT_TYPE_FUNC,   NULL, opt_bind_address_cb },
   1084    */
   1085   { '-', "c",          0,   OPT_TYPE_FUNC,   NULL,                                   opt_class_cb    },
   1086   /* -f file */
   1087   { '-', "h",          0,   OPT_TYPE_BOOL,   &global_config.is_help,                 NULL            },
   1088   /* -k keyfile */
   1089   /* -m (memory usage debugging) */
   1090   { '-', "p",          0,   OPT_TYPE_U16,    &global_config.opts.port,               NULL            },
   1091   { '-', "q",          0,   OPT_TYPE_STRING, &global_config.name,                    NULL            },
   1092   { '-', "r",          0,   OPT_TYPE_BOOL,   &global_config.no_rcfile,               NULL            },
   1093   { '-', "s",          0,   OPT_TYPE_STRING, &global_config.servers,                 NULL            },
   1094   { '-', "t",          0,   OPT_TYPE_FUNC,   NULL,                                   opt_type_cb     },
   1095   /* -u (print microseconds instead of milliseconds) */
   1096   { '-', "x",          0,   OPT_TYPE_FUNC,   NULL,                                   opt_ptr_cb      },
   1097   /* -y [hmac:]keynam:secret */
   1098   { '+', "aaflag",     0,   OPT_TYPE_BOOL,   &global_config.opts.aa_flag,            NULL            },
   1099   { '+', "aaonly",     0,   OPT_TYPE_BOOL,   &global_config.opts.aa_flag,            NULL            },
   1100   { '+', "additional", 0,   OPT_TYPE_BOOL,   &global_config.opts.display_additional,
   1101    NULL                                                                                              },
   1102   { '+', "adflag",     0,   OPT_TYPE_BOOL,   &global_config.opts.ad_flag,            NULL            },
   1103   { '+', "aliases",    0,   OPT_TYPE_BOOL,   &global_config.opts.aliases,            NULL            },
   1104   { '+', "all",        '=', OPT_TYPE_FUNC,   NULL,                                   opt_all_cb      },
   1105   { '+', "answer",     0,   OPT_TYPE_BOOL,   &global_config.opts.display_answer,     NULL            },
   1106   { '+', "authority",  0,   OPT_TYPE_BOOL,   &global_config.opts.display_authority,
   1107    NULL                                                                                              },
   1108   { '+', "bufsize",    '=', OPT_TYPE_SIZE_T, &global_config.opts.udp_size,           NULL            },
   1109   { '+', "cdflag",     0,   OPT_TYPE_BOOL,   &global_config.opts.cd_flag,            NULL            },
   1110   { '+', "class",      0,   OPT_TYPE_BOOL,   &global_config.opts.display_class,      NULL            },
   1111   { '+', "cmd",        0,   OPT_TYPE_BOOL,   &global_config.opts.display_command,    NULL            },
   1112   { '+', "comments",   0,   OPT_TYPE_BOOL,   &global_config.opts.display_comments,
   1113    NULL                                                                                              },
   1114   { '+', "defname",    0,   OPT_TYPE_BOOL,   &global_config.opts.do_search,          NULL            },
   1115   { '+', "dns0x20",    0,   OPT_TYPE_BOOL,   &global_config.opts.dns0x20,            NULL            },
   1116   { '+', "domain",     '=', OPT_TYPE_STRING, &global_config.opts.search,             NULL            },
   1117   { '+', "edns",       '=', OPT_TYPE_FUNC,   NULL,                                   opt_edns_cb     },
   1118   { '+', "keepopen",   0,   OPT_TYPE_BOOL,   &global_config.opts.stayopen,           NULL            },
   1119   { '+', "ignore",     0,   OPT_TYPE_BOOL,   &global_config.opts.ignore_tc,          NULL            },
   1120   { '+', "ndots",      '=', OPT_TYPE_SIZE_T, &global_config.opts.ndots,              NULL            },
   1121   { '+', "primary",    0,   OPT_TYPE_BOOL,   &global_config.opts.primary,            NULL            },
   1122   { '+', "qr",         0,   OPT_TYPE_BOOL,   &global_config.opts.display_query,      NULL            },
   1123   { '+', "question",   0,   OPT_TYPE_BOOL,   &global_config.opts.display_question,
   1124    NULL                                                                                              },
   1125   { '+', "recurse",    0,   OPT_TYPE_BOOL,   &global_config.opts.rd_flag,            NULL            },
   1126   { '+', "retry",      '=', OPT_TYPE_FUNC,   NULL,                                   opt_retry_cb    },
   1127   { '+', "search",     0,   OPT_TYPE_BOOL,   &global_config.opts.do_search,          NULL            },
   1128   { '+', "stats",      0,   OPT_TYPE_BOOL,   &global_config.opts.display_stats,      NULL            },
   1129   { '+', "tcp",        0,   OPT_TYPE_BOOL,   &global_config.opts.tcp,                NULL            },
   1130   { '+', "tries",      '=', OPT_TYPE_SIZE_T, &global_config.opts.tries,              NULL            },
   1131   { '+', "ttlid",      0,   OPT_TYPE_BOOL,   &global_config.opts.display_ttl,        NULL            },
   1132   { '+', "vc",         0,   OPT_TYPE_BOOL,   &global_config.opts.tcp,                NULL            },
   1133   { 0,   NULL,         0,   OPT_TYPE_FUNC,   NULL,                                   opt_dig_bare_cb },
   1134   { 0,   NULL,         0,   0,               NULL,                                   NULL            }
   1135 };
   1136 
   1137 static ares_bool_t read_cmdline(int argc, const char * const *argv,
   1138                                 int start_idx)
   1139 {
   1140   int    arg;
   1141   size_t opt;
   1142 
   1143   for (arg = start_idx; arg < argc; arg++) {
   1144     ares_bool_t option_handled = ARES_FALSE;
   1145 
   1146     for (opt = 0; !option_handled &&
   1147                   (dig_options[opt].opt != NULL || dig_options[opt].cb != NULL);
   1148          opt++) {
   1149       ares_bool_t is_true = ARES_TRUE;
   1150       const char *value   = NULL;
   1151       const char *nameptr = NULL;
   1152       size_t      namelen;
   1153 
   1154       /* Match prefix character */
   1155       if (dig_options[opt].prefix != 0 &&
   1156           dig_options[opt].prefix != *(argv[arg])) {
   1157         continue;
   1158       }
   1159 
   1160       nameptr = argv[arg];
   1161 
   1162       /* skip prefix */
   1163       if (dig_options[opt].prefix != 0) {
   1164         nameptr++;
   1165       }
   1166 
   1167       /* Negated option if it has a 'no' prefix */
   1168       if (ares_streq_max(nameptr, "no", 2)) {
   1169         is_true  = ARES_FALSE;
   1170         nameptr += 2;
   1171       }
   1172 
   1173       if (dig_options[opt].separator != 0) {
   1174         const char *ptr = strchr(nameptr, dig_options[opt].separator);
   1175         if (ptr == NULL) {
   1176           namelen = ares_strlen(nameptr);
   1177         } else {
   1178           namelen = (size_t)(ptr - nameptr);
   1179           value   = ptr + 1;
   1180         }
   1181       } else {
   1182         namelen = ares_strlen(nameptr);
   1183       }
   1184 
   1185       /* Match name */
   1186       if (dig_options[opt].name != NULL &&
   1187           !ares_streq_max(nameptr, dig_options[opt].name, namelen)) {
   1188         continue;
   1189       }
   1190 
   1191       if (dig_options[opt].name == NULL) {
   1192         value = nameptr;
   1193       }
   1194 
   1195       /* We need another argument for the value */
   1196       if (dig_options[opt].type != OPT_TYPE_BOOL &&
   1197           dig_options[opt].prefix != 0 && dig_options[opt].separator == 0) {
   1198         if (arg == argc - 1) {
   1199           snprintf(global_config.error, sizeof(global_config.error),
   1200                    "insufficient arguments for %c%s", dig_options[opt].prefix,
   1201                    dig_options[opt].name);
   1202           return ARES_FALSE;
   1203         }
   1204         arg++;
   1205         value = argv[arg];
   1206       }
   1207 
   1208       switch (dig_options[opt].type) {
   1209         case OPT_TYPE_BOOL:
   1210           {
   1211             ares_bool_t *b = dig_options[opt].opt;
   1212             if (b == NULL) {
   1213               snprintf(global_config.error, sizeof(global_config.error),
   1214                        "invalid use for %c%s", dig_options[opt].prefix,
   1215                        dig_options[opt].name);
   1216               return ARES_FALSE;
   1217             }
   1218             *b = is_true;
   1219           }
   1220           break;
   1221         case OPT_TYPE_STRING:
   1222           {
   1223             char **str = dig_options[opt].opt;
   1224             if (str == NULL) {
   1225               snprintf(global_config.error, sizeof(global_config.error),
   1226                        "invalid use for %c%s", dig_options[opt].prefix,
   1227                        dig_options[opt].name);
   1228               return ARES_FALSE;
   1229             }
   1230             if (value == NULL) {
   1231               snprintf(global_config.error, sizeof(global_config.error),
   1232                        "missing value for %c%s", dig_options[opt].prefix,
   1233                        dig_options[opt].name);
   1234               return ARES_FALSE;
   1235             }
   1236             if (*str != NULL) {
   1237               free(*str);
   1238             }
   1239             *str = strdup(value);
   1240             break;
   1241           }
   1242         case OPT_TYPE_SIZE_T:
   1243           {
   1244             size_t *s = dig_options[opt].opt;
   1245             if (s == NULL) {
   1246               snprintf(global_config.error, sizeof(global_config.error),
   1247                        "invalid use for %c%s", dig_options[opt].prefix,
   1248                        dig_options[opt].name);
   1249               return ARES_FALSE;
   1250             }
   1251             if (value == NULL) {
   1252               snprintf(global_config.error, sizeof(global_config.error),
   1253                        "missing value for %c%s", dig_options[opt].prefix,
   1254                        dig_options[opt].name);
   1255               return ARES_FALSE;
   1256             }
   1257             if (!ares_str_isnum(value)) {
   1258               snprintf(global_config.error, sizeof(global_config.error),
   1259                        "%c%s is not a numeric value", dig_options[opt].prefix,
   1260                        dig_options[opt].name);
   1261               return ARES_FALSE;
   1262             }
   1263             *s = strtoul(value, NULL, 10);
   1264             break;
   1265           }
   1266         case OPT_TYPE_U16:
   1267           {
   1268             unsigned short *s = dig_options[opt].opt;
   1269             if (s == NULL) {
   1270               snprintf(global_config.error, sizeof(global_config.error),
   1271                        "invalid use for %c%s", dig_options[opt].prefix,
   1272                        dig_options[opt].name);
   1273               return ARES_FALSE;
   1274             }
   1275             if (value == NULL) {
   1276               snprintf(global_config.error, sizeof(global_config.error),
   1277                        "missing value for %c%s", dig_options[opt].prefix,
   1278                        dig_options[opt].name);
   1279               return ARES_FALSE;
   1280             }
   1281             if (!ares_str_isnum(value)) {
   1282               snprintf(global_config.error, sizeof(global_config.error),
   1283                        "%c%s is not a numeric value", dig_options[opt].prefix,
   1284                        dig_options[opt].name);
   1285               return ARES_FALSE;
   1286             }
   1287             *s = (unsigned short)strtoul(value, NULL, 10);
   1288             break;
   1289           }
   1290         case OPT_TYPE_FUNC:
   1291           if (dig_options[opt].cb == NULL) {
   1292             snprintf(global_config.error, sizeof(global_config.error),
   1293                      "missing callback");
   1294             return ARES_FALSE;
   1295           }
   1296           if (!dig_options[opt].cb(dig_options[opt].prefix,
   1297                                    dig_options[opt].name, is_true, value)) {
   1298             return ARES_FALSE;
   1299           }
   1300           break;
   1301       }
   1302       option_handled = ARES_TRUE;
   1303     }
   1304 
   1305     if (!option_handled) {
   1306       snprintf(global_config.error, sizeof(global_config.error),
   1307                "unrecognized option %s", argv[arg]);
   1308       return ARES_FALSE;
   1309     }
   1310   }
   1311 
   1312   return ARES_TRUE;
   1313 }
   1314 
   1315 static ares_bool_t read_rcfile(void)
   1316 {
   1317   char         configdir[PATH_MAX];
   1318   unsigned int cdlen = 0;
   1319 
   1320 #if !defined(WIN32)
   1321 #  if !defined(__APPLE__)
   1322   char *configdir_xdg;
   1323 #  endif
   1324   char *homedir;
   1325 #endif
   1326 
   1327   char          rcfile[PATH_MAX];
   1328   unsigned int  rclen;
   1329 
   1330   size_t        rcargc;
   1331   char        **rcargv;
   1332   ares_buf_t   *rcbuf;
   1333   ares_status_t rcstatus;
   1334 
   1335 #if defined(WIN32)
   1336   cdlen = (unsigned int)snprintf(configdir, sizeof(configdir), "%s/%s",
   1337                                  getenv("APPDATA"), "c-ares");
   1338 
   1339 #elif defined(__APPLE__)
   1340   homedir = getenv("HOME");
   1341   if (homedir != NULL) {
   1342     cdlen = (unsigned int)snprintf(configdir, sizeof(configdir), "%s/%s/%s/%s",
   1343                                    homedir, "Library", "Application Support",
   1344                                    "c-ares");
   1345   }
   1346 
   1347 #else
   1348   configdir_xdg = getenv("XDG_CONFIG_HOME");
   1349 
   1350   if (configdir_xdg == NULL) {
   1351     homedir = getenv("HOME");
   1352     if (homedir != NULL) {
   1353       cdlen = (unsigned int)snprintf(configdir, sizeof(configdir), "%s/%s",
   1354                                      homedir, ".config");
   1355     }
   1356   } else {
   1357     cdlen =
   1358       (unsigned int)snprintf(configdir, sizeof(configdir), "%s", configdir_xdg);
   1359   }
   1360 
   1361 #endif
   1362 
   1363   DEBUGF(fprintf(stderr, "read_cmdline() configdir: %s\n", configdir));
   1364 
   1365   if (cdlen == 0 || cdlen > sizeof(configdir)) {
   1366     DEBUGF(
   1367       fprintf(stderr, "read_cmdline() skipping rcfile parsing on directory\n"));
   1368     return ARES_TRUE;
   1369   }
   1370 
   1371   rclen =
   1372     (unsigned int)snprintf(rcfile, sizeof(rcfile), "%s/adigrc", configdir);
   1373 
   1374   if (rclen > sizeof(rcfile)) {
   1375     DEBUGF(fprintf(stderr, "read_cmdline() skipping rcfile parsing on file\n"));
   1376     return ARES_TRUE;
   1377   }
   1378 
   1379   rcbuf = ares_buf_create();
   1380   if (ares_buf_load_file(rcfile, rcbuf) == ARES_SUCCESS) {
   1381     rcstatus = ares_buf_split_str(rcbuf, (const unsigned char *)"\n ", 2,
   1382                                   ARES_BUF_SPLIT_TRIM, 0, &rcargv, &rcargc);
   1383 
   1384     if (rcstatus == ARES_SUCCESS) {
   1385       read_cmdline((int)rcargc, (const char * const *)rcargv, 0);
   1386 
   1387     } else {
   1388       snprintf(global_config.error, sizeof(global_config.error),
   1389                "rcfile is invalid: %s", ares_strerror((int)rcstatus));
   1390     }
   1391 
   1392     ares_free_array(rcargv, rcargc, ares_free);
   1393 
   1394     if (rcstatus != ARES_SUCCESS) {
   1395       ares_buf_destroy(rcbuf);
   1396       return ARES_FALSE;
   1397     }
   1398 
   1399   } else {
   1400     DEBUGF(fprintf(stderr, "read_cmdline() failed to load rcfile"));
   1401   }
   1402   ares_buf_destroy(rcbuf);
   1403 
   1404   return ARES_TRUE;
   1405 }
   1406 
   1407 static void config_defaults(void)
   1408 {
   1409   memset(&global_config, 0, sizeof(global_config));
   1410 
   1411   global_config.opts.tries              = 3;
   1412   global_config.opts.ndots              = 1;
   1413   global_config.opts.rd_flag            = ARES_TRUE;
   1414   global_config.opts.edns               = ARES_TRUE;
   1415   global_config.opts.udp_size           = 1232;
   1416   global_config.opts.aliases            = ARES_TRUE;
   1417   global_config.opts.display_class      = ARES_TRUE;
   1418   global_config.opts.display_ttl        = ARES_TRUE;
   1419   global_config.opts.display_command    = ARES_TRUE;
   1420   global_config.opts.display_stats      = ARES_TRUE;
   1421   global_config.opts.display_question   = ARES_TRUE;
   1422   global_config.opts.display_answer     = ARES_TRUE;
   1423   global_config.opts.display_authority  = ARES_TRUE;
   1424   global_config.opts.display_additional = ARES_TRUE;
   1425   global_config.opts.display_comments   = ARES_TRUE;
   1426   global_config.qclass                  = ARES_CLASS_IN;
   1427   global_config.qtype                   = ARES_REC_TYPE_A;
   1428 }
   1429 
   1430 static void config_opts(void)
   1431 {
   1432   global_config.optmask = ARES_OPT_FLAGS;
   1433   if (global_config.opts.tcp) {
   1434     global_config.options.flags |= ARES_FLAG_USEVC;
   1435   }
   1436   if (global_config.opts.primary) {
   1437     global_config.options.flags |= ARES_FLAG_PRIMARY;
   1438   }
   1439   if (global_config.opts.edns) {
   1440     global_config.options.flags |= ARES_FLAG_EDNS;
   1441   }
   1442   if (global_config.opts.stayopen) {
   1443     global_config.options.flags |= ARES_FLAG_STAYOPEN;
   1444   }
   1445   if (global_config.opts.dns0x20) {
   1446     global_config.options.flags |= ARES_FLAG_DNS0x20;
   1447   }
   1448   if (!global_config.opts.aliases) {
   1449     global_config.options.flags |= ARES_FLAG_NOALIASES;
   1450   }
   1451   if (!global_config.opts.rd_flag) {
   1452     global_config.options.flags |= ARES_FLAG_NORECURSE;
   1453   }
   1454   if (!global_config.opts.do_search) {
   1455     global_config.options.flags |= ARES_FLAG_NOSEARCH;
   1456   }
   1457   if (global_config.opts.ignore_tc) {
   1458     global_config.options.flags |= ARES_FLAG_IGNTC;
   1459   }
   1460   if (global_config.opts.port) {
   1461     global_config.optmask          |= ARES_OPT_UDP_PORT;
   1462     global_config.optmask          |= ARES_OPT_TCP_PORT;
   1463     global_config.options.udp_port  = global_config.opts.port;
   1464     global_config.options.tcp_port  = global_config.opts.port;
   1465   }
   1466 
   1467   global_config.optmask       |= ARES_OPT_TRIES;
   1468   global_config.options.tries  = (int)global_config.opts.tries;
   1469 
   1470   global_config.optmask       |= ARES_OPT_NDOTS;
   1471   global_config.options.ndots  = (int)global_config.opts.ndots;
   1472 
   1473   global_config.optmask         |= ARES_OPT_EDNSPSZ;
   1474   global_config.options.ednspsz  = (int)global_config.opts.udp_size;
   1475 
   1476   if (global_config.opts.search != NULL) {
   1477     global_config.optmask          |= ARES_OPT_DOMAINS;
   1478     global_config.options.domains   = &global_config.opts.search;
   1479     global_config.options.ndomains  = 1;
   1480   }
   1481 }
   1482 
   1483 int main(int argc, char **argv)
   1484 {
   1485   ares_channel_t *channel = NULL;
   1486   ares_status_t   status;
   1487   int             rv = 0;
   1488 
   1489 #ifdef USE_WINSOCK
   1490   WORD    wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
   1491   WSADATA wsaData;
   1492   WSAStartup(wVersionRequested, &wsaData);
   1493 #endif
   1494 
   1495   status = (ares_status_t)ares_library_init(ARES_LIB_INIT_ALL);
   1496   if (status != ARES_SUCCESS) {
   1497     fprintf(stderr, "ares_library_init: %s\n", ares_strerror((int)status));
   1498     return 1;
   1499   }
   1500 
   1501   config_defaults();
   1502 
   1503   if (!read_cmdline(argc, (const char * const *)argv, 1)) {
   1504     printf("\n** ERROR: %s\n\n", global_config.error);
   1505     print_help();
   1506     rv = 1;
   1507     goto done;
   1508   }
   1509 
   1510   if (global_config.no_rcfile && !read_rcfile()) {
   1511     fprintf(stderr, "\n** ERROR: %s\n", global_config.error);
   1512   }
   1513 
   1514   if (global_config.is_help) {
   1515     print_help();
   1516     goto done;
   1517   }
   1518 
   1519   if (global_config.name == NULL) {
   1520     printf("missing query name\n");
   1521     print_help();
   1522     rv = 1;
   1523     goto done;
   1524   }
   1525 
   1526   config_opts();
   1527 
   1528   status = (ares_status_t)ares_init_options(&channel, &global_config.options,
   1529                                             global_config.optmask);
   1530   if (status != ARES_SUCCESS) {
   1531     fprintf(stderr, "ares_init_options: %s\n", ares_strerror((int)status));
   1532     rv = 1;
   1533     goto done;
   1534   }
   1535 
   1536   if (global_config.servers) {
   1537     status =
   1538       (ares_status_t)ares_set_servers_ports_csv(channel, global_config.servers);
   1539     if (status != ARES_SUCCESS) {
   1540       fprintf(stderr, "ares_set_servers_ports_csv: %s: %s\n",
   1541               ares_strerror((int)status), global_config.servers);
   1542       rv = 1;
   1543       goto done;
   1544     }
   1545   }
   1546 
   1547   /* Debug */
   1548   if (global_config.opts.display_command) {
   1549     printf("\n; <<>> c-ares DiG %s <<>>", ares_version(NULL));
   1550     printf(" %s", global_config.name);
   1551     printf("\n");
   1552   }
   1553 
   1554   /* Enqueue a query for each separate name */
   1555   status = enqueue_query(channel);
   1556   if (status != ARES_SUCCESS) {
   1557     fprintf(stderr, "Failed to create query for %s: %s\n", global_config.name,
   1558             ares_strerror((int)status));
   1559     rv = 1;
   1560     goto done;
   1561   }
   1562 
   1563   /* Process events */
   1564   rv = event_loop(channel);
   1565 
   1566 done:
   1567   free_config();
   1568   ares_destroy(channel);
   1569   ares_library_cleanup();
   1570 
   1571 #ifdef USE_WINSOCK
   1572   WSACleanup();
   1573 #endif
   1574   return rv;
   1575 }