624 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			624 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright 1998 by the Massachusetts Institute of Technology.
 | |
|  *
 | |
|  * Permission to use, copy, modify, and distribute this
 | |
|  * software and its documentation for any purpose and without
 | |
|  * fee is hereby granted, provided that the above copyright
 | |
|  * notice appear in all copies and that both that copyright
 | |
|  * notice and this permission notice appear in supporting
 | |
|  * documentation, and that the name of M.I.T. not be used in
 | |
|  * advertising or publicity pertaining to distribution of the
 | |
|  * software without specific, written prior permission.
 | |
|  * M.I.T. makes no representations about the suitability of
 | |
|  * this software for any purpose.  It is provided "as is"
 | |
|  * without express or implied warranty.
 | |
|  */
 | |
| 
 | |
| static const char rcsid[] = "$Id$";
 | |
| 
 | |
| #include <sys/types.h>
 | |
| 
 | |
| #ifdef WIN32
 | |
| #include "nameser.h"
 | |
| #else
 | |
| #include <sys/time.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <arpa/nameser.h>
 | |
| #include <unistd.h>
 | |
| #include <netdb.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <ctype.h>
 | |
| #include <errno.h>
 | |
| #include "ares.h"
 | |
| #include "ares_dns.h"
 | |
| 
 | |
| #ifndef INADDR_NONE
 | |
| #define	INADDR_NONE 0xffffffff
 | |
| #endif
 | |
| 
 | |
| /* Mac OS X portability check */
 | |
| #ifndef T_SRV
 | |
| #define T_SRV 33 /* server selection */
 | |
| #endif
 | |
| 
 | |
| extern int optind;
 | |
| extern char *optarg;
 | |
| 
 | |
| struct nv {
 | |
|   const char *name;
 | |
|   int value;
 | |
| };
 | |
| 
 | |
| static const struct nv flags[] = {
 | |
|   { "usevc",		ARES_FLAG_USEVC },
 | |
|   { "primary",		ARES_FLAG_PRIMARY },
 | |
|   { "igntc",		ARES_FLAG_IGNTC },
 | |
|   { "norecurse",	ARES_FLAG_NORECURSE },
 | |
|   { "stayopen",		ARES_FLAG_STAYOPEN },
 | |
|   { "noaliases",	ARES_FLAG_NOALIASES }
 | |
| };
 | |
| static const int nflags = sizeof(flags) / sizeof(flags[0]);
 | |
| 
 | |
| static const struct nv classes[] = {
 | |
|   { "IN",	C_IN },
 | |
|   { "CHAOS",	C_CHAOS },
 | |
|   { "HS",	C_HS },
 | |
|   { "ANY",	C_ANY }
 | |
| };
 | |
| static const int nclasses = sizeof(classes) / sizeof(classes[0]);
 | |
| 
 | |
| static const struct nv types[] = {
 | |
|   { "A",	T_A },
 | |
|   { "NS",	T_NS },
 | |
|   { "MD",	T_MD },
 | |
|   { "MF",	T_MF },
 | |
|   { "CNAME",	T_CNAME },
 | |
|   { "SOA",	T_SOA },
 | |
|   { "MB",	T_MB },
 | |
|   { "MG",	T_MG },
 | |
|   { "MR",	T_MR },
 | |
|   { "NULL",	T_NULL },
 | |
|   { "WKS",	T_WKS },
 | |
|   { "PTR",	T_PTR },
 | |
|   { "HINFO",	T_HINFO },
 | |
|   { "MINFO",	T_MINFO },
 | |
|   { "MX",	T_MX },
 | |
|   { "TXT",	T_TXT },
 | |
|   { "RP",	T_RP },
 | |
|   { "AFSDB",	T_AFSDB },
 | |
|   { "X25",	T_X25 },
 | |
|   { "ISDN",	T_ISDN },
 | |
|   { "RT",	T_RT },
 | |
|   { "NSAP",	T_NSAP },
 | |
|   { "NSAP_PTR",	T_NSAP_PTR },
 | |
|   { "SIG",	T_SIG },
 | |
|   { "KEY",	T_KEY },
 | |
|   { "PX",	T_PX },
 | |
|   { "GPOS",	T_GPOS },
 | |
|   { "AAAA",	T_AAAA },
 | |
|   { "LOC",	T_LOC },
 | |
|   { "SRV",	T_SRV },
 | |
|   { "AXFR",	T_AXFR },
 | |
|   { "MAILB",	T_MAILB },
 | |
|   { "MAILA",	T_MAILA },
 | |
|   { "ANY",	T_ANY }
 | |
| };
 | |
| static const int ntypes = sizeof(types) / sizeof(types[0]);
 | |
| 
 | |
| static const char *opcodes[] = {
 | |
|   "QUERY", "IQUERY", "STATUS", "(reserved)", "NOTIFY",
 | |
|   "(unknown)", "(unknown)", "(unknown)", "(unknown)",
 | |
|   "UPDATEA", "UPDATED", "UPDATEDA", "UPDATEM", "UPDATEMA",
 | |
|   "ZONEINIT", "ZONEREF"
 | |
| };
 | |
| 
 | |
| static const char *rcodes[] = {
 | |
|   "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED",
 | |
|   "(unknown)", "(unknown)", "(unknown)", "(unknown)", "(unknown)",
 | |
|   "(unknown)", "(unknown)", "(unknown)", "(unknown)", "NOCHANGE"
 | |
| };
 | |
| 
 | |
| static void callback(void *arg, int status, unsigned char *abuf, int alen);
 | |
| static const unsigned char *display_question(const unsigned char *aptr,
 | |
| 					     const unsigned char *abuf,
 | |
| 					     int alen);
 | |
| static const unsigned char *display_rr(const unsigned char *aptr,
 | |
| 				       const unsigned char *abuf, int alen);
 | |
| static const char *type_name(int type);
 | |
| static const char *class_name(int dnsclass);
 | |
| static void usage(void);
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|   ares_channel channel;
 | |
|   int c, i, optmask = ARES_OPT_FLAGS, dnsclass = C_IN, type = T_A;
 | |
|   int status, nfds, count;
 | |
|   struct ares_options options;
 | |
|   struct hostent *hostent;
 | |
|   fd_set read_fds, write_fds;
 | |
|   struct timeval *tvp, tv;
 | |
|   char *errmem;
 | |
| 
 | |
| #ifdef WIN32
 | |
|   WORD wVersionRequested = MAKEWORD(1,1);
 | |
|   WSADATA wsaData;
 | |
|   WSAStartup(wVersionRequested, &wsaData);
 | |
| #endif  
 | |
| 
 | |
|   options.flags = ARES_FLAG_NOCHECKRESP;
 | |
|   options.servers = NULL;
 | |
|   options.nservers = 0;
 | |
|   while ((c = getopt(argc, argv, "f:s:c:t:T:U:")) != -1)
 | |
|     {
 | |
|       switch (c)
 | |
| 	{
 | |
| 	case 'f':
 | |
| 	  /* Add a flag. */
 | |
| 	  for (i = 0; i < nflags; i++)
 | |
| 	    {
 | |
| 	      if (strcmp(flags[i].name, optarg) == 0)
 | |
| 		break;
 | |
| 	    }
 | |
| 	  if (i == nflags)
 | |
| 	    usage();
 | |
| 	  options.flags |= flags[i].value;
 | |
| 	  break;
 | |
| 
 | |
| 	case 's':
 | |
| 	  /* Add a server, and specify servers in the option mask. */
 | |
| 	  hostent = gethostbyname(optarg);
 | |
| 	  if (!hostent || hostent->h_addrtype != AF_INET)
 | |
| 	    {
 | |
| 	      fprintf(stderr, "adig: server %s not found.\n", optarg);
 | |
| 	      return 1;
 | |
| 	    }
 | |
| 	  options.servers = realloc(options.servers, (options.nservers + 1)
 | |
| 				    * sizeof(struct in_addr));
 | |
| 	  if (!options.servers)
 | |
| 	    {
 | |
| 	      fprintf(stderr, "Out of memory!\n");
 | |
| 	      return 1;
 | |
| 	    }
 | |
| 	  memcpy(&options.servers[options.nservers], hostent->h_addr,
 | |
| 		 sizeof(struct in_addr));
 | |
| 	  options.nservers++;
 | |
| 	  optmask |= ARES_OPT_SERVERS;
 | |
| 	  break;
 | |
| 
 | |
| 	case 'c':
 | |
| 	  /* Set the query class. */
 | |
| 	  for (i = 0; i < nclasses; i++)
 | |
| 	    {
 | |
| 	      if (strcasecmp(classes[i].name, optarg) == 0)
 | |
| 		break;
 | |
| 	    }
 | |
| 	  if (i == nclasses)
 | |
| 	    usage();
 | |
| 	  dnsclass = classes[i].value;
 | |
| 	  break;
 | |
| 
 | |
| 	case 't':
 | |
| 	  /* Set the query type. */
 | |
| 	  for (i = 0; i < ntypes; i++)
 | |
| 	    {
 | |
| 	      if (strcasecmp(types[i].name, optarg) == 0)
 | |
| 		break;
 | |
| 	    }
 | |
| 	  if (i == ntypes)
 | |
| 	    usage();
 | |
| 	  type = types[i].value;
 | |
| 	  break;
 | |
| 
 | |
| 	case 'T':
 | |
| 	  /* Set the TCP port number. */
 | |
| 	  if (!isdigit((unsigned char)*optarg))
 | |
| 	    usage();
 | |
| 	  options.tcp_port = strtol(optarg, NULL, 0);
 | |
| 	  optmask |= ARES_OPT_TCP_PORT;
 | |
| 	  break;
 | |
| 
 | |
| 	case 'U':
 | |
| 	  /* Set the UDP port number. */
 | |
| 	  if (!isdigit((unsigned char)*optarg))
 | |
| 	    usage();
 | |
| 	  options.udp_port = strtol(optarg, NULL, 0);
 | |
| 	  optmask |= ARES_OPT_UDP_PORT;
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
|   argc -= optind;
 | |
|   argv += optind;
 | |
|   if (argc == 0)
 | |
|     usage();
 | |
| 
 | |
|   status = ares_init_options(&channel, &options, optmask);
 | |
| 
 | |
|   if (status != ARES_SUCCESS)
 | |
|     {
 | |
|       fprintf(stderr, "ares_init_options: %s\n",
 | |
| 	      ares_strerror(status, &errmem));
 | |
|       ares_free_errmem(errmem);
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   /* Initiate the queries, one per command-line argument.  If there is
 | |
|    * only one query to do, supply NULL as the callback argument;
 | |
|    * otherwise, supply the query name as an argument so we can
 | |
|    * distinguish responses for the user when printing them out.
 | |
|    */
 | |
|   if (argc == 1)
 | |
|     ares_query(channel, *argv, dnsclass, type, callback, (char *) NULL);
 | |
|   else
 | |
|     {
 | |
|       for (; *argv; argv++)
 | |
| 	ares_query(channel, *argv, dnsclass, type, callback, *argv);
 | |
|     }
 | |
| 
 | |
|   /* Wait for all queries to complete. */
 | |
|   while (1)
 | |
|     {
 | |
|       FD_ZERO(&read_fds);
 | |
|       FD_ZERO(&write_fds);
 | |
|       nfds = ares_fds(channel, &read_fds, &write_fds);
 | |
|       if (nfds == 0)
 | |
| 	break;
 | |
|       tvp = ares_timeout(channel, NULL, &tv);
 | |
|       count = select(nfds, &read_fds, &write_fds, NULL, tvp);
 | |
|       if (count < 0 && errno != EINVAL)
 | |
| 	{
 | |
| 	  perror("select");
 | |
| 	  return 1;
 | |
| 	}
 | |
|       ares_process(channel, &read_fds, &write_fds);
 | |
|     }
 | |
| 
 | |
|   ares_destroy(channel);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static void callback(void *arg, int status, unsigned char *abuf, int alen)
 | |
| {
 | |
|   char *name = (char *) arg, *errmem;
 | |
|   int id, qr, opcode, aa, tc, rd, ra, rcode, i;
 | |
|   unsigned int qdcount, ancount, nscount, arcount;
 | |
|   const unsigned char *aptr;
 | |
| 
 | |
|   /* Display the query name if given. */
 | |
|   if (name)
 | |
|     printf("Answer for query %s:\n", name);
 | |
| 
 | |
|   /* Display an error message if there was an error, but only stop if
 | |
|    * we actually didn't get an answer buffer.
 | |
|    */
 | |
|   if (status != ARES_SUCCESS)
 | |
|     {
 | |
|       printf("%s\n", ares_strerror(status, &errmem));
 | |
|       ares_free_errmem(errmem);
 | |
|       if (!abuf)
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|   /* Won't happen, but check anyway, for safety. */
 | |
|   if (alen < HFIXEDSZ)
 | |
|     return;
 | |
| 
 | |
|   /* Parse the answer header. */
 | |
|   id = DNS_HEADER_QID(abuf);
 | |
|   qr = DNS_HEADER_QR(abuf);
 | |
|   opcode = DNS_HEADER_OPCODE(abuf);
 | |
|   aa = DNS_HEADER_AA(abuf);
 | |
|   tc = DNS_HEADER_TC(abuf);
 | |
|   rd = DNS_HEADER_RD(abuf);
 | |
|   ra = DNS_HEADER_RA(abuf);
 | |
|   rcode = DNS_HEADER_RCODE(abuf);
 | |
|   qdcount = DNS_HEADER_QDCOUNT(abuf);
 | |
|   ancount = DNS_HEADER_ANCOUNT(abuf);
 | |
|   nscount = DNS_HEADER_NSCOUNT(abuf);
 | |
|   arcount = DNS_HEADER_ARCOUNT(abuf);
 | |
| 
 | |
|   /* Display the answer header. */
 | |
|   printf("id: %d\n", id);
 | |
|   printf("flags: %s%s%s%s%s\n",
 | |
| 	 qr ? "qr " : "",
 | |
| 	 aa ? "aa " : "",
 | |
| 	 tc ? "tc " : "",
 | |
| 	 rd ? "rd " : "",
 | |
| 	 ra ? "ra " : "");
 | |
|   printf("opcode: %s\n", opcodes[opcode]);
 | |
|   printf("rcode: %s\n", rcodes[rcode]);
 | |
| 
 | |
|   /* Display the questions. */
 | |
|   printf("Questions:\n");
 | |
|   aptr = abuf + HFIXEDSZ;
 | |
|   for (i = 0; i < qdcount; i++)
 | |
|     {
 | |
|       aptr = display_question(aptr, abuf, alen);
 | |
|       if (aptr == NULL)
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|   /* Display the answers. */
 | |
|   printf("Answers:\n");
 | |
|   for (i = 0; i < ancount; i++)
 | |
|     {
 | |
|       aptr = display_rr(aptr, abuf, alen);
 | |
|       if (aptr == NULL)
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|   /* Display the NS records. */
 | |
|   printf("NS records:\n");
 | |
|   for (i = 0; i < nscount; i++)
 | |
|     {
 | |
|       aptr = display_rr(aptr, abuf, alen);
 | |
|       if (aptr == NULL)
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|   /* Display the additional records. */
 | |
|   printf("Additional records:\n");
 | |
|   for (i = 0; i < arcount; i++)
 | |
|     {
 | |
|       aptr = display_rr(aptr, abuf, alen);
 | |
|       if (aptr == NULL)
 | |
| 	return;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const unsigned char *display_question(const unsigned char *aptr,
 | |
| 					     const unsigned char *abuf,
 | |
| 					     int alen)
 | |
| {
 | |
|   char *name;
 | |
|   int type, dnsclass, status, len;
 | |
| 
 | |
|   /* Parse the question name. */
 | |
|   status = ares_expand_name(aptr, abuf, alen, &name, &len);
 | |
|   if (status != ARES_SUCCESS)
 | |
|     return NULL;
 | |
|   aptr += len;
 | |
| 
 | |
|   /* Make sure there's enough data after the name for the fixed part
 | |
|    * of the question.
 | |
|    */
 | |
|   if (aptr + QFIXEDSZ > abuf + alen)
 | |
|     {
 | |
|       free(name);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* Parse the question type and class. */
 | |
|   type = DNS_QUESTION_TYPE(aptr);
 | |
|   dnsclass = DNS_QUESTION_CLASS(aptr);
 | |
|   aptr += QFIXEDSZ;
 | |
| 
 | |
|   /* Display the question, in a format sort of similar to how we will
 | |
|    * display RRs.
 | |
|    */
 | |
|   printf("\t%-15s.\t", name);
 | |
|   if (dnsclass != C_IN)
 | |
|     printf("\t%s", class_name(dnsclass));
 | |
|   printf("\t%s\n", type_name(type));
 | |
|   free(name);
 | |
|   return aptr;
 | |
| }
 | |
| 
 | |
| static const unsigned char *display_rr(const unsigned char *aptr,
 | |
| 				       const unsigned char *abuf, int alen)
 | |
| {
 | |
|   const unsigned char *p;
 | |
|   char *name;
 | |
|   int type, dnsclass, ttl, dlen, status, len;
 | |
|   struct in_addr addr;
 | |
| 
 | |
|   /* Parse the RR name. */
 | |
|   status = ares_expand_name(aptr, abuf, alen, &name, &len);
 | |
|   if (status != ARES_SUCCESS)
 | |
|     return NULL;
 | |
|   aptr += len;
 | |
| 
 | |
|   /* Make sure there is enough data after the RR name for the fixed
 | |
|    * part of the RR.
 | |
|    */
 | |
|   if (aptr + RRFIXEDSZ > abuf + alen)
 | |
|     {
 | |
|       free(name);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* Parse the fixed part of the RR, and advance to the RR data
 | |
|    * field. */
 | |
|   type = DNS_RR_TYPE(aptr);
 | |
|   dnsclass = DNS_RR_CLASS(aptr);
 | |
|   ttl = DNS_RR_TTL(aptr);
 | |
|   dlen = DNS_RR_LEN(aptr);
 | |
|   aptr += RRFIXEDSZ;
 | |
|   if (aptr + dlen > abuf + alen)
 | |
|     {
 | |
|       free(name);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* Display the RR name, class, and type. */
 | |
|   printf("\t%-15s.\t%d", name, ttl);
 | |
|   if (dnsclass != C_IN)
 | |
|     printf("\t%s", class_name(dnsclass));
 | |
|   printf("\t%s", type_name(type));
 | |
|   free(name);
 | |
| 
 | |
|   /* Display the RR data.  Don't touch aptr. */
 | |
|   switch (type)
 | |
|     {
 | |
|     case T_CNAME:
 | |
|     case T_MB:
 | |
|     case T_MD:
 | |
|     case T_MF:
 | |
|     case T_MG:
 | |
|     case T_MR:
 | |
|     case T_NS:
 | |
|     case T_PTR:
 | |
|       /* For these types, the RR data is just a domain name. */
 | |
|       status = ares_expand_name(aptr, abuf, alen, &name, &len);
 | |
|       if (status != ARES_SUCCESS)
 | |
| 	return NULL;
 | |
|       printf("\t%s.", name);
 | |
|       free(name);
 | |
|       break;
 | |
| 
 | |
|     case T_HINFO:
 | |
|       /* The RR data is two length-counted character strings. */
 | |
|       p = aptr;
 | |
|       len = *p;
 | |
|       if (p + len + 1 > aptr + dlen)
 | |
| 	return NULL;
 | |
|       printf("\t%.*s", len, p + 1);
 | |
|       p += len + 1;
 | |
|       len = *p;
 | |
|       if (p + len + 1 > aptr + dlen)
 | |
| 	return NULL;
 | |
|       printf("\t%.*s", len, p + 1);
 | |
|       break;
 | |
| 
 | |
|     case T_MINFO:
 | |
|       /* The RR data is two domain names. */
 | |
|       p = aptr;
 | |
|       status = ares_expand_name(p, abuf, alen, &name, &len);
 | |
|       if (status != ARES_SUCCESS)
 | |
| 	return NULL;
 | |
|       printf("\t%s.", name);
 | |
|       free(name);
 | |
|       p += len;
 | |
|       status = ares_expand_name(p, abuf, alen, &name, &len);
 | |
|       if (status != ARES_SUCCESS)
 | |
| 	return NULL;
 | |
|       printf("\t%s.", name);
 | |
|       free(name);
 | |
|       break;
 | |
| 
 | |
|     case T_MX:
 | |
|       /* The RR data is two bytes giving a preference ordering, and
 | |
|        * then a domain name.
 | |
|        */
 | |
|       if (dlen < 2)
 | |
| 	return NULL;
 | |
|       printf("\t%d", (aptr[0] << 8) | aptr[1]);
 | |
|       status = ares_expand_name(aptr + 2, abuf, alen, &name, &len);
 | |
|       if (status != ARES_SUCCESS)
 | |
| 	return NULL;
 | |
|       printf("\t%s.", name);
 | |
|       free(name);
 | |
|       break;
 | |
| 
 | |
|     case T_SOA:
 | |
|       /* The RR data is two domain names and then five four-byte
 | |
|        * numbers giving the serial number and some timeouts.
 | |
|        */
 | |
|       p = aptr;
 | |
|       status = ares_expand_name(p, abuf, alen, &name, &len);
 | |
|       if (status != ARES_SUCCESS)
 | |
| 	return NULL;
 | |
|       printf("\t%s.\n", name);
 | |
|       free(name);
 | |
|       p += len;
 | |
|       status = ares_expand_name(p, abuf, alen, &name, &len);
 | |
|       if (status != ARES_SUCCESS)
 | |
| 	return NULL;
 | |
|       printf("\t\t\t\t\t\t%s.\n", name);
 | |
|       free(name);
 | |
|       p += len;
 | |
|       if (p + 20 > aptr + dlen)
 | |
| 	return NULL;
 | |
|       printf("\t\t\t\t\t\t( %d %d %d %d %d )",
 | |
| 	     (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3],
 | |
| 	     (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7],
 | |
| 	     (p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11],
 | |
| 	     (p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15],
 | |
| 	     (p[16] << 24) | (p[17] << 16) | (p[18] << 8) | p[19]);
 | |
|       break;
 | |
| 
 | |
|     case T_TXT:
 | |
|       /* The RR data is one or more length-counted character
 | |
|        * strings. */
 | |
|       p = aptr;
 | |
|       while (p < aptr + dlen)
 | |
| 	{
 | |
| 	  len = *p;
 | |
| 	  if (p + len + 1 > aptr + dlen)
 | |
| 	    return NULL;
 | |
| 	  printf("\t%.*s", len, p + 1);
 | |
| 	  p += len + 1;
 | |
| 	}
 | |
|       break;
 | |
| 
 | |
|     case T_A:
 | |
|       /* The RR data is a four-byte Internet address. */
 | |
|       if (dlen != 4)
 | |
| 	return NULL;
 | |
|       memcpy(&addr, aptr, sizeof(struct in_addr));
 | |
|       printf("\t%s", inet_ntoa(addr));
 | |
|       break;
 | |
| 
 | |
|     case T_WKS:
 | |
|       /* Not implemented yet */
 | |
|       break;
 | |
| 
 | |
|     case T_SRV:
 | |
|       /* The RR data is three two-byte numbers representing the
 | |
|        * priority, weight, and port, followed by a domain name.
 | |
|        */
 | |
|       
 | |
|       printf("\t%d", DNS__16BIT(aptr));
 | |
|       printf(" %d", DNS__16BIT(aptr + 2));
 | |
|       printf(" %d", DNS__16BIT(aptr + 4));
 | |
|       
 | |
|       status = ares_expand_name(aptr + 6, abuf, alen, &name, &len);
 | |
|       if (status != ARES_SUCCESS)
 | |
|         return NULL;
 | |
|       printf("\t%s.", name);
 | |
|       free(name);
 | |
|       break;
 | |
|       
 | |
|     default:
 | |
|       printf("\t[Unknown RR; cannot parse]");
 | |
|     }
 | |
|   printf("\n");
 | |
| 
 | |
|   return aptr + dlen;
 | |
| }
 | |
| 
 | |
| static const char *type_name(int type)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < ntypes; i++)
 | |
|     {
 | |
|       if (types[i].value == type)
 | |
| 	return types[i].name;
 | |
|     }
 | |
|   return "(unknown)";
 | |
| }
 | |
| 
 | |
| static const char *class_name(int dnsclass)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < nclasses; i++)
 | |
|     {
 | |
|       if (classes[i].value == dnsclass)
 | |
| 	return classes[i].name;
 | |
|     }
 | |
|   return "(unknown)";
 | |
| }
 | |
| 
 | |
| static void usage(void)
 | |
| {
 | |
|   fprintf(stderr, "usage: adig [-f flag] [-s server] [-c class] "
 | |
| 	  "[-t type] [-p port] name ...\n");
 | |
|   exit(1);
 | |
| }
 | 
