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