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