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