1 /* Copyright 1998 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose. It is provided "as is"
13 * without express or implied warranty.
14 */
15
16 static const char rcsid[] = "$Id: adig.c,v 1.9 2001/05/18 20:59:51 ghudson Exp $";
17
18 #include <sys/types.h>
19 #include <sys/time.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include "ares.h"
32 #include "ares_dns.h"
33
34 #ifndef INADDR_NONE
35 #define INADDR_NONE 0xffffffff
36 #endif
37
38 extern int optind;
39 extern char *optarg;
40
41 struct nv {
42 const char *name;
43 int value;
44 };
45
46 static const struct nv flags[] = {
47 { "usevc", ARES_FLAG_USEVC },
48 { "primary", ARES_FLAG_PRIMARY },
49 { "igntc", ARES_FLAG_IGNTC },
50 { "norecurse", ARES_FLAG_NORECURSE },
51 { "stayopen", ARES_FLAG_STAYOPEN },
52 { "noaliases", ARES_FLAG_NOALIASES }
53 };
54 static const int nflags = sizeof(flags) / sizeof(flags[0]);
55
56 static const struct nv classes[] = {
57 { "IN", C_IN },
58 { "CHAOS", C_CHAOS },
59 { "HS", C_HS },
60 { "ANY", C_ANY }
61 };
62 static const int nclasses = sizeof(classes) / sizeof(classes[0]);
63
64 static const struct nv types[] = {
65 { "A", T_A },
66 { "NS", T_NS },
67 { "MD", T_MD },
68 { "MF", T_MF },
69 { "CNAME", T_CNAME },
70 { "SOA", T_SOA },
71 { "MB", T_MB },
72 { "MG", T_MG },
73 { "MR", T_MR },
74 { "NULL", T_NULL },
75 { "WKS", T_WKS },
76 { "PTR", T_PTR },
77 { "HINFO", T_HINFO },
78 { "MINFO", T_MINFO },
79 { "MX", T_MX },
80 { "TXT", T_TXT },
81 { "RP", T_RP },
82 { "AFSDB", T_AFSDB },
83 { "X25", T_X25 },
84 { "ISDN", T_ISDN },
85 { "RT", T_RT },
86 { "NSAP", T_NSAP },
87 { "NSAP_PTR", T_NSAP_PTR },
88 { "SIG", T_SIG },
89 { "KEY", T_KEY },
90 { "PX", T_PX },
91 { "GPOS", T_GPOS },
92 { "AAAA", T_AAAA },
93 { "LOC", T_LOC },
94 { "SRV", T_SRV },
95 { "AXFR", T_AXFR },
96 { "MAILB", T_MAILB },
97 { "MAILA", T_MAILA },
98 { "ANY", T_ANY }
99 };
100 static const int ntypes = sizeof(types) / sizeof(types[0]);
101
102 static const char *opcodes[] = {
103 "QUERY", "IQUERY", "STATUS", "(reserved)", "NOTIFY",
104 "(unknown)", "(unknown)", "(unknown)", "(unknown)",
105 "UPDATEA", "UPDATED", "UPDATEDA", "UPDATEM", "UPDATEMA",
106 "ZONEINIT", "ZONEREF"
107 };
108
109 static const char *rcodes[] = {
110 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED",
111 "(unknown)", "(unknown)", "(unknown)", "(unknown)", "(unknown)",
112 "(unknown)", "(unknown)", "(unknown)", "(unknown)", "NOCHANGE"
113 };
114
115 static void callback(void *arg, int status, unsigned char *abuf, int alen);
116 static const unsigned char *display_question(const unsigned char *aptr,
117 const unsigned char *abuf,
118 int alen);
119 static const unsigned char *display_rr(const unsigned char *aptr,
120 const unsigned char *abuf, int alen);
121 static const char *type_name(int type);
122 static const char *class_name(int dnsclass);
123 static void usage(void);
124
main(int argc,char ** argv)125 int main(int argc, char **argv)
126 {
127 ares_channel channel;
128 int c, i, optmask = ARES_OPT_FLAGS, dnsclass = C_IN, type = T_A;
129 int status, nfds, count;
130 struct ares_options options;
131 struct hostent *hostent;
132 fd_set read_fds, write_fds;
133 struct timeval *tvp, tv;
134 char *errmem;
135
136 options.flags = ARES_FLAG_NOCHECKRESP;
137 options.servers = NULL;
138 options.nservers = 0;
139 while ((c = getopt(argc, argv, "f:s:c:t:T:U:")) != -1)
140 {
141 switch (c)
142 {
143 case 'f':
144 /* Add a flag. */
145 for (i = 0; i < nflags; i++)
146 {
147 if (strcmp(flags[i].name, optarg) == 0)
148 break;
149 }
150 if (i == nflags)
151 usage();
152 options.flags |= flags[i].value;
153 break;
154
155 case 's':
156 /* Add a server, and specify servers in the option mask. */
157 hostent = gethostbyname(optarg);
158 if (!hostent || hostent->h_addrtype != AF_INET)
159 {
160 fprintf(stderr, "adig: server %s not found.\n", optarg);
161 return 1;
162 }
163 options.servers = realloc(options.servers, (options.nservers + 1)
164 * sizeof(struct in_addr));
165 if (!options.servers)
166 {
167 fprintf(stderr, "Out of memory!\n");
168 return 1;
169 }
170 memcpy(&options.servers[options.nservers], hostent->h_addr,
171 sizeof(struct in_addr));
172 options.nservers++;
173 optmask |= ARES_OPT_SERVERS;
174 break;
175
176 case 'c':
177 /* Set the query class. */
178 for (i = 0; i < nclasses; i++)
179 {
180 if (strcasecmp(classes[i].name, optarg) == 0)
181 break;
182 }
183 if (i == nclasses)
184 usage();
185 dnsclass = classes[i].value;
186 break;
187
188 case 't':
189 /* Set the query type. */
190 for (i = 0; i < ntypes; i++)
191 {
192 if (strcasecmp(types[i].name, optarg) == 0)
193 break;
194 }
195 if (i == ntypes)
196 usage();
197 type = types[i].value;
198 break;
199
200 case 'T':
201 /* Set the TCP port number. */
202 if (!isdigit((unsigned char)*optarg))
203 usage();
204 options.tcp_port = strtol(optarg, NULL, 0);
205 optmask |= ARES_OPT_TCP_PORT;
206 break;
207
208 case 'U':
209 /* Set the UDP port number. */
210 if (!isdigit((unsigned char)*optarg))
211 usage();
212 options.udp_port = strtol(optarg, NULL, 0);
213 optmask |= ARES_OPT_UDP_PORT;
214 break;
215 }
216 }
217 argc -= optind;
218 argv += optind;
219 if (argc == 0)
220 usage();
221
222 status = ares_init_options(&channel, &options, optmask);
223 if (status != ARES_SUCCESS)
224 {
225 fprintf(stderr, "ares_init_options: %s\n",
226 ares_strerror(status, &errmem));
227 ares_free_errmem(errmem);
228 return 1;
229 }
230
231 /* Initiate the queries, one per command-line argument. If there is
232 * only one query to do, supply NULL as the callback argument;
233 * otherwise, supply the query name as an argument so we can
234 * distinguish responses for the user when printing them out.
235 */
236 if (argc == 1)
237 ares_query(channel, *argv, dnsclass, type, callback, (char *) NULL);
238 else
239 {
240 for (; *argv; argv++)
241 ares_query(channel, *argv, dnsclass, type, callback, *argv);
242 }
243
244 /* Wait for all queries to complete. */
245 while (1)
246 {
247 FD_ZERO(&read_fds);
248 FD_ZERO(&write_fds);
249 nfds = ares_fds(channel, &read_fds, &write_fds);
250 if (nfds == 0)
251 break;
252 tvp = ares_timeout(channel, NULL, &tv);
253 count = select(nfds, &read_fds, &write_fds, NULL, tvp);
254 if (count < 0 && errno != EINVAL)
255 {
256 perror("select");
257 return 1;
258 }
259 ares_process(channel, &read_fds, &write_fds);
260 }
261
262 ares_destroy(channel);
263 return 0;
264 }
265
callback(void * arg,int status,unsigned char * abuf,int alen)266 static void callback(void *arg, int status, unsigned char *abuf, int alen)
267 {
268 char *name = (char *) arg, *errmem;
269 int id, qr, opcode, aa, tc, rd, ra, rcode, i;
270 unsigned int qdcount, ancount, nscount, arcount;
271 const unsigned char *aptr;
272
273 /* Display the query name if given. */
274 if (name)
275 printf("Answer for query %s:\n", name);
276
277 /* Display an error message if there was an error, but only stop if
278 * we actually didn't get an answer buffer.
279 */
280 if (status != ARES_SUCCESS)
281 {
282 printf("%s\n", ares_strerror(status, &errmem));
283 ares_free_errmem(errmem);
284 if (!abuf)
285 return;
286 }
287
288 /* Won't happen, but check anyway, for safety. */
289 if (alen < HFIXEDSZ)
290 return;
291
292 /* Parse the answer header. */
293 id = DNS_HEADER_QID(abuf);
294 qr = DNS_HEADER_QR(abuf);
295 opcode = DNS_HEADER_OPCODE(abuf);
296 aa = DNS_HEADER_AA(abuf);
297 tc = DNS_HEADER_TC(abuf);
298 rd = DNS_HEADER_RD(abuf);
299 ra = DNS_HEADER_RA(abuf);
300 rcode = DNS_HEADER_RCODE(abuf);
301 qdcount = DNS_HEADER_QDCOUNT(abuf);
302 ancount = DNS_HEADER_ANCOUNT(abuf);
303 nscount = DNS_HEADER_NSCOUNT(abuf);
304 arcount = DNS_HEADER_ARCOUNT(abuf);
305
306 /* Display the answer header. */
307 printf("id: %d\n", id);
308 printf("flags: %s%s%s%s%s\n",
309 qr ? "qr " : "",
310 aa ? "aa " : "",
311 tc ? "tc " : "",
312 rd ? "rd " : "",
313 ra ? "ra " : "");
314 printf("opcode: %s\n", opcodes[opcode]);
315 printf("rcode: %s\n", rcodes[rcode]);
316
317 /* Display the questions. */
318 printf("Questions:\n");
319 aptr = abuf + HFIXEDSZ;
320 for (i = 0; i < qdcount; i++)
321 {
322 aptr = display_question(aptr, abuf, alen);
323 if (aptr == NULL)
324 return;
325 }
326
327 /* Display the answers. */
328 printf("Answers:\n");
329 for (i = 0; i < ancount; i++)
330 {
331 aptr = display_rr(aptr, abuf, alen);
332 if (aptr == NULL)
333 return;
334 }
335
336 /* Display the NS records. */
337 printf("NS records:\n");
338 for (i = 0; i < nscount; i++)
339 {
340 aptr = display_rr(aptr, abuf, alen);
341 if (aptr == NULL)
342 return;
343 }
344
345 /* Display the additional records. */
346 printf("Additional records:\n");
347 for (i = 0; i < arcount; i++)
348 {
349 aptr = display_rr(aptr, abuf, alen);
350 if (aptr == NULL)
351 return;
352 }
353 }
354
display_question(const unsigned char * aptr,const unsigned char * abuf,int alen)355 static const unsigned char *display_question(const unsigned char *aptr,
356 const unsigned char *abuf,
357 int alen)
358 {
359 char *name;
360 int type, dnsclass, status, len;
361
362 /* Parse the question name. */
363 status = ares_expand_name(aptr, abuf, alen, &name, &len);
364 if (status != ARES_SUCCESS)
365 return NULL;
366 aptr += len;
367
368 /* Make sure there's enough data after the name for the fixed part
369 * of the question.
370 */
371 if (aptr + QFIXEDSZ > abuf + alen)
372 {
373 free(name);
374 return NULL;
375 }
376
377 /* Parse the question type and class. */
378 type = DNS_QUESTION_TYPE(aptr);
379 dnsclass = DNS_QUESTION_CLASS(aptr);
380 aptr += QFIXEDSZ;
381
382 /* Display the question, in a format sort of similar to how we will
383 * display RRs.
384 */
385 printf("\t%-15s.\t", name);
386 if (dnsclass != C_IN)
387 printf("\t%s", class_name(dnsclass));
388 printf("\t%s\n", type_name(type));
389 free(name);
390 return aptr;
391 }
392
display_rr(const unsigned char * aptr,const unsigned char * abuf,int alen)393 static const unsigned char *display_rr(const unsigned char *aptr,
394 const unsigned char *abuf, int alen)
395 {
396 const unsigned char *p;
397 char *name;
398 int type, dnsclass, ttl, dlen, status, len;
399 struct in_addr addr;
400
401 /* Parse the RR name. */
402 status = ares_expand_name(aptr, abuf, alen, &name, &len);
403 if (status != ARES_SUCCESS)
404 return NULL;
405 aptr += len;
406
407 /* Make sure there is enough data after the RR name for the fixed
408 * part of the RR.
409 */
410 if (aptr + RRFIXEDSZ > abuf + alen)
411 {
412 free(name);
413 return NULL;
414 }
415
416 /* Parse the fixed part of the RR, and advance to the RR data
417 * field. */
418 type = DNS_RR_TYPE(aptr);
419 dnsclass = DNS_RR_CLASS(aptr);
420 ttl = DNS_RR_TTL(aptr);
421 dlen = DNS_RR_LEN(aptr);
422 aptr += RRFIXEDSZ;
423 if (aptr + dlen > abuf + alen)
424 {
425 free(name);
426 return NULL;
427 }
428
429 /* Display the RR name, class, and type. */
430 printf("\t%-15s.\t%d", name, ttl);
431 if (dnsclass != C_IN)
432 printf("\t%s", class_name(dnsclass));
433 printf("\t%s", type_name(type));
434 free(name);
435
436 /* Display the RR data. Don't touch aptr. */
437 switch (type)
438 {
439 case T_CNAME:
440 case T_MB:
441 case T_MD:
442 case T_MF:
443 case T_MG:
444 case T_MR:
445 case T_NS:
446 case T_PTR:
447 /* For these types, the RR data is just a domain name. */
448 status = ares_expand_name(aptr, abuf, alen, &name, &len);
449 if (status != ARES_SUCCESS)
450 return NULL;
451 printf("\t%s.", name);
452 free(name);
453 break;
454
455 case T_HINFO:
456 /* The RR data is two length-counted character strings. */
457 p = aptr;
458 len = *p;
459 if (p + len + 1 > aptr + dlen)
460 return NULL;
461 printf("\t%.*s", len, p + 1);
462 p += len + 1;
463 len = *p;
464 if (p + len + 1 > aptr + dlen)
465 return NULL;
466 printf("\t%.*s", len, p + 1);
467 break;
468
469 case T_MINFO:
470 /* The RR data is two domain names. */
471 p = aptr;
472 status = ares_expand_name(p, abuf, alen, &name, &len);
473 if (status != ARES_SUCCESS)
474 return NULL;
475 printf("\t%s.", name);
476 free(name);
477 p += len;
478 status = ares_expand_name(p, abuf, alen, &name, &len);
479 if (status != ARES_SUCCESS)
480 return NULL;
481 printf("\t%s.", name);
482 free(name);
483 break;
484
485 case T_MX:
486 /* The RR data is two bytes giving a preference ordering, and
487 * then a domain name.
488 */
489 if (dlen < 2)
490 return NULL;
491 printf("\t%d", (aptr[0] << 8) | aptr[1]);
492 status = ares_expand_name(aptr + 2, abuf, alen, &name, &len);
493 if (status != ARES_SUCCESS)
494 return NULL;
495 printf("\t%s.", name);
496 free(name);
497 break;
498
499 case T_SOA:
500 /* The RR data is two domain names and then five four-byte
501 * numbers giving the serial number and some timeouts.
502 */
503 p = aptr;
504 status = ares_expand_name(p, abuf, alen, &name, &len);
505 if (status != ARES_SUCCESS)
506 return NULL;
507 printf("\t%s.\n", name);
508 free(name);
509 p += len;
510 status = ares_expand_name(p, abuf, alen, &name, &len);
511 if (status != ARES_SUCCESS)
512 return NULL;
513 printf("\t\t\t\t\t\t%s.\n", name);
514 free(name);
515 p += len;
516 if (p + 20 > aptr + dlen)
517 return NULL;
518 printf("\t\t\t\t\t\t( %d %d %d %d %d )",
519 (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3],
520 (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7],
521 (p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11],
522 (p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15],
523 (p[16] << 24) | (p[17] << 16) | (p[18] << 8) | p[19]);
524 break;
525
526 case T_TXT:
527 /* The RR data is one or more length-counted character
528 * strings. */
529 p = aptr;
530 while (p < aptr + dlen)
531 {
532 len = *p;
533 if (p + len + 1 > aptr + dlen)
534 return NULL;
535 printf("\t%.*s", len, p + 1);
536 p += len + 1;
537 }
538 break;
539
540 case T_A:
541 /* The RR data is a four-byte Internet address. */
542 if (dlen != 4)
543 return NULL;
544 memcpy(&addr, aptr, sizeof(struct in_addr));
545 printf("\t%s", inet_ntoa(addr));
546 break;
547
548 case T_WKS:
549 /* Not implemented yet */
550 break;
551
552 case T_SRV:
553 /* The RR data is three two-byte numbers representing the
554 * priority, weight, and port, followed by a domain name.
555 */
556
557 printf("\t%d", DNS__16BIT(aptr));
558 printf(" %d", DNS__16BIT(aptr + 2));
559 printf(" %d", DNS__16BIT(aptr + 4));
560
561 status = ares_expand_name(aptr + 6, abuf, alen, &name, &len);
562 if (status != ARES_SUCCESS)
563 return NULL;
564 printf("\t%s.", name);
565 free(name);
566 break;
567
568 default:
569 printf("\t[Unknown RR; cannot parse]");
570 }
571 printf("\n");
572
573 return aptr + dlen;
574 }
575
type_name(int type)576 static const char *type_name(int type)
577 {
578 int i;
579
580 for (i = 0; i < ntypes; i++)
581 {
582 if (types[i].value == type)
583 return types[i].name;
584 }
585 return "(unknown)";
586 }
587
class_name(int dnsclass)588 static const char *class_name(int dnsclass)
589 {
590 int i;
591
592 for (i = 0; i < nclasses; i++)
593 {
594 if (classes[i].value == dnsclass)
595 return classes[i].name;
596 }
597 return "(unknown)";
598 }
599
usage(void)600 static void usage(void)
601 {
602 fprintf(stderr, "usage: adig [-f flag] [-s server] [-c class] "
603 "[-t type] [-p port] name ...\n");
604 exit(1);
605 }
606