• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
free_config(void)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 
print_help(void)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 
print_flags(ares_dns_flags_t flags)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 
print_header(const ares_dns_record_t * dnsrec)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 
print_question(const ares_dns_record_t * dnsrec)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 
print_opt_none(const unsigned char * val,size_t val_len)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 
print_opt_addr_list(const unsigned char * val,size_t val_len)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 
print_opt_addr6_list(const unsigned char * val,size_t val_len)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 
print_opt_u8_list(const unsigned char * val,size_t val_len)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 
print_opt_u16_list(const unsigned char * val,size_t val_len)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 
print_opt_u32_list(const unsigned char * val,size_t val_len)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 
print_opt_str_list(const unsigned char * val,size_t val_len)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 
print_opt_name(const unsigned char * val,size_t val_len)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 
print_opt_bin(const unsigned char * val,size_t val_len)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 
adig_isprint(int ch)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 
print_opt_binp(const unsigned char * val,size_t val_len)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 
print_opts(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_addr(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_addr6(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_u8(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_u16(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_u32(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_name(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_str(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_bin(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_binp(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_abinp(const ares_dns_rr_t * rr,ares_dns_rr_key_t key)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 
print_rr(const ares_dns_rr_t * rr)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 
has_opt(const ares_dns_record_t * dnsrec,ares_dns_section_t section)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 
print_section(const ares_dns_record_t * dnsrec,ares_dns_section_t section)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 
print_opt_psuedosection(const ares_dns_record_t * dnsrec)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 
print_record(const ares_dns_record_t * dnsrec)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 
callback(void * arg,ares_status_t status,size_t timeouts,const ares_dns_record_t * dnsrec)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 
enqueue_query(ares_channel_t * channel)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 
event_loop(ares_channel_t * channel)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 
opt_class_cb(char prefix,const char * name,ares_bool_t is_true,const char * value)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 
opt_type_cb(char prefix,const char * name,ares_bool_t is_true,const char * value)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 
opt_ptr_cb(char prefix,const char * name,ares_bool_t is_true,const char * value)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 
opt_all_cb(char prefix,const char * name,ares_bool_t is_true,const char * value)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 
opt_edns_cb(char prefix,const char * name,ares_bool_t is_true,const char * value)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 
opt_retry_cb(char prefix,const char * name,ares_bool_t is_true,const char * value)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 
opt_dig_bare_cb(char prefix,const char * name,ares_bool_t is_true,const char * value)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 
read_cmdline(int argc,const char * const * argv,int start_idx)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 
read_rcfile(void)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 
config_defaults(void)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 
config_opts(void)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 
main(int argc,char ** argv)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 }
1576